|
|
|
@ -322,46 +322,104 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
|
|
|
|
|
dmsBillsMaintInstance.setMaintStatus(DmsConstants.DMS_BILLS_MAINT_INSTANCE_STATUS_MAINTING);
|
|
|
|
|
// 设置实际开始保养时间为当前时间
|
|
|
|
|
dmsBillsMaintInstance.setRealBeginTime(currentDate);
|
|
|
|
|
// FIXME:工作流相关
|
|
|
|
|
// 创建远程启动流程对象
|
|
|
|
|
RemoteStartProcess startProcess = new RemoteStartProcess();
|
|
|
|
|
// 设置流程代码
|
|
|
|
|
startProcess.setFlowCode(DmsConstants.DMS_BILLS_MAINT_INSTANCE_WF_CODE);
|
|
|
|
|
// 获取业务ID(保养实例ID)
|
|
|
|
|
String businessId = dmsBillsMaintInstance.getMaintInstanceId().toString();
|
|
|
|
|
// 设置业务ID
|
|
|
|
|
startProcess.setBusinessId(businessId);
|
|
|
|
|
// 启动工作流程
|
|
|
|
|
RemoteStartProcessReturn remoteStartProcessReturn = remoteWorkflowService.startWorkFlow(startProcess);
|
|
|
|
|
// 获取任务ID
|
|
|
|
|
Long taskId = remoteStartProcessReturn.getTaskId();
|
|
|
|
|
// 如果任务ID为空,抛出流程启动失败异常
|
|
|
|
|
if (StringUtils.isNull(taskId)) {
|
|
|
|
|
throw new ServiceException("流程启动失败");
|
|
|
|
|
}else{
|
|
|
|
|
// ** 异步线程上下文传递【核心问题】 **
|
|
|
|
|
// 在主线程中,用户的登录信息(Token)是存放在一个叫 ThreadLocal 的"线程口袋"里的。
|
|
|
|
|
// 当我们使用 CompletableFuture.runAsync() 开启一个异步线程(子线程)时,
|
|
|
|
|
// 这个"口袋"里的东西默认是不会被带到子线程去的。
|
|
|
|
|
//
|
|
|
|
|
// ** 【解决方案】 **
|
|
|
|
|
// 1. 【捕获Token】: 在主线程里,我们先用 StpUtil.getTokenValue() 把Token拿出来,存好。
|
|
|
|
|
final String tokenValue = StpUtil.getTokenValue();
|
|
|
|
|
// 2. 【传递与恢复】: 使用 TtlRunnable (TransmittableThreadLocal Runnable) 包装我们的异步任务。
|
|
|
|
|
// 它就像一个特殊的"接力棒",能在线程切换时把主线程"口袋"里的东西(包括Token)传递给子线程。
|
|
|
|
|
CompletableFuture.runAsync(TtlRunnable.get(() -> {
|
|
|
|
|
try {
|
|
|
|
|
// 3. 【设置上下文】: 在子线程任务开始时,立刻用 StpUtil.setTokenValue() 把传递过来的Token放进子线程自己的"口袋"。
|
|
|
|
|
StpUtil.setTokenValue(tokenValue);
|
|
|
|
|
// 4. 【执行业务】: 现在子线程就像登录过一样,可以带着Token去调用其他需要权限的服务了。
|
|
|
|
|
updateWorkFlow(dmsBillsMaintInstance, taskId);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 简单记录错误,不影响主流程
|
|
|
|
|
log.error("异步更新保养工作流失败,getBillsMaintCode: {}, 错误: {}",
|
|
|
|
|
dmsBillsMaintInstance.getBillsMaintCode(), e.getMessage(), e);
|
|
|
|
|
// 可以考虑记录到失败表,后续补偿处理
|
|
|
|
|
|
|
|
|
|
// 【关键修复】检查工作流是否已经启动 - 先重新查询最新数据
|
|
|
|
|
// 重新查询保养实例,确保获取到最新的工作流ID(避免使用缓存中的旧数据)
|
|
|
|
|
DmsBillsMaintInstance latestInstance = baseMapper.selectById(dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
|
|
|
|
|
if (latestInstance != null && latestInstance.getWfDefinitionId() == null) {
|
|
|
|
|
// 【工作流启动】创建远程启动流程对象
|
|
|
|
|
RemoteStartProcess startProcess = new RemoteStartProcess();
|
|
|
|
|
// 设置流程代码
|
|
|
|
|
startProcess.setFlowCode(DmsConstants.DMS_BILLS_MAINT_INSTANCE_WF_CODE);
|
|
|
|
|
// 获取业务ID(保养实例ID)
|
|
|
|
|
String businessId = dmsBillsMaintInstance.getMaintInstanceId().toString();
|
|
|
|
|
// 设置业务ID
|
|
|
|
|
startProcess.setBusinessId(businessId);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 启动工作流程
|
|
|
|
|
RemoteStartProcessReturn remoteStartProcessReturn = remoteWorkflowService.startWorkFlow(startProcess);
|
|
|
|
|
|
|
|
|
|
// 【关键修复】获取流程实例ID和任务ID
|
|
|
|
|
Long processInstanceId = remoteStartProcessReturn.getProcessInstanceId();
|
|
|
|
|
Long taskId = remoteStartProcessReturn.getTaskId();
|
|
|
|
|
|
|
|
|
|
// 如果流程实例ID为空,抛出流程启动失败异常
|
|
|
|
|
if (processInstanceId == null) {
|
|
|
|
|
throw new ServiceException("保养工作流启动失败");
|
|
|
|
|
} else {
|
|
|
|
|
// 【关键修复】存储流程实例ID到wfDefinitionId
|
|
|
|
|
dmsBillsMaintInstance.setWfDefinitionId(processInstanceId);
|
|
|
|
|
|
|
|
|
|
// ** 异步线程上下文传递【核心问题】 **
|
|
|
|
|
// 1. 【捕获Token】: 在主线程里,我们先用 StpUtil.getTokenValue() 把Token拿出来,存好。
|
|
|
|
|
final String tokenValue = StpUtil.getTokenValue();
|
|
|
|
|
// 2. 【传递与恢复】: 使用 TtlRunnable 包装我们的异步任务。
|
|
|
|
|
CompletableFuture.runAsync(TtlRunnable.get(() -> {
|
|
|
|
|
try {
|
|
|
|
|
// 3. 【设置上下文】: 在子线程任务开始时,立刻用 StpUtil.setTokenValue() 把传递过来的Token放进子线程自己的"口袋"。
|
|
|
|
|
StpUtil.setTokenValue(tokenValue);
|
|
|
|
|
// 4. 【执行业务】: 现在子线程就像登录过一样,可以带着Token去调用其他需要权限的服务了。
|
|
|
|
|
updateWorkFlow(dmsBillsMaintInstance, taskId);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 简单记录错误,不影响主流程
|
|
|
|
|
log.error("异步更新保养工作流失败,getBillsMaintCode: {}, 错误: {}",
|
|
|
|
|
dmsBillsMaintInstance.getBillsMaintCode(), e.getMessage(), e);
|
|
|
|
|
// 可以考虑记录到失败表,后续补偿处理
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
// 【关键修复】捕获工作流启动异常,避免重复启动导致的错误
|
|
|
|
|
if (e.getMessage() != null && e.getMessage().contains("该单据已提交过申请")) {
|
|
|
|
|
log.warn("保养工单 {} 工作流已存在,跳过启动: {}", dmsBillsMaintInstance.getMaintInstanceId(), e.getMessage());
|
|
|
|
|
// 重新查询获取工作流ID
|
|
|
|
|
DmsBillsMaintInstance updatedInstance = baseMapper.selectById(dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
if (updatedInstance != null && updatedInstance.getWfDefinitionId() != null) {
|
|
|
|
|
dmsBillsMaintInstance.setWfDefinitionId(updatedInstance.getWfDefinitionId());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.error("保养工单 {} 工作流启动失败: {}", dmsBillsMaintInstance.getMaintInstanceId(), e.getMessage());
|
|
|
|
|
throw new ServiceException("保养工作流启动失败: " + e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} else if (latestInstance != null) {
|
|
|
|
|
// 【关键修复】工作流已启动,根据流程实例ID查询当前活跃任务
|
|
|
|
|
// 使用最新查询到的工作流ID
|
|
|
|
|
Long processInstanceId = latestInstance.getWfDefinitionId();
|
|
|
|
|
dmsBillsMaintInstance.setWfDefinitionId(processInstanceId); // 同步更新到当前对象
|
|
|
|
|
|
|
|
|
|
log.info("保养工单 {} 工作流已启动,流程实例ID: {},查询当前活跃任务",
|
|
|
|
|
dmsBillsMaintInstance.getMaintInstanceId(), processInstanceId);
|
|
|
|
|
|
|
|
|
|
// 根据流程实例ID获取当前活跃任务ID
|
|
|
|
|
Long currentActiveTaskId = remoteWorkflowService.getCurrentTaskIdByInstanceId(processInstanceId);
|
|
|
|
|
|
|
|
|
|
if (currentActiveTaskId != null) {
|
|
|
|
|
log.info("保养工单 {} 找到当前活跃任务ID: {}",
|
|
|
|
|
dmsBillsMaintInstance.getMaintInstanceId(), currentActiveTaskId);
|
|
|
|
|
|
|
|
|
|
// 直接推进工作流
|
|
|
|
|
RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask();
|
|
|
|
|
remoteCompleteTask.setTaskId(currentActiveTaskId);
|
|
|
|
|
remoteCompleteTask.setMessage("保养开始,推进工作流");
|
|
|
|
|
// 设置消息类型为非null的List
|
|
|
|
|
List<String> messageTypes = new ArrayList<>();
|
|
|
|
|
messageTypes.add("user"); // 用户操作
|
|
|
|
|
remoteCompleteTask.setMessageType(messageTypes);
|
|
|
|
|
|
|
|
|
|
boolean taskCompleted = remoteWorkflowService.completeTask(remoteCompleteTask);
|
|
|
|
|
if (!taskCompleted) {
|
|
|
|
|
log.error("保养工作流推进失败,任务ID: {}", currentActiveTaskId);
|
|
|
|
|
} else {
|
|
|
|
|
log.info("保养工单 {} 工作流推进成功", dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.warn("保养工单 {} 无法获取当前活跃任务,流程可能已完成", dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.error("保养工单 {} 不存在,无法处理工作流", dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置保养明细的实际开始时间为当前时间
|
|
|
|
@ -409,23 +467,35 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
|
|
|
|
|
return dmsBillsMaintDetail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 【异步工作流更新方法】更新保养工单的工作流信息并推进工作流
|
|
|
|
|
*
|
|
|
|
|
* @param dmsBillsMaintInstance 保养工单实例(已包含流程实例ID)
|
|
|
|
|
* @param taskId 工作流任务ID(用于推进工作流)
|
|
|
|
|
* @return 更新是否成功
|
|
|
|
|
*/
|
|
|
|
|
private Boolean updateWorkFlow(DmsBillsMaintInstance dmsBillsMaintInstance, Long taskId){
|
|
|
|
|
// 设置工作流定义ID
|
|
|
|
|
dmsBillsMaintInstance.setWfDefinitionId(taskId);
|
|
|
|
|
// 重新设置实际开始时间为当前时间(可能已变化)
|
|
|
|
|
// 【关键修复】不再设置工作流ID,因为流程实例ID已经在主方法中设置
|
|
|
|
|
// 【更新保养工单信息】保存实际开始时间
|
|
|
|
|
dmsBillsMaintInstance.setRealBeginTime(new Date());
|
|
|
|
|
// FIXME:完成第一步,待测试待完善
|
|
|
|
|
// 创建远程完成任务对象
|
|
|
|
|
int updateResult = baseMapper.updateById(dmsBillsMaintInstance);
|
|
|
|
|
if (updateResult <= 0) {
|
|
|
|
|
log.error("保养工单工作流信息更新失败,maintInstanceId: {}", dmsBillsMaintInstance.getMaintInstanceId());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 【自动推进工作流】完成第一步任务,推进到下一步
|
|
|
|
|
RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask();
|
|
|
|
|
// 设置任务ID
|
|
|
|
|
remoteCompleteTask.setTaskId(taskId);
|
|
|
|
|
// 设置完成后的消息
|
|
|
|
|
remoteCompleteTask.setMessage("自动推进");
|
|
|
|
|
// 关键:设置messageType为非null的List(解决原始错误)
|
|
|
|
|
// 设置完成消息
|
|
|
|
|
remoteCompleteTask.setMessage("保养工单启动,自动推进");
|
|
|
|
|
// 【关键】设置消息类型为非null的List(避免原始错误)
|
|
|
|
|
List<String> messageTypes = new ArrayList<>();
|
|
|
|
|
messageTypes.add("system"); // 系统消息
|
|
|
|
|
remoteCompleteTask.setMessageType(messageTypes);
|
|
|
|
|
// 完成该任务
|
|
|
|
|
|
|
|
|
|
// 【执行任务完成】推进工作流到下一步
|
|
|
|
|
boolean b = remoteWorkflowService.completeTask(remoteCompleteTask);
|
|
|
|
|
if (!b){
|
|
|
|
|
return false;
|
|
|
|
@ -489,14 +559,30 @@ public class DmsBillsMaintInstanceServiceImpl implements IDmsBillsMaintInstanceS
|
|
|
|
|
// 设置实际结束时间为当前时间
|
|
|
|
|
dmsBillsMaintInstance.setRealEndTime(currentDate);
|
|
|
|
|
|
|
|
|
|
//FIXME:工作流相关
|
|
|
|
|
RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask();
|
|
|
|
|
remoteCompleteTask.setTaskId(dmsBillsMaintInstance.getWfDefinitionId());
|
|
|
|
|
remoteCompleteTask.setMessage("自动推进");
|
|
|
|
|
List<String> messageTypes = new ArrayList<>() ;
|
|
|
|
|
messageTypes.add("system");
|
|
|
|
|
remoteCompleteTask.setMessageType(messageTypes);
|
|
|
|
|
remoteWorkflowService.completeTask(remoteCompleteTask);
|
|
|
|
|
// 【工作流完成】完成保养工作流
|
|
|
|
|
// 【关键修复】不再使用存储的wfDefinitionId(那是流程实例ID)
|
|
|
|
|
// 而是根据业务ID动态获取当前活跃的任务ID
|
|
|
|
|
String businessId = maintInstanceId.toString();
|
|
|
|
|
Long currentActiveTaskId = remoteWorkflowService.getCurrentActiveTaskId(businessId);
|
|
|
|
|
|
|
|
|
|
if (currentActiveTaskId != null) {
|
|
|
|
|
RemoteCompleteTask remoteCompleteTask = new RemoteCompleteTask();
|
|
|
|
|
remoteCompleteTask.setTaskId(currentActiveTaskId); // 使用当前活跃任务ID
|
|
|
|
|
remoteCompleteTask.setMessage("保养工单完成,自动推进");
|
|
|
|
|
// 【关键】设置消息类型为非null的List(避免原始错误)
|
|
|
|
|
List<String> messageTypes = new ArrayList<>();
|
|
|
|
|
messageTypes.add("system"); // 系统消息
|
|
|
|
|
remoteCompleteTask.setMessageType(messageTypes);
|
|
|
|
|
// 完成工作流任务
|
|
|
|
|
boolean taskCompleted = remoteWorkflowService.completeTask(remoteCompleteTask);
|
|
|
|
|
if (!taskCompleted) {
|
|
|
|
|
log.error("保养工作流完成失败,当前活跃任务ID: {}", currentActiveTaskId);
|
|
|
|
|
} else {
|
|
|
|
|
log.info("保养工单 {} 工作流完成成功,任务ID: {}", maintInstanceId, currentActiveTaskId);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
log.warn("保养工单 {} 无法获取当前活跃任务ID,工作流可能已完成或异常", maintInstanceId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新保养实例信息
|
|
|
|
|
baseMapper.updateById(dmsBillsMaintInstance);
|
|
|
|
|