Commit f7e4293ed2b792b072ec50f401f6ff8e4c6ae84b

Authored by 芋道源码
Committed by Gitee
2 parents d0fbb767 15d0a11b

!1254 BPM-子流程

Merge pull request !1254 from Lesan/feature/bpm-子流程
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; @@ -14,7 +14,8 @@ import lombok.Getter;
14 public enum BpmBoundaryEventTypeEnum { 14 public enum BpmBoundaryEventTypeEnum {
15 15
16 USER_TASK_TIMEOUT(1, "用户任务超时"), 16 USER_TASK_TIMEOUT(1, "用户任务超时"),
17 - DELAY_TIMER_TIMEOUT(2, "延迟器超时"); 17 + DELAY_TIMER_TIMEOUT(2, "延迟器超时"),
  18 + CHILD_PROCESS_TIMEOUT(3, "子流程超时");
18 19
19 private final Integer type; 20 private final Integer type;
20 private final String name; 21 private final String name;
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> { @@ -30,8 +30,7 @@ public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable<Integer> {
30 DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), 30 DELAY_TIMER_NODE(14, "延迟器", "receiveTask"),
31 TRIGGER_NODE(15, "触发器", "serviceTask"), 31 TRIGGER_NODE(15, "触发器", "serviceTask"),
32 32
33 - CHILD_PROCESS(20, "子流程", "callActivity"), // TODO @lesan:CHILD_PROCESS、ASYNC_CHILD_PROCESS 可以合并为一个么?  
34 - ASYNC_CHILD_PROCESS(21, "异步子流程", "callActivity"), 33 + CHILD_PROCESS(20, "子流程", "callActivity"),
35 34
36 // 50 ~ 条件分支 35 // 50 ~ 条件分支
37 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 36 CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式
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 { @@ -425,35 +425,35 @@ public class BpmSimpleModelNodeVO {
425 @Valid 425 @Valid
426 public static class ChildProcessSetting { 426 public static class ChildProcessSetting {
427 427
428 - // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题?  
429 @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") 428 @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
430 @NotEmpty(message = "被调用流程不能为空") 429 @NotEmpty(message = "被调用流程不能为空")
431 - private String calledElement; 430 + private String calledProcessDefinitionKey;
432 431
433 @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") 432 @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
434 @NotEmpty(message = "被调用流程名称不能为空") 433 @NotEmpty(message = "被调用流程名称不能为空")
435 - private String calledElementName; 434 + private String calledProcessDefinitionName;
436 435
437 @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") 436 @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
438 @NotNull(message = "是否异步不能为空") 437 @NotNull(message = "是否异步不能为空")
439 private Boolean async; 438 private Boolean async;
440 439
441 - // TODO @lesan:inVariables  
442 - @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]")  
443 - private List<IOParameter> inVariable; 440 + @Schema(description = "输入参数(主->子)", example = "[]")
  441 + private List<IOParameter> inVariables;
444 442
445 - // TODO @lesan:outVariables  
446 @Schema(description = "输出参数(子->主)", example = "[]") 443 @Schema(description = "输出参数(子->主)", example = "[]")
447 - private List<IOParameter> outVariable; 444 + private List<IOParameter> outVariables;
448 445
449 - @Schema(description = "是否自动跳过子流程发起节点", example = "false") 446 + @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
450 @NotNull(message = "是否自动跳过子流程发起节点不能为空") 447 @NotNull(message = "是否自动跳过子流程发起节点不能为空")
451 private Boolean skipStartUserNode; 448 private Boolean skipStartUserNode;
452 449
453 @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") 450 @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
454 - // TODO @lesan:这个应该也必须填写? 451 + @NotNull(message = "子流程发起人配置不能为空")
455 private StartUserSetting startUserSetting; 452 private StartUserSetting startUserSetting;
456 453
  454 + @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
  455 + private TimeoutSetting timeoutSetting;
  456 +
457 @Schema(description = "子流程发起人配置") 457 @Schema(description = "子流程发起人配置")
458 @Data 458 @Data
459 @Valid 459 @Valid
@@ -467,11 +467,28 @@ public class BpmSimpleModelNodeVO { @@ -467,11 +467,28 @@ public class BpmSimpleModelNodeVO {
467 @Schema(description = "表单", example = "xxx") 467 @Schema(description = "表单", example = "xxx")
468 private String formField; 468 private String formField;
469 469
470 - // TODO @lesan:emptyHandleType => emptyType,和 type 对上?  
471 @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 470 @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
472 @NotNull(message = "当子流程发起人为空时类型不能为空") 471 @NotNull(message = "当子流程发起人为空时类型不能为空")
473 @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) 472 @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)
474 - private Integer emptyHandleType; 473 + private Integer emptyType;
  474 +
  475 + }
  476 +
  477 + @Schema(description = "超时设置")
  478 + @Data
  479 + @Valid
  480 + public static class TimeoutSetting {
  481 +
  482 + @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  483 + @NotNull(message = "是否开启超时设置不能为空")
  484 + private Boolean enable;
  485 +
  486 + @Schema(description = "时间类型", example = "1")
  487 + @InEnum(BpmDelayTimerTypeEnum.class)
  488 + private Integer type;
  489 +
  490 + @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00")
  491 + private String timeExpression;
475 492
476 } 493 }
477 494
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 { @@ -110,6 +110,9 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
110 } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { 110 } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) {
111 String taskKey = boundaryEvent.getAttachedToRefId(); 111 String taskKey = boundaryEvent.getAttachedToRefId();
112 taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey); 112 taskService.triggerReceiveTask(event.getProcessInstanceId(), taskKey);
  113 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) {
  114 + String taskKey = boundaryEvent.getAttachedToRefId();
  115 + taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey);
113 } 116 }
114 } 117 }
115 118
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2
  3 +import cn.hutool.core.map.MapUtil;
