Commit 35f0d689f1e4126a78f99b832074aaae0402f8b1

Authored by 芋道源码
Committed by Gitee
2 parents 3f1cd805 8a9d64bb

!1262 feat: 子流程-多实例

Merge pull request !1262 from Lesan/feature/bpm-子流程
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.enums.definition;
  2 +
  3 +import cn.hutool.core.util.ArrayUtil;
  4 +import cn.iocoder.yudao.framework.common.core.ArrayValuable;
  5 +import lombok.AllArgsConstructor;
  6 +import lombok.Getter;
  7 +
  8 +import java.util.Arrays;
  9 +
  10 +/**
  11 + * BPM 子流程多实例来源类型枚举
  12 + *
  13 + * @author Lesan
  14 + */
  15 +@Getter
  16 +@AllArgsConstructor
  17 +public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable<Integer> {
  18 +
  19 + FIXED_QUANTITY(1, "固定数量"),
  20 + DIGITAL_FORM(2, "数字表单"),
  21 + MULTI_FORM(3, "多项表单");
  22 +
  23 + private final Integer type;
  24 + private final String name;
  25 +
  26 + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new);
  27 +
  28 + public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) {
  29 + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values());
  30 + }
  31 +
  32 + @Override
  33 + public Integer[] array() {
  34 + return ARRAYS;
  35 + }
  36 +}
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
@@ -452,6 +452,9 @@ public class BpmSimpleModelNodeVO { @@ -452,6 +452,9 @@ public class BpmSimpleModelNodeVO {
452 @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") 452 @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
453 private TimeoutSetting timeoutSetting; 453 private TimeoutSetting timeoutSetting;
454 454
  455 + @Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
  456 + private MultiInstanceSetting multiInstanceSetting;
  457 +
455 @Schema(description = "子流程发起人配置") 458 @Schema(description = "子流程发起人配置")
456 @Data 459 @Data
457 @Valid 460 @Valid
@@ -490,5 +493,33 @@ public class BpmSimpleModelNodeVO { @@ -490,5 +493,33 @@ public class BpmSimpleModelNodeVO {
490 493
491 } 494 }
492 495
  496 + @Schema(description = "多实例设置")
  497 + @Data
  498 + @Valid
  499 + public static class MultiInstanceSetting {
  500 +
  501 + @Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  502 + @NotNull(message = "是否开启多实例不能为空")
  503 + private Boolean enable;
  504 +
  505 + @Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  506 + @NotNull(message = "是否串行不能为空")
  507 + private Boolean sequential;
  508 +
  509 + @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
  510 + @NotNull(message = "完成比例不能为空")
  511 + private Integer completeRatio;
  512 +
  513 + @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  514 + @NotNull(message = "多实例来源类型不能为空")
  515 + @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class)
  516 + private Integer sourceType;
  517 +
  518 + @Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  519 + @NotNull(message = "多实例来源不能为空")
  520 + private String source;
  521 +
  522 + }
  523 +
493 } 524 }
494 } 525 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum;
4 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 8 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
6 -import cn.iocoder.yudao.framework.common.util.collection.SetUtils;  
7 import lombok.Setter; 9 import lombok.Setter;
8 import org.flowable.bpmn.model.Activity; 10 import org.flowable.bpmn.model.Activity;
  11 +import org.flowable.bpmn.model.CallActivity;
  12 +import org.flowable.bpmn.model.FlowElement;
  13 +import org.flowable.bpmn.model.UserTask;
9 import org.flowable.engine.delegate.DelegateExecution; 14 import org.flowable.engine.delegate.DelegateExecution;
10 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; 15 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
11 import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; 16 import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
12 17
  18 +import java.util.List;
