Browse Source

!1183 Simple设计器-路由分支
Merge pull request !1183 from Lesan/feature/bpm-路由分支

芋道源码 7 months ago
parent
commit
34c8f4cae1

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventType.java

@@ -14,7 +14,7 @@ import lombok.Getter;
 public enum BpmBoundaryEventType {
 
     USER_TASK_TIMEOUT(1, "用户任务超时"),
-    DELAY_TIMER_TIMEOUT(2, "触发器超时");
+    DELAY_TIMER_TIMEOUT(2, "延迟器超时");
 
     private final Integer type;
     private final String name;

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmDelayTimerType.java

@@ -7,7 +7,7 @@ import lombok.Getter;
 import java.util.Arrays;
 
 /**
- * BPM 延器类型枚举
+ * BPM 延器类型枚举
  *
  * @author Lesan
  */

+ 3 - 1
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java

@@ -33,6 +33,7 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
     CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"),
     PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"),
     INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"),
+    ROUTE_BRANCH_NODE(54, "路由分支", "exclusiveGateway")
     ;
 
     public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
@@ -49,7 +50,8 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
     public static boolean isBranchNode(Integer type) {
         return Objects.equals(CONDITION_BRANCH_NODE.getType(), type)
                 || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type)
-                || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type);
+                || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type)
+                || Objects.equals(ROUTE_BRANCH_NODE.getType(), type);
     }
 
     public static BpmSimpleModelNodeType valueOf(Integer type) {

+ 27 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java

@@ -271,4 +271,31 @@ public class BpmSimpleModelNodeVO {
         private String delayTime;
     }
 
+    @Schema(description = "路由分支组", example = "[]")
+    private List<RouteCondition> routeGroup;
+
+    @Schema(description = "默认分支id", example = "Flow_xxx")
+    private String defaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTE_BRANCH_NODE
+
+    @Schema(description = "路由分支")
+    @Data
+    @Valid
+    public static class RouteCondition {
+
+        @Schema(description = "节点Id", example = "Activity_xxx")
+        @NotEmpty(message = "节点Id不能为空")
+        private String nodeId;
+
+        @Schema(description = "条件类型", example = "1")
+        @InEnum(BpmSimpleModeConditionType.class)
+        @NotNull(message = "条件类型不能为空")
+        private Integer conditionType;
+
+        @Schema(description = "条件表达式", example = "${day>3}")
+        private String conditionExpression;
+
+        @Schema(description = "条件组", example = "{}")
+        private ConditionGroups conditionGroups;
+    }
+
 }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java

@@ -106,7 +106,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
                     BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE);
             String taskKey = boundaryEvent.getAttachedToRefId();
             taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType));
-            // 2.2 触发器超时处理
+            // 2.2 延迟器超时处理
         } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventType.DELAY_TIMER_TIMEOUT)) {
             String taskKey = boundaryEvent.getAttachedToRefId();
             taskService.processDelayTimerTimeout(event.getProcessInstanceId(), taskKey);

+ 71 - 23
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java

@@ -42,7 +42,8 @@ public class SimpleModelUtils {
         List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
                 new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(),
                 new DelayTimerNodeConvert(),
-                new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert());
+                new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(),
+                new RouteBranchNodeConvert());
         converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
     }
 