3 import cn.hutool.core.util.ObjectUtil; 4 import cn.hutool.core.util.ObjectUtil;
4 -import cn.hutool.core.util.StrUtil;  
5 import cn.hutool.extra.spring.SpringUtil; 5 import cn.hutool.extra.spring.SpringUtil;
6 import cn.iocoder.yudao.framework.common.core.KeyValue; 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -244,8 +244,7 @@ public class FlowableUtils { @@ -244,8 +244,7 @@ public class FlowableUtils {
244 return formFieldsMap.entrySet().stream() 244 return formFieldsMap.entrySet().stream()
245 .limit(3) 245 .limit(3)
246 .map(entry -> new KeyValue<>(entry.getValue().getTitle(), 246 .map(entry -> new KeyValue<>(entry.getValue().getTitle(),
247 - // TODO @lesan: MapUtil.getStr 可以更简单?  
248 - StrUtil.toStringOrEmpty(processVariables.getOrDefault(entry.getValue().getField(), "")))) 247 + MapUtil.getStr(processVariables, entry.getValue().getField(), "")))
249 .collect(Collectors.toList()); 248 .collect(Collectors.toList());
250 } 249 }
251 250
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 { @@ -48,7 +48,7 @@ public class SimpleModelUtils {
48 new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), 48 new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(),
49 new DelayTimerNodeConvert(), new TriggerNodeConvert(), 49 new DelayTimerNodeConvert(), new TriggerNodeConvert(),
50 new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), 50 new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(),
51 - new ChildProcessConvert(), new AsyncChildProcessConvert()); 51 + new ChildProcessConvert());
52 converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); 52 converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
53 } 53 }
54 54
@@ -820,48 +820,41 @@ public class SimpleModelUtils { @@ -820,48 +820,41 @@ public class SimpleModelUtils {
820 private static class ChildProcessConvert implements NodeConvert { 820 private static class ChildProcessConvert implements NodeConvert {
821 821
822 @Override 822 @Override
823 - public CallActivity convert(BpmSimpleModelNodeVO node) { 823 + public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {
  824 + List<FlowElement> flowElements = new ArrayList<>(2);
824 BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); 825 BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();
825 - List<IOParameter> inVariable = childProcessSetting.getInVariable() == null ?  
826 - new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); 826 + List<IOParameter> inVariables = childProcessSetting.getInVariables() == null ?
  827 + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables());
827 CallActivity callActivity = new CallActivity(); 828 CallActivity callActivity = new CallActivity();
828 callActivity.setId(node.getId()); 829 callActivity.setId(node.getId());
829 callActivity.setName(node.getName()); 830 callActivity.setName(node.getName());
830 - callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈? 831 + callActivity.setCalledElementType("key");
831 // 1. 是否异步 832 // 1. 是否异步
832 - callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); 833 + if (node.getChildProcessSetting().getAsync()) {
  834 + // TODO @lesan: 这里目前测试没有跳过执行call activity 后面的节点
  835 + callActivity.setAsynchronous(true);
  836 + }
833 837
834 // 2. 调用的子流程 838 // 2. 调用的子流程
835 - callActivity.setCalledElement(childProcessSetting.getCalledElement());  
836 - callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); 839 + callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey());
  840 + callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName());
