Commit 2c2512e40550f0950202f5332b0b309f4375e999

Authored by YunaiV
1 parent 8469b2b8

【代码优化】工作流:将元素相关的操作,挪到 BpmnModelUtils 中

【代码优化】工作流:新增 NodeConvert 接口以及其实现,将 SimpleModelUtils 统一收敛
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java
@@ -10,6 +10,8 @@ import lombok.Data; @@ -10,6 +10,8 @@ import lombok.Data;
10 @Data 10 @Data
11 public class BpmApprovalDetailReqVO { 11 public class BpmApprovalDetailReqVO {
12 12
  13 + // TODO @jason:这里要不注释说明下,什么情况下,使用 processDefinitionId、processInstanceId、activityId、taskId。
  14 +
13 @Schema(description = "流程定义的编号", example = "1024") 15 @Schema(description = "流程定义的编号", example = "1024")
14 private String processDefinitionId; // 发起流程的时候传流程定义 Id 16 private String processDefinitionId; // 发起流程的时候传流程定义 Id
15 17
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -4,11 +4,14 @@ import cn.hutool.core.collection.CollUtil; @@ -4,11 +4,14 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.map.MapUtil; 4 import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.ArrayUtil; 5 import cn.hutool.core.util.ArrayUtil;
6 import cn.hutool.core.util.StrUtil; 6 import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
7 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
8 import cn.iocoder.yudao.framework.common.util.string.StrUtils; 9 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
  10 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
9 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; 11 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
10 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; 12 import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 13 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
  14 +import com.google.common.collect.Maps;
12 import org.flowable.bpmn.converter.BpmnXMLConverter; 15 import org.flowable.bpmn.converter.BpmnXMLConverter;
13 import org.flowable.bpmn.model.Process; 16 import org.flowable.bpmn.model.Process;
14 import org.flowable.bpmn.model.*; 17 import org.flowable.bpmn.model.*;
@@ -18,12 +21,67 @@ import java.util.*; @@ -18,12 +21,67 @@ import java.util.*;
18 21
19 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; 22 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
20 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; 23 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
  24 +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
21 25
22 /** 26 /**
23 * 流程模型转操作工具类 27 * 流程模型转操作工具类
24 */ 28 */
25 public class BpmnModelUtils { 29 public class BpmnModelUtils {
26 30
  31 + public static void addExtensionElement(FlowElement element, String name, String value) {
  32 + if (value == null) {
  33 + return;
  34 + }
  35 + ExtensionElement extensionElement = new ExtensionElement();
  36 + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
  37 + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
  38 + extensionElement.setElementText(value);
  39 + extensionElement.setName(name);
  40 + element.addExtensionElement(extensionElement);
  41 + }
  42 +
  43 + public static void addExtensionElement(FlowElement element, String name, Integer value) {
  44 + if (value == null) {
  45 + return;
  46 + }
  47 + addExtensionElement(element, name, String.valueOf(value));
  48 + }
  49 +
  50 + public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
  51 + if (attributes == null) {
  52 + return;
  53 + }
  54 + ExtensionElement extensionElement = new ExtensionElement();
  55 + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
  56 + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
  57 + extensionElement.setName(name);
  58 + attributes.forEach((key, value) -> {
  59 + ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);
  60 + extensionAttribute.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
  61 + extensionElement.addAttribute(extensionAttribute);
  62 + });
  63 + element.addExtensionElement(extensionElement);
  64 + }
  65 +
  66 + /**
  67 + * 给节点添加候选人元素
  68 + *
  69 + * @param candidateStrategy 候选人策略
  70 + * @param candidateParam 候选人参数,允许空
  71 + * @param flowElement 节点
  72 + */
  73 + public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) {
  74 + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
  75 + candidateStrategy == null ? null : candidateStrategy.toString());
  76 + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam);
  77 + }
  78 +
  79 + /**
  80 + * 解析候选人策略
  81 + *
  82 + * @param userTask 节点
  83 + * @return 候选人策略
  84 + */
27 public static Integer parseCandidateStrategy(FlowElement userTask) { 85 public static Integer parseCandidateStrategy(FlowElement userTask) {
28 Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( 86 Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
29 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); 87 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
@@ -49,6 +107,14 @@ public class BpmnModelUtils { @@ -49,6 +107,14 @@ public class BpmnModelUtils {
49 return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); 107 return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE));
50 } 108 }
51 109
  110 + public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) {
  111 + if (rejectHandler == null) {
  112 + return;
  113 + }
  114 + addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType()));
  115 + addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());
  116 + }
  117 +
52 public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) { 118 public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
53 Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); 119 Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
54 return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); 120 return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
@@ -58,6 +124,21 @@ public class BpmnModelUtils { @@ -58,6 +124,21 @@ public class BpmnModelUtils {
58 return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); 124 return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
59 } 125 }
60 126
  127 + public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {
  128 + if (assignStartUserHandlerType == null) {
  129 + return;
  130 + }
  131 + addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());
  132 + }
  133 +
  134 + public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) {
  135 + if (emptyHandler == null) {
  136 + return;
  137 + }
  138 + addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType()));
  139 + addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds()));
  140 + }
  141 +
