Эх сурвалжийг харах

!1254 BPM-子流程
Merge pull request !1254 from Lesan/feature/bpm-子流程

芋道源码 5 сар өмнө
parent
commit
f7e4293ed2
10 өөрчлөгдсөн 108 нэмэгдсэн , 69 устгасан
  1. 2 1
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java
  2. 1 2
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
  3. 29 12
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
  4. 3 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
  5. 2 3
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
  6. 42 42
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
  7. 3 4
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
  8. 8 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
  9. 13 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
  10. 5 4
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java

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

@@ -14,7 +14,8 @@ import lombok.Getter;
 public enum BpmBoundaryEventTypeEnum {
 public enum BpmBoundaryEventTypeEnum {
 
 
     USER_TASK_TIMEOUT(1, "用户任务超时"),
     USER_TASK_TIMEOUT(1, "用户任务超时"),
-    DELAY_TIMER_TIMEOUT(2, "延迟器超时");
+    DELAY_TIMER_TIMEOUT(2, "延迟器超时"),
+    CHILD_PROCESS_TIMEOUT(3, "子流程超时");
 
 
     private final Integer type;
     private final Integer type;
     private final String name;
     private final String name;

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

@@ -30,8 +30,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
     DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
     DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
     TRIGGER_NODE(15, "触发器", "serviceTask"),
     TRIGGER_NODE(15, "触发器", "serviceTask"),
 
 
-    CHILD_PROCESS(20, "子流程", "callActivity"), // TODO @lesan:CHILD_PROCESS、ASYNC_CHILD_PROCESS 可以合并为一个么?
-    ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"),
+    CHILD_PROCESS(20, "子流程", "callActivity"),
 
 
     // 50 ~ 条件分支
     // 50 ~ 条件分支
     CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
     CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式

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

@@ -425,35 +425,35 @@ public class BpmSimpleModelNodeVO {
     @Valid
     @Valid
     public static class ChildProcessSetting {
     public static class ChildProcessSetting {
 
 
-        // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题?
         @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
         @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
         @NotEmpty(message = "被调用流程不能为空")
         @NotEmpty(message = "被调用流程不能为空")
-        private String calledElement;
+        private String calledProcessDefinitionKey;
 
 
         @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
         @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
         @NotEmpty(message = "被调用流程名称不能为空")
         @NotEmpty(message = "被调用流程名称不能为空")
-        private String calledElementName;
+        private String calledProcessDefinitionName;
 
 
         @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
         @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
         @NotNull(message = "是否异步不能为空")
         @NotNull(message = "是否异步不能为空")
         private Boolean async;
         private Boolean async;
 
 
-        // TODO @lesan:inVariables
-        @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]")
-        private List<IOParameter> inVariable;
+        @Schema(description = "输入参数(主->子)", example = "[]")
+        private List<IOParameter> inVariables;
 
 
-        // TODO @lesan:outVariables
         @Schema(description = "输出参数(子->主)", example = "[]")
         @Schema(description = "输出参数(子->主)", example = "[]")
-        private List<IOParameter> outVariable;
+        private List<IOParameter> outVariables;
 
 
-        @Schema(description = "是否自动跳过子流程发起节点", example = "false")
+        @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
         @NotNull(message = "是否自动跳过子流程发起节点不能为空")
         @NotNull(message = "是否自动跳过子流程发起节点不能为空")
         private Boolean skipStartUserNode;
         private Boolean skipStartUserNode;
 
 
         @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
         @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
-        // TODO @lesan:这个应该也必须填写?
+        @NotNull(message = "子流程发起人配置不能为空")
         private StartUserSetting startUserSetting;
         private StartUserSetting startUserSetting;
 
 
+        @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
+        private TimeoutSetting timeoutSetting;
+
         @Schema(description = "子流程发起人配置")
         @Schema(description = "子流程发起人配置")
         @Data
         @Data
         @Valid
         @Valid
@@ -467,11 +467,28 @@ public class BpmSimpleModelNodeVO {
             @Schema(description = "表单", example = "xxx")
             @Schema(description = "表单", example = "xxx")
             private String formField;
             private String formField;
 
 
-            // TODO @lesan:emptyHandleType => emptyType,和 type 对上?
             @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
             @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
             @NotNull(message = "当子流程发起人为空时类型不能为空")
             @NotNull(message = "当子流程发起人为空时类型不能为空")
             @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)
             @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)
-            private Integer emptyHandleType;
+            private Integer emptyType;
+
+        }
+
+        @Schema(description = "超时设置")
+        @Data
+        @Valid
+        public static class TimeoutSetting {
+
+            @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+            @NotNull(message = "是否开启超时设置不能为空")
+            private Boolean enable;
+
+            @Schema(description = "时间类型", example = "1")
+            @InEnum(BpmDelayTimerTypeEnum.class)
+            private Integer type;
+
+            @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00")
+            private String timeExpression;
 
 
         }
         }
 
 

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

@@ -110,6 +110,9 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
         } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) {
         } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) {
             String taskKey = boundaryEvent.getAttachedToRefId();
             String taskKey = boundaryEvent.getAttachedToRefId();
             taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey);
             taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey);