837 841
838 // 3. 是否自动跳过子流程发起节点 842 // 3. 是否自动跳过子流程发起节点
839 - // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()???  
840 - if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) {  
841 - IOParameter ioParameter = new IOParameter();  
842 - ioParameter.setSourceExpression("true");  
843 - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);  
844 - inVariable.add(ioParameter);  
845 - } else {  
846 - IOParameter ioParameter = new IOParameter();  
847 - ioParameter.setSourceExpression("false");  
848 - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);  
849 - inVariable.add(ioParameter);  
850 - }  
851 -  
852 - // 4. 主→子变量传递  
853 - // 4.1 【默认需要传递的一些变量】流程状态  
854 - // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈  
855 IOParameter ioParameter = new IOParameter(); 843 IOParameter ioParameter = new IOParameter();
  844 + ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString());
  845 + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
  846 + inVariables.add(ioParameter);
  847 +
  848 + // 4. 【默认需要传递的一些变量】流程状态
  849 + ioParameter = new IOParameter();
856 ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); 850 ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
857 ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); 851 ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
858 - inVariable.add(ioParameter);  
859 - callActivity.setInParameters(inVariable); 852 + inVariables.add(ioParameter);
860 853
861 - // 5. 子→主变量传递  
862 - // TODO @lesan:通过 isNotEmpty 这种哈  
863 - if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) {  
864 - callActivity.setOutParameters(childProcessSetting.getOutVariable()); 854 + // 5. 主→子变量传递、子->主变量传递
  855 + callActivity.setInParameters(inVariables);
  856 + if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) {
  857 + callActivity.setOutParameters(childProcessSetting.getOutVariables());
865 } 858 }
866 859
867 // 6. 子流程发起人配置 860 // 6. 子流程发起人配置
@@ -877,9 +870,27 @@ public class SimpleModelUtils { @@ -877,9 +870,27 @@ public class SimpleModelUtils {
877 executionListeners.add(flowableListener); 870 executionListeners.add(flowableListener);
878 callActivity.setExecutionListeners(executionListeners); 871 callActivity.setExecutionListeners(executionListeners);
879 872
  873 + // 7. 超时设置
  874 + if (childProcessSetting.getTimeoutSetting() != null) {
  875 + BoundaryEvent boundaryEvent = new BoundaryEvent();
  876 + boundaryEvent.setId("Event-" + IdUtil.fastUUID());
  877 + boundaryEvent.setCancelActivity(false);
  878 + boundaryEvent.setAttachedToRef(callActivity);
  879 + TimerEventDefinition eventDefinition = new TimerEventDefinition();
  880 + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
  881 + eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression());
  882 + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
  883 + eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression());
  884 + }
  885 + boundaryEvent.addEventDefinition(eventDefinition);
  886 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType());
  887 + flowElements.add(boundaryEvent);
  888 + }
  889 +
