新增AfterImplDeptLeaderStoreJsonListener监听器用于存储json数据
This commit is contained in:
+138
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user