61 public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { 142 public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
62 return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); 143 return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
63 } 144 }
@@ -78,6 +159,25 @@ public class BpmnModelUtils { @@ -78,6 +159,25 @@ public class BpmnModelUtils {
78 return element != null ? element.getElementText() : null; 159 return element != null ? element.getElementText() : null;
79 } 160 }
80 161
  162 + /**
  163 + * 给节点添加表单字段权限元素
  164 + *
  165 + * @param fieldsPermissions 表单字段权限
  166 + * @param flowElement 节点
  167 + */
  168 + public static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) {
  169 + if (CollUtil.isNotEmpty(fieldsPermissions)) {
  170 + fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item));
  171 + }
  172 + }
  173 +
  174 + /**
  175 + * 解析表单字段权限
  176 + *
  177 + * @param bpmnModel bpmnModel 对象
  178 + * @param flowElementId 元素 ID
  179 + * @return 表单字段权限
  180 + */
81 public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) { 181 public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {
82 if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) { 182 if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {
83 return null; 183 return null;
@@ -101,6 +201,22 @@ public class BpmnModelUtils { @@ -101,6 +201,22 @@ public class BpmnModelUtils {
101 return fieldsPermission; 201 return fieldsPermission;
102 } 202 }
103 203
  204 + /**
  205 + * 给节点添加操作按钮设置元素
  206 + */
  207 + public static void addButtonsSetting(List<BpmSimpleModelNodeVO.OperationButtonSetting> buttonsSetting, UserTask userTask) {
  208 + if (CollUtil.isNotEmpty(buttonsSetting)) {
  209 + List<Map<String, String>> list = CollectionUtils.convertList(buttonsSetting, item -> {
  210 + Map<String, String> settingMap = Maps.newHashMapWithExpectedSize(3);
  211 + settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId()));
  212 + settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName());
  213 + settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable()));
  214 + return settingMap;
  215 + });
  216 + list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item));
  217 + }
  218 + }
  219 +
104 public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) { 220 public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) {
105 FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); 221 FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
106 if (flowElement == null) { 222 if (flowElement == null) {
@@ -193,7 +309,7 @@ public class BpmnModelUtils { @@ -193,7 +309,7 @@ public class BpmnModelUtils {
193 309
194 public static EndEvent getEndEvent(BpmnModel model) { 310 public static EndEvent getEndEvent(BpmnModel model) {
195 Process process = model.getMainProcess(); 311 Process process = model.getMainProcess();
196 - // 从 flowElementList 找 endEvent. TODO 多个 EndEvent 会有问题 312 + // 从 flowElementList 找 endEvent
197 return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent); 313 return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);
198 } 314 }
199 315
@@ -316,7 +432,6 @@ public class BpmnModelUtils { @@ -316,7 +432,6 @@ public class BpmnModelUtils {
316 return userTaskList; 432 return userTaskList;
317 } 433 }
318 434
319 -  
320 /** 435 /**
321 * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 436 * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
322 * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 437 * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
@@ -7,32 +7,24 @@ import cn.hutool.core.util.*; @@ -7,32 +7,24 @@ import cn.hutool.core.util.*;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; 9 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.RejectHandler;  
11 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmBoundaryEventType;  
12 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModeConditionType;  
13 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;  
14 -import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum; 10 +import cn.iocoder.yudao.module.bpm.enums.definition.*;
  11 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
15 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; 12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
16 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; 13 import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate;
17 import org.flowable.bpmn.BpmnAutoLayout; 14 import org.flowable.bpmn.BpmnAutoLayout;
  15 +import org.flowable.bpmn.constants.BpmnXMLConstants;
18 import org.flowable.bpmn.model.Process; 16 import org.flowable.bpmn.model.Process;
19 import org.flowable.bpmn.model.*; 17 import org.flowable.bpmn.model.*;
20 18
21 -import java.util.ArrayList;  
22 -import java.util.List;  
23 -import java.util.Map;  
24 -import java.util.Objects; 19 +import java.util.*;
25 20
26 -import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.OperationButtonSetting;  
27 import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; 21 import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler;
28 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; 22 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*;
29 -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum.*;  
30 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER; 23 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum.USER;
31 -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP;  
32 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER; 24 import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum.REMINDER;
33 -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum.START_USER;  
34 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; 25 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
35 -import static org.flowable.bpmn.constants.BpmnXMLConstants.*; 26 +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
  27 +import static java.util.Arrays.asList;
36 28
37 /** 29 /**
38 * 仿钉钉/飞书的模型相关的工具方法 30 * 仿钉钉/飞书的模型相关的工具方法
@@ -46,8 +38,17 @@ public class SimpleModelUtils { @@ -46,8 +38,17 @@ public class SimpleModelUtils {
46 */ 38 */
47 public static final String JOIN_GATE_WAY_NODE_ID_SUFFIX = "_join"; 39 public static final String JOIN_GATE_WAY_NODE_ID_SUFFIX = "_join";
48 40
  41 + private static final Map<BpmSimpleModelNodeType, NodeConvert> NODE_CONVERTS = MapUtil.newHashMap();
  42 +
  43 + static {
  44 + List<NodeConvert> converts = asList(new StartNodeConvert(), new EndNodeConvert(),
  45 + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(),
  46 + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert());
  47 + converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert));
  48 + }
  49 +
