diff --git a/common/src/main/java/com/sdm/common/feign/impl/flowable/FlowableClientFeignClientImpl.java b/common/src/main/java/com/sdm/common/feign/impl/flowable/FlowableClientFeignClientImpl.java index d9a92205..77fe95d1 100644 --- a/common/src/main/java/com/sdm/common/feign/impl/flowable/FlowableClientFeignClientImpl.java +++ b/common/src/main/java/com/sdm/common/feign/impl/flowable/FlowableClientFeignClientImpl.java @@ -21,10 +21,10 @@ public class FlowableClientFeignClientImpl implements IFlowableFeignClient { @Autowired private IFlowableFeignClient flowableFeignClient; - public SdmResponse startByProcessDefinitionId(String processDefinitionId, Map variables) { + public SdmResponse startByProcessDefinitionId(String processDefinitionId,String targetNodeId, Map variables) { SdmResponse response; try { - response = flowableFeignClient.startByProcessDefinitionId(processDefinitionId, variables); + response = flowableFeignClient.startByProcessDefinitionId(processDefinitionId,targetNodeId, variables); log.info("启动流程实例:"+ response); return response; } catch (Exception e) { diff --git a/common/src/main/java/com/sdm/common/feign/inter/flowable/IFlowableFeignClient.java b/common/src/main/java/com/sdm/common/feign/inter/flowable/IFlowableFeignClient.java index d505329d..994e7645 100644 --- a/common/src/main/java/com/sdm/common/feign/inter/flowable/IFlowableFeignClient.java +++ b/common/src/main/java/com/sdm/common/feign/inter/flowable/IFlowableFeignClient.java @@ -20,7 +20,7 @@ import java.util.Map; public interface IFlowableFeignClient { @GetMapping("/process/startByProcessDefinitionId") - SdmResponse startByProcessDefinitionId(@RequestParam String processDefinitionId, @RequestBody(required = false) Map variables); + SdmResponse startByProcessDefinitionId(@RequestParam String processDefinitionId, @RequestParam(required = false) String targetNodeId, @RequestBody(required = false) Map variables); @PostMapping("/process/deploy") SdmResponse deploy(@RequestBody ProcessDefinitionDTO processDTO); diff --git a/flowable/src/main/java/com/sdm/flowable/controller/ProcessController.java b/flowable/src/main/java/com/sdm/flowable/controller/ProcessController.java index f0ac6ae0..046ba4ae 100644 --- a/flowable/src/main/java/com/sdm/flowable/controller/ProcessController.java +++ b/flowable/src/main/java/com/sdm/flowable/controller/ProcessController.java @@ -119,14 +119,23 @@ public class ProcessController implements IFlowableFeignClient { * 根据流程定义ID启动流程实例 * * @param processDefinitionId 流程定义ID(指定版本) + * @param targetNodeId 可选,指定从该节点启动(内部会智能修正到真实入口节点) * @param variables 可选的流程启动变量 */ @PostMapping("/startByProcessDefinitionId") public SdmResponse startByProcessDefinitionId( @RequestParam String processDefinitionId, + @RequestParam(required = false) String targetNodeId, @RequestBody(required = false) Map variables) { - log.info("开始启动流程定义: {}",processDefinitionId); - ProcessInstance processInstance = processService.startByProcessDefinitionId(processDefinitionId, variables); + log.info("开始启动流程定义: {}, targetNodeId: {}", processDefinitionId, targetNodeId); + + ProcessInstance processInstance; + if (targetNodeId != null && !targetNodeId.isBlank()) { + processInstance = processService.startProcessAtNode(processDefinitionId, targetNodeId, variables); + } else { + processInstance = processService.startByProcessDefinitionId(processDefinitionId, variables); + } + ProcessInstanceResp processInstanceResp = new ProcessInstanceResp(); processInstanceResp.setProcessInstanceId(processInstance.getId()); processInstanceResp.setProcessDefinitionId(processInstance.getProcessDefinitionId()); 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 32396083..55a30680 100644 --- a/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java +++ b/flowable/src/main/java/com/sdm/flowable/process/ProcessService.java @@ -46,6 +46,7 @@ import org.flowable.validation.ProcessValidatorFactory; import org.flowable.validation.ValidationError; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestBody; @@ -149,14 +150,71 @@ public class ProcessService implements Iprocess{ if (variables == null) { variables = Collections.emptyMap(); } - + log.info("根据流程定义ID启动流程实例, 流程定义ID: {}, 变量数量: {}", processDefinitionId, variables.size()); ProcessInstance instance = runtimeService.startProcessInstanceById(processDefinitionId, variables); - - log.info("流程实例启动成功, 实例ID: {}, 流程定义ID: {}, 业务Key: {}", - instance.getId(), instance.getProcessDefinitionId(), instance.getBusinessKey()); - + + log.info("流程实例启动成功, 实例ID: {}, 流程定义ID: {}, 业务Key: {}", + instance.getId(), instance.getProcessDefinitionId(), instance.getBusinessKey()); + + return instance; + } + + /** + * 从任意节点启动流程(利用 StartEvent 异步特性,在同事务内启动 + 跳转) + * + * @param processDefinitionId 流程定义ID + * @param targetNodeId 目标节点ID(可传原始节点ID或复合节点ID) + * @param variables 流程变量 + * @return 启动后的流程实例 + */ + @Transactional(rollbackFor = Exception.class) + public ProcessInstance startProcessAtNode(String processDefinitionId, String targetNodeId, Map variables) { + log.info("开始从指定节点启动流程. 流程定义: {}, 目标节点: {}", processDefinitionId, targetNodeId); + + // 1. 获取 BPMN 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); + if (bpmnModel == null || bpmnModel.getMainProcess() == null) { + throw new RuntimeException("流程定义不存在: " + processDefinitionId); + } + + // 2. 找 StartEvent + StartEvent startEvent = bpmnModel.getMainProcess().findFlowElementsOfType(StartEvent.class).stream() + .findFirst() + .orElseThrow(() -> new RuntimeException("流程模型中缺少 StartEvent")); + String startEventId = startEvent.getId(); + + // 3. 智能修正目标节点(复用现有复合节点规则) + String realTargetNodeId = targetNodeId; + String originalNodeId = FlowNodeIdUtils.parseOriginalNodeId(targetNodeId); + + String registerNodeId = FlowNodeIdUtils.generateRegisterTaskId(originalNodeId); + String waitUserNodeId = FlowNodeIdUtils.generateWaitUserTaskId(originalNodeId); + + if (bpmnModel.getMainProcess().getFlowElement(registerNodeId) != null) { + realTargetNodeId = registerNodeId; + } else if (bpmnModel.getMainProcess().getFlowElement(waitUserNodeId) != null) { + realTargetNodeId = waitUserNodeId; + } else if (bpmnModel.getMainProcess().getFlowElement(realTargetNodeId) == null) { + throw new RuntimeException("目标节点不存在: " + targetNodeId); + } + + log.info("原始目标节点: {}, 智能修正后的真实入口: {}", targetNodeId, realTargetNodeId); + + // 4. 启动流程(StartEvent 异步,流程会先停在 StartEvent) + if (variables == null) { + variables = new HashMap<>(); + } + ProcessInstance instance = runtimeService.startProcessInstanceById(processDefinitionId, variables); + + // 5. 同事务内立刻跳转到目标节点 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(instance.getId()) + .moveActivityIdTo(startEventId, realTargetNodeId) + .changeState(); + + log.info("流程已成功从节点 [{}] 启动,实例ID: {}", realTargetNodeId, instance.getId()); return instance; } diff --git a/project/src/main/java/com/sdm/project/model/req/SpdmTaskRunReq.java b/project/src/main/java/com/sdm/project/model/req/SpdmTaskRunReq.java index c600ef70..0e45eaad 100644 --- a/project/src/main/java/com/sdm/project/model/req/SpdmTaskRunReq.java +++ b/project/src/main/java/com/sdm/project/model/req/SpdmTaskRunReq.java @@ -18,5 +18,8 @@ public class SpdmTaskRunReq { @Schema(description = "流程模板id") private String templateId; + @Schema(description = "指定启动节点") + private String targetNodeId; + } diff --git a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java index 122c50da..d9141ea8 100644 --- a/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java +++ b/project/src/main/java/com/sdm/project/service/impl/SimulationRunServiceImpl.java @@ -2512,7 +2512,7 @@ public class SimulationRunServiceImpl extends ServiceImpl sdmResponse = flowableFeignClient.startByProcessDefinitionId(simulationRun.getProcessDefinitionId(), variables); + SdmResponse sdmResponse = flowableFeignClient.startByProcessDefinitionId(simulationRun.getProcessDefinitionId(),req.getTargetNodeId(), variables); if (sdmResponse.getData() != null) { this.lambdaUpdate() .set(SimulationRun::getFlowInstanceId, sdmResponse.getData().getProcessInstanceId())