Commit 3f1cd8057399e8a12c9d25da588ff04571e10374

Authored by jason
2 parents 5e31062d 197c4ad9

Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm

# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
Showing 12 changed files with 115 additions and 74 deletions
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
@@ -423,35 +423,35 @@ public class BpmSimpleModelNodeVO { @@ -423,35 +423,35 @@ public class BpmSimpleModelNodeVO {
423 @Valid 423 @Valid
424 public static class ChildProcessSetting { 424 public static class ChildProcessSetting {
425 425
426 - // TODO @lesan:calledElement => calledProcessDefinitionKey ? 这样更容易理解?不过如果一个流程多次发起,key 变了,好像会有问题?  
427 @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") 426 @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
428 @NotEmpty(message = "被调用流程不能为空") 427 @NotEmpty(message = "被调用流程不能为空")
429 - private String calledElement; 428 + private String calledProcessDefinitionKey;
430 429
431 @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") 430 @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx")
432 @NotEmpty(message = "被调用流程名称不能为空") 431 @NotEmpty(message = "被调用流程名称不能为空")
433 - private String calledElementName; 432 + private String calledProcessDefinitionName;
434 433
435 @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") 434 @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
436 @NotNull(message = "是否异步不能为空") 435 @NotNull(message = "是否异步不能为空")
437 private Boolean async; 436 private Boolean async;
438 437
439 - // TODO @lesan:inVariables  
440 - @Schema(description = "输入参数(主->子)", requiredMode = Schema.RequiredMode.REQUIRED, example = "[]")  
441 - private List<IOParameter> inVariable; 438 + @Schema(description = "输入参数(主->子)", example = "[]")
  439 + private List<IOParameter> inVariables;
442 440
443 - // TODO @lesan:outVariables  
444 @Schema(description = "输出参数(子->主)", example = "[]") 441 @Schema(description = "输出参数(子->主)", example = "[]")
445 - private List<IOParameter> outVariable; 442 + private List<IOParameter> outVariables;
446 443
447 - @Schema(description = "是否自动跳过子流程发起节点", example = "false") 444 + @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
448 @NotNull(message = "是否自动跳过子流程发起节点不能为空") 445 @NotNull(message = "是否自动跳过子流程发起节点不能为空")
449 private Boolean skipStartUserNode; 446 private Boolean skipStartUserNode;
450 447
451 @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") 448 @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
452 - // TODO @lesan:这个应该也必须填写? 449 + @NotNull(message = "子流程发起人配置不能为空")
453 private StartUserSetting startUserSetting; 450 private StartUserSetting startUserSetting;
454 451
  452 + @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
  453 + private TimeoutSetting timeoutSetting;
  454 +
455 @Schema(description = "子流程发起人配置") 455 @Schema(description = "子流程发起人配置")
456 @Data 456 @Data
457 @Valid 457 @Valid
@@ -465,11 +465,28 @@ public class BpmSimpleModelNodeVO { @@ -465,11 +465,28 @@ public class BpmSimpleModelNodeVO {
465 @Schema(description = "表单", example = "xxx") 465 @Schema(description = "表单", example = "xxx")
466 private String formField; 466 private String formField;
467 467
468 - // TODO @lesan:emptyHandleType => emptyType,和 type 对上?  
469 @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 468 @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
470 @NotNull(message = "当子流程发起人为空时类型不能为空") 469 @NotNull(message = "当子流程发起人为空时类型不能为空")
471 @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) 470 @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class)
472 - private Integer emptyHandleType; 471 + private Integer emptyType;
  472 +
  473 + }
  474 +
  475 + @Schema(description = "超时设置")
  476 + @Data
  477 + @Valid
  478 + public static class TimeoutSetting {
  479 +
  480 + @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  481 + @NotNull(message = "是否开启超时设置不能为空")
  482 + private Boolean enable;
  483 +
  484 + @Schema(description = "时间类型", example = "1")
  485 + @InEnum(BpmDelayTimerTypeEnum.class)
  486 + private Integer type;
  487 +
  488 + @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00")
  489 + private String timeExpression;
473 490
474 } 491 }
475 492
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
@@ -110,6 +110,10 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -110,6 +110,10 @@ 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.triggerTask(event.getProcessInstanceId(), taskKey); 112 taskService.triggerTask(event.getProcessInstanceId(), taskKey);
  113 + // 2.3 子流程超时处理
  114 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) {
  115 + String taskKey = boundaryEvent.getAttachedToRefId();
  116 + taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey);