49 /** 50 /**
50 - * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model 51 + * 仿钉钉流程设计模型数据结构(json)转换成 Bpmn Model
51 * 52 *
52 * @param processId 流程标识 53 * @param processId 流程标识
53 * @param processName 流程名称 54 * @param processName 流程名称
@@ -57,7 +58,7 @@ public class SimpleModelUtils { @@ -57,7 +58,7 @@ public class SimpleModelUtils {
57 public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { 58 public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
58 // 1. 创建 BpmnModel 59 // 1. 创建 BpmnModel
59 BpmnModel bpmnModel = new BpmnModel(); 60 BpmnModel bpmnModel = new BpmnModel();
60 - bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 61 + bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常
61 // 创建 Process 对象 62 // 创建 Process 对象
62 Process process = new Process(); 63 Process process = new Process();
63 process.setId(processId); 64 process.setId(processId);
@@ -69,14 +70,14 @@ public class SimpleModelUtils { @@ -69,14 +70,14 @@ public class SimpleModelUtils {
69 // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 70 // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点
70 BpmSimpleModelNodeVO startNode = buildStartNode(); 71 BpmSimpleModelNodeVO startNode = buildStartNode();
71 startNode.setChildNode(simpleModelNode); 72 startNode.setChildNode(simpleModelNode);
72 - // 从 前端模型数据结构 SimpleModel 构建 FlowNode 并添加到 Main Process 73 + // 2.2 将前端传递的 simpleModelNode 数据结构(json),转换成从 BPMN FlowNode 元素,并添加到 Main Process 中
73 traverseNodeToBuildFlowNode(startNode, process); 74 traverseNodeToBuildFlowNode(startNode, process);
74 - // 找到 end event  
75 - EndEvent endEvent = (EndEvent) CollUtil.findOne(process.getFlowElements(), item -> item instanceof EndEvent);  
76 75
77 - // 构建并添加节点之间的连线 Sequence Flow 76 + // 3. 构建并添加节点之间的连线 Sequence Flow
  77 + EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
78 traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); 78 traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId());
79 - // 自动布局 79 +
  80 + // 3. 自动布局
80 new BpmnAutoLayout(bpmnModel).execute(); 81 new BpmnAutoLayout(bpmnModel).execute();
81 return bpmnModel; 82 return bpmnModel;
82 } 83 }
@@ -85,6 +86,31 @@ public class SimpleModelUtils { @@ -85,6 +86,31 @@ public class SimpleModelUtils {
85 return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID).setName(START_EVENT_NODE_NAME).setType(START_NODE.getType()); 86 return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID).setName(START_EVENT_NODE_NAME).setType(START_NODE.getType());
86 } 87 }
87 88
  89 + private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) {
  90 + // 1. 判断是否有效节点
  91 + if (!isValidNode(node)) {
  92 + return;
  93 + }
  94 + BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
  95 + Assert.notNull(nodeType, "模型节点类型({})不支持", node.getType());
  96 +
  97 + // 2. 处理当前节点
  98 + NodeConvert nodeConvert = NODE_CONVERTS.get(nodeType);
  99 + Assert.notNull(nodeConvert, "模型节点类型的转换器({})不存在", node.getType());
  100 + List<? extends FlowElement> flowElements = nodeConvert.convertList(node);
  101 + flowElements.forEach(process::addFlowElement);
  102 +
  103 + // 如果不是网关类型的接口, 并且chileNode为空退出
  104 + // 如果是“分支”节点,则递归处理条件
  105 + if (BpmSimpleModelNodeType.isBranchNode(node.getType())
  106 + && ArrayUtil.isNotEmpty(node.getConditionNodes())) {
  107 + node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process));
  108 + }
  109 +
  110 + // 如果有“子”节点,则递归处理子节点
  111 + traverseNodeToBuildFlowNode(node.getChildNode(), process);
  112 + }
  113 +
88 // TODO @芋艿:在优化下这个注释 114 // TODO @芋艿:在优化下这个注释
89 private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { 115 private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) {
90 // 1.1 无效节点返回 116 // 1.1 无效节点返回
@@ -94,7 +120,7 @@ public class SimpleModelUtils { @@ -94,7 +120,7 @@ public class SimpleModelUtils {
94 // 1.2 END_NODE 直接返回 120 // 1.2 END_NODE 直接返回
95 BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType()); 121 BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
96 Assert.notNull(nodeType, "模型节点类型不支持"); 122 Assert.notNull(nodeType, "模型节点类型不支持");
97 - if (nodeType == END_NODE) { 123 + if (nodeType == BpmSimpleModelNodeType.END_NODE) {
98 return; 124 return;
99 } 125 }
100 // 2.1 情况一:普通节点 126 // 2.1 情况一:普通节点
@@ -238,371 +264,295 @@ public class SimpleModelUtils { @@ -238,371 +264,295 @@ public class SimpleModelUtils {
238 return sequenceFlow; 264 return sequenceFlow;
239 } 265 }
240 266
241 - // TODO @芋艿 改成了 traverseNodeToBuildFlowNode, 连线的叫 traverseNodeToBuildSequenceFlow  
242 - private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) {  
243 - // 判断是否有效节点  
244 - if (!isValidNode(node)) {  
245 - return;  
246 - }  
247 - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());  
248 - Assert.notNull(nodeType, "模型节点类型不支持");  
249 -  
250 - List<FlowElement> flowElements = buildFlowNode(node, nodeType);  
251 - flowElements.forEach(process::addFlowElement);  
252 -  
253 - // 如果不是网关类型的接口, 并且chileNode为空退出  
254 - // 如果是“分支”节点,则递归处理条件  
255 - if (BpmSimpleModelNodeType.isBranchNode(node.getType())  
256 - && ArrayUtil.isNotEmpty(node.getConditionNodes())) {  
257 - node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process));  
258 - }  
259 -  
260 - // 如果有“子”节点,则递归处理子节点  
261 - traverseNodeToBuildFlowNode(node.getChildNode(), process);  
262 - }  
263 -  
264 public static boolean isValidNode(BpmSimpleModelNodeVO node) { 267 public static boolean isValidNode(BpmSimpleModelNodeVO node) {
265 return node != null && node.getId() != null; 268 return node != null && node.getId() != null;
266 } 269 }
267 270
268 public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) { 271 public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) {
269 - return APPROVE_NODE.getType().equals(node.getType()) && SEQUENTIAL.getMethod().equals(node.getApproveMethod()); 272 + return APPROVE_NODE.getType().equals(node.getType())
  273 + && BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod());
270 } 274 }
271 275
272 - private static List<FlowElement> buildFlowNode(BpmSimpleModelNodeVO node, BpmSimpleModelNodeType nodeType) {  
273 - List<FlowElement> list = new ArrayList<>();  
274 - switch (nodeType) {  
275 - case START_NODE: { // 开始节点  
276 - StartEvent startEvent = convertStartNode(node);  
277 - list.add(startEvent);  
278 - break;  
279 - }  
280 - case END_NODE: { // 结束节点  
281 - EndEvent endEvent = convertEndNode(node);  
282 - list.add(endEvent);  
283 - break;  
284 - }  
285 - case START_USER_NODE: { // 发起人节点  
286 - UserTask userTask = convertStartUserNode(node);  
287 - list.add(userTask);  
288 - break;  
289 - }  
290 - case APPROVE_NODE: { // 审批节点  
291 - List<FlowElement> flowElements = convertApproveNode(node);  
292 - list.addAll(flowElements);  
293 - break;  
294 - }  
295 - case COPY_NODE: { // 抄送节点  
296 - ServiceTask serviceTask = convertCopyNode(node);  
297 - list.add(serviceTask);  
298 - break;  
299 - }  
300 - case CONDITION_BRANCH_NODE: {  
301 - ExclusiveGateway exclusiveGateway = convertConditionBranchNode(node);  
302 - list.add(exclusiveGateway);  
303 - break;  
304 - }  
305 - case PARALLEL_BRANCH_NODE: {  
306 - List<ParallelGateway> parallelGateways = convertParallelBranchNode(node);  
307 - list.addAll(parallelGateways);  
308 - break;  
309 - }  
310 - case INCLUSIVE_BRANCH_NODE: {  
311 - // TODO jason 待实现  
312 - break;  
313 - }  
314 - default: {  
315 - // TODO 其它节点类型的实现 276 + private static InclusiveGateway convertInclusiveBranchNode(BpmSimpleModelNodeVO node, Boolean isFork) {
  277 + InclusiveGateway inclusiveGateway = new InclusiveGateway();
  278 + inclusiveGateway.setId(node.getId());
  279 + // TODO @jason:这里是不是 setName 哈;
  280 +
  281 + // @芋艿 isFork 为 false 就是合并网关。由前端传入。这个前端暂时还未实现
  282 + if (isFork) {
  283 + Assert.notEmpty(node.getConditionNodes(), "条件节点不能为空");
  284 + // 寻找默认的序列流
  285 + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(
  286 + node.getConditionNodes(), item -> BooleanUtil.isTrue(item.getDefaultFlow()));
  287 + if (defaultSeqFlow != null) {
  288 + inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());
316 } 289 }
317 } 290 }
318 - return list; 291 + return inclusiveGateway;
319 } 292 }
320 293
321 - private static UserTask convertStartUserNode(BpmSimpleModelNodeVO node) {  
322 - return buildBpmnStartUserTask(node);  
323 - } 294 + // ========== 各种 convert 节点的方法: BpmSimpleModelNodeVO => BPMN FlowElement ==========
324 295
325 - private static List<FlowElement> convertApproveNode(BpmSimpleModelNodeVO node) {  
326 - List<FlowElement> flowElements = new ArrayList<>();  
327 - UserTask userTask = buildBpmnUserTask(node);  
328 - flowElements.add(userTask); 296 + private interface NodeConvert {
329 297
330 - // 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理  
331 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {  
332 - BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler());  
333 - flowElements.add(boundaryEvent); 298 + default List<? extends FlowElement> convertList(BpmSimpleModelNodeVO node) {
  299 + return Collections.singletonList(convert(node));
334 } 300 }
335 - return flowElements;  
336 - }  
337 301
338 - /**  
339 - * 添加 UserTask 用户的审批超时 BoundaryEvent 事件  
340 - *  
341 - * @param userTask 审批任务  
342 - * @param timeoutHandler 超时处理器  
343 - * @return BoundaryEvent 超时事件  
344 - */  
345 - private static BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) {  
346 - // 1.1 定时器边界事件  
347 - BoundaryEvent boundaryEvent = new BoundaryEvent();  
348 - boundaryEvent.setId("Event-" + IdUtil.fastUUID());  
349 - boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断  
350 - boundaryEvent.setAttachedToRef(userTask);  
351 - // 1.2 定义超时时间、最大提醒次数  
352 - TimerEventDefinition eventDefinition = new TimerEventDefinition();  
353 - eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration());  
354 - if (Objects.equals(REMINDER.getType(), timeoutHandler.getType()) &&  
355 - timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {  
356 - eventDefinition.setTimeCycle(String.format("R%d/%s",  
357 - timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration())); 302 + default FlowElement convert(BpmSimpleModelNodeVO node) {
  303 + throw new UnsupportedOperationException("请实现该方法");
358 } 304 }
359 - boundaryEvent.addEventDefinition(eventDefinition);  
360 305
361 - // 2.1 添加定时器边界事件类型  
362 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString());  
363 - // 2.2 添加超时执行动作元素  
364 - addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, StrUtil.toStringOrNull(timeoutHandler.getType()));  
365 - return boundaryEvent;  
366 - } 306 + BpmSimpleModelNodeType getType();
367 307
368 - private static List<ParallelGateway> convertParallelBranchNode(BpmSimpleModelNodeVO node) {  
369 - ParallelGateway parallelGateway = new ParallelGateway();  
370 - parallelGateway.setId(node.getId());  
371 - // TODO @jason:setName  
372 -  
373 - // TODO @芋艿 + jason:合并网关;是不是要有条件啥的。微信讨论  
374 - // 并行聚合网关有程序创建。前端不需要传入  
375 - ParallelGateway joinParallelGateway = new ParallelGateway();  
376 - joinParallelGateway.setId(node.getId() + JOIN_GATE_WAY_NODE_ID_SUFFIX);  
377 - return CollUtil.newArrayList(parallelGateway, joinParallelGateway);  
378 } 308 }
379 309
380 - private static ServiceTask convertCopyNode(BpmSimpleModelNodeVO node) {  
381 - ServiceTask serviceTask = new ServiceTask();  
382 - serviceTask.setId(node.getId());  
383 - serviceTask.setName(node.getName());  
384 - serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);  
385 - serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}");  
386 -  
387 - // 添加抄送候选人元素  
388 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask);  
389 - // 添加表单字段权限属性元素  
390 - addFormFieldsPermission(node.getFieldsPermission(), serviceTask);  
391 - return serviceTask;  
392 - } 310 + private static class StartNodeConvert implements NodeConvert {
393 311
394 - /**  
395 - * 给节点添加候选人元素  
396 - */  
397 - private static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) {  
398 - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,  
399 - candidateStrategy == null ? null : candidateStrategy.toString());  
400 - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam);  
401 - } 312 + @Override
  313 + public StartEvent convert(BpmSimpleModelNodeVO node) {
  314 + StartEvent startEvent = new StartEvent();
  315 + startEvent.setId(node.getId());
  316 + startEvent.setName(node.getName());
  317 + return startEvent;
  318 + }