+        } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) {
+            String taskKey = boundaryEvent.getAttachedToRefId();
+            taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey);
         }
         }
     }
     }
 
 

+ 2 - 3
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java

@@ -1,7 +1,7 @@
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
 
 
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -244,8 +244,7 @@ public class FlowableUtils {
         return formFieldsMap.entrySet().stream()
         return formFieldsMap.entrySet().stream()
                 .limit(3)
                 .limit(3)
                 .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
                 .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
-                        // TODO @lesan: MapUtil.getStr 可以更简单?
-                        StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), ""))))
+                        MapUtil.getStr(processVariables, entry.getValue().getField(), "")))
                 .collect(Collectors.toList());
                 .collect(Collectors.toList());
     }
     }
 
 

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

@@ -48,7 +48,7 @@ public class SimpleModelUtils {
                 new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
                 new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
                 new DelayTimerNodeConvert(), new TriggerNodeConvert(),
                 new DelayTimerNodeConvert(), new TriggerNodeConvert(),
                 new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(),
                 new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(),
-                new ChildProcessConvert(), new AsyncChildProcessConvert());
+                new ChildProcessConvert());
         converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
         converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
     }
     }
 
 
@@ -820,48 +820,41 @@ public class SimpleModelUtils {
     private static class ChildProcessConvert implements NodeConvert {
     private static class ChildProcessConvert implements NodeConvert {
 
 
         @Override
         @Override
-        public CallActivity convert(BpmSimpleModelNodeVO node) {
+        public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {
+            List<FlowElement> flowElements = new ArrayList<>(2);
             BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();
             BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();
-            List<IOParameter> inVariable = childProcessSetting.getInVariable() == null ?
-                    new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable());
+            List<IOParameter> inVariables = childProcessSetting.getInVariables() == null ?
+                    new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables());
             CallActivity callActivity = new CallActivity();
             CallActivity callActivity = new CallActivity();
             callActivity.setId(node.getId());
             callActivity.setId(node.getId());
             callActivity.setName(node.getName());
             callActivity.setName(node.getName());
-            callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈?
+            callActivity.setCalledElementType("key");
             // 1. 是否异步
             // 1. 是否异步
-            callActivity.setAsynchronous(node.getChildProcessSetting().getAsync());
+            if (node.getChildProcessSetting().getAsync()) {
+                // TODO @lesan: 这里目前测试没有跳过执行call activity 后面的节点
+                callActivity.setAsynchronous(true);
+            }
 
 
             // 2. 调用的子流程
             // 2. 调用的子流程
-            callActivity.setCalledElement(childProcessSetting.getCalledElement());
-            callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName());
+            callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey());
+            callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName());
 
 
             // 3. 是否自动跳过子流程发起节点
             // 3. 是否自动跳过子流程发起节点
-            // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()???
-            if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) {
-                IOParameter ioParameter = new IOParameter();
-                ioParameter.setSourceExpression("true");
-                ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
-                inVariable.add(ioParameter);
-            } else {
-                IOParameter ioParameter = new IOParameter();
-                ioParameter.setSourceExpression("false");
-                ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
-                inVariable.add(ioParameter);
-            }
-
-            // 4. 主→子变量传递
-            // 4.1 【默认需要传递的一些变量】流程状态
-            // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈
             IOParameter ioParameter = new IOParameter();
             IOParameter ioParameter = new IOParameter();