@@ -184,10 +185,12 @@ public class SimpleModelUtils {
         BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
         BpmSimpleModelNodeVO childNode = node.getChildNode();
         List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
-        Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空");
+        // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?
+//        Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空");
         // 分支终点节点 ID
         String branchEndNodeId = null;
-        if (nodeType == BpmSimpleModelNodeType.CONDITION_BRANCH_NODE) { // 条件分支
+        if (nodeType == BpmSimpleModelNodeType.CONDITION_BRANCH_NODE
+                || nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) { // 条件分支或路由分支
             // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID
             branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;
         } else if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE
@@ -198,31 +201,45 @@ public class SimpleModelUtils {
         Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空");
 
         // 3. 遍历分支节点
-        // 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->D->E。其中,A 为分支节点, D 为 A 孩子节点
-        for (BpmSimpleModelNodeVO item : conditionNodes) {
-            Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeType.CONDITION_NODE.getType()),
-                    "条件节点类型({})不符合", item.getType());
-            BpmSimpleModelNodeVO conditionChildNode = item.getChildNode();
-            // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
-            if (isValidNode(conditionChildNode)) {
-                // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
-                SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item);
-                process.addFlowElement(sequenceFlow);
-                // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
-                traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
-            } else {
-                // 3.2 分支没有后续节点。例如说,建立 A->D 的连线
-                SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item);
-                process.addFlowElement(sequenceFlow);
+        if (nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) {
+            // 路由分支遍历
+            for (BpmSimpleModelNodeVO.RouteCondition route : node.getRouteGroup()) {
+                SequenceFlow sFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), route);
+                process.addFlowElement(sFlow);
+            }
+        } else {
+            // 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->D->E。其中,A 为分支节点, D 为 A 孩子节点
+            for (BpmSimpleModelNodeVO item : conditionNodes) {
+                Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeType.CONDITION_NODE.getType()),
+                        "条件节点类型({})不符合", item.getType());
+                BpmSimpleModelNodeVO conditionChildNode = item.getChildNode();
+                // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况
+                if (isValidNode(conditionChildNode)) {
+                    // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线
+                    SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item);
+                    process.addFlowElement(sequenceFlow);
+                    // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线
+                    traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId);
+                } else {
+                    // 3.2 分支没有后续节点。例如说,建立 A->D 的连线
+                    SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item);
+                    process.addFlowElement(sequenceFlow);
+                }
             }
         }
 
-        // 4. 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线
+        // 4. 各分支节点所需特殊处理
         if (nodeType == BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE
                 || nodeType == BpmSimpleModelNodeType.INCLUSIVE_BRANCH_NODE ) {
+            // 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线
             String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId;
             SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId);
             process.addFlowElement(sequenceFlow);
+        } else if (nodeType == BpmSimpleModelNodeType.ROUTE_BRANCH_NODE) {
+            // 如果是路由分支,需要连接后续节点为默认路由
+            SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getDefaultFlowId(),
+                    null, null);
+            process.addFlowElement(sequenceFlow);
         }
 
         // 5. 递归调用后续节点 继续递归。例如说,建立 D->E 的连线
@@ -597,12 +614,20 @@ public class SimpleModelUtils {
          * @param node 条件节点
          */
         public static String buildConditionExpression(BpmSimpleModelNodeVO node) {
-            BpmSimpleModeConditionType conditionTypeEnum = BpmSimpleModeConditionType.valueOf(node.getConditionType());
+            return buildConditionExpression(node.getConditionType(), node.getConditionExpression(),
+                    node.getConditionGroups());
+        }
+        public static String buildConditionExpression(BpmSimpleModelNodeVO.RouteCondition route) {
+            return buildConditionExpression(route.getConditionType(), route.getConditionExpression(),
+                    route.getConditionGroups());
+        }
+        public static String buildConditionExpression(Integer conditionType, String conditionExpression,
+                                                      ConditionGroups conditionGroups) {
+            BpmSimpleModeConditionType conditionTypeEnum = BpmSimpleModeConditionType.valueOf(conditionType);
             if (conditionTypeEnum == BpmSimpleModeConditionType.EXPRESSION) {
-                return node.getConditionExpression();
+                return conditionExpression;
             }
             if (conditionTypeEnum == BpmSimpleModeConditionType.RULE) {
-                ConditionGroups conditionGroups = node.getConditionGroups();
                 if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) {
                     return null;
                 }
@@ -665,6 +690,29 @@ public class SimpleModelUtils {
         }
     }
 
+    public static class RouteBranchNodeConvert implements NodeConvert {
+
+        @Override
+        public ExclusiveGateway convert(BpmSimpleModelNodeVO node) {
+            ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
+            exclusiveGateway.setId(node.getId());
+
+            // 设置默认的序列流(条件)
+            exclusiveGateway.setDefaultFlow(node.getDefaultFlowId());
+            return exclusiveGateway;
+        }
+
+        @Override
+        public BpmSimpleModelNodeType getType() {
+            return BpmSimpleModelNodeType.ROUTE_BRANCH_NODE;
+        }
+
+        public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouteCondition route) {
+            String conditionExpression = ConditionNodeConvert.buildConditionExpression(route);
+            return buildBpmnSequenceFlow(nodeId, route.getNodeId(), null, null, conditionExpression);
+        }
+    }
+
     private static String buildGatewayJoinId(String id) {
         return id + "_join";
     }

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java

@@ -276,7 +276,7 @@ public interface BpmTaskService {
     void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
 
     /**
-     * 处理 延器 超时事件
+     * 处理 延器 超时事件
      *
      * @param processInstanceId 流程示例编号
      * @param taskDefineKey     任务 Key