402 319
403 - private static ExclusiveGateway convertConditionBranchNode(BpmSimpleModelNodeVO node) {  
404 - Assert.notEmpty(node.getConditionNodes(), "条件分支节点不能为空");  
405 - ExclusiveGateway exclusiveGateway = new ExclusiveGateway();  
406 - exclusiveGateway.setId(node.getId());  
407 - // 寻找默认的序列流  
408 - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),  
409 - item -> BooleanUtil.isTrue(item.getDefaultFlow()));  
410 - if (defaultSeqFlow != null) {  
411 - exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); 320 + @Override
  321 + public BpmSimpleModelNodeType getType() {
  322 + return BpmSimpleModelNodeType.START_NODE;
412 } 323 }
413 - return exclusiveGateway; 324 +
414 } 325 }
415 326
416 - private static InclusiveGateway convertInclusiveBranchNode(BpmSimpleModelNodeVO node, Boolean isFork) {  
417 - InclusiveGateway inclusiveGateway = new InclusiveGateway();  
418 - inclusiveGateway.setId(node.getId());  
419 - // TODO @jason:这里是不是 setName 哈; 327 + private static class EndNodeConvert implements NodeConvert {
420 328
421 - // TODO @芋艿 + jason:是不是搞个合并网关;这里微信讨论下,有点奇怪;  
422 - // @芋艿 isFork 为 false 就是合并网关。由前端传入。这个前端暂时还未实现  
423 - if (isFork) {  
424 - Assert.notEmpty(node.getConditionNodes(), "条件节点不能为空");  
425 - // 寻找默认的序列流  
426 - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(  
427 - node.getConditionNodes(), item -> BooleanUtil.isTrue(item.getDefaultFlow()));  
428 - if (defaultSeqFlow != null) {  
429 - inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());  
430 - } 329 + @Override
  330 + public EndEvent convert(BpmSimpleModelNodeVO node) {
  331 + EndEvent endEvent = new EndEvent();
  332 + endEvent.setId(node.getId());
  333 + endEvent.setName(node.getName());
  334 + // TODO @芋艿 + jason:要不要加一个终止定义?
  335 + return endEvent;
431 } 336 }
432 - return inclusiveGateway;  
433 - }  
434 337
435 - private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) {  
436 - UserTask userTask = new UserTask();  
437 - userTask.setId(node.getId());  
438 - userTask.setName(node.getName()); 338 + @Override
  339 + public BpmSimpleModelNodeType getType() {
  340 + return BpmSimpleModelNodeType.END_NODE;
  341 + }
  342 +
  343 + }