113 } 117 }
114 } 118 }
115 119
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
@@ -416,6 +416,7 @@ public class SimpleModelUtils { @@ -416,6 +416,7 @@ public class SimpleModelUtils {
416 private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, 416 private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask,
417 BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { 417 BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) {
418 // 1.1 定时器边界事件 418 // 1.1 定时器边界事件
  419 + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么?
419 BoundaryEvent boundaryEvent = new BoundaryEvent(); 420 BoundaryEvent boundaryEvent = new BoundaryEvent();
420 boundaryEvent.setId("Event-" + IdUtil.fastUUID()); 421 boundaryEvent.setId("Event-" + IdUtil.fastUUID());
421 boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 422 boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断
@@ -723,6 +724,7 @@ public class SimpleModelUtils { @@ -723,6 +724,7 @@ public class SimpleModelUtils {
723 // 2. 添加接收任务的 Timer Boundary Event 724 // 2. 添加接收任务的 Timer Boundary Event
724 if (node.getDelaySetting() != null) { 725 if (node.getDelaySetting() != null) {
725 // 2.1 定时器边界事件 726 // 2.1 定时器边界事件
  727 + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么?
726 BoundaryEvent boundaryEvent = new BoundaryEvent(); 728 BoundaryEvent boundaryEvent = new BoundaryEvent();
727 boundaryEvent.setId("Event-" + IdUtil.fastUUID()); 729 boundaryEvent.setId("Event-" + IdUtil.fastUUID());
728 boundaryEvent.setCancelActivity(false); 730 boundaryEvent.setCancelActivity(false);
@@ -816,48 +818,41 @@ public class SimpleModelUtils { @@ -816,48 +818,41 @@ public class SimpleModelUtils {
816 private static class ChildProcessConvert implements NodeConvert { 818 private static class ChildProcessConvert implements NodeConvert {
817 819
818 @Override 820 @Override
819 - public CallActivity convert(BpmSimpleModelNodeVO node) { 821 + public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {
  822 + List<FlowElement> flowElements = new ArrayList<>(2);
820 BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); 823 BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting();
821 - List<IOParameter> inVariable = childProcessSetting.getInVariable() == null ?  
822 - new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariable()); 824 + List<IOParameter> inVariables = childProcessSetting.getInVariables() == null ?
  825 + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables());
823 CallActivity callActivity = new CallActivity(); 826 CallActivity callActivity = new CallActivity();
824 callActivity.setId(node.getId()); 827 callActivity.setId(node.getId());
825 callActivity.setName(node.getName()); 828 callActivity.setName(node.getName());
826 - callActivity.setCalledElementType("key"); // TODO @lesan:这里为啥是 key 哈? 829 + callActivity.setCalledElementType("key");
827 // 1. 是否异步 830 // 1. 是否异步
828 - callActivity.setAsynchronous(node.getChildProcessSetting().getAsync()); 831 + if (node.getChildProcessSetting().getAsync()) {
  832 + // TODO @lesan: 这里目前测试没有跳过执行call activity 后面的节点
  833 + callActivity.setAsynchronous(true);
  834 + }
829 835
830 // 2. 调用的子流程 836 // 2. 调用的子流程
831 - callActivity.setCalledElement(childProcessSetting.getCalledElement());  
832 - callActivity.setProcessInstanceName(childProcessSetting.getCalledElementName()); 837 + callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey());
  838 + callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName());
833 839
834 // 3. 是否自动跳过子流程发起节点 840 // 3. 是否自动跳过子流程发起节点
835 - // TODO @lesan:貌似只有 SourceExpression 的区别,直接通过 valueOf childProcessSetting.getSkipStartUserNode()???  
836 - if (Boolean.TRUE.equals(childProcessSetting.getSkipStartUserNode())) {  
837 - IOParameter ioParameter = new IOParameter();  
838 - ioParameter.setSourceExpression("true");  
839 - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);  
840 - inVariable.add(ioParameter);  
841 - } else {  
842 - IOParameter ioParameter = new IOParameter();  
843 - ioParameter.setSourceExpression("false");  
844 - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);  
845 - inVariable.add(ioParameter);  
846 - }  
847 -  
848 - // 4. 主→子变量传递  
849 - // 4.1 【默认需要传递的一些变量】流程状态  
850 - // TODO @lesan:4.1 这个要不,单独一个序号,类似 3. 这个。然后下面,就是把 主→子变量传递、子→主变量传递;这样逻辑连贯点哈  
851 IOParameter ioParameter = new IOParameter(); 841 IOParameter ioParameter = new IOParameter();
  842 + ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString());
  843 + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE);
  844 + inVariables.add(ioParameter);
  845 +
  846 + // 4. 【默认需要传递的一些变量】流程状态
  847 + ioParameter = new IOParameter();
852 ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); 848 ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
853 ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); 849 ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS);
854 - inVariable.add(ioParameter);  
855 - callActivity.setInParameters(inVariable); 850 + inVariables.add(ioParameter);
856 851
857 - // 5. 子→主变量传递  
858 - // TODO @lesan:通过 isNotEmpty 这种哈  
859 - if (childProcessSetting.getOutVariable() != null && !childProcessSetting.getOutVariable().isEmpty()) {  
860 - callActivity.setOutParameters(childProcessSetting.getOutVariable()); 852 + // 5. 主→子变量传递、子->主变量传递
  853 + callActivity.setInParameters(inVariables);
  854 + if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) {
  855 + callActivity.setOutParameters(childProcessSetting.getOutVariables());
861 } 856 }
862 857
863 // 6. 子流程发起人配置 858 // 6. 子流程发起人配置
@@ -873,9 +868,28 @@ public class SimpleModelUtils { @@ -873,9 +868,28 @@ public class SimpleModelUtils {
873 executionListeners.add(flowableListener); 868 executionListeners.add(flowableListener);
874 callActivity.setExecutionListeners(executionListeners); 869 callActivity.setExecutionListeners(executionListeners);
875 870
  871 + // 7. 超时设置
  872 + if (childProcessSetting.getTimeoutSetting() != null) {
  873 + // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么?
  874 + BoundaryEvent boundaryEvent = new BoundaryEvent();
  875 + boundaryEvent.setId("Event-" + IdUtil.fastUUID());
  876 + boundaryEvent.setCancelActivity(false);
  877 + boundaryEvent.setAttachedToRef(callActivity);
  878 + TimerEventDefinition eventDefinition = new TimerEventDefinition();
  879 + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
  880 + eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression());
  881 + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
  882 + eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression());
  883 + }
  884 + boundaryEvent.addEventDefinition(eventDefinition);
  885 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType());
  886 + flowElements.add(boundaryEvent);
  887 + }
  888 +
