From 13087b15bc99f2a7411d7f28c31e49107bbbbe30 Mon Sep 17 00:00:00 2001 From: chenglijuan Date: Wed, 29 Apr 2026 15:37:15 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=84=E7=BA=A6=E6=A0=B8=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mini_program/config/WxCorpConfig.java | 5 ++ .../controller/AppointmentController.java | 16 ----- .../controller/VisitorApprovalController.java | 14 ++++ .../mini_program/entity/VisitApplication.java | 2 + .../mapper/VisitApplicationMapper.java | 11 ++- .../service/AppointmentService.java | 21 ++---- .../service/VisitorApprovalService.java | 70 +++++++++++++++++++ .../service/WxApprovalService.java | 53 +++----------- src/main/resources/application.yml | 26 +++---- .../mapper/VisitApplicationMapper.xml | 33 ++++++--- 10 files changed, 154 insertions(+), 97 deletions(-) diff --git a/src/main/java/com/example/mini_program/config/WxCorpConfig.java b/src/main/java/com/example/mini_program/config/WxCorpConfig.java index 51bf921..4971e48 100644 --- a/src/main/java/com/example/mini_program/config/WxCorpConfig.java +++ b/src/main/java/com/example/mini_program/config/WxCorpConfig.java @@ -33,4 +33,9 @@ public class WxCorpConfig { * 审批申请人用户ID(提交审批的企微用户) */ private String creatorUserid; + + /** + * 访客预约应用AgentId(用于发送应用消息) + */ + private String agentid; } diff --git a/src/main/java/com/example/mini_program/controller/AppointmentController.java b/src/main/java/com/example/mini_program/controller/AppointmentController.java index 7ea2e03..a79717c 100644 --- a/src/main/java/com/example/mini_program/controller/AppointmentController.java +++ b/src/main/java/com/example/mini_program/controller/AppointmentController.java @@ -102,22 +102,6 @@ public class AppointmentController { return success ? Result.success(true) : Result.error("审批失败,记录不存在或状态不允许"); } - - /** - * 根据id获取审批详情 - */ -// @PutMapping("/detail") -// public Result 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); -// } - /** * 受访人下拉框值 */ diff --git a/src/main/java/com/example/mini_program/controller/VisitorApprovalController.java b/src/main/java/com/example/mini_program/controller/VisitorApprovalController.java index 98fd336..3a5a5b8 100644 --- a/src/main/java/com/example/mini_program/controller/VisitorApprovalController.java +++ b/src/main/java/com/example/mini_program/controller/VisitorApprovalController.java @@ -16,6 +16,17 @@ public class VisitorApprovalController { @Autowired private VisitorApprovalService visitorApprovalService; + /** + * 通知受访者访客已到达 + * + * @param id 预约记录ID + */ + @GetMapping("/notify-host") + public void notifyHostArrival(@RequestParam String id) { + log.info("通知受访者访客已到达, id={}", id); + visitorApprovalService.notifyHostArrival(id); + } + /** * 验证URL有效性(GET请求) */ @@ -34,8 +45,11 @@ public class VisitorApprovalController { @RequestParam(value = "nonce", required = false) String nonce, @RequestBody String requestBody) { System.out.println("----------------------------------"); + // 异步处理,立即返回 visitorApprovalService.processApprovalAsync(msgSignature, timestamp, nonce, requestBody); return "success"; } + + } diff --git a/src/main/java/com/example/mini_program/entity/VisitApplication.java b/src/main/java/com/example/mini_program/entity/VisitApplication.java index eae1144..5244783 100644 --- a/src/main/java/com/example/mini_program/entity/VisitApplication.java +++ b/src/main/java/com/example/mini_program/entity/VisitApplication.java @@ -19,10 +19,12 @@ public class VisitApplication { private String visitTime; private String hostName; + private String receptionPersonId; private String area; private String status; private String statusText; private String openid; private String createTime; private String spNo; + private String checkStatus; } diff --git a/src/main/java/com/example/mini_program/mapper/VisitApplicationMapper.java b/src/main/java/com/example/mini_program/mapper/VisitApplicationMapper.java index 57f8144..e5ece1d 100644 --- a/src/main/java/com/example/mini_program/mapper/VisitApplicationMapper.java +++ b/src/main/java/com/example/mini_program/mapper/VisitApplicationMapper.java @@ -71,10 +71,19 @@ public interface VisitApplicationMapper { * @return 影响行数 */ int updateStatus(@Param("id") String id, @Param("status") String status, @Param("statusText") String statusText); + + /** + * 更新审批状态 + * + * @param id 记录ID + * @return 影响行数 + */ + void updateCheckStatusById(@Param("id") String id, @Param("checkStatus") String checkStatus); + /** * 根据审批单号更新审批状态 * - * @param spNo 审批编号 + * @param spNo 审批编号 * @param status 状态值 * @param statusText 状态文本 * @return 影响行数 diff --git a/src/main/java/com/example/mini_program/service/AppointmentService.java b/src/main/java/com/example/mini_program/service/AppointmentService.java index 4c6fec5..890fd94 100644 --- a/src/main/java/com/example/mini_program/service/AppointmentService.java +++ b/src/main/java/com/example/mini_program/service/AppointmentService.java @@ -31,9 +31,6 @@ public class AppointmentService { private final WxApprovalService wxApprovalService; @Autowired private ReceptionPersonMapper receptionPersonMapper; - @Autowired - private final WxSubscribeMessageService wxSubscribeMessageService; - @Value("${wx.corp.creator-userid:}") private String creatorUserId; @@ -60,8 +57,10 @@ public class AppointmentService { log.info("查询到 {} 条预约记录", list.size()); return list; } - public VisitApplication getDetail(String id) { - return visitApplicationMapper.selectById(id); + + public VisitApplication getDetail(String id) { + VisitApplication v = visitApplicationMapper.selectById(id); + return v; } /** @@ -89,16 +88,6 @@ public class AppointmentService { visitApplicationMapper.insert(record); 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; } @@ -173,7 +162,7 @@ public class AppointmentService { public List personSelector(String department) { List receiptPersonList = receptionPersonMapper.selectReceptionPerson(department); List voList = new ArrayList<>(); - for(ReceptionPersonPO po : receiptPersonList){ + for (ReceptionPersonPO po : receiptPersonList) { ReceptionPersonVo vo = new ReceptionPersonVo(); BeanUtils.copyProperties(po, vo); voList.add(vo); diff --git a/src/main/java/com/example/mini_program/service/VisitorApprovalService.java b/src/main/java/com/example/mini_program/service/VisitorApprovalService.java index 83a1b04..691b6df 100644 --- a/src/main/java/com/example/mini_program/service/VisitorApprovalService.java +++ b/src/main/java/com/example/mini_program/service/VisitorApprovalService.java @@ -1,8 +1,11 @@ package com.example.mini_program.service; 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.mapper.VisitApplicationMapper; +import com.example.mini_program.util.HttpUtil; +import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.json.JSONObject; import org.json.XML; @@ -11,12 +14,17 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; @Slf4j @Service 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:}") private String corpId; @@ -33,6 +41,12 @@ public class VisitorApprovalService { private VisitApplicationMapper visitApplicationMapper; @Autowired 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) { log.info("URL验证请求: msgSignature={}, timestamp={}, nonce={}", msgSignature, timestamp, nonce); @@ -95,4 +109,60 @@ public class VisitorApprovalService { } 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.getReceptionPersonId(); + 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 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 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); + } + } } diff --git a/src/main/java/com/example/mini_program/service/WxApprovalService.java b/src/main/java/com/example/mini_program/service/WxApprovalService.java index b8ae38b..ca152fd 100644 --- a/src/main/java/com/example/mini_program/service/WxApprovalService.java +++ b/src/main/java/com/example/mini_program/service/WxApprovalService.java @@ -35,7 +35,9 @@ public class WxApprovalService { private final WxCorpConfig wxCorpConfig; private final ObjectMapper objectMapper; - /** 获取企业微信 access_token */ + /** + * 获取企业微信 access_token + */ public String getAccessToken() { String url = String.format(GET_TOKEN_URL, wxCorpConfig.getCorpid(), wxCorpConfig.getCorpsecret()); try { @@ -49,10 +51,12 @@ public class WxApprovalService { } } - /** 提交审批申请 */ + /** + * 提交审批申请 + */ public String submitApproval(String creatorUserId, String visitorName, String visitorPhone, - String visitorCompany, String visitPurpose, String visitTime, - String visiteeName, String visitArea) { + String visitorCompany, String visitPurpose, String visitTime, + String visiteeName, String visitArea) { String url = String.format(SUBMIT_URL, getAccessToken()); Map body = new HashMap<>(); @@ -77,34 +81,10 @@ 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 resp = objectMapper.readValue(HttpUtil.postJson(url, json), Map.class); - if ("0".equals(String.valueOf(resp.get("errcode")))) { - @SuppressWarnings("unchecked") - Map info = (Map) 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 buildApplyData(String visitorName, String visitorPhone, - String visitorCompany, String visitPurpose, - String visitTime, String visiteeName, String visitArea) { + String visitorCompany, String visitPurpose, + String visitTime, String visiteeName, String visitArea) { List> contents = new ArrayList<>(); contents.add(textControl("Text-1776786661954", visitorName)); contents.add(textControl("Text-1776786666351", visitorPhone)); @@ -126,7 +106,7 @@ public class WxApprovalService { if (dateTime != null && !dateTime.isEmpty()) { try { timestamp = LocalDateTime.parse(dateTime, - DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")) .atZone(ZoneId.of("Asia/Shanghai")).toInstant().getEpochSecond(); } catch (Exception e) { log.warn("日期解析失败: {}", dateTime); @@ -145,17 +125,6 @@ public class WxApprovalService { ); } - 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; diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0f98f7c..4461c8c 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -20,21 +20,23 @@ wx: token-type: stable # token类型: standard(标准版), stable(稳定版) token-expire-buffer: 300 # token提前过期缓冲时间(秒) corp: # 企业ID - corpid: ww257614cff8a1b61b - # 应用Secret - corpsecret: y05BBs2pxgwT0n0__cbYd5GRthlrzfDyrKQPOLtiWkg - # 访客预约审批模板ID - approval-template-id: C4ej9uEntM19iNJbrtJsUqZakPFfjBNTPNLSKPno2 - # 审批回调URL(可选) - callback-url: http://your-domain.com/api/wx-corp/approval-callback - # 审批申请人用户ID(提交审批的企微用户) - creator-userid: ChengLiJuan + corpid: ww257614cff8a1b61b + # 应用Secret + corpsecret: y05BBs2pxgwT0n0__cbYd5GRthlrzfDyrKQPOLtiWkg + # 访客预约审批模板ID + approval-template-id: C4ej9uEntM19iNJbrtJsUqZakPFfjBNTPNLSKPno2 + # 审批回调URL(可选) + callback-url: http://your-domain.com/api/wx-corp/approval-callback + # 审批申请人用户ID(提交审批的企微用户) + creator-userid: ChengLiJuan + # 访客预约应用AgentId(用于发送应用消息) + agentid: 1000006 - # 【访客预约】token - token: V4fj9vnzfrCaEza4MWB4IOUhgJ0p2c + # 【访客预约】token + token: V4fj9vnzfrCaEza4MWB4IOUhgJ0p2c # 【访客预约】EncodingAESKey - encodingAESKey: PTQGH2kHgA8QFPswNrVBknWVKwljNt7NjzRARSd6DOU + encodingAESKey: PTQGH2kHgA8QFPswNrVBknWVKwljNt7NjzRARSd6DOU # MyBatis配置 mybatis: diff --git a/src/main/resources/mapper/VisitApplicationMapper.xml b/src/main/resources/mapper/VisitApplicationMapper.xml index cc27b88..2c630fe 100644 --- a/src/main/resources/mapper/VisitApplicationMapper.xml +++ b/src/main/resources/mapper/VisitApplicationMapper.xml @@ -11,16 +11,18 @@ + + - id, name, phone, company, reason, + id, name, phone, company, reason,reception_person_id,check_status, DATE_FORMAT(visit_date, '%Y-%m-%d') AS visit_date, DATE_FORMAT(visit_time, '%H:%i') AS visit_time, host_name, area, status, status_text, openid, @@ -29,7 +31,8 @@ INSERT INTO visit_application (id, name, phone, company, reason, - visit_date, visit_time, host_name, area, - status, status_text, openid, create_time, sp_no) + visit_date, visit_time, host_name, area, + status, status_text, openid, create_time, sp_no) VALUES (#{id}, #{name}, #{phone}, #{company}, #{reason}, - #{visitDate}, #{visitTime}, #{hostName}, #{area}, - #{status}, #{statusText}, #{openid}, NOW(), #{spNo}) + #{visitDate}, #{visitTime}, #{hostName}, #{area}, + #{status}, #{statusText}, #{openid}, NOW(), #{spNo}) @@ -73,7 +79,8 @@ @@ -84,6 +91,12 @@ WHERE id = #{id} AND status = 'pending' + + UPDATE visit_application + SET check_status = #{status} + WHERE id = #{id} + + UPDATE visit_application SET status = #{status}, status_text = #{statusText}