13 import java.util.Set; 19 import java.util.Set;
14 20
15 /** 21 /**
@@ -42,27 +48,42 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav @@ -42,27 +48,42 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
42 */ 48 */
43 @Override 49 @Override
44 protected int resolveNrOfInstances(DelegateExecution execution) { 50 protected int resolveNrOfInstances(DelegateExecution execution) {
45 - // 第一步,设置 collectionVariable 和 CollectionVariable  
46 - // 从 execution.getVariable() 读取所有任务处理人的 key  
47 - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的  
48 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());  
49 - // 从 execution.getVariable() 读取当前所有任务处理的人的 key  
50 - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); 51 + if (execution.getCurrentFlowElement() instanceof UserTask) {
  52 + // 第一步,设置 collectionVariable 和 CollectionVariable
  53 + // 从 execution.getVariable() 读取所有任务处理人的 key
  54 + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
  55 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
  56 + // 从 execution.getVariable() 读取当前所有任务处理的人的 key
  57 + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
51 58
52 - // 第二步,获取任务的所有处理人  
53 - @SuppressWarnings("unchecked")  
54 - Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);  
55 - if (assigneeUserIds == null) {  
56 - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);  
57 - if (CollUtil.isEmpty(assigneeUserIds)) {  
58 - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!  
59 - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务  
60 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时  
61 - assigneeUserIds = SetUtils.asSet((Long) null); 59 + // 第二步,获取任务的所有处理人
  60 + @SuppressWarnings("unchecked")
  61 + Set<Long> assigneeUserIds = (Set<Long>) execution.getVariable(super.collectionVariable, Set.class);
  62 + if (assigneeUserIds == null) {
  63 + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
  64 + if (CollUtil.isEmpty(assigneeUserIds)) {
  65 + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
  66 + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务
  67 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时
  68 + assigneeUserIds = SetUtils.asSet((Long) null);
  69 + }
  70 + execution.setVariableLocal(super.collectionVariable, assigneeUserIds);
62 } 71 }
63 - execution.setVariableLocal(super.collectionVariable, assigneeUserIds); 72 + return assigneeUserIds.size();
64 } 73 }
65 - return assigneeUserIds.size(); 74 +
  75 + if (execution.getCurrentFlowElement() instanceof CallActivity) {
  76 + FlowElement flowElement = execution.getCurrentFlowElement();
  77 + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement);
  78 + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) {
  79 + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class);
  80 + }
  81 + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) {
  82 + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size();
  83 + }
  84 + }
  85 +
  86 + return super.resolveNrOfInstances(execution);
66 } 87 }
67 88
68 } 89 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java
@@ -2,14 +2,20 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior; @@ -2,14 +2,20 @@ package cn.iocoder.yudao.module.bpm.framework.flowable.core.behavior;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.util.collection.SetUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum;
5 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 8 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
7 import lombok.Setter; 9 import lombok.Setter;
8 import org.flowable.bpmn.model.Activity; 10 import org.flowable.bpmn.model.Activity;
  11 +import org.flowable.bpmn.model.CallActivity;
  12 +import org.flowable.bpmn.model.FlowElement;
  13 +import org.flowable.bpmn.model.UserTask;
9 import org.flowable.engine.delegate.DelegateExecution; 14 import org.flowable.engine.delegate.DelegateExecution;
10 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; 15 import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
11 import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; 16 import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
12 17
  18 +import java.util.List;
