Commit 9456d461f9337c6cc590d0201c31501ca9a4074b

Authored by jason
1 parent 1e97ca28

仿钉钉流程设计- code review 修改。扩展属性保存在 extensionElement 尝试

yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeType.java
... ... @@ -18,20 +18,17 @@ import java.util.Objects;
18 18 public enum BpmSimpleModelNodeType implements IntArrayValuable {
19 19  
20 20 // TODO @jaosn:-1、0、1、4、-2 是前端已经定义好的么?感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈;类似 usertask 用户审批;
21   - // TODO @jason:_NODE 都删除掉哈;
22   - START_EVENT_NODE(0, "开始节点"),
23   - END_EVENT_NODE(-2, "结束节点"), // TODO @jaosn:挪到 START_EVENT_NODE 后;
24   -
25   - APPROVE_USER_NODE(1, "审批人节点"), // TODO @jaosn:是不是这里从 10 开始好点;相当于说,0-9 给开始和结束;10-19 给各种节点;20-29 给各种条件;TODO @jason:改成 USER_TASK 是不是好点呀
26   - // 抄送人节点、对应 BPMN 的 ScriptTask. 使用ScriptTask 原因。好像 ServiceTask 自定义属性不能写入 XML;
27   - // TODO @jason:ServiceTask 自定义 xml,有没啥报错信息;
28   - SCRIPT_TASK_NODE(2, "抄送人节点"), // TODO @jason:是不是改成 COPY_TASK 好一点哈;
29   -
30   - EXCLUSIVE_GATEWAY_NODE(4, "排他网关"), // TODO @jason:是不是改成叫 条件分支?
31   - PARALLEL_GATEWAY_FORK_NODE(5, "并行网关分叉节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦?
32   - PARALLEL_GATEWAY_JOIN_NODE(6, "并行网关聚合节点"),
33   - INCLUSIVE_GATEWAY_FORK_NODE(7, "包容网关分叉节点"),
34   - INCLUSIVE_GATEWAY_JOIN_NODE(8, "包容网关聚合节点"),
  21 + START_EVENT(0, "开始节点"),
  22 + END_EVENT(-2, "结束节点"), // TODO @jaosn:挪到 START_EVENT_NODE 后;
  23 +
  24 + USER_TASK(1, "审批人节点"), // TODO @jaosn:是不是这里从 10 开始好点;相当于说,0-9 给开始和结束;10-19 给各种节点;20-29 给各种条件; TODO 后面改改
  25 + COPY_TASK(2, "抄送人节点"),
  26 +
  27 + EXCLUSIVE_GATEWAY(4, "排他网关"), // TODO @jason:是不是改成叫 条件分支?
  28 + PARALLEL_GATEWAY_FORK(5, "并行网关分叉节点"), // TODO @jason:是不是一个 并行分支 ?就可以啦? 后面是否去掉并行网关。只用包容网关
  29 + PARALLEL_GATEWAY_JOIN(6, "并行网关聚合节点"),
  30 + INCLUSIVE_GATEWAY_FORK(7, "包容网关分叉节点"),
  31 + INCLUSIVE_GATEWAY_JOIN(8, "包容网关聚合节点"),
35 32 ;
36 33  
37 34 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
... ... @@ -39,9 +36,13 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
39 36 private final Integer type;
40 37 private final String name;
41 38  
42   - public static boolean isGatewayNode(Integer type) {
43   - return Objects.equals(EXCLUSIVE_GATEWAY_NODE.getType(), type) || Objects.equals(PARALLEL_GATEWAY_FORK_NODE.getType(), type)
44   - || Objects.equals(INCLUSIVE_GATEWAY_FORK_NODE.getType(), type) ;
  39 + /**
  40 + * 判断是否为分支节点
  41 + * @param type 节点类型
  42 + */
  43 + public static boolean isBranchNode(Integer type) {
  44 + return Objects.equals(EXCLUSIVE_GATEWAY.getType(), type) || Objects.equals(PARALLEL_GATEWAY_FORK.getType(), type)
  45 + || Objects.equals(INCLUSIVE_GATEWAY_FORK.getType(), type) ;
45 46 }
46 47  
47 48 public static BpmSimpleModelNodeType valueOf(Integer type) {
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
1 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.lang.Assert;
5   -import cn.hutool.core.map.MapUtil;
6 4 import cn.hutool.core.util.ArrayUtil;
7   -import cn.hutool.core.util.StrUtil;
8 5 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
9   -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
10   -import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
11 6 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
12   -import org.flowable.bpmn.BpmnAutoLayout;
13 7 import org.flowable.bpmn.converter.BpmnXMLConverter;
14 8 import org.flowable.bpmn.model.Process;
15 9 import org.flowable.bpmn.model.*;
16   -import org.flowable.common.engine.impl.scripting.ScriptingEngines;
17 10 import org.flowable.common.engine.impl.util.io.BytesStreamSource;
18 11  
19   -import java.util.ArrayList;
20   -import java.util.HashSet;
21   -import java.util.List;
22   -import java.util.Set;
23   -
24   -import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
  12 +import java.util.*;
25 13  
26 14 /**
27 15 * 流程模型转操作工具类
28 16 */
29 17 public class BpmnModelUtils {
30 18  
31   - public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
32   -
33 19 public static Integer parseCandidateStrategy(FlowElement userTask) {
34   - return NumberUtils.parseInt(userTask.getAttributeValue(
  20 + Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
35 21 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
  22 + // @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限
  23 + if (candidateStrategy == null) {
  24 + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
  25 + candidateStrategy = NumberUtils.parseInt(Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null));
  26 +
  27 + }
  28 + return candidateStrategy;
36 29 }
37 30  
38 31 public static String parseCandidateParam(FlowElement userTask) {
39   - return userTask.getAttributeValue(
  32 + String candidateParam = userTask.getAttributeValue(
40 33 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
  34 + if (candidateParam == null) {
  35 + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
  36 + candidateParam = Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null);
  37 + }
  38 + return candidateParam;
41 39 }
42 40  
43 41 /**
... ... @@ -339,231 +337,4 @@ public class BpmnModelUtils {
339 337 }
340 338 return userTaskList;
341 339 }
342   -
343   - // ========== TODO @jason:单独出一个 SimpleModelUtils;定位上,它是 BPMN 的精简模式 ==========
344   -
345   - /**
346   - * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善)
347   - *
348   - * @param processId 流程标识
349   - * @param processName 流程名称
350   - * @param simpleModelNode 仿钉钉流程设计模型数据结构
351   - * @return Bpmn Model
352   - */
353   - public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
354   - BpmnModel bpmnModel = new BpmnModel();
355   - Process mainProcess = new Process();
356   - mainProcess.setId(processId);
357   - mainProcess.setName(processName);
358   - mainProcess.setExecutable(Boolean.TRUE);
359   - bpmnModel.addProcess(mainProcess);
360   - // 前端模型数据结构。 有 start event 节点. 没有 end event 节点。
361   - // 添加 FlowNode
362   - addBpmnFlowNode(mainProcess, simpleModelNode);
363   - // 单独添加 end event 节点
364   - addBpmnEndEventNode(mainProcess);
365   - // 添加节点之间的连线 Sequence Flow
366   - addBpmnSequenceFlow(mainProcess, simpleModelNode, BpmnModelConstants.END_EVENT_ID);
367   - // 自动布局
368   - new BpmnAutoLayout(bpmnModel).execute();
369   - return bpmnModel;
370   - }
371   -
372   - private static void addBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node, String endId) {
373   - // 节点为 null 退出
374   - if (node == null || node.getId() == null) {
375   - return;
376   - }
377   - BpmSimpleModelNodeVO childNode = node.getChildNode();
378   - // 如果不是网关节点、且后续节点为 null. 添加与结束节点的连线
379   - if (!BpmSimpleModelNodeType.isGatewayNode(node.getType()) && (childNode == null || childNode.getId() == null)) {
380   - addBpmnSequenceFlowElement(mainProcess, node.getId(), endId, null, null);
381   - return;
382   - }
383   - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
384   - Assert.notNull(nodeType, "模型节点类型不支持");
385   - // TODO @jason:建议是,addXXX 都改成 buildXXX,构建出一个什么;然后返回之后,让这个方法添加到自己的结果里;
386   - switch (nodeType) {
387   - case START_EVENT_NODE:
388   - case APPROVE_USER_NODE:
389   - case SCRIPT_TASK_NODE:
390   - case PARALLEL_GATEWAY_JOIN_NODE:
391   - case INCLUSIVE_GATEWAY_JOIN_NODE:{
392   - addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
393   - // 递归调用后续节点
394   - addBpmnSequenceFlow(mainProcess, childNode, endId);
395   - break;
396   - }
397   - case PARALLEL_GATEWAY_FORK_NODE:
398   - case EXCLUSIVE_GATEWAY_NODE:
399   - case INCLUSIVE_GATEWAY_FORK_NODE:{
400   - String gateWayEndId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
401   - List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
402   - Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
403   - for (int i = 0; i < conditionNodes.size(); i++) {
404   - BpmSimpleModelNodeVO item = conditionNodes.get(i);
405   - BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
406   - if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
407   - addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),
408   - String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
409   - addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);
410   - } else {
411   - addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,
412   - String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
413   - }
414   - }
415   - // 递归调用后续节点
416   - addBpmnSequenceFlow(mainProcess, childNode, endId);
417   - break;
418   - }
419   - default: {
420   - // TODO 其它节点类型的实现
421   - }
422   - }
423   -
424   - }
425   -
426   - private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {
427   - SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
428   - if (StrUtil.isNotEmpty(conditionExpression)) {
429   - sequenceFlow.setConditionExpression(conditionExpression);
430   - }
431   - if (StrUtil.isNotEmpty(seqFlowId)) {
432   - sequenceFlow.setId(seqFlowId);
433   - }
434   - mainProcess.addFlowElement(sequenceFlow);
435   - }
436   -
437   - private static void addBpmnFlowNode(Process mainProcess, BpmSimpleModelNodeVO simpleModelNode) {
438   - // 节点为 null 退出
439   - if (simpleModelNode == null || simpleModelNode.getId() == null) {
440   - return;
441   - }
442   - BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());
443   - Assert.notNull(nodeType, "模型节点类型不支持");
444   - switch (nodeType) {
445   - case START_EVENT_NODE:
446   - addBpmnStartEventNode(mainProcess, simpleModelNode);
447   - break;
448   - case APPROVE_USER_NODE:
449   - addBpmnUserTaskNode(mainProcess, simpleModelNode);
450   - break;
451   - case SCRIPT_TASK_NODE:
452   - addBpmnScriptTaSskNode(mainProcess, simpleModelNode);
453   - break;
454   - case EXCLUSIVE_GATEWAY_NODE:
455   - addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);
456   - break;
457   - case PARALLEL_GATEWAY_FORK_NODE:
458   - case PARALLEL_GATEWAY_JOIN_NODE:
459   - addBpmnParallelGatewayNode(mainProcess, simpleModelNode);
460   - break;
461   - case INCLUSIVE_GATEWAY_FORK_NODE:
462   - addBpmnInclusiveGatewayNode(mainProcess, simpleModelNode, Boolean.TRUE);
463   - break;
464   - case INCLUSIVE_GATEWAY_JOIN_NODE:
465   - addBpmnInclusiveGatewayNode(mainProcess, simpleModelNode, Boolean.FALSE);
466   - break;
467   - default: {
468   - // TODO 其它节点类型的实现
469   - }
470   - }
471   -
472   - // 如果不是网关类型的接口, 并且chileNode为空退出
473   - if (!BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && simpleModelNode.getChildNode() == null) {
474   - return;
475   - }
476   -
477   - // 如果是网关类型接口. 递归添加条件节点
478   - if (BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && ArrayUtil.isNotEmpty(simpleModelNode.getConditionNodes())) {
479   - for (BpmSimpleModelNodeVO node : simpleModelNode.getConditionNodes()) {
480   - addBpmnFlowNode(mainProcess, node.getChildNode());
481   - }
482   - }
483   -
484   - // chileNode不为空,递归添加子节点
485   - if (simpleModelNode.getChildNode() != null) {
486   - addBpmnFlowNode(mainProcess, simpleModelNode.getChildNode());
487   - }
488   - }
489   -
490   - private static void addBpmnParallelGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
491   - ParallelGateway parallelGateway = new ParallelGateway();
492   - parallelGateway.setId(node.getId());
493   - mainProcess.addFlowElement(parallelGateway);
494   - }
495   -
496   - private static void addBpmnScriptTaSskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
497   - ScriptTask scriptTask = new ScriptTask();
498   - scriptTask.setId(node.getId());
499   - scriptTask.setName(node.getName());
500   - // TODO @jason:建议使用 ServiceTask,通过 executionListeners 实现;
501   - scriptTask.setScriptFormat(ScriptingEngines.DEFAULT_SCRIPTING_LANGUAGE);
502   - scriptTask.setScript(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
503   - // 添加自定义属性
504   - // TODO @jason:可以使用 ServiceTask 搞 ExtensionAttribute 么?
505   - addExtensionAttributes(node, scriptTask);
506   - mainProcess.addFlowElement(scriptTask);
507   - }
508   -
509   - private static void addExtensionAttributes(BpmSimpleModelNodeVO node, FlowElement flowElement) {
510   - Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
511   - addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
512   - candidateStrategy == null ? null : String.valueOf(candidateStrategy));
513   - addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
514   - MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
515   - }
516   -
517   - private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
518   - Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
519   - ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
520   - exclusiveGateway.setId(node.getId());
521   - // 网关的最后一个条件为 网关的 default sequence flow
522   - exclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
523   - mainProcess.addFlowElement(exclusiveGateway);
524   - }
525   -
526   - private static void addBpmnInclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node, Boolean isFork) {
527   - InclusiveGateway inclusiveGateway = new InclusiveGateway();
528   - inclusiveGateway.setId(node.getId());
529   - if (isFork) {
530   - Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
531   - // 网关的最后一个条件为 网关的 default sequence flow
532   - inclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
533   - }
534   - mainProcess.addFlowElement(inclusiveGateway);
535   - }
536   -
537   - private static void addBpmnEndEventNode(Process mainProcess) {
538   - EndEvent endEvent = new EndEvent();
539   - endEvent.setId(BpmnModelConstants.END_EVENT_ID);
540   - endEvent.setName("结束");
541   - mainProcess.addFlowElement(endEvent);
542   - }
543   -
544   - private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
545   - UserTask userTask = new UserTask();
546   - userTask.setId(node.getId());
547   - userTask.setName(node.getName());
548   - addExtensionAttributes(node, userTask);
549   - mainProcess.addFlowElement(userTask);
550   - }
551   -
552   - private static void addExtensionAttributes(FlowElement element, String namespace, String name, String value) {
553   - if (value == null) {
554   - return;
555   - }
556   - ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
557   - extensionAttribute.setNamespace(namespace);
558   - extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
559   - element.addAttribute(extensionAttribute);
560   - }
561   -
562   - private static void addBpmnStartEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {
563   - StartEvent startEvent = new StartEvent();
564   - startEvent.setId(node.getId());
565   - startEvent.setName(node.getName());
566   - mainProcess.addFlowElement(startEvent);
567   - }
568   -
569 340 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
  2 +
  3 +import cn.hutool.core.lang.Assert;
  4 +import cn.hutool.core.map.MapUtil;
  5 +import cn.hutool.core.util.ArrayUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
  8 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
  9 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
  10 +import org.flowable.bpmn.BpmnAutoLayout;
  11 +import org.flowable.bpmn.model.*;
  12 +import org.flowable.bpmn.model.Process;
  13 +
  14 +import java.util.List;
  15 +
  16 +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
  17 +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
  18 +
  19 +/**
  20 + * 仿钉钉快搭模型相关的工具方法
  21 + *
  22 + * @author jason
  23 + */
  24 +public class SimpleModelUtils {
  25 +
  26 + public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
  27 +
  28 + /**
  29 + * 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善)
  30 + *
  31 + * @param processId 流程标识
  32 + * @param processName 流程名称
  33 + * @param simpleModelNode 仿钉钉流程设计模型数据结构
  34 + * @return Bpmn Model
  35 + */
  36 + public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
  37 + BpmnModel bpmnModel = new BpmnModel();
  38 + Process mainProcess = new Process();
  39 + mainProcess.setId(processId);
  40 + mainProcess.setName(processName);
  41 + mainProcess.setExecutable(Boolean.TRUE);
  42 + bpmnModel.addProcess(mainProcess);
  43 + // 前端模型数据结构。 有 start event 节点. 没有 end event 节点。
  44 + // 从 SimpleModel 构建 FlowNode 并添加到 Main Process
  45 + buildAndAddBpmnFlowNode(simpleModelNode, mainProcess);
  46 + // 单独构建 end event 节点
  47 + buildAndAddBpmnEndEvent(mainProcess);
  48 + // 构建并添加节点之间的连线 Sequence Flow
  49 + buildAndAddBpmnSequenceFlow(mainProcess, simpleModelNode, BpmnModelConstants.END_EVENT_ID);
  50 + // 自动布局
  51 + new BpmnAutoLayout(bpmnModel).execute();
  52 + return bpmnModel;
  53 + }
  54 +
  55 + private static void buildAndAddBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node, String targetId) {
  56 + // 节点为 null 退出
  57 + if (node == null || node.getId() == null) {
  58 + return;
  59 + }
  60 + BpmSimpleModelNodeVO childNode = node.getChildNode();
  61 + // 如果不是网关节点、且后续节点为 null. 添加与结束节点的连线
  62 + if (!BpmSimpleModelNodeType.isBranchNode(node.getType()) && (childNode == null || childNode.getId() == null)) {
  63 + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), targetId, null, null);
  64 + mainProcess.addFlowElement(sequenceFlow);
  65 + return;
  66 + }
  67 + BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
  68 + Assert.notNull(nodeType, "模型节点类型不支持");
  69 + switch (nodeType) {
  70 + case START_EVENT:
  71 + case USER_TASK:
  72 + case COPY_TASK:
  73 + case PARALLEL_GATEWAY_JOIN:
  74 + case INCLUSIVE_GATEWAY_JOIN:{
  75 + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null);
  76 + mainProcess.addFlowElement(sequenceFlow);
  77 + // 递归调用后续节点
  78 + buildAndAddBpmnSequenceFlow(mainProcess, childNode, targetId);
  79 + break;
  80 + }
  81 + case PARALLEL_GATEWAY_FORK:
  82 + case EXCLUSIVE_GATEWAY:
  83 + case INCLUSIVE_GATEWAY_FORK:{
  84 + String sequenceFlowTargetId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
  85 + List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
  86 + Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
  87 + for (int i = 0; i < conditionNodes.size(); i++) {
  88 + BpmSimpleModelNodeVO item = conditionNodes.get(i);
  89 + BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
  90 + if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
  91 + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), nextNodeOnCondition.getId(),
  92 + String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
  93 + mainProcess.addFlowElement(sequenceFlow);
  94 + // 递归调用后续节点
  95 + buildAndAddBpmnSequenceFlow(mainProcess, nextNodeOnCondition, sequenceFlowTargetId);
  96 + } else {
  97 + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), sequenceFlowTargetId,
  98 + String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
  99 + mainProcess.addFlowElement(sequenceFlow);
  100 + }
  101 + }
  102 + // 递归调用后续节点
  103 + buildAndAddBpmnSequenceFlow(mainProcess, childNode, targetId);
  104 + break;
  105 + }
  106 + default: {
  107 + // TODO 其它节点类型的实现
  108 + }
  109 + }
  110 +
  111 + }
  112 +
  113 + private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, String seqFlowId, String conditionExpression) {
  114 + SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
  115 + if (StrUtil.isNotEmpty(conditionExpression)) {
  116 + sequenceFlow.setConditionExpression(conditionExpression);
  117 + }
  118 + if (StrUtil.isNotEmpty(seqFlowId)) {
  119 + sequenceFlow.setId(seqFlowId);
  120 + }
  121 + return sequenceFlow;
  122 + }
  123 +
  124 + private static void buildAndAddBpmnFlowNode(BpmSimpleModelNodeVO simpleModelNode, Process mainProcess) {
  125 + // 节点为 null 退出
  126 + if (simpleModelNode == null || simpleModelNode.getId() == null) {
  127 + return;
  128 + }
  129 + BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());
  130 + Assert.notNull(nodeType, "模型节点类型不支持");
  131 + switch (nodeType) {
  132 + case START_EVENT: {
  133 + StartEvent startEvent = buildBpmnStartEvent(simpleModelNode);
  134 + mainProcess.addFlowElement(startEvent);
  135 + break;
  136 + }
  137 + case USER_TASK: {
  138 + UserTask userTask = buildBpmnUserTask(simpleModelNode);
  139 + mainProcess.addFlowElement(userTask);
  140 + break;
  141 + }
  142 + case COPY_TASK: {
  143 + ServiceTask serviceTask = buildBpmnServiceTask(simpleModelNode);
  144 + mainProcess.addFlowElement(serviceTask);
  145 + break;
  146 + }
  147 + case EXCLUSIVE_GATEWAY: {
  148 + ExclusiveGateway exclusiveGateway = buildBpmnExclusiveGateway(simpleModelNode);
  149 + mainProcess.addFlowElement(exclusiveGateway);
  150 + break;
  151 + }
  152 + case PARALLEL_GATEWAY_FORK:
  153 + case PARALLEL_GATEWAY_JOIN: {
  154 + ParallelGateway parallelGateway = buildBpmnParallelGateway(simpleModelNode);
  155 + mainProcess.addFlowElement(parallelGateway);
  156 + break;
  157 + }
  158 + case INCLUSIVE_GATEWAY_FORK: {
  159 + InclusiveGateway inclusiveGateway = buildBpmnInclusiveGateway(simpleModelNode, Boolean.TRUE);
  160 + mainProcess.addFlowElement(inclusiveGateway);
  161 + break;
  162 + }
  163 + case INCLUSIVE_GATEWAY_JOIN: {
  164 + InclusiveGateway inclusiveGateway = buildBpmnInclusiveGateway(simpleModelNode, Boolean.FALSE);
  165 + mainProcess.addFlowElement(inclusiveGateway);
  166 + break;
  167 + }
  168 + default: {
  169 + // TODO 其它节点类型的实现
  170 + }
  171 + }
  172 +
  173 + // 如果不是网关类型的接口, 并且chileNode为空退出
  174 + if (!BpmSimpleModelNodeType.isBranchNode(simpleModelNode.getType()) && simpleModelNode.getChildNode() == null) {
  175 + return;
  176 + }
  177 +
  178 + // 如果是网关类型接口. 递归添加条件节点
  179 + if (BpmSimpleModelNodeType.isBranchNode(simpleModelNode.getType()) && ArrayUtil.isNotEmpty(simpleModelNode.getConditionNodes())) {
  180 + for (BpmSimpleModelNodeVO node : simpleModelNode.getConditionNodes()) {
  181 + buildAndAddBpmnFlowNode(node.getChildNode(), mainProcess);
  182 + }
  183 + }
  184 +
  185 + // chileNode不为空,递归添加子节点
  186 + if (simpleModelNode.getChildNode() != null) {
  187 + buildAndAddBpmnFlowNode(simpleModelNode.getChildNode(), mainProcess);
  188 + }
  189 + }
  190 +
  191 + private static ParallelGateway buildBpmnParallelGateway(BpmSimpleModelNodeVO node) {
  192 + ParallelGateway parallelGateway = new ParallelGateway();
  193 + parallelGateway.setId(node.getId());
  194 + return parallelGateway;
  195 + }
  196 +
  197 + private static ServiceTask buildBpmnServiceTask(BpmSimpleModelNodeVO node) {
  198 + ServiceTask serviceTask = new ServiceTask();
  199 + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION);
  200 + serviceTask.setImplementation(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
  201 + serviceTask.setId(node.getId());
  202 + serviceTask.setName(node.getName());
  203 + // TODO @jason:建议使用 ServiceTask,通过 executionListeners 实现;
  204 + // @芋艿 ServiceTask 就可以了吧。 不需要 executionListeners
  205 + addExtensionElement(node, serviceTask);
  206 + return serviceTask;
  207 + }
  208 +
  209 + private static void addExtensionElement(BpmSimpleModelNodeVO node, FlowElement flowElement) {
  210 + Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
  211 + addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
  212 + candidateStrategy == null ? null : String.valueOf(candidateStrategy));
  213 + addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
  214 + MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
  215 + }
  216 +
  217 + private static ExclusiveGateway buildBpmnExclusiveGateway(BpmSimpleModelNodeVO node) {
  218 + Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
  219 + ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
  220 + exclusiveGateway.setId(node.getId());
  221 + // 网关的最后一个条件为 网关的 default sequence flow
  222 + exclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
  223 + return exclusiveGateway;
  224 + }
  225 +
  226 + private static InclusiveGateway buildBpmnInclusiveGateway(BpmSimpleModelNodeVO node, Boolean isFork) {
  227 + InclusiveGateway inclusiveGateway = new InclusiveGateway();
  228 + inclusiveGateway.setId(node.getId());
  229 + if (isFork) {
  230 + Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
  231 + // 网关的最后一个条件为 网关的 default sequence flow
  232 + inclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
  233 + }
  234 + return inclusiveGateway;
  235 + }
  236 +
  237 + private static void buildAndAddBpmnEndEvent(Process mainProcess) {
  238 + EndEvent endEvent = new EndEvent();
  239 + endEvent.setId(BpmnModelConstants.END_EVENT_ID);
  240 + endEvent.setName("结束");
  241 + mainProcess.addFlowElement(endEvent);
  242 + }
  243 +
  244 + private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) {
  245 + UserTask userTask = new UserTask();
  246 + userTask.setId(node.getId());
  247 + userTask.setName(node.getName());
  248 + addExtensionElement(node, userTask);
  249 + return userTask;
  250 + }
  251 +
  252 + private static void addExtensionElement(FlowElement element, String namespace, String name, String value) {
  253 + if (value == null) {
  254 + return;
  255 + }
  256 + ExtensionElement extensionElement = new ExtensionElement();
  257 + extensionElement.setNamespace(namespace);
  258 + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
  259 + extensionElement.setElementText(value);
  260 + extensionElement.setName(name);
  261 + element.addExtensionElement(extensionElement);
  262 + }
  263 +
  264 + private static StartEvent buildBpmnStartEvent(BpmSimpleModelNodeVO node) {
  265 + StartEvent startEvent = new StartEvent();
  266 + startEvent.setId(node.getId());
  267 + startEvent.setName(node.getName());
  268 + return startEvent;
  269 + }
  270 +}
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/definition/BpmSimpleModelServiceImpl.java
... ... @@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimp
10 10 import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
11 11 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
12 12 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
  13 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