439 344
440 - // 如果不是审批人节点,则直接返回  
441 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType()));  
442 - if (ObjectUtil.notEqual(node.getApproveType(), USER.getType())) { 345 + private static class StartUserNodeConvert implements NodeConvert {
  346 +
  347 + @Override
  348 + public UserTask convert(BpmSimpleModelNodeVO node) {
  349 + UserTask userTask = new UserTask();
  350 + userTask.setId(node.getId());
  351 + userTask.setName(node.getName());
  352 +
  353 + // 人工审批
  354 + addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType());
  355 + // 候选人策略为发起人自己
  356 + addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask);
  357 + // 添加表单字段权限属性元素
  358 + addFormFieldsPermission(node.getFieldsPermission(), userTask);
  359 + // 添加操作按钮配置属性元素
  360 + addButtonsSetting(node.getButtonsSetting(), userTask);
  361 + // 使用自动通过策略
  362 + // TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过;
  363 + addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask);
443 return userTask; 364 return userTask;
444 } 365 }
445 366
446 - // 添加候选人元素  
447 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask);  
448 - // 添加表单字段权限属性元素  
449 - addFormFieldsPermission(node.getFieldsPermission(), userTask);  
450 - // 添加操作按钮配置属性元素  
451 - addButtonsSetting(node.getButtonsSetting(), userTask);  
452 - // 处理多实例(审批方式)  
453 - processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask);  
454 - // 添加任务被拒绝的处理元素  
455 - addTaskRejectElements(node.getRejectHandler(), userTask);  
456 - // 添加用户任务的审批人与发起人相同时的处理元素  
457 - addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask);  
458 - // 添加用户任务的空处理元素  
459 - addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask);  
460 - // 设置审批任务的截止时间  
461 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {  
462 - userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); 367 + @Override
  368 + public BpmSimpleModelNodeType getType() {
  369 + return BpmSimpleModelNodeType.START_USER_NODE;
463 } 370 }
464 - return userTask; 371 +
465 } 372 }
466 373
467 - private static void addTaskRejectElements(RejectHandler rejectHandler, UserTask userTask) {  
468 - if (rejectHandler == null) {  
469 - return; 374 + private static class ApproveNodeConvert implements NodeConvert {
  375 +
  376 + @Override
  377 + public List<FlowElement> convertList(BpmSimpleModelNodeVO node) {
  378 + List<FlowElement> flowElements = new ArrayList<>(2);
  379 + // 1. 构建用户任务
  380 + UserTask userTask = buildBpmnUserTask(node);
  381 + flowElements.add(userTask);
  382 +
  383 + // 2. 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理
  384 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
  385 + BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler());
  386 + flowElements.add(boundaryEvent);
  387 + }
  388 + return flowElements;
470 } 389 }
471 - addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType()));  
472 - addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());  
473 - }  
474 390
475 - private static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {  
476 - if (assignStartUserHandlerType == null) {  
477 - return; 391 + @Override
  392 + public BpmSimpleModelNodeType getType() {
  393 + return BpmSimpleModelNodeType.APPROVE_NODE;
478 } 394 }
479 - addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());  
480 - }  
481 395
482 - private static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) {  
483 - if (emptyHandler == null) {  
484 - return; 396 + /**
  397 + * 添加 UserTask 用户的审批超时 BoundaryEvent 事件
  398 + *
  399 + * @param userTask 审批任务
  400 + * @param timeoutHandler 超时处理器
  401 + * @return BoundaryEvent 超时事件
  402 + */
  403 + private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, TimeoutHandler timeoutHandler) {
  404 + // 1.1 定时器边界事件
  405 + BoundaryEvent boundaryEvent = new BoundaryEvent();
  406 + boundaryEvent.setId("Event-" + IdUtil.fastUUID());
  407 + boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断
  408 + boundaryEvent.setAttachedToRef(userTask);
  409 + // 1.2 定义超时时间、最大提醒次数
  410 + TimerEventDefinition eventDefinition = new TimerEventDefinition();
  411 + eventDefinition.setTimeDuration(timeoutHandler.getTimeDuration());
  412 + if (Objects.equals(REMINDER.getType(), timeoutHandler.getType()) &&
  413 + timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) {
  414 + eventDefinition.setTimeCycle(String.format("R%d/%s",
  415 + timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()));
  416 + }
  417 + boundaryEvent.addEventDefinition(eventDefinition);
  418 +
  419 + // 2.1 添加定时器边界事件类型
  420 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, BpmBoundaryEventType.USER_TASK_TIMEOUT.getType().toString());
  421 + // 2.2 添加超时执行动作元素
  422 + addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, StrUtil.toStringOrNull(timeoutHandler.getType()));
  423 + return boundaryEvent;