13 import java.util.Set; 19 import java.util.Set;
14 20
15 /** 21 /**
@@ -35,28 +41,43 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB @@ -35,28 +41,43 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB
35 */ 41 */
36 @Override 42 @Override
37 protected int resolveNrOfInstances(DelegateExecution execution) { 43 protected int resolveNrOfInstances(DelegateExecution execution) {
38 - // 第一步,设置 collectionVariable 和 CollectionVariable  
39 - // 从 execution.getVariable() 读取所有任务处理人的 key  
40 - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的  
41 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());  
42 - // 从 execution.getVariable() 读取当前所有任务处理的人的 key  
43 - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());  
44 -  
45 - // 第二步,获取任务的所有处理人  
46 - // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人  
47 - @SuppressWarnings("unchecked")  
48 - Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);  
49 - if (assigneeUserIds == null) {  
50 - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);  
51 - if (CollUtil.isEmpty(assigneeUserIds)) {  
52 - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!  
53 - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务  
54 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时  
55 - assigneeUserIds = SetUtils.asSet((Long) null); 44 + if (execution.getCurrentFlowElement() instanceof UserTask) {
  45 + // 第一步,设置 collectionVariable 和 CollectionVariable
  46 + // 从 execution.getVariable() 读取所有任务处理人的 key
  47 + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的
  48 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
  49 + // 从 execution.getVariable() 读取当前所有任务处理的人的 key
  50 + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
  51 +
  52 + // 第二步,获取任务的所有处理人
  53 + // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人
  54 + @SuppressWarnings("unchecked")
  55 + Set<Long> assigneeUserIds = (Set<Long>) execution.getVariableLocal(super.collectionVariable, Set.class);
  56 + if (assigneeUserIds == null) {
  57 + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
  58 + if (CollUtil.isEmpty(assigneeUserIds)) {
  59 + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过!
  60 + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务
  61 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时
  62 + assigneeUserIds = SetUtils.asSet((Long) null);
  63 + }
  64 + execution.setVariableLocal(super.collectionVariable, assigneeUserIds);
  65 + }
  66 + return assigneeUserIds.size();
  67 + }
  68 +
  69 + if (execution.getCurrentFlowElement() instanceof CallActivity) {
  70 + FlowElement flowElement = execution.getCurrentFlowElement();
  71 + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement);
  72 + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType())) {
  73 + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class);
  74 + }
  75 + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) {
  76 + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size();
56 } 77 }
57 - execution.setVariableLocal(super.collectionVariable, assigneeUserIds);  
58 } 78 }
59 - return assigneeUserIds.size(); 79 +
  80 + return super.resolveNrOfInstances(execution);
60 } 81 }
61 82
62 } 83 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java
@@ -69,6 +69,11 @@ public interface BpmnModelConstants { @@ -69,6 +69,11 @@ public interface BpmnModelConstants {
69 String USER_TASK_APPROVE_METHOD = "approveMethod"; 69 String USER_TASK_APPROVE_METHOD = "approveMethod";
70 70
71 /** 71 /**
  72 + * BPMN Child Process 的扩展属性,用于标记多实例来源类型
  73 + */
  74 + String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = "childProcessMultiInstanceSourceType";
  75 +
  76 + /**
72 * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 77 * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限
73 */ 78 */
74 String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; 79 String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission";
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -159,6 +159,17 @@ public class BpmnModelUtils { @@ -159,6 +159,17 @@ public class BpmnModelUtils {
159 } 159 }
160 160
161 /** 161 /**
  162 + * 解析子流程多实例来源类型
  163 + *
  164 + * @see BpmChildProcessMultiInstanceSourceTypeEnum
  165 + * @param element 任务节点
  166 + * @return 多实例来源类型
  167 + */
  168 + public static Integer parseMultiInstanceSourceType(FlowElement element) {
  169 + return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE));
  170 + }
  171 +
  172 + /**
162 * 添加任务拒绝处理元素 173 * 添加任务拒绝处理元素
163 * 174 *
164 * @param rejectHandler 任务拒绝处理 175 * @param rejectHandler 任务拒绝处理
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
@@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.B @@ -10,7 +10,6 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.B
10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; 10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
11 import cn.iocoder.yudao.module.bpm.enums.definition.*; 11 import cn.iocoder.yudao.module.bpm.enums.definition.*;
12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; 12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
13 -import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;  
14 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; 13 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
15 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; 14 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate;
16 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; 15 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate;
@@ -84,7 +83,7 @@ public class SimpleModelUtils { @@ -84,7 +83,7 @@ public class SimpleModelUtils {
84 traverseNodeToBuildFlowNode(startNode, process); 83 traverseNodeToBuildFlowNode(startNode, process);
85 84
86 // 3. 构建并添加节点之间的连线 Sequence Flow 85 // 3. 构建并添加节点之间的连线 Sequence Flow
87 - EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); 86 + EndEvent endEvent = getEndEvent(bpmnModel);
88 traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); 87 traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId());
89 88
90 // 4. 自动布局 89 // 4. 自动布局
@@ -364,7 +363,7 @@ public class SimpleModelUtils { @@ -364,7 +363,7 @@ public class SimpleModelUtils {
364 userTask.setName(node.getName()); 363 userTask.setName(node.getName());
365 364
366 // 人工审批 365 // 人工审批
367 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); 366 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType());
368 // 候选人策略为发起人自己 367 // 候选人策略为发起人自己
369 addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask); 368 addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask);
370 // 添加表单字段权限属性元素 369 // 添加表单字段权限属性元素
@@ -415,25 +414,17 @@ public class SimpleModelUtils { @@ -415,25 +414,17 @@ public class SimpleModelUtils {
415 */ 414 */
416 private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, 415 private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask,
417 BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { 416 BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) {
418 - // 1.1 定时器边界事件  
419 - // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么?  
420 - BoundaryEvent boundaryEvent = new BoundaryEvent();  
421 - boundaryEvent.setId("Event-" + IdUtil.fastUUID());  
422 - boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断  
423 - boundaryEvent.setAttachedToRef(userTask);  
424 - // 1.2 定义超时时间、最大提醒次数  
425 - TimerEventDefinition eventDefinition = new TimerEventDefinition();  
426 - eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration()); 417 + // 1. 创建 Timeout Boundary Event
  418 + String timeCycle = null;