+            ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString());
+            ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
+            inVariables.add(ioParameter);
+
+            // 4. 【默认需要传递的一些变量】流程状态
+            ioParameter = new IOParameter();
             ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
             ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
             ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
             ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
-            inVariable.add(ioParameter);
-            callActivity.setInParameters(inVariable);
+            inVariables.add(ioParameter);
 
 
-            // 5. 子→主变量传递
-            // TODO @lesan:通过 isNotEmpty 这种哈
-            if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) {
-                callActivity.setOutParameters(childProcessSetting.getOutVariable());
+            // 5. 主→子变量传递、子->主变量传递
+            callActivity.setInParameters(inVariables);
+            if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) {
+                callActivity.setOutParameters(childProcessSetting.getOutVariables());
             }
             }
 
 
             // 6. 子流程发起人配置
             // 6. 子流程发起人配置
@@ -877,9 +870,27 @@ public class SimpleModelUtils {
             executionListeners.add(flowableListener);
             executionListeners.add(flowableListener);
             callActivity.setExecutionListeners(executionListeners);
             callActivity.setExecutionListeners(executionListeners);
 
 
+            // 7. 超时设置
+            if (childProcessSetting.getTimeoutSetting() != null) {
+                BoundaryEvent boundaryEvent = new BoundaryEvent();
+                boundaryEvent.setId("Event-" + IdUtil.fastUUID());
+                boundaryEvent.setCancelActivity(false);
+                boundaryEvent.setAttachedToRef(callActivity);
+                TimerEventDefinition eventDefinition = new TimerEventDefinition();
+                if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
+                    eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression());
+                } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
+                    eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression());
+                }
+                boundaryEvent.addEventDefinition(eventDefinition);
+                addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType());
+                flowElements.add(boundaryEvent);
+            }
+
             // 添加节点类型
             // 添加节点类型
             addNodeType(node.getType(), callActivity);
             addNodeType(node.getType(), callActivity);
-            return callActivity;
+            flowElements.add(callActivity);
+            return flowElements;
         }
         }
 
 
         @Override
         @Override
@@ -889,16 +900,6 @@ public class SimpleModelUtils {
 
 
     }
     }
 
 
-    private static class AsyncChildProcessConvert extends ChildProcessConvert {
-
-        @Override
-        public BpmSimpleModelNodeTypeEnum getType() {
-            return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS;
-        }
-
-    }
-
-
     private static String buildGatewayJoinId(String id) {
     private static String buildGatewayJoinId(String id) {
         return id + "_join";
         return id + "_join";
     }
     }
@@ -929,7 +930,6 @@ public class SimpleModelUtils {
                 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
                 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS
                 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS
-                || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS
                 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
                 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
             // 添加元素
             // 添加元素
             resultNodes.add(currentNode);
             resultNodes.add(currentNode);

+ 3 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java

@@ -320,7 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         // 遍历 tasks 列表,只处理已结束的 UserTask
         // 遍历 tasks 列表,只处理已结束的 UserTask
         // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
         // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
         // 的话,它无法成为一个节点
         // 的话,它无法成为一个节点
-        // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会有啥影响?微信聊?
+        // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会导致timeline不会展示已结束的子流程
         List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
         List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
         List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
         List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
             FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
             FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
@@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
             // 处理每个任务的 tasks 属性
             // 处理每个任务的 tasks 属性
             for (HistoricActivityInstance activity : taskActivities) {
             for (HistoricActivityInstance activity : taskActivities) {
                 HistoricTaskInstance task = taskMap.get(activity.getTaskId());
                 HistoricTaskInstance task = taskMap.get(activity.getTaskId());
-                // TODO @lesan:这里为啥 continue 哈?
+                // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错
                 if (task == null) {
                 if (task == null) {
                     continue;
                     continue;
                 }
                 }
@@ -510,8 +510,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
         }
         }
 
 
         // 4. 子流程节点
         // 4. 子流程节点
-        if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType()) ||
-                BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS.getType().equals(node.getType())) {
+        if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) {
             return activityNode;
             return activityNode;
         }
         }
         return null;
         return null;

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