485 } 424 }
486 - addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType()));  
487 - addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds()));  
488 - }  
489 425
490 - private static void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {  
491 - BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);  
492 - Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum);  
493 - // 添加审批方式的扩展属性  
494 - addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, approveMethod.toString());  
495 - if (approveMethodEnum == RANDOM) {  
496 - // 随机审批,不需要设置多实例属性  
497 - return; 426 + private UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) {
  427 + UserTask userTask = new UserTask();
  428 + userTask.setId(node.getId());
  429 + userTask.setName(node.getName());
  430 +
  431 + // 如果不是审批人节点,则直接返回
  432 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, StrUtil.toStringOrNull(node.getApproveType()));
  433 + if (ObjectUtil.notEqual(node.getApproveType(), USER.getType())) {
  434 + return userTask;
  435 + }
  436 +
  437 + // 添加候选人元素
  438 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask);
  439 + // 添加表单字段权限属性元素
  440 + addFormFieldsPermission(node.getFieldsPermission(), userTask);
  441 + // 添加操作按钮配置属性元素
  442 + addButtonsSetting(node.getButtonsSetting(), userTask);
  443 + // 处理多实例(审批方式)
  444 + processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask);
  445 + // 添加任务被拒绝的处理元素
  446 + addTaskRejectElements(node.getRejectHandler(), userTask);
  447 + // 添加用户任务的审批人与发起人相同时的处理元素
  448 + addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask);
  449 + // 添加用户任务的空处理元素
  450 + addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask);
  451 + // 设置审批任务的截止时间
  452 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) {
  453 + userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());
  454 + }
  455 + return userTask;
