预约核验
This commit is contained in:
@@ -33,4 +33,9 @@ public class WxCorpConfig {
|
|||||||
* 审批申请人用户ID(提交审批的企微用户)
|
* 审批申请人用户ID(提交审批的企微用户)
|
||||||
*/
|
*/
|
||||||
private String creatorUserid;
|
private String creatorUserid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 访客预约应用AgentId(用于发送应用消息)
|
||||||
|
*/
|
||||||
|
private String agentid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,22 +102,6 @@ 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);
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 受访人下拉框值
|
* 受访人下拉框值
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -16,6 +16,17 @@ public class VisitorApprovalController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private VisitorApprovalService visitorApprovalService;
|
private VisitorApprovalService visitorApprovalService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知受访者访客已到达
|
||||||
|
*
|
||||||
|
* @param id 预约记录ID
|
||||||
|
*/
|
||||||
|
@GetMapping("/notify-host")
|
||||||
|
public void notifyHostArrival(@RequestParam String id) {
|
||||||
|
log.info("通知受访者访客已到达, id={}", id);
|
||||||
|
visitorApprovalService.notifyHostArrival(id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 验证URL有效性(GET请求)
|
* 验证URL有效性(GET请求)
|
||||||
*/
|
*/
|
||||||
@@ -34,8 +45,11 @@ public class VisitorApprovalController {
|
|||||||
@RequestParam(value = "nonce", required = false) String nonce,
|
@RequestParam(value = "nonce", required = false) String nonce,
|
||||||
@RequestBody String requestBody) {
|
@RequestBody String requestBody) {
|
||||||
System.out.println("----------------------------------");
|
System.out.println("----------------------------------");
|
||||||
|
|
||||||
// 异步处理,立即返回
|
// 异步处理,立即返回
|
||||||
visitorApprovalService.processApprovalAsync(msgSignature, timestamp, nonce, requestBody);
|
visitorApprovalService.processApprovalAsync(msgSignature, timestamp, nonce, requestBody);
|
||||||
return "success";
|
return "success";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ public class VisitApplication {
|
|||||||
private String visitTime;
|
private String visitTime;
|
||||||
|
|
||||||
private String hostName;
|
private String hostName;
|
||||||
|
private String receptionPersonId;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,15 @@ 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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新审批状态
|
||||||
|
*
|
||||||
|
* @param id 记录ID
|
||||||
|
* @return 影响行数
|
||||||
|
*/
|
||||||
|
void updateCheckStatusById(@Param("id") String id, @Param("checkStatus") String checkStatus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据审批单号更新审批状态
|
* 根据审批单号更新审批状态
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ 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;
|
||||||
|
|
||||||
@@ -60,8 +57,10 @@ public class AppointmentService {
|
|||||||
log.info("查询到 {} 条预约记录", list.size());
|
log.info("查询到 {} 条预约记录", list.size());
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VisitApplication getDetail(String id) {
|
public VisitApplication getDetail(String id) {
|
||||||
return visitApplicationMapper.selectById(id);
|
VisitApplication v = visitApplicationMapper.selectById(id);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -89,16 +88,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -95,4 +109,60 @@ public class VisitorApprovalService {
|
|||||||
}
|
}
|
||||||
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.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<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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,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,7 +51,9 @@ public class WxApprovalService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 提交审批申请 */
|
/**
|
||||||
|
* 提交审批申请
|
||||||
|
*/
|
||||||
public String submitApproval(String creatorUserId, String visitorName, String visitorPhone,
|
public String submitApproval(String creatorUserId, String visitorName, String visitorPhone,
|
||||||
String visitorCompany, String visitPurpose, String visitTime,
|
String visitorCompany, String visitPurpose, String visitTime,
|
||||||
String visiteeName, String visitArea) {
|
String visiteeName, String visitArea) {
|
||||||
@@ -77,31 +81,7 @@ 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(String visitorName, String visitorPhone,
|
private Map<String, Object> buildApplyData(String visitorName, String visitorPhone,
|
||||||
String visitorCompany, String visitPurpose,
|
String visitorCompany, String visitPurpose,
|
||||||
String visitTime, String visiteeName, String visitArea) {
|
String visitTime, String visiteeName, String visitArea) {
|
||||||
@@ -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
|
@Data
|
||||||
public static class ApprovalStatus {
|
public static class ApprovalStatus {
|
||||||
private String spNo;
|
private String spNo;
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ wx:
|
|||||||
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
|
||||||
|
|||||||
@@ -11,16 +11,18 @@
|
|||||||
<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="reception_person_id" property="receptionPersonId"/>
|
||||||
<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"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
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_date, '%Y-%m-%d') AS visit_date,
|
||||||
DATE_FORMAT(visit_time, '%H:%i') AS 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,
|
||||||
@@ -29,7 +31,8 @@
|
|||||||
</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,14 +49,16 @@
|
|||||||
</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>
|
||||||
@@ -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 = #{status}
|
||||||
|
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