入伍保留学籍申请、外宿申请-上一级审批结束之后,给下一级发送审批提醒
This commit is contained in:
@@ -5,12 +5,15 @@ import com.srs.common.core.domain.entity.SysUser;
|
|||||||
|
|
||||||
import com.srs.common.doman.vo.TeacherVo;
|
import com.srs.common.doman.vo.TeacherVo;
|
||||||
import com.srs.common.utils.SecurityUtils;
|
import com.srs.common.utils.SecurityUtils;
|
||||||
|
import com.srs.common.utils.WeChatUtil;
|
||||||
import com.srs.common.utils.spring.SpringUtils;
|
import com.srs.common.utils.spring.SpringUtils;
|
||||||
import com.srs.flowable.domain.EnlistmentReserve;
|
import com.srs.flowable.domain.EnlistmentReserve;
|
||||||
import com.srs.flowable.domain.EnlistmentReserveApproval;
|
import com.srs.flowable.domain.EnlistmentReserveApproval;
|
||||||
import com.srs.flowable.mapper.EnlistmentReserveApprovalMapper;
|
import com.srs.flowable.mapper.EnlistmentReserveApprovalMapper;
|
||||||
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
||||||
|
import com.srs.flowable.mapper.RelieveMapper;
|
||||||
import com.srs.system.service.ISysUserService;
|
import com.srs.system.service.ISysUserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.FlowNode;
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
import org.flowable.engine.HistoryService;
|
import org.flowable.engine.HistoryService;
|
||||||
@@ -28,9 +31,10 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批流程负责人自动流转监听器
|
* 审批流程负责人自动流转监听器
|
||||||
* 用于节点审批通过时,添加审批记录
|
* 用于节点审批通过时,添加审批记录 + 给学生发送企业微信通知
|
||||||
*/
|
*/
|
||||||
@Component("approvalAssigneeListener") // Spring Bean名称,与BPMN表达式对应
|
@Component("approvalAssigneeListener") // Spring Bean名称,与BPMN表达式对应
|
||||||
|
@Slf4j
|
||||||
public class ApprovalAssigneeListener implements ExecutionListener {
|
public class ApprovalAssigneeListener implements ExecutionListener {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -103,8 +107,89 @@ public class ApprovalAssigneeListener implements ExecutionListener {
|
|||||||
// 改变申请表中的审核状态(审核通过)
|
// 改变申请表中的审核状态(审核通过)
|
||||||
enlistmentReserve.setApplyStatus(status);
|
enlistmentReserve.setApplyStatus(status);
|
||||||
rtEnlistmentReserveMapper.updateRtEnlistmentReserve(enlistmentReserve);
|
rtEnlistmentReserveMapper.updateRtEnlistmentReserve(enlistmentReserve);
|
||||||
|
// ========== 给学生发送入伍保留学籍审批通知 ==========
|
||||||
|
sendStudentWeChatNotification(execution, enlistmentReserve, currentNodeName, approvalResult, approvalOpinion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给学生发送入伍保留学籍审批结果的企业微信通知
|
||||||
|
*/
|
||||||
|
private void sendStudentWeChatNotification(DelegateExecution execution, EnlistmentReserve enlistmentReserve,
|
||||||
|
String currentNodeName, Long approvalResult, String approvalOpinion) {
|
||||||
|
try {
|
||||||
|
// 1. 获取需要的Mapper和工具类
|
||||||
|
RelieveMapper relieveMapper = (RelieveMapper) SpringUtils.getBean("relieveMapper");
|
||||||
|
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||||
|
|
||||||
|
// 2. 获取学生基础信息
|
||||||
|
String studentNo = enlistmentReserve.getStudentNo();
|
||||||
|
String studentName = enlistmentReserve.getStudentName();
|
||||||
|
|
||||||
|
// 3. 获取学生userId(优先从流程变量取)
|
||||||
|
Long stuUserId = null;
|
||||||
|
if (execution.hasVariable("stuUserId")) {
|
||||||
|
stuUserId = Long.valueOf(execution.getVariable("stuUserId").toString());
|
||||||
|
} else {
|
||||||
|
// 兜底:通过申请表获取学生id
|
||||||
|
stuUserId = enlistmentReserve.getStudentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stuUserId == null) {
|
||||||
|
log.warn("⚠ 未找到学生(学号:{},姓名:{})对应的用户ID,无法发送通知", studentNo, studentName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 查询学生的企业微信账号
|
||||||
|
String stuUserName = relieveMapper.getUserNameByUserId(stuUserId);
|
||||||
|
|
||||||
|
if (stuUserName != null && !stuUserName.isEmpty()) {
|
||||||
|
// 5. 拼接审批结果描述
|
||||||
|
String approveResultDesc = getApprovalResultDesc(approvalResult, currentNodeName);
|
||||||
|
|
||||||
|
// 6. 构造通知内容
|
||||||
|
String content = String.format("您的入伍保留学籍申请有新的审批进展:%s(%s),%s,<a href='https://zhxg.gxsdxy.cn/wab/#/pages/Approval/index'>点此查看详情</a>",
|
||||||
|
currentNodeName,
|
||||||
|
approveResultDesc,
|
||||||
|
(approvalOpinion != null && !approvalOpinion.isEmpty()) ? "审批意见:" + approvalOpinion : "");
|
||||||
|
|
||||||
|
// 7. 发送企业微信消息
|
||||||
|
weChatUtil.sendTextMessage(stuUserName, content);
|
||||||
|
log.info("✅ 已成功向学生(姓名:{},学号:{},userName:{})发送入伍保留学籍审批通知。审批节点:{},结果:{}",
|
||||||
|
studentName, studentNo, stuUserName, currentNodeName, approveResultDesc);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 发送日志
|
||||||
|
log.warn("⚠ 找到了学生(学号:{},姓名:{},userId:{}),但其对应的企业微信账号为空,无法发送通知。",
|
||||||
|
studentNo, studentName, stuUserId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 异常处理:记录日志+不影响主流程
|
||||||
|
String studentNo = enlistmentReserve.getStudentNo();
|
||||||
|
log.error("❌ 向学生(学号:{})发送入伍保留学籍审批通知时出现异常,但流程将继续。错误详情:", studentNo, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换审批结果为可读描述(适配入伍保留学籍审批状态)
|
||||||
|
*/
|
||||||
|
private String getApprovalResultDesc(Long approvalResult, String currentNodeName) {
|
||||||
|
if (approvalResult == null) {
|
||||||
|
return "审批已处理";
|
||||||
|
}
|
||||||
|
// 匹配项目中实际的审批状态值(0=驳回/1=通过/2=审批中)
|
||||||
|
switch (approvalResult.intValue()) {
|
||||||
|
case 0:
|
||||||
|
return currentNodeName + "驳回";
|
||||||
|
case 1:
|
||||||
|
return currentNodeName + "通过";
|
||||||
|
case 2:
|
||||||
|
return "审批中";
|
||||||
|
default:
|
||||||
|
return "审批已完成";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据节点ID和流程定义ID,查询节点名称(兼容所有版本的核心方法)
|
* 根据节点ID和流程定义ID,查询节点名称(兼容所有版本的核心方法)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -4,13 +4,18 @@ import com.srs.common.core.domain.entity.SysUser;
|
|||||||
|
|
||||||
|
|
||||||
import com.srs.common.doman.vo.TeacherVo;
|
import com.srs.common.doman.vo.TeacherVo;
|
||||||
|
import com.srs.common.utils.DateUtils;
|
||||||
import com.srs.common.utils.SecurityUtils;
|
import com.srs.common.utils.SecurityUtils;
|
||||||
|
import com.srs.common.utils.WeChatUtil;
|
||||||
import com.srs.common.utils.spring.SpringUtils;
|
import com.srs.common.utils.spring.SpringUtils;
|
||||||
import com.srs.flowable.domain.EnlistmentReserve;
|
import com.srs.flowable.domain.EnlistmentReserve;
|
||||||
import com.srs.flowable.domain.EnlistmentReserveApproval;
|
import com.srs.flowable.domain.EnlistmentReserveApproval;
|
||||||
|
import com.srs.flowable.domain.NotificationManage;
|
||||||
import com.srs.flowable.mapper.EnlistmentReserveApprovalMapper;
|
import com.srs.flowable.mapper.EnlistmentReserveApprovalMapper;
|
||||||
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
||||||
|
import com.srs.flowable.mapper.LeaveMapper;
|
||||||
import com.srs.system.service.ISysUserService;
|
import com.srs.system.service.ISysUserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.FlowNode;
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
import org.flowable.engine.HistoryService;
|
import org.flowable.engine.HistoryService;
|
||||||
@@ -31,6 +36,7 @@ import java.util.List;
|
|||||||
* 用于节点审批之前时,更新下一个节点的负责人变量(approval)
|
* 用于节点审批之前时,更新下一个节点的负责人变量(approval)
|
||||||
*/
|
*/
|
||||||
@Component("startApprovalAssigneeListener") // Spring Bean名称,与BPMN表达式对应
|
@Component("startApprovalAssigneeListener") // Spring Bean名称,与BPMN表达式对应
|
||||||
|
@Slf4j
|
||||||
public class StartApprovalAssigneeListener implements ExecutionListener {
|
public class StartApprovalAssigneeListener implements ExecutionListener {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -46,7 +52,7 @@ public class StartApprovalAssigneeListener implements ExecutionListener {
|
|||||||
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
||||||
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
||||||
|
|
||||||
// 2. 根据节点ID查询节点名称(核心修正:通过流程定义获取名称)
|
// 2. 根据节点ID查询节点名称(通过流程定义获取名称)
|
||||||
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
||||||
if (currentNodeName == null) {
|
if (currentNodeName == null) {
|
||||||
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
||||||
@@ -61,6 +67,8 @@ public class StartApprovalAssigneeListener implements ExecutionListener {
|
|||||||
if (nextAssigneeId != null) {
|
if (nextAssigneeId != null) {
|
||||||
execution.setVariable("approval", nextAssigneeId);
|
execution.setVariable("approval", nextAssigneeId);
|
||||||
execution.setVariable("currentNode", currentNodeName);
|
execution.setVariable("currentNode", currentNodeName);
|
||||||
|
// ========== 发送通知逻辑 ==========
|
||||||
|
sendApprovalNotification(execution, currentNodeName, nextAssigneeId, enlistmentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,4 +182,99 @@ public class StartApprovalAssigneeListener implements ExecutionListener {
|
|||||||
throw new RuntimeException("未配置节点[" + currentNodeName + "]的当前负责人规则");
|
throw new RuntimeException("未配置节点[" + currentNodeName + "]的当前负责人规则");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送审批通知(系统通知+企业微信通知)
|
||||||
|
*/
|
||||||
|
private void sendApprovalNotification(DelegateExecution execution, String currentNodeName,
|
||||||
|
Long nextAssigneeId, Long enlistmentId) {
|
||||||
|
try {
|
||||||
|
// 获取需要的Mapper和工具类
|
||||||
|
EnlistmentReserveMapper enlistmentReserveMapper = (EnlistmentReserveMapper) SpringUtils.getBean(EnlistmentReserveMapper.class);
|
||||||
|
LeaveMapper leaveMapper = (LeaveMapper) SpringUtils.getBean("leaveMapper");
|
||||||
|
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||||
|
|
||||||
|
// 1. 查询入伍保留学籍申请详情
|
||||||
|
EnlistmentReserve enlistmentReserve = enlistmentReserveMapper.selectRtEnlistmentReserveById(enlistmentId);
|
||||||
|
if (enlistmentReserve == null) {
|
||||||
|
log.warn("未找到入伍保留学籍申请记录,ID:{}", enlistmentId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询下一个审批人的信息(获取用户名/工号)
|
||||||
|
SysUser nextApprover = sysUserService.selectUserById(nextAssigneeId);
|
||||||
|
if (nextApprover == null) {
|
||||||
|
log.warn("未找到审批人信息,用户ID:{}", nextAssigneeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String approverUserName = nextApprover.getUserName(); // 审批人工号/用户名
|
||||||
|
String approverName = nextApprover.getNickName(); // 审批人姓名
|
||||||
|
|
||||||
|
log.info("开始发送【入伍保留学籍审批】通知,审批节点:{},审批人:{}({})",
|
||||||
|
currentNodeName, approverName, approverUserName);
|
||||||
|
|
||||||
|
// 3. 处理系统通知(先删后加,避免重复通知)
|
||||||
|
NotificationManage notificationManage = new NotificationManage();
|
||||||
|
notificationManage.setContent(String.format("您有一条【入伍保留学籍审批】待处理(节点:%s)", currentNodeName));
|
||||||
|
notificationManage.setReceiver(nextAssigneeId); // 接收人:下一个审批人
|
||||||
|
|
||||||
|
// 3.1 查询是否已有相同通知,有则删除
|
||||||
|
NotificationManage existNotify = leaveMapper.selectCphMsgListForFlowable(notificationManage);
|
||||||
|
if (existNotify != null) {
|
||||||
|
int delRes = leaveMapper.deleteCphMsgById(existNotify.getId());
|
||||||
|
log.info("删除重复的系统通知,通知ID:{},删除结果:{}", existNotify.getId(), delRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.2 添加新的系统通知
|
||||||
|
notificationManage.setSender(SecurityUtils.getUserId()); // 发送人:当前操作人
|
||||||
|
notificationManage.setCreateTime(DateUtils.getNowDate()); // 创建时间
|
||||||
|
int addRes = leaveMapper.insertCphMsg(notificationManage);
|
||||||
|
log.info("新增系统通知成功,接收人ID:{},添加结果:{}", nextAssigneeId, addRes);
|
||||||
|
|
||||||
|
// 4. 企业微信推送消息
|
||||||
|
if (approverUserName != null && !approverUserName.isEmpty()) {
|
||||||
|
// 构造带超链接的消息内容
|
||||||
|
String weChatContent = String.format(
|
||||||
|
"您有一条【入伍保留学籍审批】待处理(节点:%s),<a href='https://zhxg.gxsdxy.cn/wab/#/pages/Approval/index'>请点击前往处理</a>",
|
||||||
|
currentNodeName
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发送企业微信文本消息
|
||||||
|
weChatUtil.sendTextMessage(approverUserName, weChatContent);
|
||||||
|
log.info("企业微信通知发送成功,接收人工号:{},节点:{}", approverUserName, currentNodeName);
|
||||||
|
} else {
|
||||||
|
log.warn("审批人工号为空,无法发送企业微信通知,用户ID:{}", nextAssigneeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 删除当前审批人的待处理通知 ==========
|
||||||
|
// 4.1 获取当前审批人ID(当前操作人=已完成审批的人)
|
||||||
|
Long currentApproverId = SecurityUtils.getUserId();
|
||||||
|
if (currentApproverId == null) {
|
||||||
|
log.warn("当前审批人ID为空,无法删除其待处理通知");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4.2 构造当前审批人的待处理通知查询条件
|
||||||
|
NotificationManage currentNotifyQuery = new NotificationManage();
|
||||||
|
// 内容匹配:当前节点的待处理通知(关键词+节点名称)
|
||||||
|
currentNotifyQuery.setContent(String.format("您有一条【入伍保留学籍审批】待处理(节点:%s)", currentNodeName));
|
||||||
|
currentNotifyQuery.setReceiver(currentApproverId); // 接收人=当前审批人
|
||||||
|
|
||||||
|
// 4.3 查询当前审批人的该节点待处理通知
|
||||||
|
NotificationManage currentExistNotify = leaveMapper.selectCphMsgListForFlowable(currentNotifyQuery);
|
||||||
|
if (currentExistNotify != null) {
|
||||||
|
// 4.4 删除该通知(清理已处理的待办)
|
||||||
|
int currentDelRes = leaveMapper.deleteCphMsgById(currentExistNotify.getId());
|
||||||
|
log.info("删除当前审批人({})的【{}】待处理通知,通知ID:{},删除结果:{}",
|
||||||
|
currentApproverId, currentNodeName, currentExistNotify.getId(), currentDelRes);
|
||||||
|
} else {
|
||||||
|
log.info("当前审批人({})无【{}】节点的待处理通知,无需删除", currentApproverId, currentNodeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有异常,仅记录日志,不影响主流程
|
||||||
|
log.error("发送入伍保留学籍审批通知失败,节点:{},审批人ID:{},错误信息:{}",
|
||||||
|
currentNodeName, nextAssigneeId, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,12 +2,15 @@ package com.srs.flowable.listener.outsideAccommodation;
|
|||||||
|
|
||||||
|
|
||||||
import com.srs.common.utils.SecurityUtils;
|
import com.srs.common.utils.SecurityUtils;
|
||||||
|
import com.srs.common.utils.WeChatUtil;
|
||||||
import com.srs.common.utils.spring.SpringUtils;
|
import com.srs.common.utils.spring.SpringUtils;
|
||||||
import com.srs.flowable.domain.DormitoryStudent;
|
import com.srs.flowable.domain.DormitoryStudent;
|
||||||
import com.srs.flowable.domain.OutsideAccommodationApply;
|
import com.srs.flowable.domain.OutsideAccommodationApply;
|
||||||
import com.srs.flowable.domain.OutsideAccommodationApproval;
|
import com.srs.flowable.domain.OutsideAccommodationApproval;
|
||||||
import com.srs.flowable.mapper.OutsideAccommodationApplyMapper;
|
import com.srs.flowable.mapper.OutsideAccommodationApplyMapper;
|
||||||
import com.srs.flowable.mapper.OutsideAccommodationApprovalMapper;
|
import com.srs.flowable.mapper.OutsideAccommodationApprovalMapper;
|
||||||
|
import com.srs.flowable.mapper.RelieveMapper;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.FlowNode;
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
import org.flowable.engine.RepositoryService;
|
import org.flowable.engine.RepositoryService;
|
||||||
@@ -21,9 +24,10 @@ import java.util.Date;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 审批流程负责人自动流转监听器
|
* 审批流程负责人自动流转监听器
|
||||||
* 用于节点审批通过时,添加审批记录
|
* 用于节点审批通过时,添加审批记录 + 给学生发送企业微信通知
|
||||||
*/
|
*/
|
||||||
@Component("OutsideAccommodationEndListener") // Spring Bean名称,与BPMN表达式对应
|
@Component("OutsideAccommodationEndListener") // Spring Bean名称,与BPMN表达式对应
|
||||||
|
@Slf4j
|
||||||
public class OutsideAccommodationEndListener implements ExecutionListener {
|
public class OutsideAccommodationEndListener implements ExecutionListener {
|
||||||
|
|
||||||
// 用于查询流程定义中的节点信息
|
// 用于查询流程定义中的节点信息
|
||||||
@@ -36,7 +40,7 @@ public class OutsideAccommodationEndListener implements ExecutionListener {
|
|||||||
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
||||||
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
||||||
|
|
||||||
// 2. 根据节点ID查询节点名称(核心修正:通过流程定义获取名称)
|
// 2. 根据节点ID查询节点名称(通过流程定义获取名称)
|
||||||
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
||||||
if (currentNodeName == null) {
|
if (currentNodeName == null) {
|
||||||
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
||||||
@@ -88,6 +92,84 @@ public class OutsideAccommodationEndListener implements ExecutionListener {
|
|||||||
}
|
}
|
||||||
outsideAccommodationApply.setStatus(status);
|
outsideAccommodationApply.setStatus(status);
|
||||||
outsideAccommodationApplyMapper.updateDmsOutsideAccommodationApply(outsideAccommodationApply);
|
outsideAccommodationApplyMapper.updateDmsOutsideAccommodationApply(outsideAccommodationApply);
|
||||||
|
// ========== 给学生发送企业微信通知 ==========
|
||||||
|
sendStudentWeChatNotification(execution, outsideAccommodationApply, currentNodeName, approvalResult, approvalOpinion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 给学生发送外宿申请审批结果的企业微信通知
|
||||||
|
*/
|
||||||
|
private void sendStudentWeChatNotification(DelegateExecution execution, OutsideAccommodationApply outsideAccommodationApply,
|
||||||
|
String currentNodeName, Long approvalResult, String approvalOpinion) {
|
||||||
|
try {
|
||||||
|
// 1. 获取需要的Mapper和工具类
|
||||||
|
RelieveMapper relieveMapper = (RelieveMapper) SpringUtils.getBean("relieveMapper");
|
||||||
|
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||||
|
|
||||||
|
// 2. 获取学生学号和姓名(从外宿申请对象中获取)
|
||||||
|
String studentNo = outsideAccommodationApply.getStudentNo();
|
||||||
|
String studentName = outsideAccommodationApply.getStudentName();
|
||||||
|
|
||||||
|
// 3. 先尝试从流程变量获取学生userId
|
||||||
|
Long stuUserId = null;
|
||||||
|
if (execution.hasVariable("stuUserId")) {
|
||||||
|
stuUserId = Long.valueOf(execution.getVariable("stuUserId").toString());
|
||||||
|
} else {
|
||||||
|
// 补充逻辑:如果流程变量中没有stuUserId,通过申请表获取
|
||||||
|
stuUserId = outsideAccommodationApply.getStudentId();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stuUserId == null) {
|
||||||
|
log.warn("⚠ 未找到学生(学号:{},姓名:{})对应的用户ID,无法发送通知", studentNo, studentName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 使用userId查询对应的企业微信账号
|
||||||
|
String stuUserName = relieveMapper.getUserNameByUserId(stuUserId);
|
||||||
|
|
||||||
|
if (stuUserName != null && !stuUserName.isEmpty()) {
|
||||||
|
// 5. 拼接审批结果提示信息(适配外宿申请场景)
|
||||||
|
String approveResultDesc = getApprovalResultDesc(approvalResult, currentNodeName);
|
||||||
|
|
||||||
|
// 6. 拼接最终通知内容
|
||||||
|
String content = String.format("您的外宿申请有新的审批进展:%s(%s),%s,<a href='https://zhxg.gxsdxy.cn/wab/#/pages/Approval/index'>点此查看详情</a>",
|
||||||
|
currentNodeName,
|
||||||
|
approveResultDesc,
|
||||||
|
(approvalOpinion != null && !approvalOpinion.isEmpty()) ? "审批意见:" + approvalOpinion : "");
|
||||||
|
|
||||||
|
// 7. 发送企业微信消息
|
||||||
|
weChatUtil.sendTextMessage(stuUserName, content);
|
||||||
|
log.info("✅ 已成功向学生(姓名:{},学号:{},userName:{})发送外宿申请审批通知。审批节点:{},结果:{}",
|
||||||
|
studentName, studentNo, stuUserName, currentNodeName, approveResultDesc);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.warn("⚠ 找到了学生(学号:{},姓名:{},userId:{}),但其对应的企业微信账号为空,无法发送通知。",
|
||||||
|
studentNo, studentName, stuUserId);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有异常,仅记录日志,不影响主流程
|
||||||
|
String studentNo = outsideAccommodationApply.getStudentNo();
|
||||||
|
log.error("❌ 向学生(学号:{})发送外宿申请审批通知时出现异常,但流程将继续。错误详情:", studentNo, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换审批结果为可读描述(适配外宿申请的审批状态)
|
||||||
|
*/
|
||||||
|
private String getApprovalResultDesc(Long approvalResult, String currentNodeName) {
|
||||||
|
if (approvalResult == null) {
|
||||||
|
return "审批已处理";
|
||||||
|
}
|
||||||
|
switch (approvalResult.intValue()) {
|
||||||
|
case 0:
|
||||||
|
return currentNodeName + "驳回";
|
||||||
|
case 1:
|
||||||
|
return currentNodeName + "通过";
|
||||||
|
case 2:
|
||||||
|
return "审批中";
|
||||||
|
default:
|
||||||
|
return "审批已完成";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,13 +2,19 @@ package com.srs.flowable.listener.outsideAccommodation;
|
|||||||
|
|
||||||
import com.srs.common.core.domain.entity.SysUser;
|
import com.srs.common.core.domain.entity.SysUser;
|
||||||
import com.srs.common.doman.vo.TeacherVo;
|
import com.srs.common.doman.vo.TeacherVo;
|
||||||
|
import com.srs.common.utils.DateUtils;
|
||||||
|
import com.srs.common.utils.SecurityUtils;
|
||||||
|
import com.srs.common.utils.WeChatUtil;
|
||||||
import com.srs.common.utils.spring.SpringUtils;
|
import com.srs.common.utils.spring.SpringUtils;
|
||||||
import com.srs.dormitory.domain.DmsOutsideAccommodationApply;
|
import com.srs.dormitory.domain.DmsOutsideAccommodationApply;
|
||||||
import com.srs.flowable.domain.EnlistmentReserve;
|
import com.srs.flowable.domain.EnlistmentReserve;
|
||||||
|
import com.srs.flowable.domain.NotificationManage;
|
||||||
import com.srs.flowable.domain.OutsideAccommodationApply;
|
import com.srs.flowable.domain.OutsideAccommodationApply;
|
||||||
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
import com.srs.flowable.mapper.EnlistmentReserveMapper;
|
||||||
|
import com.srs.flowable.mapper.LeaveMapper;
|
||||||
import com.srs.flowable.mapper.OutsideAccommodationApplyMapper;
|
import com.srs.flowable.mapper.OutsideAccommodationApplyMapper;
|
||||||
import com.srs.system.service.ISysUserService;
|
import com.srs.system.service.ISysUserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.flowable.bpmn.model.BpmnModel;
|
import org.flowable.bpmn.model.BpmnModel;
|
||||||
import org.flowable.bpmn.model.FlowNode;
|
import org.flowable.bpmn.model.FlowNode;
|
||||||
import org.flowable.engine.RepositoryService;
|
import org.flowable.engine.RepositoryService;
|
||||||
@@ -25,6 +31,7 @@ import java.util.List;
|
|||||||
* 用于节点审批之前时,更新下一个节点的负责人变量(approval)
|
* 用于节点审批之前时,更新下一个节点的负责人变量(approval)
|
||||||
*/
|
*/
|
||||||
@Component("OutsideAccommodationStartListener") // Spring Bean名称,与BPMN表达式对应
|
@Component("OutsideAccommodationStartListener") // Spring Bean名称,与BPMN表达式对应
|
||||||
|
@Slf4j
|
||||||
public class OutsideAccommodationStartListener implements ExecutionListener {
|
public class OutsideAccommodationStartListener implements ExecutionListener {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -40,7 +47,7 @@ public class OutsideAccommodationStartListener implements ExecutionListener {
|
|||||||
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
String currentActivityId = execution.getCurrentActivityId(); // 当前节点ID(兼容所有版本)
|
||||||
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
String processDefinitionId = execution.getProcessDefinitionId(); // 流程定义ID
|
||||||
|
|
||||||
// 2. 根据节点ID查询节点名称(核心修正:通过流程定义获取名称)
|
// 2. 根据节点ID查询节点名称(通过流程定义获取名称)
|
||||||
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
String currentNodeName = getNodeNameByActivityId(processDefinitionId, currentActivityId);
|
||||||
if (currentNodeName == null) {
|
if (currentNodeName == null) {
|
||||||
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
throw new RuntimeException("未找到节点ID=" + currentActivityId + "的名称");
|
||||||
@@ -55,6 +62,8 @@ public class OutsideAccommodationStartListener implements ExecutionListener {
|
|||||||
if (nextAssigneeId != null) {
|
if (nextAssigneeId != null) {
|
||||||
execution.setVariable("approval", nextAssigneeId);
|
execution.setVariable("approval", nextAssigneeId);
|
||||||
execution.setVariable("currentNode", currentNodeName);
|
execution.setVariable("currentNode", currentNodeName);
|
||||||
|
// ========== 外宿申请审批通知逻辑 ==========
|
||||||
|
sendOutsideAccommodationNotification(execution, currentNodeName, nextAssigneeId, accommodationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,4 +177,98 @@ public class OutsideAccommodationStartListener implements ExecutionListener {
|
|||||||
throw new RuntimeException("未配置节点[" + currentNodeName + "]的当前负责人规则");
|
throw new RuntimeException("未配置节点[" + currentNodeName + "]的当前负责人规则");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 发送外宿申请审批通知(系统通知+企业微信通知)
|
||||||
|
*/
|
||||||
|
private void sendOutsideAccommodationNotification(DelegateExecution execution, String currentNodeName,
|
||||||
|
Long nextAssigneeId, Long accommodationId) {
|
||||||
|
try {
|
||||||
|
// 获取需要的Mapper和工具类
|
||||||
|
OutsideAccommodationApplyMapper outsideAccommodationApplyMapper = SpringUtils.getBean(OutsideAccommodationApplyMapper.class);
|
||||||
|
LeaveMapper leaveMapper = (LeaveMapper) SpringUtils.getBean("leaveMapper");
|
||||||
|
WeChatUtil weChatUtil = SpringUtils.getBean(WeChatUtil.class);
|
||||||
|
|
||||||
|
// 1. 查询外宿申请详情
|
||||||
|
com.srs.flowable.domain.OutsideAccommodationApply outsideAccommodationApply = outsideAccommodationApplyMapper.selectDmsOutsideAccommodationApplyById(accommodationId);
|
||||||
|
if (outsideAccommodationApply == null) {
|
||||||
|
log.warn("未找到外宿申请记录,ID:{}", accommodationId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 查询下一个审批人的信息(获取用户名/工号)
|
||||||
|
SysUser nextApprover = sysUserService.selectUserById(nextAssigneeId);
|
||||||
|
if (nextApprover == null) {
|
||||||
|
log.warn("未找到审批人信息,用户ID:{}", nextAssigneeId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String approverUserName = nextApprover.getUserName(); // 审批人工号/用户名
|
||||||
|
String approverName = nextApprover.getNickName(); // 审批人姓名
|
||||||
|
|
||||||
|
log.info("开始发送【外宿申请审批】通知,审批节点:{},审批人:{}({})",
|
||||||
|
currentNodeName, approverName, approverUserName);
|
||||||
|
|
||||||
|
// 3. 处理系统通知(先删后加,避免重复通知)
|
||||||
|
NotificationManage notificationManage = new NotificationManage();
|
||||||
|
notificationManage.setContent(String.format("您有一条【外宿申请审批】待处理(节点:%s)", currentNodeName));
|
||||||
|
notificationManage.setReceiver(nextAssigneeId); // 接收人:下一个审批人
|
||||||
|
|
||||||
|
// 3.1 查询是否已有相同通知,有则删除
|
||||||
|
NotificationManage existNotify = leaveMapper.selectCphMsgListForFlowable(notificationManage);
|
||||||
|
if (existNotify != null) {
|
||||||
|
int delRes = leaveMapper.deleteCphMsgById(existNotify.getId());
|
||||||
|
log.info("删除重复的系统通知,通知ID:{},删除结果:{}", existNotify.getId(), delRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.2 添加新的系统通知
|
||||||
|
notificationManage.setSender(SecurityUtils.getUserId() != null ? SecurityUtils.getUserId() : 0L); // 发送人:当前操作人(兜底处理)
|
||||||
|
notificationManage.setCreateTime(DateUtils.getNowDate()); // 创建时间
|
||||||
|
int addRes = leaveMapper.insertCphMsg(notificationManage);
|
||||||
|
log.info("新增系统通知成功,接收人ID:{},添加结果:{}", nextAssigneeId, addRes);
|
||||||
|
|
||||||
|
// 4. 企业微信推送消息
|
||||||
|
if (approverUserName != null && !approverUserName.isEmpty()) {
|
||||||
|
// 构造带超链接的消息内容
|
||||||
|
String weChatContent = String.format(
|
||||||
|
"您有一条【外宿申请审批】待处理(节点:%s),<a href='https://zhxg.gxsdxy.cn/wab/#/pages/Approval/index'>请点击前往处理</a>",
|
||||||
|
currentNodeName
|
||||||
|
);
|
||||||
|
|
||||||
|
// 发送企业微信文本消息
|
||||||
|
weChatUtil.sendTextMessage(approverUserName, weChatContent);
|
||||||
|
log.info("企业微信通知发送成功,接收人工号:{},节点:{}", approverUserName, currentNodeName);
|
||||||
|
} else {
|
||||||
|
log.warn("审批人工号为空,无法发送企业微信通知,用户ID:{}", nextAssigneeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 删除当前审批人的待处理通知 ==========
|
||||||
|
// 4.1 获取当前审批人ID(当前操作人=已完成审批的人)
|
||||||
|
Long currentApproverId = SecurityUtils.getUserId();
|
||||||
|
if (currentApproverId == null) {
|
||||||
|
log.warn("当前审批人ID为空,无法删除其待处理通知");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4.2 构造当前审批人的待处理通知查询条件
|
||||||
|
NotificationManage currentNotifyQuery = new NotificationManage();
|
||||||
|
// 内容匹配:当前节点的待处理通知(关键词+节点名称)
|
||||||
|
currentNotifyQuery.setContent(String.format("您有一条【外宿申请审批】待处理(节点:%s)", currentNodeName));
|
||||||
|
currentNotifyQuery.setReceiver(currentApproverId); // 接收人=当前审批人
|
||||||
|
|
||||||
|
// 4.3 查询当前审批人的该节点待处理通知
|
||||||
|
NotificationManage currentExistNotify = leaveMapper.selectCphMsgListForFlowable(currentNotifyQuery);
|
||||||
|
if (currentExistNotify != null) {
|
||||||
|
// 4.4 删除该通知(清理已处理的待办)
|
||||||
|
int currentDelRes = leaveMapper.deleteCphMsgById(currentExistNotify.getId());
|
||||||
|
log.info("删除当前审批人({})的【{}】待处理通知,通知ID:{},删除结果:{}",
|
||||||
|
currentApproverId, currentNodeName, currentExistNotify.getId(), currentDelRes);
|
||||||
|
} else {
|
||||||
|
log.info("当前审批人({})无【{}】节点的待处理通知,无需删除", currentApproverId, currentNodeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 捕获所有异常,仅记录日志,不影响主流程
|
||||||
|
log.error("发送外宿申请审批通知失败,节点:{},审批人ID:{},错误信息:{}",
|
||||||
|
currentNodeName, nextAssigneeId, e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user