Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10df776bca | |||
| baea28c604 | |||
| 10b7c15dc7 | |||
| 8ddeb97c7e | |||
| 5151ce3040 | |||
| 5528d6812b | |||
| 72e6be8395 | |||
| f30726a9a7 | |||
| 13087b15bc | |||
| 67c359dd2b |
@@ -28,4 +28,14 @@ public class WxCorpConfig {
|
|||||||
* 审批回调URL(可选)
|
* 审批回调URL(可选)
|
||||||
*/
|
*/
|
||||||
private String callbackUrl;
|
private String callbackUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批申请人用户ID(提交审批的企微用户)
|
||||||
|
*/
|
||||||
|
private String creatorUserid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访客预约应用AgentId(用于发送应用消息)
|
||||||
|
*/
|
||||||
|
private String agentid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ package com.example.mini_program.controller;
|
|||||||
import com.example.mini_program.common.Result;
|
import com.example.mini_program.common.Result;
|
||||||
import com.example.mini_program.entity.VisitApplication;
|
import com.example.mini_program.entity.VisitApplication;
|
||||||
import com.example.mini_program.service.AppointmentService;
|
import com.example.mini_program.service.AppointmentService;
|
||||||
|
import com.example.mini_program.entity.ReceptionDepartmentPo;
|
||||||
import com.example.mini_program.vo.ReceptionPersonVo;
|
import com.example.mini_program.vo.ReceptionPersonVo;
|
||||||
import com.example.mini_program.vo.VisitApplicationVo;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -14,6 +16,7 @@ import java.util.List;
|
|||||||
@RequestMapping("/api/wx-mini/appointment")
|
@RequestMapping("/api/wx-mini/appointment")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class AppointmentController {
|
public class AppointmentController {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(VisitorApprovalController.class);
|
||||||
|
|
||||||
private final AppointmentService appointmentService;
|
private final AppointmentService appointmentService;
|
||||||
|
|
||||||
@@ -34,6 +37,7 @@ public class AppointmentController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/detail")
|
@GetMapping("/detail")
|
||||||
public Result<VisitApplication> getDetail(@RequestParam String id) {
|
public Result<VisitApplication> getDetail(@RequestParam String id) {
|
||||||
|
log.info("获取预约记录详情, id={}", id);
|
||||||
if (id == null || id.trim().isEmpty()) {
|
if (id == null || id.trim().isEmpty()) {
|
||||||
return Result.error("id不能为空");
|
return Result.error("id不能为空");
|
||||||
}
|
}
|
||||||
@@ -102,30 +106,21 @@ public class AppointmentController {
|
|||||||
return success ? Result.success(true) : Result.error("审批失败,记录不存在或状态不允许");
|
return success ? Result.success(true) : Result.error("审批失败,记录不存在或状态不允许");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据id获取审批详情
|
* 被访人下拉框值
|
||||||
*/
|
|
||||||
// @PutMapping("/detail")
|
|
||||||
// public Result<VisitApplicationVo> detail(@RequestParam String id, @RequestParam String openid) throws Exception {
|
|
||||||
// if (id == null || id.trim().isEmpty()) {
|
|
||||||
// return Result.error("id不能为空");
|
|
||||||
// }
|
|
||||||
// if (openid == null || openid.trim().isEmpty()) {
|
|
||||||
// return Result.error("openid不能为空");
|
|
||||||
// }
|
|
||||||
// VisitApplicationVo vo = appointmentService.detail(id, openid);
|
|
||||||
// return Result.success(vo);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 受访人下拉框值
|
|
||||||
*/
|
*/
|
||||||
@GetMapping("/person/selector")
|
@GetMapping("/person/selector")
|
||||||
public Result<List<ReceptionPersonVo>> personSelector(@RequestParam String department) {
|
public Result<List<ReceptionPersonVo>> personSelector(@RequestParam String department) {
|
||||||
System.out.println("----------------------------------");
|
|
||||||
// 异步处理,立即返回
|
|
||||||
List<ReceptionPersonVo> list = appointmentService.personSelector(department);
|
List<ReceptionPersonVo> list = appointmentService.personSelector(department);
|
||||||
return Result.success(list);
|
return Result.success(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 被访区域下拉框值
|
||||||
|
*/
|
||||||
|
@GetMapping("/department/selector")
|
||||||
|
public Result<List<ReceptionDepartmentPo>> departmentSelector() {
|
||||||
|
List<ReceptionDepartmentPo> list = appointmentService.departmentSelector();
|
||||||
|
return Result.success(list);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.example.mini_program.controller;
|
package com.example.mini_program.controller;
|
||||||
|
|
||||||
|
import com.example.mini_program.common.Result;
|
||||||
import com.example.mini_program.service.VisitorApprovalService;
|
import com.example.mini_program.service.VisitorApprovalService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -16,6 +17,27 @@ public class VisitorApprovalController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private VisitorApprovalService visitorApprovalService;
|
private VisitorApprovalService visitorApprovalService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知受访者访客已到达
|
||||||
|
*
|
||||||
|
* @param id 预约记录ID
|
||||||
|
* @return 操作结果
|
||||||
|
*/
|
||||||
|
@GetMapping("/notify-host")
|
||||||
|
public Result<String> notifyHostArrival(@RequestParam String id) {
|
||||||
|
log.info("通知受访者访客已到达, id={}", id);
|
||||||
|
if (id == null || id.trim().isEmpty()) {
|
||||||
|
return Result.error("预约记录ID不能为空");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
visitorApprovalService.notifyHostArrival(id);
|
||||||
|
return Result.success("通知发送成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("通知发送失败, id={}", id, e);
|
||||||
|
return Result.error("通知发送失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证URL有效性(GET请求)
|
* 验证URL有效性(GET请求)
|
||||||
*/
|
*/
|
||||||
@@ -33,9 +55,10 @@ public class VisitorApprovalController {
|
|||||||
@RequestParam(value = "timestamp", required = false) String timestamp,
|
@RequestParam(value = "timestamp", required = false) String timestamp,
|
||||||
@RequestParam(value = "nonce", required = false) String nonce,
|
@RequestParam(value = "nonce", required = false) String nonce,
|
||||||
@RequestBody String requestBody) {
|
@RequestBody String requestBody) {
|
||||||
System.out.println("----------------------------------");
|
|
||||||
// 异步处理,立即返回
|
// 异步处理,立即返回
|
||||||
visitorApprovalService.processApprovalAsync(msgSignature, timestamp, nonce, requestBody);
|
visitorApprovalService.processApprovalAsync(msgSignature, timestamp, nonce, requestBody);
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.example.mini_program.entity;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ReceptionDepartmentPo {
|
||||||
|
|
||||||
|
private String departmentCode;
|
||||||
|
private String departmentName;
|
||||||
|
}
|
||||||
@@ -9,4 +9,5 @@ public class ReceptionPersonPO {
|
|||||||
private String personId;
|
private String personId;
|
||||||
private String personName;
|
private String personName;
|
||||||
private String department;
|
private String department;
|
||||||
|
private String departmentName;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,13 @@ public class VisitApplication {
|
|||||||
private String visitTime;
|
private String visitTime;
|
||||||
|
|
||||||
private String hostName;
|
private String hostName;
|
||||||
|
private String personId;
|
||||||
private String area;
|
private String area;
|
||||||
private String status;
|
private String status;
|
||||||
private String statusText;
|
private String statusText;
|
||||||
private String openid;
|
private String openid;
|
||||||
private String createTime;
|
private String createTime;
|
||||||
private String spNo;
|
private String spNo;
|
||||||
|
private String checkStatus;
|
||||||
|
private String plateNumber;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.example.mini_program.mapper;
|
package com.example.mini_program.mapper;
|
||||||
|
|
||||||
|
import com.example.mini_program.entity.ReceptionDepartmentPo;
|
||||||
import com.example.mini_program.entity.ReceptionPersonPO;
|
import com.example.mini_program.entity.ReceptionPersonPO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
@@ -9,4 +10,6 @@ import java.util.List;
|
|||||||
@Mapper
|
@Mapper
|
||||||
public interface ReceptionPersonMapper {
|
public interface ReceptionPersonMapper {
|
||||||
List<ReceptionPersonPO> selectReceptionPerson(@Param("department") String department);
|
List<ReceptionPersonPO> selectReceptionPerson(@Param("department") String department);
|
||||||
|
|
||||||
|
List<ReceptionDepartmentPo> selectReceptionDepartment();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,10 +71,20 @@ public interface VisitApplicationMapper {
|
|||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
*/
|
*/
|
||||||
int updateStatus(@Param("id") String id, @Param("status") String status, @Param("statusText") String statusText);
|
int updateStatus(@Param("id") String id, @Param("status") String status, @Param("statusText") String statusText);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID更新预约记录的核验状态
|
||||||
|
*
|
||||||
|
* @param id 预约记录ID
|
||||||
|
* @param checkStatus 核验状态
|
||||||
|
*/
|
||||||
|
void updateCheckStatusById(@Param("id") String id, @Param("checkStatus") String checkStatus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据审批单号更新审批状态
|
* 根据审批单号更新审批状态
|
||||||
*
|
*
|
||||||
* @param spNo 审批编号
|
* @param spNo 审批编号
|
||||||
* @param status 状态值
|
* @param status 状态值
|
||||||
* @param statusText 状态文本
|
* @param statusText 状态文本
|
||||||
* @return 影响行数
|
* @return 影响行数
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.example.mini_program.entity.ReceptionPersonPO;
|
|||||||
import com.example.mini_program.entity.VisitApplication;
|
import com.example.mini_program.entity.VisitApplication;
|
||||||
import com.example.mini_program.mapper.ReceptionPersonMapper;
|
import com.example.mini_program.mapper.ReceptionPersonMapper;
|
||||||
import com.example.mini_program.mapper.VisitApplicationMapper;
|
import com.example.mini_program.mapper.VisitApplicationMapper;
|
||||||
|
import com.example.mini_program.entity.ReceptionDepartmentPo;
|
||||||
import com.example.mini_program.vo.ReceptionPersonVo;
|
import com.example.mini_program.vo.ReceptionPersonVo;
|
||||||
import com.example.mini_program.vo.VisitApplicationVo;
|
import com.example.mini_program.vo.VisitApplicationVo;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -31,12 +32,12 @@ public class AppointmentService {
|
|||||||
private final WxApprovalService wxApprovalService;
|
private final WxApprovalService wxApprovalService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ReceptionPersonMapper receptionPersonMapper;
|
private ReceptionPersonMapper receptionPersonMapper;
|
||||||
@Autowired
|
|
||||||
private final WxSubscribeMessageService wxSubscribeMessageService;
|
|
||||||
|
|
||||||
@Value("${wx.corp.creator-userid:}")
|
@Value("${wx.corp.creator-userid:}")
|
||||||
private String creatorUserId;
|
private String creatorUserId;
|
||||||
|
|
||||||
|
@Value("${wx.corp.approval-userid:}")
|
||||||
|
private String approvalUserId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据openid获取最新的一条预约记录
|
* 根据openid获取最新的一条预约记录
|
||||||
*/
|
*/
|
||||||
@@ -44,9 +45,9 @@ public class AppointmentService {
|
|||||||
log.info("查询用户最新预约记录, openid: {}", openid);
|
log.info("查询用户最新预约记录, openid: {}", openid);
|
||||||
VisitApplication result = visitApplicationMapper.selectLatestByOpenid(openid);
|
VisitApplication result = visitApplicationMapper.selectLatestByOpenid(openid);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
log.info("找到预约记录, id: {}", result.getId());
|
log.info("找到用户最新预约记录, id: {}", result.getId());
|
||||||
} else {
|
} else {
|
||||||
log.info("未找到预约记录");
|
log.info("未找到用户最新预约记录");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -60,8 +61,10 @@ public class AppointmentService {
|
|||||||
log.info("查询到 {} 条预约记录", list.size());
|
log.info("查询到 {} 条预约记录", list.size());
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
public VisitApplication getDetail(String id) {
|
|
||||||
return visitApplicationMapper.selectById(id);
|
public VisitApplication getDetail(String id) {
|
||||||
|
VisitApplication v = visitApplicationMapper.selectById(id);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,16 +74,9 @@ public class AppointmentService {
|
|||||||
record.setId(UUID.randomUUID().toString().replace("-", ""));
|
record.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||||
record.setStatus("pending");
|
record.setStatus("pending");
|
||||||
record.setStatusText("待审核");
|
record.setStatusText("待审核");
|
||||||
|
|
||||||
// 发起企业微信审批
|
// 发起企业微信审批
|
||||||
try {
|
try {
|
||||||
String visitTime = record.getVisitDate();
|
String spNo = wxApprovalService.submitApproval(creatorUserId, approvalUserId, record);
|
||||||
if (record.getVisitTime() != null && !record.getVisitTime().isEmpty()) {
|
|
||||||
visitTime = record.getVisitDate() + " " + record.getVisitTime();
|
|
||||||
}
|
|
||||||
String spNo = wxApprovalService.submitApproval(creatorUserId, record.getName(), record.getPhone(),
|
|
||||||
record.getCompany(), record.getReason(), visitTime, record.getHostName(), record.getArea()
|
|
||||||
);
|
|
||||||
record.setSpNo(spNo);
|
record.setSpNo(spNo);
|
||||||
log.info("企业微信审批提交成功, spNo: {}", spNo);
|
log.info("企业微信审批提交成功, spNo: {}", spNo);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -89,16 +85,6 @@ public class AppointmentService {
|
|||||||
|
|
||||||
visitApplicationMapper.insert(record);
|
visitApplicationMapper.insert(record);
|
||||||
log.info("创建预约成功, id={}", record.getId());
|
log.info("创建预约成功, id={}", record.getId());
|
||||||
|
|
||||||
// // 推送订阅消息
|
|
||||||
// try {
|
|
||||||
// wxSubscribeMessageService.sendSubscribeMessage(
|
|
||||||
// record.getOpenid(), record.getName(), record.getReason(),
|
|
||||||
// formatVisitTime(record), record.getArea(), "待审核");
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("订阅消息推送失败", e);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,12 +154,12 @@ public class AppointmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取受访者下拉框的值
|
* 获取被访者下拉框的值
|
||||||
*/
|
*/
|
||||||
public List<ReceptionPersonVo> personSelector(String department) {
|
public List<ReceptionPersonVo> personSelector(String department) {
|
||||||
List<ReceptionPersonPO> receiptPersonList = receptionPersonMapper.selectReceptionPerson(department);
|
List<ReceptionPersonPO> receiptPersonList = receptionPersonMapper.selectReceptionPerson(department);
|
||||||
List<ReceptionPersonVo> voList = new ArrayList<>();
|
List<ReceptionPersonVo> voList = new ArrayList<>();
|
||||||
for(ReceptionPersonPO po : receiptPersonList){
|
for (ReceptionPersonPO po : receiptPersonList) {
|
||||||
ReceptionPersonVo vo = new ReceptionPersonVo();
|
ReceptionPersonVo vo = new ReceptionPersonVo();
|
||||||
BeanUtils.copyProperties(po, vo);
|
BeanUtils.copyProperties(po, vo);
|
||||||
voList.add(vo);
|
voList.add(vo);
|
||||||
@@ -181,4 +167,11 @@ public class AppointmentService {
|
|||||||
return voList;
|
return voList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取被访区域下拉框的值
|
||||||
|
*/
|
||||||
|
public List<ReceptionDepartmentPo> departmentSelector() {
|
||||||
|
return receptionPersonMapper.selectReceptionDepartment();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
package com.example.mini_program.service;
|
package com.example.mini_program.service;
|
||||||
|
|
||||||
import com.example.mini_program.aes.WXBizMsgCrypt;
|
import com.example.mini_program.aes.WXBizMsgCrypt;
|
||||||
|
import com.example.mini_program.config.WxCorpConfig;
|
||||||
import com.example.mini_program.entity.VisitApplication;
|
import com.example.mini_program.entity.VisitApplication;
|
||||||
import com.example.mini_program.mapper.VisitApplicationMapper;
|
import com.example.mini_program.mapper.VisitApplicationMapper;
|
||||||
|
import com.example.mini_program.util.HttpUtil;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.json.XML;
|
import org.json.XML;
|
||||||
@@ -11,12 +14,17 @@ import org.springframework.beans.factory.annotation.Value;
|
|||||||
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.scheduling.annotation.Async;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
public class VisitorApprovalService {
|
public class VisitorApprovalService {
|
||||||
|
|
||||||
|
private static final String SEND_MSG_URL =
|
||||||
|
"https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s";
|
||||||
|
|
||||||
@Value("${wx.corp.corpid:}")
|
@Value("${wx.corp.corpid:}")
|
||||||
private String corpId;
|
private String corpId;
|
||||||
|
|
||||||
@@ -33,6 +41,12 @@ public class VisitorApprovalService {
|
|||||||
private VisitApplicationMapper visitApplicationMapper;
|
private VisitApplicationMapper visitApplicationMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private WxSubscribeMessageService wxSubscribeMessageService;
|
private WxSubscribeMessageService wxSubscribeMessageService;
|
||||||
|
@Autowired
|
||||||
|
private WxCorpConfig wxCorpConfig;
|
||||||
|
@Autowired
|
||||||
|
private WxApprovalService wxApprovalService;
|
||||||
|
@Autowired
|
||||||
|
private ObjectMapper objectMapper;
|
||||||
|
|
||||||
public String verifyUrl(String msgSignature, String timestamp, String nonce, String echostr) {
|
public String verifyUrl(String msgSignature, String timestamp, String nonce, String echostr) {
|
||||||
log.info("URL验证请求: msgSignature={}, timestamp={}, nonce={}", msgSignature, timestamp, nonce);
|
log.info("URL验证请求: msgSignature={}, timestamp={}, nonce={}", msgSignature, timestamp, nonce);
|
||||||
@@ -91,8 +105,64 @@ public class VisitorApprovalService {
|
|||||||
private String formatVisitTime(VisitApplication record) {
|
private String formatVisitTime(VisitApplication record) {
|
||||||
String time = record.getVisitDate();
|
String time = record.getVisitDate();
|
||||||
if (record.getVisitTime() != null && !record.getVisitTime().isEmpty()) {
|
if (record.getVisitTime() != null && !record.getVisitTime().isEmpty()) {
|
||||||
time = record.getVisitDate() + " " + record.getVisitTime();
|
time = record.getVisitDate() + " " + record.getVisitTime().split("-")[0]+":00";
|
||||||
}
|
}
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知受访者访客已到达 核销记录
|
||||||
|
* 根据预约ID查询企微用户ID,发送应用消息
|
||||||
|
*
|
||||||
|
* @param id 预约记录ID
|
||||||
|
*/
|
||||||
|
public void notifyHostArrival(String id) {
|
||||||
|
|
||||||
|
VisitApplication record = visitApplicationMapper.selectById(id);
|
||||||
|
if (record == null) {
|
||||||
|
log.warn("【通知受访者访客已到达】未找到预约记录, id={}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ("1".equals(record.getCheckStatus())) {
|
||||||
|
log.warn("【通知受访者访客已到达】访客预约记录已核销, id={}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
visitApplicationMapper.updateCheckStatusById(id, "1");
|
||||||
|
//受访者企微id
|
||||||
|
String receptionPersonId = record.getPersonId();
|
||||||
|
if (receptionPersonId == null || receptionPersonId.isEmpty()) {
|
||||||
|
log.warn("通知受访者访客已到达】受访者ID为空, id={}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String message = String.format("访客【%s】已到达,访问区域:【%s】,来访事由:【%s】",
|
||||||
|
record.getName(), record.getArea(), record.getReason());
|
||||||
|
sendMessageToUser(receptionPersonId, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送应用消息给企微用户
|
||||||
|
*/
|
||||||
|
private void sendMessageToUser(String userId, String content) {
|
||||||
|
try {
|
||||||
|
String accessToken = wxApprovalService.getAccessToken();
|
||||||
|
String url = String.format(SEND_MSG_URL, accessToken);
|
||||||
|
|
||||||
|
Map<String, Object> body = new HashMap<>();
|
||||||
|
body.put("touser", userId);
|
||||||
|
body.put("msgtype", "text");
|
||||||
|
body.put("agentid", wxCorpConfig.getAgentid());
|
||||||
|
body.put("text", Map.of("content", content));
|
||||||
|
|
||||||
|
String json = objectMapper.writeValueAsString(body);
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Map<String, Object> resp = objectMapper.readValue(HttpUtil.postJson(url, json), Map.class);
|
||||||
|
if ("0".equals(String.valueOf(resp.get("errcode")))) {
|
||||||
|
log.info("应用消息发送成功, userId={}", userId);
|
||||||
|
} else {
|
||||||
|
log.error("应用消息发送失败: {}", resp);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("发送应用消息异常, userId={}", userId, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,13 @@
|
|||||||
package com.example.mini_program.service;
|
package com.example.mini_program.service;
|
||||||
|
|
||||||
import com.example.mini_program.config.WxCorpConfig;
|
import com.example.mini_program.config.WxCorpConfig;
|
||||||
|
import com.example.mini_program.entity.VisitApplication;
|
||||||
import com.example.mini_program.util.HttpUtil;
|
import com.example.mini_program.util.HttpUtil;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.Data;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZoneId;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -35,7 +32,9 @@ public class WxApprovalService {
|
|||||||
private final WxCorpConfig wxCorpConfig;
|
private final WxCorpConfig wxCorpConfig;
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
/** 获取企业微信 access_token */
|
/**
|
||||||
|
* 获取企业微信 access_token
|
||||||
|
*/
|
||||||
public String getAccessToken() {
|
public String getAccessToken() {
|
||||||
String url = String.format(GET_TOKEN_URL, wxCorpConfig.getCorpid(), wxCorpConfig.getCorpsecret());
|
String url = String.format(GET_TOKEN_URL, wxCorpConfig.getCorpid(), wxCorpConfig.getCorpsecret());
|
||||||
try {
|
try {
|
||||||
@@ -49,19 +48,21 @@ public class WxApprovalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 提交审批申请 */
|
/**
|
||||||
public String submitApproval(String creatorUserId, String visitorName, String visitorPhone,
|
* 提交审批申请
|
||||||
String visitorCompany, String visitPurpose, String visitTime,
|
*/
|
||||||
String visiteeName, String visitArea) {
|
public String submitApproval(String creatorUserId, String approvalUserId, VisitApplication record) {
|
||||||
String url = String.format(SUBMIT_URL, getAccessToken());
|
String url = String.format(SUBMIT_URL, getAccessToken());
|
||||||
|
|
||||||
Map<String, Object> body = new HashMap<>();
|
Map<String, Object> body = new HashMap<>();
|
||||||
body.put("creator_userid", creatorUserId);
|
body.put("creator_userid", creatorUserId);
|
||||||
body.put("template_id", wxCorpConfig.getApprovalTemplateId());
|
body.put("template_id", wxCorpConfig.getApprovalTemplateId());
|
||||||
body.put("use_template_approver", 1);
|
body.put("use_template_approver", 0);
|
||||||
body.put("apply_data", buildApplyData(visitorName, visitorPhone, visitorCompany,
|
body.put("approver", List.of(
|
||||||
visitPurpose, visitTime, visiteeName, visitArea));
|
Map.of("attr", 1, "userid", List.of(approvalUserId))
|
||||||
body.put("summary_list", buildSummaryList(visitorName, visitPurpose));
|
));
|
||||||
|
body.put("apply_data", buildApplyData(record));
|
||||||
|
body.put("summary_list", buildSummaryList(record.getName(), record.getReason()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String json = objectMapper.writeValueAsString(body);
|
String json = objectMapper.writeValueAsString(body);
|
||||||
@@ -77,42 +78,21 @@ public class WxApprovalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 查询审批状态 */
|
|
||||||
public ApprovalStatus getApprovalStatus(String spNo) {
|
|
||||||
String url = String.format(DETAIL_URL, getAccessToken());
|
|
||||||
try {
|
|
||||||
String json = objectMapper.writeValueAsString(Map.of("sp_no", spNo));
|
|
||||||
Map<String, Object> resp = objectMapper.readValue(HttpUtil.postJson(url, json), Map.class);
|
|
||||||
if ("0".equals(String.valueOf(resp.get("errcode")))) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> info = (Map<String, Object>) resp.get("info");
|
|
||||||
ApprovalStatus status = new ApprovalStatus();
|
|
||||||
if (info != null) {
|
|
||||||
status.setSpNo((String) info.get("sp_no"));
|
|
||||||
status.setSpStatus((Integer) info.get("sp_status"));
|
|
||||||
status.setSpStatusText(toStatusText((Integer) info.get("sp_status")));
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
throw new RuntimeException("审批状态查询失败: " + resp);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("查询审批状态异常: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- 内部方法 ----
|
// ---- 内部方法 ----
|
||||||
|
private Map<String, Object> buildApplyData(VisitApplication record) {
|
||||||
private Map<String, Object> buildApplyData(String visitorName, String visitorPhone,
|
|
||||||
String visitorCompany, String visitPurpose,
|
|
||||||
String visitTime, String visiteeName, String visitArea) {
|
|
||||||
List<Map<String, Object>> contents = new ArrayList<>();
|
List<Map<String, Object>> contents = new ArrayList<>();
|
||||||
contents.add(textControl("Text-1776786661954", visitorName));
|
contents.add(textControl("Text-1778484376383", record.getName()));
|
||||||
contents.add(textControl("Text-1776786666351", visitorPhone));
|
contents.add(textControl("Text-1778484403871", record.getPhone()));
|
||||||
contents.add(textControl("Text-1776786668098", visitorCompany));
|
contents.add(textControl("Text-1778484415326", record.getCompany()));
|
||||||
contents.add(textControl("Text-1776786672408", visitPurpose));
|
contents.add(textControl("Text-1778484463818", record.getReason()));
|
||||||
contents.add(dateControl("Date-1776786680089", visitTime));
|
contents.add(textControl("Text-1778484492597", record.getPlateNumber()));
|
||||||
contents.add(textControl("Text-1776786690968", visiteeName));
|
String visitTime = record.getVisitDate();
|
||||||
contents.add(textControl("Text-1776786692400", visitArea));
|
if (record.getVisitTime() != null && !record.getVisitTime().isEmpty()) {
|
||||||
|
visitTime = record.getVisitDate() + " " + record.getVisitTime();
|
||||||
|
}
|
||||||
|
contents.add(textControl("Text-1778484505176", visitTime));
|
||||||
|
contents.add(textControl("Text-1778484515357", record.getHostName()));
|
||||||
|
contents.add(textControl("Text-1778484525261", record.getArea()));
|
||||||
return Map.of("contents", contents);
|
return Map.of("contents", contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,21 +101,6 @@ public class WxApprovalService {
|
|||||||
"value", Map.of("text", value != null ? value : ""));
|
"value", Map.of("text", value != null ? value : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> dateControl(String id, String dateTime) {
|
|
||||||
long timestamp = 0;
|
|
||||||
if (dateTime != null && !dateTime.isEmpty()) {
|
|
||||||
try {
|
|
||||||
timestamp = LocalDateTime.parse(dateTime,
|
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
|
||||||
.atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("日期解析失败: {}", dateTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Map.of("control", "Date", "id", id,
|
|
||||||
"value", Map.of("date", Map.of("type", "hour", "s_timestamp", String.valueOf(timestamp))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Map<String, Object>> buildSummaryList(String visitorName, String visitPurpose) {
|
private List<Map<String, Object>> buildSummaryList(String visitorName, String visitPurpose) {
|
||||||
return List.of(
|
return List.of(
|
||||||
Map.of("summary_info", List.of(
|
Map.of("summary_info", List.of(
|
||||||
@@ -144,22 +109,4 @@ public class WxApprovalService {
|
|||||||
Map.of("text", "目的: " + (visitPurpose != null ? visitPurpose : ""), "lang", "zh_CN")))
|
Map.of("text", "目的: " + (visitPurpose != null ? visitPurpose : ""), "lang", "zh_CN")))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toStatusText(Integer spStatus) {
|
|
||||||
if (spStatus == null) return "未知";
|
|
||||||
return switch (spStatus) {
|
|
||||||
case 1 -> "审批中";
|
|
||||||
case 2 -> "已通过";
|
|
||||||
case 3 -> "已拒绝";
|
|
||||||
case 4 -> "已撤销";
|
|
||||||
default -> "未知状态";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class ApprovalStatus {
|
|
||||||
private String spNo;
|
|
||||||
private Integer spStatus;
|
|
||||||
private String spStatusText;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package com.example.mini_program.service;
|
package com.example.mini_program.service;
|
||||||
|
|
||||||
import com.example.mini_program.config.WxMiniAppConfig;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
@@ -17,23 +15,17 @@ import java.nio.charset.StandardCharsets;
|
|||||||
public class WxService {
|
public class WxService {
|
||||||
|
|
||||||
private static final String WXACODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
|
private static final String WXACODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";
|
||||||
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
|
|
||||||
private static final int TOKEN_EXPIRE_BUFFER = 300; // access_token提前过期时间(秒)
|
|
||||||
|
|
||||||
private final WxMiniAppConfig wxMiniAppConfig;
|
|
||||||
private final RestTemplate restTemplate;
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
private final WxTokenService wxTokenService;
|
||||||
|
|
||||||
|
@Getter
|
||||||
@Value("${wx.miniapp.env:release}")
|
@Value("${wx.miniapp.env:release}")
|
||||||
private String defaultEnvVersion;
|
private String defaultEnvVersion;
|
||||||
|
|
||||||
private String cachedAccessToken;
|
public WxService(WxTokenService wxTokenService) {
|
||||||
private long tokenExpireTime;
|
|
||||||
|
|
||||||
public WxService(WxMiniAppConfig wxMiniAppConfig, RestTemplate restTemplate) {
|
|
||||||
this.wxMiniAppConfig = wxMiniAppConfig;
|
|
||||||
this.restTemplate = restTemplate;
|
|
||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
|
this.wxTokenService = wxTokenService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -73,32 +65,11 @@ public class WxService {
|
|||||||
return java.util.Base64.getEncoder().encodeToString(response);
|
return java.util.Base64.getEncoder().encodeToString(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultEnvVersion() {
|
|
||||||
return defaultEnvVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取微信access_token(带缓存)
|
* 获取微信access_token(使用统一的TokenService)
|
||||||
*/
|
*/
|
||||||
private String getAccessToken() throws Exception {
|
private String getAccessToken() throws Exception {
|
||||||
if (cachedAccessToken != null && System.currentTimeMillis() < tokenExpireTime) {
|
return wxTokenService.getAccessToken();
|
||||||
return cachedAccessToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
String url = String.format(TOKEN_URL, wxMiniAppConfig.getAppid(), wxMiniAppConfig.getSecret());
|
|
||||||
log.info("刷新access_token");
|
|
||||||
|
|
||||||
String response = restTemplate.getForObject(url, String.class);
|
|
||||||
JsonNode json = objectMapper.readTree(response);
|
|
||||||
|
|
||||||
String accessToken = json.get("access_token").asText();
|
|
||||||
int expiresIn = json.get("expires_in").asInt();
|
|
||||||
|
|
||||||
cachedAccessToken = accessToken;
|
|
||||||
tokenExpireTime = System.currentTimeMillis() + (expiresIn - TOKEN_EXPIRE_BUFFER) * 1000L;
|
|
||||||
|
|
||||||
log.info("access_token已更新,有效期: {} 秒", expiresIn);
|
|
||||||
return accessToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.example.mini_program.service;
|
package com.example.mini_program.service;
|
||||||
|
|
||||||
import com.example.mini_program.config.WxMiniAppConfig;
|
|
||||||
import com.example.mini_program.util.HttpUtil;
|
import com.example.mini_program.util.HttpUtil;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@@ -27,33 +26,15 @@ import java.util.Map;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class WxSubscribeMessageService {
|
public class WxSubscribeMessageService {
|
||||||
|
|
||||||
private static final String GET_TOKEN_URL =
|
|
||||||
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
|
|
||||||
private static final String SEND_MSG_URL =
|
private static final String SEND_MSG_URL =
|
||||||
"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s";
|
"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=%s";
|
||||||
|
|
||||||
private final WxMiniAppConfig wxMiniAppConfig;
|
|
||||||
private final ObjectMapper objectMapper;
|
private final ObjectMapper objectMapper;
|
||||||
|
private final WxTokenService wxTokenService;
|
||||||
|
|
||||||
@Value("${wx.miniapp.subscribe-template-id:}")
|
@Value("${wx.miniapp.subscribe-template-id:}")
|
||||||
private String templateId;
|
private String templateId;
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取小程序 access_token
|
|
||||||
*/
|
|
||||||
public String getAccessToken() {
|
|
||||||
String url = String.format(GET_TOKEN_URL, wxMiniAppConfig.getAppid(), wxMiniAppConfig.getSecret());
|
|
||||||
try {
|
|
||||||
Map<String, Object> resp = objectMapper.readValue(HttpUtil.get(url), Map.class);
|
|
||||||
if (resp.containsKey("access_token")) {
|
|
||||||
return (String) resp.get("access_token");
|
|
||||||
}
|
|
||||||
throw new RuntimeException("获取access_token失败: " + resp);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("获取access_token异常: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送订阅消息
|
* 发送订阅消息
|
||||||
* 所有字段不允许为空字符串(微信返回47003),name类型不接受纯数字
|
* 所有字段不允许为空字符串(微信返回47003),name类型不接受纯数字
|
||||||
@@ -64,7 +45,7 @@ public class WxSubscribeMessageService {
|
|||||||
log.warn("未配置 subscribe-template-id,跳过订阅消息推送");
|
log.warn("未配置 subscribe-template-id,跳过订阅消息推送");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String accessToken = getAccessToken();
|
String accessToken = wxTokenService.getAccessToken();
|
||||||
String url = String.format(SEND_MSG_URL, accessToken);
|
String url = String.format(SEND_MSG_URL, accessToken);
|
||||||
|
|
||||||
// name1 是 name 类型,纯数字会被微信拒绝,需加前缀兜底
|
// name1 是 name 类型,纯数字会被微信拒绝,需加前缀兜底
|
||||||
@@ -87,7 +68,7 @@ public class WxSubscribeMessageService {
|
|||||||
body.put("page", "pages/records/records");
|
body.put("page", "pages/records/records");
|
||||||
body.put("data", data);
|
body.put("data", data);
|
||||||
//跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
|
//跳转小程序类型:developer为开发版;trial为体验版;formal为正式版;默认为正式版
|
||||||
body.put("miniprogram_state", "trial");
|
body.put("miniprogram_state", "formal");
|
||||||
body.put("lang", "zh_CN");
|
body.put("lang", "zh_CN");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.example.mini_program.service;
|
||||||
|
|
||||||
|
import com.example.mini_program.config.WxMiniAppConfig;
|
||||||
|
import com.example.mini_program.util.HttpUtil;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信 access_token 统一管理服务
|
||||||
|
* 支持标准接口和稳定版接口
|
||||||
|
* 提供缓存机制,避免重复获取
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class WxTokenService {
|
||||||
|
|
||||||
|
private static final String STANDARD_TOKEN_URL =
|
||||||
|
"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
|
||||||
|
private static final String STABLE_TOKEN_URL =
|
||||||
|
"https://api.weixin.qq.com/cgi-bin/stable_token";
|
||||||
|
|
||||||
|
private final WxMiniAppConfig wxMiniAppConfig;
|
||||||
|
private final ObjectMapper objectMapper;
|
||||||
|
|
||||||
|
@Value("${wx.miniapp.token-type:standard}")
|
||||||
|
private String tokenType; // standard 或 stable
|
||||||
|
|
||||||
|
@Value("${wx.miniapp.token-expire-buffer:300}")
|
||||||
|
private int tokenExpireBuffer; // 提前过期缓冲时间(秒)
|
||||||
|
|
||||||
|
// 缓存:key为appid,value为token信息和过期时间
|
||||||
|
private static class TokenCache {
|
||||||
|
String token;
|
||||||
|
long expireTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Map<String, TokenCache> tokenCacheMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取微信小程序 access_token
|
||||||
|
* @return access_token
|
||||||
|
*/
|
||||||
|
public String getAccessToken() {
|
||||||
|
String appid = wxMiniAppConfig.getAppid();
|
||||||
|
String secret = wxMiniAppConfig.getSecret();
|
||||||
|
|
||||||
|
// 检查缓存
|
||||||
|
TokenCache cache = tokenCacheMap.get(appid);
|
||||||
|
if (cache != null && System.currentTimeMillis() < cache.expireTime) {
|
||||||
|
log.debug("使用缓存的 access_token");
|
||||||
|
return cache.token;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新token
|
||||||
|
String accessToken;
|
||||||
|
int expiresIn;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ("stable".equalsIgnoreCase(tokenType)) {
|
||||||
|
log.info("使用稳定版接口获取 access_token");
|
||||||
|
Map<String, String> requestBody = Map.of(
|
||||||
|
"grant_type", "client_credential",
|
||||||
|
"appid", appid,
|
||||||
|
"secret", secret
|
||||||
|
);
|
||||||
|
String jsonBody = objectMapper.writeValueAsString(requestBody);
|
||||||
|
String response = HttpUtil.postJson(STABLE_TOKEN_URL, jsonBody);
|
||||||
|
Map<String, Object> resp = objectMapper.readValue(response, Map.class);
|
||||||
|
|
||||||
|
if (resp.containsKey("access_token")) {
|
||||||
|
accessToken = (String) resp.get("access_token");
|
||||||
|
expiresIn = (Integer) resp.get("expires_in");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("稳定版接口获取token失败: " + resp);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("使用标准接口获取 access_token");
|
||||||
|
String url = String.format(STANDARD_TOKEN_URL, appid, secret);
|
||||||
|
String response = HttpUtil.get(url);
|
||||||
|
Map<String, Object> resp = objectMapper.readValue(response, Map.class);
|
||||||
|
|
||||||
|
if (resp.containsKey("access_token")) {
|
||||||
|
accessToken = (String) resp.get("access_token");
|
||||||
|
expiresIn = (Integer) resp.get("expires_in");
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("标准接口获取token失败: " + resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新缓存
|
||||||
|
TokenCache newCache = new TokenCache();
|
||||||
|
newCache.token = accessToken;
|
||||||
|
newCache.expireTime = System.currentTimeMillis() + (expiresIn - tokenExpireBuffer) * 1000L;
|
||||||
|
tokenCacheMap.put(appid, newCache);
|
||||||
|
|
||||||
|
log.info("access_token 已更新,有效期: {} 秒,缓存过期时间: {} 秒后",
|
||||||
|
expiresIn, expiresIn - tokenExpireBuffer);
|
||||||
|
return accessToken;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取 access_token 异常", e);
|
||||||
|
throw new RuntimeException("获取 access_token 失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存,强制刷新token
|
||||||
|
*/
|
||||||
|
public void clearCache() {
|
||||||
|
tokenCacheMap.clear();
|
||||||
|
log.info("access_token 缓存已清除");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前缓存状态
|
||||||
|
*/
|
||||||
|
public boolean hasValidCache() {
|
||||||
|
String appid = wxMiniAppConfig.getAppid();
|
||||||
|
TokenCache cache = tokenCacheMap.get(appid);
|
||||||
|
return cache != null && System.currentTimeMillis() < cache.expireTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
server:
|
server:
|
||||||
port: 8080
|
port: 8091
|
||||||
|
ssl:
|
||||||
|
enabled: true
|
||||||
|
key-store: classpath:bmser.com.pfx
|
||||||
|
key-store-type: PKCS12
|
||||||
|
key-store-password: zl7cs0dp
|
||||||
|
protocol: TLS
|
||||||
|
enabled-protocols: TLSv1.2,TLSv1.3
|
||||||
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: mini_program
|
name: mini_program
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://yun.588580.xyz:3306/mini?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://10.30.1.208:3306/mini?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
username: root
|
username: chenglijuan
|
||||||
password: 271922abF
|
password: 271922abf
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
# 微信小程序配置
|
# 微信小程序配置
|
||||||
@@ -17,22 +24,26 @@ wx:
|
|||||||
secret: e82fa407fad13a9df35503f2d176e5a4
|
secret: e82fa407fad13a9df35503f2d176e5a4
|
||||||
subscribe-template-id: Csf_dJU7DhvVFt_03sphPPBCGlnmcWQSPhgqfxHZ5RQ
|
subscribe-template-id: Csf_dJU7DhvVFt_03sphPPBCGlnmcWQSPhgqfxHZ5RQ
|
||||||
env: develop # 环境版本: release(正式版), trial(体验版), develop(开发版)
|
env: develop # 环境版本: release(正式版), trial(体验版), develop(开发版)
|
||||||
|
token-type: stable # token类型: standard(标准版), stable(稳定版)
|
||||||
|
token-expire-buffer: 300 # token提前过期缓冲时间(秒)
|
||||||
corp: # 企业ID
|
corp: # 企业ID
|
||||||
corpid: ww257614cff8a1b61b
|
corpid: ww257614cff8a1b61b
|
||||||
# 应用Secret
|
# 应用Secret
|
||||||
corpsecret: y05BBs2pxgwT0n0__cbYd5GRthlrzfDyrKQPOLtiWkg
|
corpsecret: y05BBs2pxgwT0n0__cbYd5GRthlrzfDyrKQPOLtiWkg
|
||||||
# 访客预约审批模板ID
|
# 访客预约审批模板ID
|
||||||
approval-template-id: C4ej9uEntM19iNJbrtJsUqZakPFfjBNTPNLSKPno2
|
approval-template-id: C4ej9uEntM19iNJbrtJsUqZakPFfjBNTPNLSKPno2
|
||||||
# 审批回调URL(可选)
|
# 审批回调URL(可选)
|
||||||
callback-url: http://your-domain.com/api/wx-corp/approval-callback
|
callback-url: http://your-domain.com/api/wx-corp/approval-callback
|
||||||
# 审批申请人用户ID(提交审批的企微用户)
|
# 审批申请人用户ID(提交审批的企微用户)
|
||||||
creator-userid: ChengLiJuan
|
creator-userid: ChengLiJuan
|
||||||
|
# 访客预约应用AgentId(用于发送应用消息)
|
||||||
|
agentid: 1000006
|
||||||
|
|
||||||
|
|
||||||
# 【访客预约】token
|
# 【访客预约】token
|
||||||
token: V4fj9vnzfrCaEza4MWB4IOUhgJ0p2c
|
token: V4fj9vnzfrCaEza4MWB4IOUhgJ0p2c
|
||||||
# 【访客预约】EncodingAESKey
|
# 【访客预约】EncodingAESKey
|
||||||
encodingAESKey: PTQGH2kHgA8QFPswNrVBknWVKwljNt7NjzRARSd6DOU
|
encodingAESKey: PTQGH2kHgA8QFPswNrVBknWVKwljNt7NjzRARSd6DOU
|
||||||
|
|
||||||
# MyBatis配置
|
# MyBatis配置
|
||||||
mybatis:
|
mybatis:
|
||||||
@@ -40,5 +51,6 @@ mybatis:
|
|||||||
type-aliases-package: com.example.mini_program.entity
|
type-aliases-package: com.example.mini_program.entity
|
||||||
configuration:
|
configuration:
|
||||||
map-underscore-to-camel-case: true
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
# 企业微信配置
|
# 企业微信配置
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
server:
|
||||||
|
port: 8091
|
||||||
|
ssl:
|
||||||
|
enabled: true
|
||||||
|
key-store: classpath:bmser.com.pfx
|
||||||
|
key-store-type: PKCS12
|
||||||
|
key-store-password: zl7cs0dp
|
||||||
|
protocol: TLS
|
||||||
|
enabled-protocols: TLSv1.2,TLSv1.3
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: mini_program
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/mini?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: root
|
||||||
|
password: 271922abf
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
# 微信小程序配置
|
||||||
|
wx:
|
||||||
|
miniapp:
|
||||||
|
# 小程序的应用id,小程序开发管理
|
||||||
|
appid: wx4286144359eeafe5
|
||||||
|
secret: 5abacb8a93e864dabc13b09006aefdc1
|
||||||
|
subscribe-template-id: EF5CDtuZwrGbt8iyOoi-sY7J6hZamX0AbWPLoK-qnEw
|
||||||
|
env: develop # 环境版本: release(正式版), trial(体验版), develop(开发版)
|
||||||
|
token-type: stable # token类型: standard(标准版), stable(稳定版)
|
||||||
|
token-expire-buffer: 300 # token提前过期缓冲时间(秒)
|
||||||
|
corp: # 企业ID
|
||||||
|
corpid: wwe22ebc930200694e
|
||||||
|
# 应用Secret
|
||||||
|
corpsecret: _CgfvRdJRZ713k5lVey2_7Qdrb0JD3HkmR_mesn0144
|
||||||
|
# 访客预约审批模板ID
|
||||||
|
approval-template-id: C4ejArCjiUifaVKyMEEwpcssEbLxKjqMtn5SrHW2t
|
||||||
|
# 审批申请人用户ID(提交审批的企微用户)
|
||||||
|
creator-userid: systemservice
|
||||||
|
# 访客预约审批人用户ID(审批的企微用户)
|
||||||
|
approval-userid: ChengLiJuan
|
||||||
|
# 访客预约应用AgentId(用于发送应用消息)
|
||||||
|
agentid: 1000084
|
||||||
|
# 【访客预约】token
|
||||||
|
token: 3ZnkI0zGML7rL
|
||||||
|
# 【访客预约】EncodingAESKey
|
||||||
|
encodingAESKey: TLtEXX7Nfm3Tu5rx1bSAvbtNlMx9ud8qhTQQurXEfQH
|
||||||
|
|
||||||
|
# MyBatis配置
|
||||||
|
mybatis:
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
type-aliases-package: com.example.mini_program.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
# 企业微信配置
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
server:
|
||||||
|
port: 8091
|
||||||
|
ssl:
|
||||||
|
enabled: true
|
||||||
|
key-store: classpath:bmser.com.pfx
|
||||||
|
key-store-type: PKCS12
|
||||||
|
key-store-password: zl7cs0dp
|
||||||
|
protocol: TLS
|
||||||
|
enabled-protocols: TLSv1.2,TLSv1.3
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: mini_program
|
||||||
|
datasource:
|
||||||
|
url: jdbc:mysql://127.0.0.1:3306/mini?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
|
username: root
|
||||||
|
password: 271922abf
|
||||||
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
# 微信小程序配置
|
||||||
|
wx:
|
||||||
|
miniapp:
|
||||||
|
# 小程序的应用id,小程序开发管理
|
||||||
|
appid: wx4286144359eeafe5
|
||||||
|
secret: 5abacb8a93e864dabc13b09006aefdc1
|
||||||
|
subscribe-template-id: EF5CDtuZwrGbt8iyOoi-sY7J6hZamX0AbWPLoK-qnEw
|
||||||
|
env: develop # 环境版本: release(正式版), trial(体验版), develop(开发版)
|
||||||
|
token-type: stable # token类型: standard(标准版), stable(稳定版)
|
||||||
|
token-expire-buffer: 300 # token提前过期缓冲时间(秒)
|
||||||
|
corp: # 企业ID
|
||||||
|
corpid: wwe22ebc930200694e
|
||||||
|
# 应用Secret
|
||||||
|
corpsecret: _CgfvRdJRZ713k5lVey2_7Qdrb0JD3HkmR_mesn0144
|
||||||
|
# 访客预约审批模板ID
|
||||||
|
approval-template-id: C4ejArCjiUifaVKyMEEwpcssEbLxKjqMtn5SrHW2t
|
||||||
|
# 审批申请人用户ID(提交审批的企微用户)
|
||||||
|
creator-userid: ChengLiJuan
|
||||||
|
# 访客预约应用AgentId(用于发送应用消息)
|
||||||
|
agentid: 1000084
|
||||||
|
# 【访客预约】token
|
||||||
|
token: 3ZnkI0zGML7rL
|
||||||
|
# 【访客预约】EncodingAESKey
|
||||||
|
encodingAESKey: TLtEXX7Nfm3Tu5rx1bSAvbtNlMx9ud8qhTQQurXEfQH
|
||||||
|
|
||||||
|
# MyBatis配置
|
||||||
|
mybatis:
|
||||||
|
mapper-locations: classpath:mapper/*.xml
|
||||||
|
type-aliases-package: com.example.mini_program.entity
|
||||||
|
configuration:
|
||||||
|
map-underscore-to-camel-case: true
|
||||||
|
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||||
|
|
||||||
|
# 企业微信配置
|
||||||
Binary file not shown.
@@ -7,6 +7,12 @@
|
|||||||
<result column="person_name" property="personName"/>
|
<result column="person_name" property="personName"/>
|
||||||
<result column="person_id" property="personId"/>
|
<result column="person_id" property="personId"/>
|
||||||
<result column="department" property="department"/>
|
<result column="department" property="department"/>
|
||||||
|
<result column="department_name" property="departmentName"/>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<resultMap id="DepartmentResultMap" type="com.example.mini_program.entity.ReceptionDepartmentPo">
|
||||||
|
<result column="department" property="departmentCode"/>
|
||||||
|
<result column="department_name" property="departmentName"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectReceptionPerson" resultMap="BaseResultMap">
|
<select id="selectReceptionPerson" resultMap="BaseResultMap">
|
||||||
@@ -15,4 +21,10 @@
|
|||||||
where department = #{department}
|
where department = #{department}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectReceptionDepartment" resultMap="DepartmentResultMap">
|
||||||
|
SELECT distinct department,department_name
|
||||||
|
FROM reception_person
|
||||||
|
order by department
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -11,25 +11,28 @@
|
|||||||
<result column="visit_date" property="visitDate"/>
|
<result column="visit_date" property="visitDate"/>
|
||||||
<result column="visit_time" property="visitTime"/>
|
<result column="visit_time" property="visitTime"/>
|
||||||
<result column="host_name" property="hostName"/>
|
<result column="host_name" property="hostName"/>
|
||||||
|
<result column="person_id" property="personId"/>
|
||||||
<result column="area" property="area"/>
|
<result column="area" property="area"/>
|
||||||
<result column="status" property="status"/>
|
<result column="status" property="status"/>
|
||||||
<result column="status_text" property="statusText"/>
|
<result column="status_text" property="statusText"/>
|
||||||
<result column="openid" property="openid"/>
|
<result column="openid" property="openid"/>
|
||||||
<result column="create_time" property="createTime"/>
|
<result column="create_time" property="createTime"/>
|
||||||
<result column="sp_no" property="spNo"/>
|
<result column="sp_no" property="spNo"/>
|
||||||
|
<result column="check_status" property="checkStatus"/>
|
||||||
|
<result column="plate_number" property="plateNumber"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
id, name, phone, company, reason,
|
id, name, phone, company, reason,person_id,check_status,plate_number,
|
||||||
DATE_FORMAT(visit_date, '%Y-%m-%d') AS visit_date,
|
DATE_FORMAT(visit_date, '%Y-%m-%d') AS visit_date, visit_time,
|
||||||
DATE_FORMAT(visit_time, '%H:%i') AS visit_time,
|
|
||||||
host_name, area, status, status_text, openid,
|
host_name, area, status, status_text, openid,
|
||||||
DATE_FORMAT(create_time, '%Y-%m-%dT%H:%i:%s.000+00:00') AS create_time,
|
DATE_FORMAT(create_time, '%Y-%m-%dT%H:%i:%s.000+00:00') AS create_time,
|
||||||
sp_no
|
sp_no
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
<select id="selectLatestByOpenid" resultMap="BaseResultMap">
|
<select id="selectLatestByOpenid" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
FROM visit_application
|
FROM visit_application
|
||||||
WHERE openid = #{openid}
|
WHERE openid = #{openid}
|
||||||
ORDER BY create_time DESC
|
ORDER BY create_time DESC
|
||||||
@@ -37,7 +40,8 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectListByOpenid" resultMap="BaseResultMap">
|
<select id="selectListByOpenid" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
FROM visit_application
|
FROM visit_application
|
||||||
WHERE openid = #{openid}
|
WHERE openid = #{openid}
|
||||||
ORDER BY create_time DESC
|
ORDER BY create_time DESC
|
||||||
@@ -45,25 +49,27 @@
|
|||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectBySpNo" resultMap="BaseResultMap">
|
<select id="selectBySpNo" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
FROM visit_application
|
FROM visit_application
|
||||||
WHERE sp_no = #{spNo}
|
WHERE sp_no = #{spNo}
|
||||||
ORDER BY create_time DESC
|
ORDER BY create_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectByIdAndOpenid" resultMap="BaseResultMap">
|
<select id="selectByIdAndOpenid" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
FROM visit_application
|
FROM visit_application
|
||||||
WHERE id = #{id} AND openid = #{openid}
|
WHERE id = #{id} AND openid = #{openid}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<insert id="insert">
|
<insert id="insert">
|
||||||
INSERT INTO visit_application (id, name, phone, company, reason,
|
INSERT INTO visit_application (id, name, phone, company, reason,
|
||||||
visit_date, visit_time, host_name, area,
|
visit_date, visit_time, host_name, area,
|
||||||
status, status_text, openid, create_time, sp_no)
|
status, status_text, openid, create_time, sp_no,person_id,plate_number)
|
||||||
VALUES (#{id}, #{name}, #{phone}, #{company}, #{reason},
|
VALUES (#{id}, #{name}, #{phone}, #{company}, #{reason},
|
||||||
#{visitDate}, #{visitTime}, #{hostName}, #{area},
|
#{visitDate}, #{visitTime}, #{hostName}, #{area},
|
||||||
#{status}, #{statusText}, #{openid}, NOW(), #{spNo})
|
#{status}, #{statusText}, #{openid}, NOW(), #{spNo},#{personId},#{plateNumber})
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
<update id="updateStatusToCancelled">
|
<update id="updateStatusToCancelled">
|
||||||
@@ -73,7 +79,8 @@
|
|||||||
</update>
|
</update>
|
||||||
|
|
||||||
<select id="selectById" resultMap="BaseResultMap">
|
<select id="selectById" resultMap="BaseResultMap">
|
||||||
SELECT <include refid="Base_Column_List"/>
|
SELECT
|
||||||
|
<include refid="Base_Column_List"/>
|
||||||
FROM visit_application
|
FROM visit_application
|
||||||
WHERE id = #{id}
|
WHERE id = #{id}
|
||||||
</select>
|
</select>
|
||||||
@@ -84,6 +91,12 @@
|
|||||||
WHERE id = #{id} AND status = 'pending'
|
WHERE id = #{id} AND status = 'pending'
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updateCheckStatusById">
|
||||||
|
UPDATE visit_application
|
||||||
|
SET check_status = #{checkStatus}
|
||||||
|
WHERE id = #{id}
|
||||||
|
</update>
|
||||||
|
|
||||||
<update id="updateStatusBySpNo">
|
<update id="updateStatusBySpNo">
|
||||||
UPDATE visit_application
|
UPDATE visit_application
|
||||||
SET status = #{status}, status_text = #{statusText}
|
SET status = #{status}, status_text = #{statusText}
|
||||||
|
|||||||
Reference in New Issue
Block a user