427 if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) && 419 if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) &&
428 timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { 420 timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {
429 - eventDefinition.setTimeCycle(String.format("R%d/%s",  
430 - timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); 421 + timeCycle = String.format("R%d/%s",
  422 + timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration());
431 } 423 }
432 - boundaryEvent.addEventDefinition(eventDefinition); 424 + BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(),
  425 + timeoutHandler.getTimeDuration(), timeCycle, null);
433 426
434 - // 2.1 添加定时器边界事件类型  
435 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType());  
436 - // 2.2 添加超时执行动作元素 427 + // 2 添加超时执行动作元素
437 addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType()); 428 addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType());
438 return boundaryEvent; 429 return boundaryEvent;
439 } 430 }
@@ -516,7 +507,7 @@ public class SimpleModelUtils { @@ -516,7 +507,7 @@ public class SimpleModelUtils {
516 BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); 507 BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);
517 Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum); 508 Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum);
518 // 添加审批方式的扩展属性 509 // 添加审批方式的扩展属性
519 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, approveMethod); 510 + addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod);
520 if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) { 511 if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) {
521 // 随机审批,不需要设置多实例属性 512 // 随机审批,不需要设置多实例属性
522 return; 513 return;
@@ -723,21 +714,14 @@ public class SimpleModelUtils { @@ -723,21 +714,14 @@ public class SimpleModelUtils {
723 714
724 // 2. 添加接收任务的 Timer Boundary Event 715 // 2. 添加接收任务的 Timer Boundary Event
725 if (node.getDelaySetting() != null) { 716 if (node.getDelaySetting() != null) {
726 - // 2.1 定时器边界事件  
727 - // TODO @lesan:一些 BoundaryEvent timeout 的,可以做一些基础的设置么?  
728 - BoundaryEvent boundaryEvent = new BoundaryEvent();  
729 - boundaryEvent.setId("Event-" + IdUtil.fastUUID());  
730 - boundaryEvent.setCancelActivity(false);  
731 - boundaryEvent.setAttachedToRef(receiveTask);  
732 - // 2.2 定义超时时间  
733 - TimerEventDefinition eventDefinition = new TimerEventDefinition(); 717 + BoundaryEvent boundaryEvent = null;
734 if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { 718 if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
735 - eventDefinition.setTimeDuration(node.getDelaySetting().getDelayTime()); 719 + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),
  720 + node.getDelaySetting().getDelayTime(), null, null);
736 } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { 721 } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
737 - eventDefinition.setTimeDate(node.getDelaySetting().getDelayTime()); 722 + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),
  723 + null, null, node.getDelaySetting().getDelayTime());
738 } 724 }
739 - boundaryEvent.addEventDefinition(eventDefinition);  
740 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType());  
741 flowElements.add(boundaryEvent); 725 flowElements.add(boundaryEvent);
742 } 726 }
743 return flowElements; 727 return flowElements;
@@ -869,23 +853,35 @@ public class SimpleModelUtils { @@ -869,23 +853,35 @@ public class SimpleModelUtils {
869 callActivity.setExecutionListeners(executionListeners); 853 callActivity.setExecutionListeners(executionListeners);
870 854
871 // 7. 超时设置 855 // 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(); 856 + if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) {
  857 + BoundaryEvent boundaryEvent = null;
879 if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { 858 if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) {
880 - eventDefinition.setTimeDuration(childProcessSetting.getTimeoutSetting().getTimeExpression()); 859 + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(),
  860 + childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null);
881 } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { 861 } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) {
882 - eventDefinition.setTimeDate(childProcessSetting.getTimeoutSetting().getTimeExpression()); 862 + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(),
  863 + null, null, childProcessSetting.getTimeoutSetting().getTimeExpression());