880 // 添加节点类型 890 // 添加节点类型
881 addNodeType(node.getType(), callActivity); 891 addNodeType(node.getType(), callActivity);
882 - return callActivity; 892 + flowElements.add(callActivity);
  893 + return flowElements;
883 } 894 }
884 895
885 @Override 896 @Override
@@ -889,16 +900,6 @@ public class SimpleModelUtils { @@ -889,16 +900,6 @@ public class SimpleModelUtils {
889 900
890 } 901 }
891 902
892 - private static class AsyncChildProcessConvert extends ChildProcessConvert {  
893 -  
894 - @Override  
895 - public BpmSimpleModelNodeTypeEnum getType() {  
896 - return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS;  
897 - }  
898 -  
899 - }  
900 -  
901 -  
902 private static String buildGatewayJoinId(String id) { 903 private static String buildGatewayJoinId(String id) {
903 return id + "_join"; 904 return id + "_join";
904 } 905 }
@@ -929,7 +930,6 @@ public class SimpleModelUtils { @@ -929,7 +930,6 @@ public class SimpleModelUtils {
929 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE 930 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
930 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE 931 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
931 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS 932 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS
932 - || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS  
933 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { 933 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
934 // 添加元素 934 // 添加元素
935 resultNodes.add(currentNode); 935 resultNodes.add(currentNode);
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 @@ -320,7 +320,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
320 // 遍历 tasks 列表,只处理已结束的 UserTask 320 // 遍历 tasks 列表,只处理已结束的 UserTask
321 // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 321 // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
322 // 的话,它无法成为一个节点 322 // 的话,它无法成为一个节点
323 - // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会有啥影响?微信聊? 323 + // TODO @芋艿:子流程只有activity,这里获取不到已结束的子流程;TODO @lesan:这个会导致timeline不会展示已结束的子流程
324 List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null); 324 List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
325 List<ActivityNode> approvalNodes = convertList(endTasks, task -> { 325 List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
326 FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); 326 FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
@@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -414,7 +414,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
414 // 处理每个任务的 tasks 属性 414 // 处理每个任务的 tasks 属性
415 for (HistoricActivityInstance activity : taskActivities) { 415 for (HistoricActivityInstance activity : taskActivities) {
416 HistoricTaskInstance task = taskMap.get(activity.getTaskId()); 416 HistoricTaskInstance task = taskMap.get(activity.getTaskId());
417 - // TODO @lesan:这里为啥 continue 哈? 417 + // TODO @lesan:这里为啥 continue 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错
418 if (task == null) { 418 if (task == null) {
419 continue; 419 continue;
420 } 420 }
@@ -510,8 +510,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService @@ -510,8 +510,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
510 } 510 }
511 511
512 // 4. 子流程节点 512 // 4. 子流程节点
513 - if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType()) ||  
514 - BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS.getType().equals(node.getType())) { 513 + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) {
515 return activityNode; 514 return activityNode;
516 } 515 }
517 return null; 516 return null;
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 { @@ -285,4 +285,12 @@ public interface BpmTaskService {
285 */ 285 */
286 void triggerReceiveTask(String processInstanceId, String taskDefineKey); 286 void triggerReceiveTask(String processInstanceId, String taskDefineKey);
287 287
  288 + /**
  289 + * 处理 子流程 审批超时事件
  290 + *
  291 + * @param processInstanceId 流程示例编号
  292 + * @param taskDefineKey 任务 Key
  293 + */
  294 + void processChildProcessTimeout(String processInstanceId, String taskDefineKey);
  295 +
288 } 296 }
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; @@ -41,6 +41,7 @@ import org.flowable.engine.ManagementService;
41 import org.flowable.engine.RuntimeService; 41 import org.flowable.engine.RuntimeService;
42 import org.flowable.engine.TaskService; 42 import org.flowable.engine.TaskService;
43 import org.flowable.engine.history.HistoricActivityInstance; 43 import org.flowable.engine.history.HistoricActivityInstance;
  44 +import org.flowable.engine.runtime.ActivityInstance;
