Commit 2c2512e40550f0950202f5332b0b309f4375e999
1 parent
8469b2b8
【代码优化】工作流:将元素相关的操作,挪到 BpmnModelUtils 中
【代码优化】工作流:新增 NodeConvert 接口以及其实现,将 SimpleModelUtils 统一收敛
Showing
3 changed files
with
396 additions
and
329 deletions
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 | 10 | @Data |
| 11 | 11 | public class BpmApprovalDetailReqVO { |
| 12 | 12 | |
| 13 | + // TODO @jason:这里要不注释说明下,什么情况下,使用 processDefinitionId、processInstanceId、activityId、taskId。 | |
| 14 | + | |
| 13 | 15 | @Schema(description = "流程定义的编号", example = "1024") |
| 14 | 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 | 4 | import cn.hutool.core.map.MapUtil; |
| 5 | 5 | import cn.hutool.core.util.ArrayUtil; |
| 6 | 6 | import cn.hutool.core.util.StrUtil; |
| 7 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| 8 | 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 | 11 | import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; |
| 10 | 12 | import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskRejectHandlerType; |
| 11 | 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 14 | +import com.google.common.collect.Maps; | |
| 12 | 15 | import org.flowable.bpmn.converter.BpmnXMLConverter; |
| 13 | 16 | import org.flowable.bpmn.model.Process; |
| 14 | 17 | import org.flowable.bpmn.model.*; |
| ... | ... | @@ -18,12 +21,67 @@ import java.util.*; |
| 18 | 21 | |
| 19 | 22 | import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; |
| 20 | 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 | 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 | 85 | public static Integer parseCandidateStrategy(FlowElement userTask) { |
| 28 | 86 | Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( |
| 29 | 87 | BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); |
| ... | ... | @@ -49,6 +107,14 @@ public class BpmnModelUtils { |
| 49 | 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 | 118 | public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) { |
| 53 | 119 | Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); |
| 54 | 120 | return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType); |
| ... | ... | @@ -58,6 +124,21 @@ public class BpmnModelUtils { |
| 58 | 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 | 142 | public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { |
| 62 | 143 | return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); |
| 63 | 144 | } |
| ... | ... | @@ -78,6 +159,25 @@ public class BpmnModelUtils { |
| 78 | 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 | 181 | public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) { |
| 82 | 182 | if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) { |
| 83 | 183 | return null; |
| ... | ... | @@ -101,6 +201,22 @@ public class BpmnModelUtils { |
| 101 | 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 | 220 | public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) { |
| 105 | 221 | FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); |
| 106 | 222 | if (flowElement == null) { |
| ... | ... | @@ -193,7 +309,7 @@ public class BpmnModelUtils { |
| 193 | 309 | |
| 194 | 310 | public static EndEvent getEndEvent(BpmnModel model) { |
| 195 | 311 | Process process = model.getMainProcess(); |
| 196 | - // 从 flowElementList 找 endEvent. TODO 多个 EndEvent 会有问题 | |
| 312 | + // 从 flowElementList 找 endEvent | |
| 197 | 313 | return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent); |
| 198 | 314 | } |
| 199 | 315 | |
| ... | ... | @@ -316,7 +432,6 @@ public class BpmnModelUtils { |
| 316 | 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 | 7 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 8 | 8 | import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; |
| 9 | 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 | 12 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
| 16 | 13 | import cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; |
| 17 | 14 | import org.flowable.bpmn.BpmnAutoLayout; |
| 15 | +import org.flowable.bpmn.constants.BpmnXMLConstants; | |
| 18 | 16 | import org.flowable.bpmn.model.Process; |
| 19 | 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 | 21 | import static cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TimeoutHandler; |
| 28 | 22 | import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.*; |
| 29 | -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum.*; | |
| 30 | 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 | 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 | 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 | 38 | */ |
| 47 | 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 | 53 | * @param processId 流程标识 |
| 53 | 54 | * @param processName 流程名称 |
| ... | ... | @@ -57,7 +58,7 @@ public class SimpleModelUtils { |
| 57 | 58 | public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { |
| 58 | 59 | // 1. 创建 BpmnModel |
| 59 | 60 | BpmnModel bpmnModel = new BpmnModel(); |
| 60 | - bpmnModel.setTargetNamespace(BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 | |
| 61 | + bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 | |
| 61 | 62 | // 创建 Process 对象 |
| 62 | 63 | Process process = new Process(); |
| 63 | 64 | process.setId(processId); |
| ... | ... | @@ -69,14 +70,14 @@ public class SimpleModelUtils { |
| 69 | 70 | // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 |
| 70 | 71 | BpmSimpleModelNodeVO startNode = buildStartNode(); |
| 71 | 72 | startNode.setChildNode(simpleModelNode); |
| 72 | - // 从 前端模型数据结构 SimpleModel 构建 FlowNode 并添加到 Main Process | |
| 73 | + // 2.2 将前端传递的 simpleModelNode 数据结构(json),转换成从 BPMN FlowNode 元素,并添加到 Main Process 中 | |
| 73 | 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 | 78 | traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); |
| 79 | - // 自动布局 | |
| 79 | + | |
| 80 | + // 3. 自动布局 | |
| 80 | 81 | new BpmnAutoLayout(bpmnModel).execute(); |
| 81 | 82 | return bpmnModel; |
| 82 | 83 | } |
| ... | ... | @@ -85,6 +86,31 @@ public class SimpleModelUtils { |
| 85 | 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 | 114 | // TODO @芋艿:在优化下这个注释 |
| 89 | 115 | private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { |
| 90 | 116 | // 1.1 无效节点返回 |
| ... | ... | @@ -94,7 +120,7 @@ public class SimpleModelUtils { |
| 94 | 120 | // 1.2 END_NODE 直接返回 |
| 95 | 121 | BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType()); |
| 96 | 122 | Assert.notNull(nodeType, "模型节点类型不支持"); |
| 97 | - if (nodeType == END_NODE) { | |
| 123 | + if (nodeType == BpmSimpleModelNodeType.END_NODE) { | |
| 98 | 124 | return; |
| 99 | 125 | } |
| 100 | 126 | // 2.1 情况一:普通节点 |
| ... | ... | @@ -238,371 +264,295 @@ public class SimpleModelUtils { |
| 238 | 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 | 267 | public static boolean isValidNode(BpmSimpleModelNodeVO node) { |
| 265 | 268 | return node != null && node.getId() != null; |
| 266 | 269 | } |
| 267 | 270 | |
| 268 | 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 | 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 | } | ... | ... |