498 } 456 }
499 457
500 - // 处理多实例审批方式  
501 - MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics();  
502 - // 设置 collectionVariable。本系统用不到,仅仅为了 Flowable 校验不报错  
503 - multiInstanceCharacteristics.setInputDataItem("${coll_userList}");  
504 - if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) {  
505 - multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());  
506 - multiInstanceCharacteristics.setSequential(false);  
507 - } else if (approveMethodEnum == SEQUENTIAL) {  
508 - multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());  
509 - multiInstanceCharacteristics.setSequential(true);  
510 - multiInstanceCharacteristics.setLoopCardinality("1");  
511 - } else if (approveMethodEnum == RATIO) {  
512 - Assert.notNull(approveRatio, "通过比例不能为空");  
513 - multiInstanceCharacteristics.setCompletionCondition(  
514 - String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D)));  
515 - multiInstanceCharacteristics.setSequential(false); 458 + private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
  459 + BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod);
  460 + Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum);
  461 + // 添加审批方式的扩展属性
  462 + addExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_METHOD, approveMethod.toString());
  463 + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) {
  464 + // 随机审批,不需要设置多实例属性
  465 + return;
  466 + }
  467 +
  468 + // 处理多实例审批方式
  469 + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics();
  470 + // 设置 collectionVariable。本系统用不到,仅仅为了 Flowable 校验不报错
  471 + multiInstanceCharacteristics.setInputDataItem("${coll_userList}");
  472 + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) {
  473 + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());
  474 + multiInstanceCharacteristics.setSequential(false);
  475 + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.SEQUENTIAL) {
  476 + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition());
  477 + multiInstanceCharacteristics.setSequential(true);
  478 + multiInstanceCharacteristics.setLoopCardinality("1");
  479 + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) {
  480 + Assert.notNull(approveRatio, "通过比例不能为空");
  481 + multiInstanceCharacteristics.setCompletionCondition(
  482 + String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D)));
  483 + multiInstanceCharacteristics.setSequential(false);
  484 + }
  485 + userTask.setLoopCharacteristics(multiInstanceCharacteristics);
516 } 486 }
517 - userTask.setLoopCharacteristics(multiInstanceCharacteristics); 487 +
518 } 488 }
519 489
520 - /**  
521 - * 给节点添加操作按钮设置元素  
522 - */  
523 - private static void addButtonsSetting(List<OperationButtonSetting> buttonsSetting, UserTask userTask) {  
524 - if (CollUtil.isNotEmpty(buttonsSetting)) {  
525 - List<Map<String, String>> list = CollectionUtils.convertList(buttonsSetting, item -> {  
526 - Map<String, String> settingMap = MapUtil.newHashMap(16);  
527 - settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId()));  
528 - settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName());  
529 - settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable()));  
530 - return settingMap;  
531 - });  
532 - list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item)); 490 + private static class CopyNodeConvert implements NodeConvert {
  491 +
  492 + @Override
  493 + public ServiceTask convert(BpmSimpleModelNodeVO node) {
  494 + ServiceTask serviceTask = new ServiceTask();
  495 + serviceTask.setId(node.getId());
  496 + serviceTask.setName(node.getName());
  497 + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
  498 + serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}");
  499 +
  500 + // 添加抄送候选人元素
  501 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask);
  502 + // 添加表单字段权限属性元素
  503 + addFormFieldsPermission(node.getFieldsPermission(), serviceTask);
  504 + return serviceTask;
