feat: 引擎多出口路由/退回/移交/或签补齐 + 定义 layout 坐标
build / build-push (push) Successful in 5m11s

- 多出口:FlwTask 增 edge_name 列(sql/20260916),FlwTaskRepository.claimAndFinish
  同步落列;FlowTaskService.handle 出度>1 时 edgeName 必填;FlowEngine.continueAdvance
  按 edge name 选路(单出口原路径逐字节兼容,未命中不推进);FlowAdvanceService/
  FlowNodeAdvanceListener 透传;BranchScope 从 FlowEngine 提取
- DefValidator:多出口出口命名必填且同节点唯一;跨分支连线发布期明确拒绝
- 退回 POST /task/{id}/returnBack:默认退主管线最近已批节点,精确失效当前节点待办,
  split 幂等 upsert 使退回重走并行段不撞 flw_branch uk
- 移交 POST /task/{id}/transfer:TRANSFERRED(5)+新建待办,AssigneeResolver.requireUserName
- 或签 ANY 补齐:首个同意失效同节点兄弟待办
- 已办口径/撤回守卫:status in (1,2,4,5);新事件 flow.returned/flow.transferred
- FlowDiagramService:EdgeVo.name/NodeVo.state/DiagramVo.layout,办件图状态计算
- 新 IT:多出口×3、ANY 或签、退回重走并行段、退回/移交/撤回

mvn test 84/84 绿
This commit is contained in:
chenxing
2026-09-16 22:23:02 +08:00
parent 55e3a3f800
commit b8441065d0
31 changed files with 981 additions and 126 deletions
@@ -2,6 +2,7 @@ package com.yunhai.flowcenter.engine;
import com.yunhai.flowcenter.entity.FlwDef;
import com.yunhai.flowcenter.entity.FlwInstance;
import com.yunhai.flowcenter.entity.FlwTask;
import com.yunhai.flowcenter.enums.FlwEnums;
import com.yunhai.flowcenter.repository.FlwDefRepository;
import com.yunhai.flowcenter.repository.FlwInstanceRepository;
@@ -27,9 +28,15 @@ public class FlowAdvanceService {
private final FlwTaskRepository taskRepository;
private final FlowEngine flowEngine;
/** @return 是否实际推进了 */
/**
* @param nodeId 待放行节点
* @param edgeName 多出口节点所选出口(办理事件携带);可空——兜底从该节点
* 最近一条已同意任务的 edge_name 列重放(推进在 AFTER_COMMIT 新事务重查,
* 事件传参只是快路径,落库的值才是权威)
* @return 是否实际推进了
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public boolean tryAdvance(Long instanceId, String nodeId) {
public boolean tryAdvance(Long instanceId, String nodeId, String edgeName) {
FlwInstance instance = instanceRepository.findById(instanceId).orElse(null);
if (instance == null
|| instance.getStatus() != FlwEnums.InstanceStatus.APPROVING.getCode()) {
@@ -55,7 +62,17 @@ public class FlowAdvanceService {
instanceId, instance.getDefId());
return false;
}
flowEngine.continueAdvance(instance, def, nodeId);
String chosen = edgeName;
if (chosen == null) {
chosen = taskRepository.findByInstanceIdAndStatus(instanceId,
FlwEnums.TaskStatus.APPROVED.getCode()).stream()
.filter(t -> nodeId.equals(t.getNodeId()) && t.getEdgeName() != null)
.max(java.util.Comparator.comparing(
FlwTask::getHandleTime, java.util.Comparator.nullsFirst(java.util.Comparator.naturalOrder())))
.map(FlwTask::getEdgeName)
.orElse(null);
}
flowEngine.continueAdvance(instance, def, nodeId, chosen);
instanceRepository.save(instance);
return true;
}