diff --git a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeakfb/listener/AfterImplDeptLeaderApproveListener.java b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeakfb/listener/AfterImplDeptLeaderApproveListener.java index 4c95cd8..62ac96e 100644 --- a/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeakfb/listener/AfterImplDeptLeaderApproveListener.java +++ b/jeecg-boot-module/jeecg-module-flow/src/main/java/org/jeecg/xispeakfb/listener/AfterImplDeptLeaderApproveListener.java @@ -2,21 +2,26 @@ package org.jeecg.xispeakfb.listener; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang.StringUtils; import org.flowable.engine.delegate.TaskListener; import org.flowable.task.service.delegate.DelegateTask; import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.bg.xispeak.entity.BgXiSpeak; import org.jeecg.modules.bg.xispeak.entity.BgXiSpeakFeedback; +import org.jeecg.modules.bg.xispeak.entity.DeptApproveDetail; +import org.jeecg.modules.bg.xispeak.entity.DeptApproveDetailMap; import org.jeecg.modules.bg.xispeak.service.IBgXiSpeakFeedbackService; +import org.jeecg.modules.bg.xispeak.service.IBgXiSpeakService; import org.jeecg.modules.tasktask.constant.FlowConstants; import org.jeecg.modules.tasktask.entity.TaskTask; import org.jeecg.modules.tasktask.service.ITaskTaskService; +import org.jeecg.xispeak.XiSpeakConfig; import org.jeecg.xispeakfb.XiSpeakFeedbackConfig; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import java.util.HashMap; -import java.util.Map; +import java.util.*; @Slf4j @Component("AfterImplDeptLeaderApproveFeedbackListener") @@ -24,13 +29,96 @@ import java.util.Map; public class AfterImplDeptLeaderApproveListener implements TaskListener { private static final long serialVersionUID = 1L; - private final XiSpeakFeedbackConfig xiSpeakFeedbackConfig; private final IBgXiSpeakFeedbackService bgXiSpeakFeedbackService; private final ITaskTaskService taskTaskService; + private final XiSpeakConfig xiSpeakConfig; + private final IBgXiSpeakService bgXiSpeakService; @Transactional(rollbackFor = Exception.class) public void notify(DelegateTask delegateTask) { + String currentWorkerName = delegateTask.getAssignee(); + Object rawBusinessKey = delegateTask.getVariable(xiSpeakConfig.getXiSpeak().getBusinessKey()); + String taskId = delegateTask.getId(); + // 1. 基础边界防御性校验 + if (StringUtils.isEmpty(currentWorkerName) || rawBusinessKey == null) { + log.warn("【任务通知】无法获取有效的审批人或业务ID,略过处理。TaskId: {}, Assignee: {}, BusinessKey: {}", + taskId, currentWorkerName, rawBusinessKey); + return; + } + + String businessKeyStr = rawBusinessKey.toString(); + + try { + // 2. 🎯 架构优化:移除 FOR UPDATE 悲观锁,改用普通查询,预防工作流交叉死锁 + BgXiSpeak xiSpeak = bgXiSpeakService.getById(businessKeyStr); + if (xiSpeak == null) { + log.error("【任务通知】业务数据不存在,终止处理。BusinessKey: {}", businessKeyStr); + return; + } + + // 3. 部门变量安全解析 + String implDeptKey = xiSpeakConfig.getXiSpeak().getImplDeptCollectionUsedKey(); + Object rawDeptVar = delegateTask.getVariableLocal(implDeptKey); + if (rawDeptVar == null) { + rawDeptVar = delegateTask.getVariable(implDeptKey); + } + + if (rawDeptVar == null) { + log.error("【任务通知】流程数据异常:未找到实施部门ID变量: {}, BusinessKey: {}", implDeptKey, businessKeyStr); + return; + } + + // 🎯 优化:更安全的解析机制 + String cleanDeptId = parseDeptId(rawDeptVar); + + // 4. 安全获取或创建部门审批详情 + DeptApproveDetailMap approveMap = xiSpeak.getApproveInfo(); + if (approveMap == null) { + approveMap = new DeptApproveDetailMap(); + } + + DeptApproveDetail detail = approveMap.get(cleanDeptId); + if (detail == null) { + detail = new DeptApproveDetail(); + detail.setDeptId(cleanDeptId); + detail.setImplUserNameList(new ArrayList<>()); + approveMap.put(cleanDeptId, detail); + } + + // 5. 状态比对与幂等更新 + if (!Objects.equals(detail.getApproverName(), currentWorkerName)) { + detail.setApproverName(currentWorkerName); + + // 写回并更新 + xiSpeak.setApproveInfo(approveMap); + bgXiSpeakService.updateById(xiSpeak); + + log.info("【任务通知】业务单据 [{}] 部门 [{}] 经办人审批记录成功更新为: {}", + businessKeyStr, cleanDeptId, currentWorkerName); + } else { + log.info("【任务通知】业务单据 [{}] 部门 [{}] 经办人未发生变化 [{}], 跳过数据库更新", + businessKeyStr, cleanDeptId, currentWorkerName); + } + + } catch (Exception e) { + // 6. 异常全上下文捕获,方便线上无缝排查 + log.error(String.format("【任务通知】处理任务结束监听器异常。TaskId: %s, BusinessKey: %s", taskId, businessKeyStr), e); + throw e; // 抛出异常以触发 Spring 事务和 Flowable 流程回滚 + } + } + + /** + * 辅助方法:安全解析部门ID,支持 Collection 和 String 表现形式 + */ + private String parseDeptId(Object rawDeptVar) { + if (rawDeptVar instanceof Collection) { + Collection collection = (Collection) rawDeptVar; + if (!collection.isEmpty()) { + return Objects.toString(collection.iterator().next(), "").trim(); + } + } + return rawDeptVar.toString().replace("[", "").replace("]", "").trim(); } }