@@ -285,4 +285,12 @@ public interface BpmTaskService {
      */
      */
     void triggerReceiveTask(String processInstanceId, String taskDefineKey);
     void triggerReceiveTask(String processInstanceId, String taskDefineKey);
 
 
+    /**
+     * 处理 子流程 审批超时事件
+     *
+     * @param processInstanceId 流程示例编号
+     * @param taskDefineKey     任务 Key
+     */
+    void processChildProcessTimeout(String processInstanceId, String taskDefineKey);
+
 }
 }

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

@@ -41,6 +41,7 @@ import org.flowable.engine.ManagementService;
 import org.flowable.engine.RuntimeService;
 import org.flowable.engine.RuntimeService;
 import org.flowable.engine.TaskService;
 import org.flowable.engine.TaskService;
 import org.flowable.engine.history.HistoricActivityInstance;
 import org.flowable.engine.history.HistoricActivityInstance;
+import org.flowable.engine.runtime.ActivityInstance;
 import org.flowable.engine.runtime.Execution;
 import org.flowable.engine.runtime.Execution;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.engine.runtime.ProcessInstance;
 import org.flowable.task.api.DelegationState;
 import org.flowable.task.api.DelegationState;
@@ -1230,7 +1231,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                 if (userTaskElement.getId().equals(START_USER_NODE_ID)
                 if (userTaskElement.getId().equals(START_USER_NODE_ID)
                         && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
                         && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
                             || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
                             || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
-                        && !Boolean.TRUE.equals(returnTaskFlag)) { // TODO @lesan:ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE) 改成这个有问题么?尽量不用 ! 取反
+                        && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
                     getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                     getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                             .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
                             .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
                     return;
                     return;
@@ -1339,6 +1340,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
                 () -> runtimeService.trigger(execution.getId()));
                 () -> runtimeService.trigger(execution.getId()));
     }
     }
 
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) {
+        List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
+                .processInstanceId(processInstanceId)
+                .activityId(taskDefineKey).list();
+        activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), () -> {
+            moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason());
+        }));
+    }
+
     /**
     /**
      * 获得自身的代理对象,解决 AOP 生效问题
      * 获得自身的代理对象,解决 AOP 生效问题
      *
      *

+ 5 - 4
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java

@@ -59,19 +59,19 @@ public class BpmCallActivityListener implements ExecutionListener {
             // 2.1 当表单值为空时
             // 2.1 当表单值为空时
             if (StrUtil.isEmpty(formFieldValue)) {
             if (StrUtil.isEmpty(formFieldValue)) {
                 // 2.1.1 来自主流程发起人
                 // 2.1.1 来自主流程发起人
-                if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) {
+                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) {
                     FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
                     FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
                     return;
                     return;
                 }
                 }
                 // 2.1.2 来自子流程管理员
                 // 2.1.2 来自子流程管理员
-                if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) {
+                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) {
                     BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
                     BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
                     List<Long> managerUserIds = processDefinition.getManagerUserIds();
                     List<Long> managerUserIds = processDefinition.getManagerUserIds();
                     FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
                     FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
                     return;
                     return;
                 }
                 }
                 // 2.1.3 来自主流程管理员
                 // 2.1.3 来自主流程管理员
-                if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) {
+                if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) {
                     BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId());
                     BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId());
                     List<Long> managerUserIds = processDefinition.getManagerUserIds();
                     List<Long> managerUserIds = processDefinition.getManagerUserIds();
                     FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
                     FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
@@ -82,7 +82,8 @@ public class BpmCallActivityListener implements ExecutionListener {
             try {
             try {
                 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));
                 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));
             } catch (Exception e) {
             } catch (Exception e) {
-                // todo @lesan:打个日志,方便排查
+                log.error("[error][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
+                        DELEGATE_EXPRESSION, formFieldValue);
                 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
                 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
             }
             }
         }
         }