44 import org.flowable.engine.runtime.Execution; 45 import org.flowable.engine.runtime.Execution;
45 import org.flowable.engine.runtime.ProcessInstance; 46 import org.flowable.engine.runtime.ProcessInstance;
46 import org.flowable.task.api.DelegationState; 47 import org.flowable.task.api.DelegationState;
@@ -1230,7 +1231,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -1230,7 +1231,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1230 if (userTaskElement.getId().equals(START_USER_NODE_ID) 1231 if (userTaskElement.getId().equals(START_USER_NODE_ID)
1231 && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 1232 && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核
1232 || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 1233 || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核
1233 - && !Boolean.TRUE.equals(returnTaskFlag)) { // TODO @lesan:ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE) 改成这个有问题么?尽量不用 ! 取反 1234 + && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
1234 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) 1235 getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
1235 .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); 1236 .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
1236 return; 1237 return;
@@ -1339,6 +1340,17 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -1339,6 +1340,17 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1339 () -> runtimeService.trigger(execution.getId())); 1340 () -> runtimeService.trigger(execution.getId()));
1340 } 1341 }
1341 1342
  1343 + @Override
  1344 + @Transactional(rollbackFor = Exception.class)
  1345 + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) {
  1346 + List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
  1347 + .processInstanceId(processInstanceId)
  1348 + .activityId(taskDefineKey).list();
  1349 + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), () -> {
  1350 + moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason());
  1351 + }));
  1352 + }
  1353 +
1342 /** 1354 /**
1343 * 获得自身的代理对象,解决 AOP 生效问题 1355 * 获得自身的代理对象,解决 AOP 生效问题
1344 * 1356 *
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 { @@ -59,19 +59,19 @@ public class BpmCallActivityListener implements ExecutionListener {
59 // 2.1 当表单值为空时 59 // 2.1 当表单值为空时
60 if (StrUtil.isEmpty(formFieldValue)) { 60 if (StrUtil.isEmpty(formFieldValue)) {
61 // 2.1.1 来自主流程发起人 61 // 2.1.1 来自主流程发起人
62 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { 62 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) {
63 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 63 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
64 return; 64 return;
65 } 65 }
66 // 2.1.2 来自子流程管理员 66 // 2.1.2 来自子流程管理员
67 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { 67 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) {
68 BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); 68 BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId());
69 List<Long> managerUserIds = processDefinition.getManagerUserIds(); 69 List<Long> managerUserIds = processDefinition.getManagerUserIds();
70 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); 70 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
71 return; 71 return;
72 } 72 }
73 // 2.1.3 来自主流程管理员 73 // 2.1.3 来自主流程管理员
74 - if (startUserSetting.getEmptyHandleType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { 74 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) {
75 BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); 75 BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId());
76 List<Long> managerUserIds = processDefinition.getManagerUserIds(); 76 List<Long> managerUserIds = processDefinition.getManagerUserIds();
77 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); 77 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
@@ -82,7 +82,8 @@ public class BpmCallActivityListener implements ExecutionListener { @@ -82,7 +82,8 @@ public class BpmCallActivityListener implements ExecutionListener {
82 try { 82 try {
83 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); 83 FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue));
84 } catch (Exception e) { 84 } catch (Exception e) {
85 - // todo @lesan:打个日志,方便排查 85 + log.error("[error][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
  86 + DELEGATE_EXPRESSION, formFieldValue);
86 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 87 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
87 } 88 }
88 } 89 }