533 } 505 }
534 - }  
535 506
536 - /**  
537 - * 给节点添加表单字段权限元素  
538 - */  
539 - private static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) {  
540 - if (CollUtil.isNotEmpty(fieldsPermissions)) {  
541 - fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); 507 + @Override
  508 + public BpmSimpleModelNodeType getType() {
  509 + return BpmSimpleModelNodeType.COPY_NODE;
542 } 510 }
  511 +
543 } 512 }
544 513
545 - private static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {  
546 - if (attributes == null) {  
547 - return; 514 + private static class ConditionBranchNodeConvert implements NodeConvert {
  515 +
  516 + @Override
  517 + public ExclusiveGateway convert(BpmSimpleModelNodeVO node) {
  518 + ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
  519 + exclusiveGateway.setId(node.getId());
  520 + // TODO @jason:setName
  521 +
  522 + // 设置默认的序列流(条件)
  523 + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(),
  524 + item -> BooleanUtil.isTrue(item.getDefaultFlow()));
  525 + Assert.notNull(defaultSeqFlow, "条件分支节点({})的默认序列流不能为空", node.getId());
  526 + exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId());
  527 + return exclusiveGateway;
548 } 528 }
549 - ExtensionElement extensionElement = new ExtensionElement();  
550 - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);  
551 - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);  
552 - extensionElement.setName(name);  
553 - attributes.forEach((key, value) -> {  
554 - ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);  
555 - extensionAttribute.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);  
556 - extensionElement.addAttribute(extensionAttribute);  
557 - });  
558 - element.addExtensionElement(extensionElement);  
559 - }  
560 529
561 - private static void addExtensionElement(FlowElement element, String name, String value) {  
562 - if (value == null) {  
563 - return; 530 + @Override
  531 + public BpmSimpleModelNodeType getType() {
  532 + return BpmSimpleModelNodeType.CONDITION_BRANCH_NODE;
564 } 533 }
565 - ExtensionElement extensionElement = new ExtensionElement();  
566 - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);  
567 - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);  
568 - extensionElement.setElementText(value);  
569 - extensionElement.setName(name);  
570 - element.addExtensionElement(extensionElement); 534 +
571 } 535 }
572 536
573 - // ========== 各种 build 节点的方法 ========== 537 + private static class ParallelBranchNodeConvert implements NodeConvert {
574 538
575 - private static StartEvent convertStartNode(BpmSimpleModelNodeVO node) {  
576 - StartEvent startEvent = new StartEvent();  
577 - startEvent.setId(node.getId());  
578 - startEvent.setName(node.getName());  
579 - return startEvent;  
580 - } 539 + @Override
  540 + public List<ParallelGateway> convertList(BpmSimpleModelNodeVO node) {
  541 + ParallelGateway parallelGateway = new ParallelGateway();
  542 + parallelGateway.setId(node.getId());
  543 + // TODO @jason:setName
581 544
582 - private static UserTask buildBpmnStartUserTask(BpmSimpleModelNodeVO node) {  
583 - UserTask userTask = new UserTask();  
584 - userTask.setId(node.getId());  
585 - userTask.setName(node.getName());  
586 - // 人工审批  
587 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, USER.getType().toString());  
588 - // 候选人策略为发起人自己  
589 - addCandidateElements(START_USER.getStrategy(), null, userTask);  
590 - // 添加表单字段权限属性元素  
591 - addFormFieldsPermission(node.getFieldsPermission(), userTask);  
592 - // 添加操作按钮配置属性元素  
593 - addButtonsSetting(node.getButtonsSetting(), userTask);  
594 - // 使用自动通过策略 TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过;  
595 - addAssignStartUserHandlerType(SKIP.getType(), userTask);  
596 - return userTask;  
597 - } 545 + // 并行聚合网关由程序创建,前端不需要传入
  546 + ParallelGateway joinParallelGateway = new ParallelGateway();
  547 + joinParallelGateway.setId(node.getId() + JOIN_GATE_WAY_NODE_ID_SUFFIX);
  548 + return CollUtil.newArrayList(parallelGateway, joinParallelGateway);
  549 + }
598 550
599 - private static EndEvent convertEndNode(BpmSimpleModelNodeVO node) {  
600 - EndEvent endEvent = new EndEvent();  
601 - endEvent.setId(node.getId());  
602 - endEvent.setName(node.getName()); 551 + @Override
  552 + public BpmSimpleModelNodeType getType() {
  553 + return BpmSimpleModelNodeType.PARALLEL_BRANCH_NODE;
  554 + }
603 555
604 - // TODO @芋艿 + jason:要不要加一个终止定义?  
605 - return endEvent;  
606 } 556 }
607 557
608 } 558 }