883 } 864 }
884 - boundaryEvent.addEventDefinition(eventDefinition);  
885 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType());  
886 flowElements.add(boundaryEvent); 865 flowElements.add(boundaryEvent);
887 } 866 }
888 867
  868 + // 8. 多实例
  869 + if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) {
  870 + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics();
  871 + multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential());
  872 + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) {
  873 + multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource());
  874 + }
  875 + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.DIGITAL_FORM.getType()) ||
  876 + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTI_FORM.getType())) {
  877 + multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource());
  878 + }
  879 + multiInstanceCharacteristics.setCompletionCondition(String.format("${ nrOfCompletedInstances/nrOfInstances >= %s}",
  880 + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getCompleteRatio() / 100D)));
  881 + callActivity.setLoopCharacteristics(multiInstanceCharacteristics);
  882 + addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType());
  883 + }
  884 +
889 // 添加节点类型 885 // 添加节点类型
890 addNodeType(node.getType(), callActivity); 886 addNodeType(node.getType(), callActivity);
891 flowElements.add(callActivity); 887 flowElements.add(callActivity);
@@ -903,6 +899,33 @@ public class SimpleModelUtils { @@ -903,6 +899,33 @@ public class SimpleModelUtils {
903 return id + "_join"; 899 return id + "_join";
904 } 900 }
905 901
  902 + private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type,
  903 + String timeDuration,
  904 + String timeCycle,
  905 + String timeDate) {
  906 + // 1.1 定时器边界事件
  907 + BoundaryEvent boundaryEvent = new BoundaryEvent();
  908 + boundaryEvent.setId("Event-" + IdUtil.fastUUID());
  909 + boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断
  910 + boundaryEvent.setAttachedToRef(attachedToRef);
  911 + // 1.2 定义超时时间表达式
  912 + TimerEventDefinition eventDefinition = new TimerEventDefinition();
  913 + if (ObjUtil.isNotNull(timeDuration)) {
  914 + eventDefinition.setTimeDuration(timeDuration);
  915 + }
  916 + if (ObjUtil.isNotNull(timeDuration)) {
  917 + eventDefinition.setTimeCycle(timeCycle);
  918 + }
  919 + if (ObjUtil.isNotNull(timeDate)) {
  920 + eventDefinition.setTimeDate(timeDate);
  921 + }
  922 + boundaryEvent.addEventDefinition(eventDefinition);
  923 +
  924 + // 2.1 添加定时器边界事件类型
  925 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type);
  926 + return boundaryEvent;
  927 + }
  928 +