13 14 import jakarta.annotation.Resource;
14 15 import org.flowable.bpmn.model.*;
15 16 import org.flowable.engine.repository.Model;
... ... @@ -22,7 +23,7 @@ import java.util.Map;
22 23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
23 24 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT;
24 25 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_NOT_EXISTS;
25   -import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_EVENT_NODE;
  26 +import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_EVENT;
26 27 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_PARAM;
27 28 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY;
28 29  
... ... @@ -56,7 +57,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
56 57 // return Boolean.FALSE;
57 58 // }
58 59 // 1. JSON 转换成 bpmnModel
59   - BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
  60 + BpmnModel bpmnModel = SimpleModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
60 61 // 2.1 保存 Bpmn XML
61 62 bpmModelService.saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(BpmnModelUtils.getBpmnXml(bpmnModel)));
62 63 // 2.2 保存 JSON 数据
... ... @@ -93,7 +94,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
93 94 return null;
94 95 }
95 96 BpmSimpleModelNodeVO rootNode = new BpmSimpleModelNodeVO();
96   - rootNode.setType(START_EVENT_NODE.getType());
  97 + rootNode.setType(START_EVENT.getType());
97 98 rootNode.setId(startEvent.getId());
98 99 rootNode.setName(startEvent.getName());
99 100 recursiveBuildSimpleModelNode(startEvent, rootNode);
... ... @@ -105,10 +106,10 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
105 106 Assert.notNull(nodeType, "节点类型不支持");
106 107 // 校验节点是否支持转仿钉钉的流程模型
107 108 List<SequenceFlow> outgoingFlows = validateCanConvertSimpleNode(nodeType, currentFlowNode);
108   - if (CollUtil.isEmpty(outgoingFlows) || outgoingFlows.get(0).getTargetFlowElement() == null) {
  109 + if (CollUtil.isEmpty(outgoingFlows) || CollUtil.getFirst(outgoingFlows).getTargetFlowElement() == null) {
109 110 return;
110 111 }
111   - FlowElement targetElement = outgoingFlows.get(0).getTargetFlowElement();
  112 + FlowElement targetElement = CollUtil.getFirst(outgoingFlows).getTargetFlowElement();
112 113 // 如果是 EndEvent 直接退出
113 114 if (targetElement instanceof EndEvent) {
114 115 return;
... ... @@ -123,7 +124,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
123 124  
124 125 private BpmSimpleModelNodeVO convertUserTaskToSimpleModelNode(UserTask userTask) {
125 126 BpmSimpleModelNodeVO simpleModelNodeVO = new BpmSimpleModelNodeVO();
126   - simpleModelNodeVO.setType(BpmSimpleModelNodeType.APPROVE_USER_NODE.getType());
  127 + simpleModelNodeVO.setType(BpmSimpleModelNodeType.USER_TASK.getType());
127 128 simpleModelNodeVO.setName(userTask.getName());
128 129 simpleModelNodeVO.setId(userTask.getId());
129 130 Map<String, Object> attributes = MapUtil.newHashMap();
... ... @@ -137,13 +138,13 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
137 138  
138 139 private List<SequenceFlow> validateCanConvertSimpleNode(BpmSimpleModelNodeType nodeType, FlowNode currentFlowNode) {
139 140 switch (nodeType) {
140   - case START_EVENT_NODE:
141   - case APPROVE_USER_NODE: {
  141 + case START_EVENT:
  142 + case USER_TASK: {
142 143 List<SequenceFlow> outgoingFlows = currentFlowNode.getOutgoingFlows();
143 144 if (CollUtil.isNotEmpty(outgoingFlows) && outgoingFlows.size() > 1) {
144 145 throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);
145 146 }
146   - validIsSupportFlowNode(outgoingFlows.get(0).getTargetFlowElement());
  147 + validIsSupportFlowNode(CollUtil.getFirst(outgoingFlows).getTargetFlowElement());
147 148 return outgoingFlows;
148 149 }
149 150 default: {
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java
... ... @@ -47,7 +47,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
47 47 // TODO @芋艿:这里多加了一个 name;
48 48 @Override
49 49 public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
50   - // 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask) TODO jason:抄送节点,会没有来源的 taskId 么?
  50 + // 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask) TODO jason:抄送节点,会没有来源的 taskId 么? @芋艿 是否校验一下 传递进来的 id 不为空就行
51 51 // Task task = taskService.getTask(taskId);
52 52 // if (ObjectUtil.isNull(task)) {
53 53 // throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmSimpleNodeService.java
... ... @@ -9,7 +9,8 @@ import org.springframework.stereotype.Service;
9 9 import java.util.Set;
10 10  
11 11 /**
12   - * 仿钉钉快搭各个节点 Service TODO @jason:注释要有空行哈;
  12 + * 仿钉钉快搭各个节点 Service
  13 + *
13 14 * @author jason
14 15 */
15 16 @Service
... ... @@ -21,7 +22,8 @@ public class BpmSimpleNodeService {
21 22 private BpmProcessInstanceCopyService processInstanceCopyService;
22 23  
23 24 /**
24   - * 仿钉钉快搭抄送 TODO @jason:注释要有空行哈;
  25 + * 仿钉钉快搭抄送
  26 + *
25 27 * @param execution 执行的任务(ScriptTask)
26 28 */
27 29 public Boolean copy(DelegateExecution execution) {
... ...