新增AfterImplDeptLeaderStoreJsonListener监听器用于存储json数据

This commit is contained in:
wsm
2026-05-22 16:30:00 +08:00
parent e555250e70
commit fa02124672
@@ -0,0 +1,138 @@
package org.jeecg.xispeak.listener;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.flowable.task.service.delegate.DelegateTask;
import org.flowable.engine.delegate.TaskListener; // 🎯 1. 必须引入标准接口
import org.jeecg.modules.bg.xispeak.entity.BgXiSpeak;
import org.jeecg.modules.bg.xispeak.entity.DeptApproveDetail;
import org.jeecg.modules.bg.xispeak.entity.DeptApproveDetailMap;
import org.jeecg.modules.bg.xispeak.service.IBgXiSpeakService;
import org.jeecg.xispeak.XiSpeakConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
@Slf4j
@Component("AfterImplDeptLeaderStoreJsonListener")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
// 🎯 2. 显式实现 TaskListener 接口,确保 Flowable 引擎能正常识别并回调
public class AfterImplDeptLeaderStoreJsonListener implements TaskListener {
private final XiSpeakConfig xiSpeakConfig;
private final IBgXiSpeakService bgXiSpeakService;
@Override // 🎯 3. 明确标记重写
@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. 🎯 并发安全提示:
// 如果此节点属于会签/并行节点,建议 bgXiSpeakService.getById() 内部实体类带有 MyBatis-Plus 的 @Version 乐观锁。
// 或者此处若业务允许,仍旧推荐使用悲观锁(如 selectForUpdate),工作流死锁通常可以通过优化流程设计或统一加锁顺序解决。
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);
if (StringUtils.isEmpty(cleanDeptId)) {
log.error("【任务通知】解析部门ID为空,跳过更新。RawDeptVar: {}, BusinessKey: {}", rawDeptVar, businessKeyStr);
return;
}
// 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);
// 🎯 如果有乐观锁,更新失败会抛出 OptimisticLockException,从而触发事务回滚与 Flowable 重试机制
bgXiSpeakService.updateById(xiSpeak);
log.info("【任务通知】业务单据 [{}] 部门 [{}] 经办人审批记录成功更新为: {}",
businessKeyStr, cleanDeptId, currentWorkerName);
} else {
log.info("【任务通知】业务单据 [{}] 部门 [{}] 经办人未发生变化 [{}], 跳过数据库更新",
businessKeyStr, cleanDeptId, currentWorkerName);
}
} catch (Exception e) {
log.error(String.format("【任务通知】处理任务结束监听器异常。TaskId: %s, BusinessKey: %s", taskId, businessKeyStr), e);
throw e;
}
}
/**
* 辅助方法:安全解析部门ID
*/
private String parseDeptId(Object rawDeptVar) {
if (rawDeptVar == null) {
return "";
}
// 如果原本就是集合
if (rawDeptVar instanceof Collection) {
Collection<?> collection = (Collection<?>) rawDeptVar;
if (!collection.isEmpty()) {
return Objects.toString(collection.iterator().next(), "").trim();
}
return "";
}
String str = rawDeptVar.toString().trim();
// 🎯 增强防御:如果是 "[1001,1002]" 这种拼成的字符串,只取第一个元素
if (str.startsWith("[") && str.endsWith("]")) {
str = str.substring(1, str.length() - 1);
if (str.contains(",")) {
str = str.split(",")[0];
}
}
return str.trim();
}
}