diff --git a/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java b/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java index 0d814b3f..055cd41d 100644 --- a/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java +++ b/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java @@ -1145,9 +1145,7 @@ public class ProcessService implements Iprocess{ public SdmResponse retryFailedNode( String processInstanceId,String failNodeId) { try { - // 2. 查找 Job ID (参考上面的代码) String jobId = findDeadJobId(processInstanceId, failNodeId); - // 3. 执行重试 managementService.moveDeadLetterJobToExecutableJob(jobId, 1); log.info("作业已恢复,等待异步执行器拾取执行..."); return SdmResponse.success("重试任务已提交"); @@ -1160,33 +1158,70 @@ public class ProcessService implements Iprocess{ /** * 任意节点跳转重试 (Rewind/Jump) * 场景:节点失败(进死信)后,用户修改参数,跳转回任意前置节点重新跑。 + * 支持智能修正目标节点:如果是本地应用(Local App)链路,自动跳转回注册节点。 * * @param procInstId 流程实例ID * @param targetNodeId 目标逻辑节点ID (用户想跳去哪里,如 "TaskA") * @param newVariables 新的参数 */ public SdmResponse retryToNode(String procInstId, String targetNodeId, Map newVariables) { - log.info("开始执行回退重试 (Rewind), 流程: {}, 目标: {}", procInstId, targetNodeId); + log.info("开始执行回退重试 (Rewind), 流程: {}, 原始目标: {}", procInstId, targetNodeId); + + // 0. 智能修正目标节点 (针对 Local App 链路) + // 获取流程定义模型 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInstId) + .singleResult(); + if (processInstance == null) { + return SdmResponse.failed("流程实例不存在或已结束"); + } + + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (bpmnModel != null) { + // 尝试解析原始节点ID (去除 _check, _wait 等后缀) + String originalNodeId = FlowNodeIdUtils.parseOriginalNodeId(targetNodeId); + // 推测是否存在对应的注册节点 (Local App 特征) + String registerNodeId = FlowNodeIdUtils.generateRegisterTaskId(originalNodeId); + + // 如果注册节点存在,说明这是 Local App 链路,且必须从注册节点重新开始 + // 避免直接跳到中间状态 (如 check 或 wait),导致本地应用没拉起来 + if (bpmnModel.getMainProcess().getFlowElement(registerNodeId) != null) { + if (!registerNodeId.equals(targetNodeId)) { + log.info("检测到 Local App 链路,自动修正重试目标: {} -> {}", targetNodeId, registerNodeId); + targetNodeId = registerNodeId; + } + } + } // 1. 获取当前流程实例中 **所有** 活跃/停滞的节点 // 包含:正在运行的、报错进死信的、UserTask等待中的 // 为什么要全拿?防止并行分支回退时产生幽灵分支。 List allActiveActivityIds = getFailedActivityIds(procInstId); + // 如果没有找到死信节点,尝试获取当前活跃节点 (可能是 UserTask 等待中,或者正常的运行中节点) + if (allActiveActivityIds.isEmpty()) { + allActiveActivityIds = runtimeService.createExecutionQuery() + .processInstanceId(procInstId) + .list() + .stream() + .map(Execution::getActivityId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + } + if (allActiveActivityIds.isEmpty()) { return SdmResponse.failed("当前流程已结束或状态异常,无法执行跳转"); } - log.info("当前活跃节点集合: {},将全部收束至目标: {}", allActiveActivityIds, targetNodeId); + log.info("当前活跃/失败节点集合: {},将全部收束至目标: {}", allActiveActivityIds, targetNodeId); // 2. 更新流程变量 if (newVariables != null && !newVariables.isEmpty()) { runtimeService.setVariables(procInstId, newVariables); } - // 3. 净室清理 (已移除,由 Delegate/Listener 自动处理) - - // 4. 执行全量跳转 + // 3. 执行全量跳转 try { runtimeService.createChangeActivityStateBuilder() .processInstanceId(procInstId)