906 // ========== SIMPLE 流程预测相关的方法 ========== 929 // ========== SIMPLE 流程预测相关的方法 ==========
907 930
908 public static List<BpmSimpleModelNodeVO> simulateProcess(BpmSimpleModelNodeVO rootNode, Map<String, Object> variables) { 931 public static List<BpmSimpleModelNodeVO> simulateProcess(BpmSimpleModelNodeVO rootNode, Map<String, Object> variables) {
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -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 哈? @芋艿:子流程的 activity 中 task 是null 下面的方法会报错;TODO @lesan:写个注释??? 417 + // ChildProcess 子流程节点仅存在于 activity 中,并且没有自身的 task ,需要跳过执行
418 if (task == null) { 418 if (task == null) {
419 continue; 419 continue;
420 } 420 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmCallActivityListener.java
1 package cn.iocoder.yudao.module.bpm.service.task.listener; 1 package cn.iocoder.yudao.module.bpm.service.task.listener;
2 2
3 import cn.hutool.core.lang.Assert; 3 import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.map.MapUtil;
4 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
6 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 7 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
@@ -9,13 +10,14 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmpt @@ -9,13 +10,14 @@ import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserEmpt
9 import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; 10 import cn.iocoder.yudao.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum;
10 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; 11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
11 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; 12 import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService;
  13 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
12 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
13 import lombok.Setter; 15 import lombok.Setter;
14 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
15 import org.flowable.engine.delegate.DelegateExecution; 17 import org.flowable.engine.delegate.DelegateExecution;
16 import org.flowable.engine.delegate.ExecutionListener; 18 import org.flowable.engine.delegate.ExecutionListener;
17 import org.flowable.engine.impl.el.FixedValue; 19 import org.flowable.engine.impl.el.FixedValue;
18 -import org.flowable.engine.impl.persistence.entity.ExecutionEntity; 20 +import org.flowable.engine.runtime.ProcessInstance;
19 import org.springframework.stereotype.Component; 21 import org.springframework.stereotype.Component;
20 22
21 import java.util.List; 23 import java.util.List;
@@ -37,30 +39,32 @@ public class BpmCallActivityListener implements ExecutionListener { @@ -37,30 +39,32 @@ public class BpmCallActivityListener implements ExecutionListener {
37 @Resource 39 @Resource
38 private BpmProcessDefinitionService processDefinitionService; 40 private BpmProcessDefinitionService processDefinitionService;
39 41
  42 + @Resource
  43 + private BpmProcessInstanceService processInstanceService;
  44 +
40 @Override 45 @Override
41 public void notify(DelegateExecution execution) { 46 public void notify(DelegateExecution execution) {
42 String expressionText = listenerConfig.getExpressionText(); 47 String expressionText = listenerConfig.getExpressionText();
43 Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); 48 Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText);
44 BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( 49 BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject(
45 expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); 50 expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class);
  51 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId());
46 52
47 // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 53 // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时
48 if (startUserSetting == null 54 if (startUserSetting == null
49 || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { 55 || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) {
50 - ExecutionEntity parent = (ExecutionEntity) execution.getParent();  
51 - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 56 + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));
52 return; 57 return;
53 } 58 }
54 59
55 // 2. 当发起人来源为表单时 60 // 2. 当发起人来源为表单时
56 if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { 61 if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) {
57 - ExecutionEntity parent = (ExecutionEntity) execution.getParent();  
58 - String formFieldValue = parent.getVariable(startUserSetting.getFormField(), String.class); 62 + String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField());
59 // 2.1 当表单值为空时 63 // 2.1 当表单值为空时
60 if (StrUtil.isEmpty(formFieldValue)) { 64 if (StrUtil.isEmpty(formFieldValue)) {
61 // 2.1.1 来自主流程发起人 65 // 2.1.1 来自主流程发起人
62 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { 66 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) {
63 - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 67 + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));
64 return; 68 return;
65 } 69 }
66 // 2.1.2 来自子流程管理员 70 // 2.1.2 来自子流程管理员
@@ -72,7 +76,7 @@ public class BpmCallActivityListener implements ExecutionListener { @@ -72,7 +76,7 @@ public class BpmCallActivityListener implements ExecutionListener {
72 } 76 }
73 // 2.1.3 来自主流程管理员 77 // 2.1.3 来自主流程管理员
74 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { 78 if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) {
75 - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(parent.getProcessDefinitionId()); 79 + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId());
76 List<Long> managerUserIds = processDefinition.getManagerUserIds(); 80 List<Long> managerUserIds = processDefinition.getManagerUserIds();
77 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); 81 FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0));
78 return; 82 return;
@@ -84,7 +88,7 @@ public class BpmCallActivityListener implements ExecutionListener { @@ -84,7 +88,7 @@ public class BpmCallActivityListener implements ExecutionListener {
84 } catch (Exception e) { 88 } catch (Exception e) {
85 log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", 89 log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]",
86 DELEGATE_EXPRESSION, formFieldValue); 90 DELEGATE_EXPRESSION, formFieldValue);
87 - FlowableUtils.setAuthenticatedUserId(Long.parseLong(parent.getStartUserId())); 91 + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId()));
88 } 92 }
89 } 93 }
90 } 94 }