876 // 添加节点类型 889 // 添加节点类型
877 addNodeType(node.getType(), callActivity); 890 addNodeType(node.getType(), callActivity);
878 - return callActivity; 891 + flowElements.add(callActivity);
  892 + return flowElements;
879 } 893 }
880 894
881 @Override 895 @Override
@@ -885,16 +899,6 @@ public class SimpleModelUtils { @@ -885,16 +899,6 @@ public class SimpleModelUtils {
885 899
886 } 900 }
887 901
888 - private static class AsyncChildProcessConvert extends ChildProcessConvert {  
889 -  
890 - @Override  
891 - public BpmSimpleModelNodeTypeEnum getType() {  
892 - return BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS;  
893 - }  
894 -  
895 - }  
896 -  
897 -  
898 private static String buildGatewayJoinId(String id) { 902 private static String buildGatewayJoinId(String id) {
899 return id + "_join"; 903 return id + "_join";
900 } 904 }
@@ -925,7 +929,6 @@ public class SimpleModelUtils { @@ -925,7 +929,6 @@ public class SimpleModelUtils {
925 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE 929 || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE
926 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE 930 || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE
927 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS 931 || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS
928 - || nodeType == BpmSimpleModelNodeTypeEnum.ASYNC_CHILD_PROCESS  
929 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { 932 || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) {
930 // 添加元素 933 // 添加元素
931 resultNodes.add(currentNode); 934 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 下面的方法会报错;TODO @lesan:写个注释???
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
@@ -276,6 +276,13 @@ public interface BpmTaskService { @@ -276,6 +276,13 @@ public interface BpmTaskService {
276 */ 276 */
277 void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); 277 void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType);
278 278
  279 + /**
  280 + * 处理 ChildProcess 子流程的审批超时事件
  281 + *
  282 + * @param processInstanceId 流程示例编号
  283 + * @param taskDefineKey 任务 Key
  284 + */
  285 + void processChildProcessTimeout(String processInstanceId, String taskDefineKey);
279 286
280 /** 287 /**
281 * 触发流程任务 (ReceiveTask) 的执行 288 * 触发流程任务 (ReceiveTask) 的执行
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;
@@ -1323,6 +1324,16 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -1323,6 +1324,16 @@ public class BpmTaskServiceImpl implements BpmTaskService {
1323 } 1324 }
1324 1325
1325 @Override 1326 @Override
  1327 + @Transactional(rollbackFor = Exception.class)
  1328 + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) {
  1329 + List<ActivityInstance> activityInstances = runtimeService.createActivityInstanceQuery()
  1330 + .processInstanceId(processInstanceId)
  1331 + .activityId(taskDefineKey).list();
  1332 + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(),
  1333 + () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason())));
  1334 + }
  1335 +
  1336 + @Override
1326 public void triggerTask(String processInstanceId, String taskDefineKey) { 1337 public void triggerTask(String processInstanceId, String taskDefineKey) {
1327 Execution execution = runtimeService.createExecutionQuery() 1338 Execution execution = runtimeService.createExecutionQuery()
1328 .processInstanceId(processInstanceId) 1339 .processInstanceId(processInstanceId)
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("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
  86 + DELEGATE_EXPRESSION, formFieldValue);
86 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 87 FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId()));
87 } 88 }
88 } 89 }
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiAccessLogServiceImpl.java
1 package cn.iocoder.yudao.module.infra.service.logger; 1 package cn.iocoder.yudao.module.infra.service.logger;
2 2
3 -import cn.hutool.core.util.StrUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.common.util.string.StrUtils;
6 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; 6 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO; 8 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiAccessLogCreateReqDTO;
@@ -35,8 +35,8 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService { @@ -35,8 +35,8 @@ public class ApiAccessLogServiceImpl implements ApiAccessLogService {
35 @Override 35 @Override
36 public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) { 36 public void createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
37 ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class); 37 ApiAccessLogDO apiAccessLog = BeanUtils.toBean(createDTO, ApiAccessLogDO.class);
38 - apiAccessLog.setRequestParams(StrUtil.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));  
39 - apiAccessLog.setResultMsg(StrUtil.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH)); 38 + apiAccessLog.setRequestParams(StrUtils.maxLength(apiAccessLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
  39 + apiAccessLog.setResultMsg(StrUtils.maxLength(apiAccessLog.getResultMsg(), RESULT_MSG_MAX_LENGTH));
40 if (TenantContextHolder.getTenantId() != null) { 40 if (TenantContextHolder.getTenantId() != null) {
41 apiAccessLogMapper.insert(apiAccessLog); 41 apiAccessLogMapper.insert(apiAccessLog);
42 } else { 42 } else {
yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java
1 package cn.iocoder.yudao.module.infra.service.logger; 1 package cn.iocoder.yudao.module.infra.service.logger;
2 2
3 -import cn.hutool.core.util.StrUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 4 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.framework.common.util.string.StrUtils;
6 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; 6 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; 7 import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
8 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO; 8 import cn.iocoder.yudao.module.infra.api.logger.dto.ApiErrorLogCreateReqDTO;
@@ -39,7 +39,7 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService { @@ -39,7 +39,7 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService {
39 public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) { 39 public void createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
40 ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class) 40 ApiErrorLogDO apiErrorLog = BeanUtils.toBean(createDTO, ApiErrorLogDO.class)
41 .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus()); 41 .setProcessStatus(ApiErrorLogProcessStatusEnum.INIT.getStatus());
42 - apiErrorLog.setRequestParams(StrUtil.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH)); 42 + apiErrorLog.setRequestParams(StrUtils.maxLength(apiErrorLog.getRequestParams(), REQUEST_PARAMS_MAX_LENGTH));
43 if (TenantContextHolder.getTenantId() != null) { 43 if (TenantContextHolder.getTenantId() != null) {
44 apiErrorLogMapper.insert(apiErrorLog); 44 apiErrorLogMapper.insert(apiErrorLog);
45 } else { 45 } else {