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

This commit is contained in:
jason 2024-04-11 21:02:38 +08:00
parent 1e97ca282b
commit 9456d461f9
6 changed files with 316 additions and 271 deletions

View File

@ -18,20 +18,17 @@ import java.util.Objects;
public enum BpmSimpleModelNodeType implements IntArrayValuable {
// TODO @jaosn-1014-2 是前端已经定义好的么感觉未来可以考虑搞成和 BPMN 尽量一致的单词哈类似 usertask 用户审批
// TODO @jason_NODE 都删除掉哈
START_EVENT_NODE(0, "开始节点"),
END_EVENT_NODE(-2, "结束节点"), // TODO @jaosn挪到 START_EVENT_NODE
START_EVENT(0, "开始节点"),
END_EVENT(-2, "结束节点"), // TODO @jaosn挪到 START_EVENT_NODE
APPROVE_USER_NODE(1, "审批人节点"), // TODO @jaosn是不是这里从 10 开始好点相当于说0-9 给开始和结束10-19 给各种节点20-29 给各种条件TODO @jason改成 USER_TASK 是不是好点呀
// 抄送人节点对应 BPMN ScriptTask. 使用ScriptTask 原因好像 ServiceTask 自定义属性不能写入 XML
// TODO @jasonServiceTask 自定义 xml有没啥报错信息
SCRIPT_TASK_NODE(2, "抄送人节点"), // TODO @jason是不是改成 COPY_TASK 好一点哈
USER_TASK(1, "审批人节点"), // TODO @jaosn是不是这里从 10 开始好点相当于说0-9 给开始和结束10-19 给各种节点20-29 给各种条件 TODO 后面改改
COPY_TASK(2, "抄送人节点"),
EXCLUSIVE_GATEWAY_NODE(4, "排他网关"), // TODO @jason是不是改成叫 条件分支
PARALLEL_GATEWAY_FORK_NODE(5, "并行网关分叉节点"), // TODO @jason是不是一个 并行分支 就可以啦
PARALLEL_GATEWAY_JOIN_NODE(6, "并行网关聚合节点"),
INCLUSIVE_GATEWAY_FORK_NODE(7, "包容网关分叉节点"),
INCLUSIVE_GATEWAY_JOIN_NODE(8, "包容网关聚合节点"),
EXCLUSIVE_GATEWAY(4, "排他网关"), // TODO @jason是不是改成叫 条件分支
PARALLEL_GATEWAY_FORK(5, "并行网关分叉节点"), // TODO @jason是不是一个 并行分支 就可以啦 后面是否去掉并行网关只用包容网关
PARALLEL_GATEWAY_JOIN(6, "并行网关聚合节点"),
INCLUSIVE_GATEWAY_FORK(7, "包容网关分叉节点"),
INCLUSIVE_GATEWAY_JOIN(8, "包容网关聚合节点"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(BpmSimpleModelNodeType::getType).toArray();
@ -39,9 +36,13 @@ public enum BpmSimpleModelNodeType implements IntArrayValuable {
private final Integer type;
private final String name;
public static boolean isGatewayNode(Integer type) {
return Objects.equals(EXCLUSIVE_GATEWAY_NODE.getType(), type) || Objects.equals(PARALLEL_GATEWAY_FORK_NODE.getType(), type)
|| Objects.equals(INCLUSIVE_GATEWAY_FORK_NODE.getType(), type) ;
/**
* 判断是否为分支节点
* @param type 节点类型
*/
public static boolean isBranchNode(Integer type) {
return Objects.equals(EXCLUSIVE_GATEWAY.getType(), type) || Objects.equals(PARALLEL_GATEWAY_FORK.getType(), type)
|| Objects.equals(INCLUSIVE_GATEWAY_FORK.getType(), type) ;
}
public static BpmSimpleModelNodeType valueOf(Integer type) {

View File

@ -1,43 +1,41 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.scripting.ScriptingEngines;
import org.flowable.common.engine.impl.util.io.BytesStreamSource;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.flowable.bpmn.constants.BpmnXMLConstants.*;
import java.util.*;
/**
* 流程模型转操作工具类
*/
public class BpmnModelUtils {
public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
public static Integer parseCandidateStrategy(FlowElement userTask) {
return NumberUtils.parseInt(userTask.getAttributeValue(
Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
// @芋艿 尝试从 ExtensionElement . 后续相关扩展是否都可以 extensionElement 如表单权限 按钮权限
if (candidateStrategy == null) {
ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
candidateStrategy = NumberUtils.parseInt(Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null));
}
return candidateStrategy;
}
public static String parseCandidateParam(FlowElement userTask) {
return userTask.getAttributeValue(
String candidateParam = userTask.getAttributeValue(
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
if (candidateParam == null) {
ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
candidateParam = Optional.ofNullable(element).map(ExtensionElement::getElementText).orElse(null);
}
return candidateParam;
}
/**
@ -339,231 +337,4 @@ public class BpmnModelUtils {
}
return userTaskList;
}
// ========== TODO @jason单独出一个 SimpleModelUtils定位上它是 BPMN 的精简模式 ==========
/**
* 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善
*
* @param processId 流程标识
* @param processName 流程名称
* @param simpleModelNode 仿钉钉流程设计模型数据结构
* @return Bpmn Model
*/
public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
BpmnModel bpmnModel = new BpmnModel();
Process mainProcess = new Process();
mainProcess.setId(processId);
mainProcess.setName(processName);
mainProcess.setExecutable(Boolean.TRUE);
bpmnModel.addProcess(mainProcess);
// 前端模型数据结构 start event 节点. 没有 end event 节点
// 添加 FlowNode
addBpmnFlowNode(mainProcess, simpleModelNode);
// 单独添加 end event 节点
addBpmnEndEventNode(mainProcess);
// 添加节点之间的连线 Sequence Flow
addBpmnSequenceFlow(mainProcess, simpleModelNode, BpmnModelConstants.END_EVENT_ID);
// 自动布局
new BpmnAutoLayout(bpmnModel).execute();
return bpmnModel;
}
private static void addBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node, String endId) {
// 节点为 null 退出
if (node == null || node.getId() == null) {
return;
}
BpmSimpleModelNodeVO childNode = node.getChildNode();
// 如果不是网关节点且后续节点为 null. 添加与结束节点的连线
if (!BpmSimpleModelNodeType.isGatewayNode(node.getType()) && (childNode == null || childNode.getId() == null)) {
addBpmnSequenceFlowElement(mainProcess, node.getId(), endId, null, null);
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
// TODO @jason建议是addXXX 都改成 buildXXX构建出一个什么然后返回之后让这个方法添加到自己的结果里
switch (nodeType) {
case START_EVENT_NODE:
case APPROVE_USER_NODE:
case SCRIPT_TASK_NODE:
case PARALLEL_GATEWAY_JOIN_NODE:
case INCLUSIVE_GATEWAY_JOIN_NODE:{
addBpmnSequenceFlowElement(mainProcess, node.getId(), childNode.getId(), null, null);
// 递归调用后续节点
addBpmnSequenceFlow(mainProcess, childNode, endId);
break;
}
case PARALLEL_GATEWAY_FORK_NODE:
case EXCLUSIVE_GATEWAY_NODE:
case INCLUSIVE_GATEWAY_FORK_NODE:{
String gateWayEndId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
for (int i = 0; i < conditionNodes.size(); i++) {
BpmSimpleModelNodeVO item = conditionNodes.get(i);
BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
addBpmnSequenceFlowElement(mainProcess, node.getId(), nextNodeOnCondition.getId(),
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
addBpmnSequenceFlow(mainProcess, nextNodeOnCondition, gateWayEndId);
} else {
addBpmnSequenceFlowElement(mainProcess, node.getId(), gateWayEndId,
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
}
}
// 递归调用后续节点
addBpmnSequenceFlow(mainProcess, childNode, endId);
break;
}
default: {
// TODO 其它节点类型的实现
}
}
}
private static void addBpmnSequenceFlowElement(Process mainProcess, String sourceId, String targetId, String seqFlowId, String conditionExpression) {
SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
if (StrUtil.isNotEmpty(conditionExpression)) {
sequenceFlow.setConditionExpression(conditionExpression);
}
if (StrUtil.isNotEmpty(seqFlowId)) {
sequenceFlow.setId(seqFlowId);
}
mainProcess.addFlowElement(sequenceFlow);
}
private static void addBpmnFlowNode(Process mainProcess, BpmSimpleModelNodeVO simpleModelNode) {
// 节点为 null 退出
if (simpleModelNode == null || simpleModelNode.getId() == null) {
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
switch (nodeType) {
case START_EVENT_NODE:
addBpmnStartEventNode(mainProcess, simpleModelNode);
break;
case APPROVE_USER_NODE:
addBpmnUserTaskNode(mainProcess, simpleModelNode);
break;
case SCRIPT_TASK_NODE:
addBpmnScriptTaSskNode(mainProcess, simpleModelNode);
break;
case EXCLUSIVE_GATEWAY_NODE:
addBpmnExclusiveGatewayNode(mainProcess, simpleModelNode);
break;
case PARALLEL_GATEWAY_FORK_NODE:
case PARALLEL_GATEWAY_JOIN_NODE:
addBpmnParallelGatewayNode(mainProcess, simpleModelNode);
break;
case INCLUSIVE_GATEWAY_FORK_NODE:
addBpmnInclusiveGatewayNode(mainProcess, simpleModelNode, Boolean.TRUE);
break;
case INCLUSIVE_GATEWAY_JOIN_NODE:
addBpmnInclusiveGatewayNode(mainProcess, simpleModelNode, Boolean.FALSE);
break;
default: {
// TODO 其它节点类型的实现
}
}
// 如果不是网关类型的接口 并且chileNode为空退出
if (!BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && simpleModelNode.getChildNode() == null) {
return;
}
// 如果是网关类型接口. 递归添加条件节点
if (BpmSimpleModelNodeType.isGatewayNode(simpleModelNode.getType()) && ArrayUtil.isNotEmpty(simpleModelNode.getConditionNodes())) {
for (BpmSimpleModelNodeVO node : simpleModelNode.getConditionNodes()) {
addBpmnFlowNode(mainProcess, node.getChildNode());
}
}
// chileNode不为空递归添加子节点
if (simpleModelNode.getChildNode() != null) {
addBpmnFlowNode(mainProcess, simpleModelNode.getChildNode());
}
}
private static void addBpmnParallelGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(node.getId());
mainProcess.addFlowElement(parallelGateway);
}
private static void addBpmnScriptTaSskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
ScriptTask scriptTask = new ScriptTask();
scriptTask.setId(node.getId());
scriptTask.setName(node.getName());
// TODO @jason建议使用 ServiceTask通过 executionListeners 实现
scriptTask.setScriptFormat(ScriptingEngines.DEFAULT_SCRIPTING_LANGUAGE);
scriptTask.setScript(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
// 添加自定义属性
// TODO @jason可以使用 ServiceTask ExtensionAttribute
addExtensionAttributes(node, scriptTask);
mainProcess.addFlowElement(scriptTask);
}
private static void addExtensionAttributes(BpmSimpleModelNodeVO node, FlowElement flowElement) {
Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
candidateStrategy == null ? null : String.valueOf(candidateStrategy));
addExtensionAttributes(flowElement, BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
}
private static void addBpmnExclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node) {
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(node.getId());
// 网关的最后一个条件为 网关的 default sequence flow
exclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
mainProcess.addFlowElement(exclusiveGateway);
}
private static void addBpmnInclusiveGatewayNode(Process mainProcess, BpmSimpleModelNodeVO node, Boolean isFork) {
InclusiveGateway inclusiveGateway = new InclusiveGateway();
inclusiveGateway.setId(node.getId());
if (isFork) {
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
// 网关的最后一个条件为 网关的 default sequence flow
inclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
}
mainProcess.addFlowElement(inclusiveGateway);
}
private static void addBpmnEndEventNode(Process mainProcess) {
EndEvent endEvent = new EndEvent();
endEvent.setId(BpmnModelConstants.END_EVENT_ID);
endEvent.setName("结束");
mainProcess.addFlowElement(endEvent);
}
private static void addBpmnUserTaskNode(Process mainProcess, BpmSimpleModelNodeVO node) {
UserTask userTask = new UserTask();
userTask.setId(node.getId());
userTask.setName(node.getName());
addExtensionAttributes(node, userTask);
mainProcess.addFlowElement(userTask);
}
private static void addExtensionAttributes(FlowElement element, String namespace, String name, String value) {
if (value == null) {
return;
}
ExtensionAttribute extensionAttribute = new ExtensionAttribute(name, value);
extensionAttribute.setNamespace(namespace);
extensionAttribute.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
element.addAttribute(extensionAttribute);
}
private static void addBpmnStartEventNode(Process mainProcess, BpmSimpleModelNodeVO node) {
StartEvent startEvent = new StartEvent();
startEvent.setId(node.getId());
startEvent.setName(node.getName());
mainProcess.addFlowElement(startEvent);
}
}

View File

@ -0,0 +1,270 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.Process;
import java.util.List;
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
/**
* 仿钉钉快搭模型相关的工具方法
*
* @author jason
*/
public class SimpleModelUtils {
public static final String BPMN_SIMPLE_COPY_EXECUTION_SCRIPT = "#{bpmSimpleNodeService.copy(execution)}";
/**
* 仿钉钉流程设计模型数据结构(json) 转换成 Bpmn Model (待完善
*
* @param processId 流程标识
* @param processName 流程名称
* @param simpleModelNode 仿钉钉流程设计模型数据结构
* @return Bpmn Model
*/
public static BpmnModel convertSimpleModelToBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) {
BpmnModel bpmnModel = new BpmnModel();
Process mainProcess = new Process();
mainProcess.setId(processId);
mainProcess.setName(processName);
mainProcess.setExecutable(Boolean.TRUE);
bpmnModel.addProcess(mainProcess);
// 前端模型数据结构 start event 节点. 没有 end event 节点
// SimpleModel 构建 FlowNode 并添加到 Main Process
buildAndAddBpmnFlowNode(simpleModelNode, mainProcess);
// 单独构建 end event 节点
buildAndAddBpmnEndEvent(mainProcess);
// 构建并添加节点之间的连线 Sequence Flow
buildAndAddBpmnSequenceFlow(mainProcess, simpleModelNode, BpmnModelConstants.END_EVENT_ID);
// 自动布局
new BpmnAutoLayout(bpmnModel).execute();
return bpmnModel;
}
private static void buildAndAddBpmnSequenceFlow(Process mainProcess, BpmSimpleModelNodeVO node, String targetId) {
// 节点为 null 退出
if (node == null || node.getId() == null) {
return;
}
BpmSimpleModelNodeVO childNode = node.getChildNode();
// 如果不是网关节点且后续节点为 null. 添加与结束节点的连线
if (!BpmSimpleModelNodeType.isBranchNode(node.getType()) && (childNode == null || childNode.getId() == null)) {
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), targetId, null, null);
mainProcess.addFlowElement(sequenceFlow);
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(node.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
switch (nodeType) {
case START_EVENT:
case USER_TASK:
case COPY_TASK:
case PARALLEL_GATEWAY_JOIN:
case INCLUSIVE_GATEWAY_JOIN:{
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), childNode.getId(), null, null);
mainProcess.addFlowElement(sequenceFlow);
// 递归调用后续节点
buildAndAddBpmnSequenceFlow(mainProcess, childNode, targetId);
break;
}
case PARALLEL_GATEWAY_FORK:
case EXCLUSIVE_GATEWAY:
case INCLUSIVE_GATEWAY_FORK:{
String sequenceFlowTargetId = (childNode == null || childNode.getId() == null) ? BpmnModelConstants.END_EVENT_ID : childNode.getId();
List<BpmSimpleModelNodeVO> conditionNodes = node.getConditionNodes();
Assert.notEmpty(conditionNodes, "网关节点的条件节点不能为空");
for (int i = 0; i < conditionNodes.size(); i++) {
BpmSimpleModelNodeVO item = conditionNodes.get(i);
BpmSimpleModelNodeVO nextNodeOnCondition = item.getChildNode();
if (nextNodeOnCondition != null && nextNodeOnCondition.getId() != null) {
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), nextNodeOnCondition.getId(),
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
mainProcess.addFlowElement(sequenceFlow);
// 递归调用后续节点
buildAndAddBpmnSequenceFlow(mainProcess, nextNodeOnCondition, sequenceFlowTargetId);
} else {
SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), sequenceFlowTargetId,
String.format("%s_SequenceFlow_%d", node.getId(), i + 1), null);
mainProcess.addFlowElement(sequenceFlow);
}
}
// 递归调用后续节点
buildAndAddBpmnSequenceFlow(mainProcess, childNode, targetId);
break;
}
default: {
// TODO 其它节点类型的实现
}
}
}
private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, String seqFlowId, String conditionExpression) {
SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId);
if (StrUtil.isNotEmpty(conditionExpression)) {
sequenceFlow.setConditionExpression(conditionExpression);
}
if (StrUtil.isNotEmpty(seqFlowId)) {
sequenceFlow.setId(seqFlowId);
}
return sequenceFlow;
}
private static void buildAndAddBpmnFlowNode(BpmSimpleModelNodeVO simpleModelNode, Process mainProcess) {
// 节点为 null 退出
if (simpleModelNode == null || simpleModelNode.getId() == null) {
return;
}
BpmSimpleModelNodeType nodeType = BpmSimpleModelNodeType.valueOf(simpleModelNode.getType());
Assert.notNull(nodeType, "模型节点类型不支持");
switch (nodeType) {
case START_EVENT: {
StartEvent startEvent = buildBpmnStartEvent(simpleModelNode);
mainProcess.addFlowElement(startEvent);
break;
}
case USER_TASK: {
UserTask userTask = buildBpmnUserTask(simpleModelNode);
mainProcess.addFlowElement(userTask);
break;
}
case COPY_TASK: {
ServiceTask serviceTask = buildBpmnServiceTask(simpleModelNode);
mainProcess.addFlowElement(serviceTask);
break;
}
case EXCLUSIVE_GATEWAY: {
ExclusiveGateway exclusiveGateway = buildBpmnExclusiveGateway(simpleModelNode);
mainProcess.addFlowElement(exclusiveGateway);
break;
}
case PARALLEL_GATEWAY_FORK:
case PARALLEL_GATEWAY_JOIN: {
ParallelGateway parallelGateway = buildBpmnParallelGateway(simpleModelNode);
mainProcess.addFlowElement(parallelGateway);
break;
}
case INCLUSIVE_GATEWAY_FORK: {
InclusiveGateway inclusiveGateway = buildBpmnInclusiveGateway(simpleModelNode, Boolean.TRUE);
mainProcess.addFlowElement(inclusiveGateway);
break;
}
case INCLUSIVE_GATEWAY_JOIN: {
InclusiveGateway inclusiveGateway = buildBpmnInclusiveGateway(simpleModelNode, Boolean.FALSE);
mainProcess.addFlowElement(inclusiveGateway);
break;
}
default: {
// TODO 其它节点类型的实现
}
}
// 如果不是网关类型的接口 并且chileNode为空退出
if (!BpmSimpleModelNodeType.isBranchNode(simpleModelNode.getType()) && simpleModelNode.getChildNode() == null) {
return;
}
// 如果是网关类型接口. 递归添加条件节点
if (BpmSimpleModelNodeType.isBranchNode(simpleModelNode.getType()) && ArrayUtil.isNotEmpty(simpleModelNode.getConditionNodes())) {
for (BpmSimpleModelNodeVO node : simpleModelNode.getConditionNodes()) {
buildAndAddBpmnFlowNode(node.getChildNode(), mainProcess);
}
}
// chileNode不为空递归添加子节点
if (simpleModelNode.getChildNode() != null) {
buildAndAddBpmnFlowNode(simpleModelNode.getChildNode(), mainProcess);
}
}
private static ParallelGateway buildBpmnParallelGateway(BpmSimpleModelNodeVO node) {
ParallelGateway parallelGateway = new ParallelGateway();
parallelGateway.setId(node.getId());
return parallelGateway;
}
private static ServiceTask buildBpmnServiceTask(BpmSimpleModelNodeVO node) {
ServiceTask serviceTask = new ServiceTask();
serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_EXPRESSION);
serviceTask.setImplementation(BPMN_SIMPLE_COPY_EXECUTION_SCRIPT);
serviceTask.setId(node.getId());
serviceTask.setName(node.getName());
// TODO @jason建议使用 ServiceTask通过 executionListeners 实现
// @芋艿 ServiceTask 就可以了吧 不需要 executionListeners
addExtensionElement(node, serviceTask);
return serviceTask;
}
private static void addExtensionElement(BpmSimpleModelNodeVO node, FlowElement flowElement) {
Integer candidateStrategy = MapUtil.getInt(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY);
addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
candidateStrategy == null ? null : String.valueOf(candidateStrategy));
addExtensionElement(flowElement, FLOWABLE_EXTENSIONS_NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM,
MapUtil.getStr(node.getAttributes(), BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
}
private static ExclusiveGateway buildBpmnExclusiveGateway(BpmSimpleModelNodeVO node) {
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(node.getId());
// 网关的最后一个条件为 网关的 default sequence flow
exclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
return exclusiveGateway;
}
private static InclusiveGateway buildBpmnInclusiveGateway(BpmSimpleModelNodeVO node, Boolean isFork) {
InclusiveGateway inclusiveGateway = new InclusiveGateway();
inclusiveGateway.setId(node.getId());
if (isFork) {
Assert.notEmpty(node.getConditionNodes(), "网关节点的条件节点不能为空");
// 网关的最后一个条件为 网关的 default sequence flow
inclusiveGateway.setDefaultFlow(String.format("%s_SequenceFlow_%d", node.getId(), node.getConditionNodes().size()));
}
return inclusiveGateway;
}
private static void buildAndAddBpmnEndEvent(Process mainProcess) {
EndEvent endEvent = new EndEvent();
endEvent.setId(BpmnModelConstants.END_EVENT_ID);
endEvent.setName("结束");
mainProcess.addFlowElement(endEvent);
}
private static UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) {
UserTask userTask = new UserTask();
userTask.setId(node.getId());
userTask.setName(node.getName());
addExtensionElement(node, userTask);
return userTask;
}
private static void addExtensionElement(FlowElement element, String namespace, String name, String value) {
if (value == null) {
return;
}
ExtensionElement extensionElement = new ExtensionElement();
extensionElement.setNamespace(namespace);
extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
extensionElement.setElementText(value);
extensionElement.setName(name);
element.addExtensionElement(extensionElement);
}
private static StartEvent buildBpmnStartEvent(BpmSimpleModelNodeVO node) {
StartEvent startEvent = new StartEvent();
startEvent.setId(node.getId());
startEvent.setName(node.getName());
return startEvent;
}
}

View File

@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.simple.BpmSimp
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
import jakarta.annotation.Resource;
import org.flowable.bpmn.model.*;
import org.flowable.engine.repository.Model;
@ -22,7 +23,7 @@ import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.MODEL_NOT_EXISTS;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_EVENT_NODE;
import static cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeType.START_EVENT;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_PARAM;
import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY;
@ -56,7 +57,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
// return Boolean.FALSE;
// }
// 1. JSON 转换成 bpmnModel
BpmnModel bpmnModel = BpmnModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
BpmnModel bpmnModel = SimpleModelUtils.convertSimpleModelToBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModelBody());
// 2.1 保存 Bpmn XML
bpmModelService.saveModelBpmnXml(model.getId(), StrUtil.utf8Bytes(BpmnModelUtils.getBpmnXml(bpmnModel)));
// 2.2 保存 JSON 数据
@ -93,7 +94,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
return null;
}
BpmSimpleModelNodeVO rootNode = new BpmSimpleModelNodeVO();
rootNode.setType(START_EVENT_NODE.getType());
rootNode.setType(START_EVENT.getType());
rootNode.setId(startEvent.getId());
rootNode.setName(startEvent.getName());
recursiveBuildSimpleModelNode(startEvent, rootNode);
@ -105,10 +106,10 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
Assert.notNull(nodeType, "节点类型不支持");
// 校验节点是否支持转仿钉钉的流程模型
List<SequenceFlow> outgoingFlows = validateCanConvertSimpleNode(nodeType, currentFlowNode);
if (CollUtil.isEmpty(outgoingFlows) || outgoingFlows.get(0).getTargetFlowElement() == null) {
if (CollUtil.isEmpty(outgoingFlows) || CollUtil.getFirst(outgoingFlows).getTargetFlowElement() == null) {
return;
}
FlowElement targetElement = outgoingFlows.get(0).getTargetFlowElement();
FlowElement targetElement = CollUtil.getFirst(outgoingFlows).getTargetFlowElement();
// 如果是 EndEvent 直接退出
if (targetElement instanceof EndEvent) {
return;
@ -123,7 +124,7 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
private BpmSimpleModelNodeVO convertUserTaskToSimpleModelNode(UserTask userTask) {
BpmSimpleModelNodeVO simpleModelNodeVO = new BpmSimpleModelNodeVO();
simpleModelNodeVO.setType(BpmSimpleModelNodeType.APPROVE_USER_NODE.getType());
simpleModelNodeVO.setType(BpmSimpleModelNodeType.USER_TASK.getType());
simpleModelNodeVO.setName(userTask.getName());
simpleModelNodeVO.setId(userTask.getId());
Map<String, Object> attributes = MapUtil.newHashMap();
@ -137,13 +138,13 @@ public class BpmSimpleModelServiceImpl implements BpmSimpleModelService {
private List<SequenceFlow> validateCanConvertSimpleNode(BpmSimpleModelNodeType nodeType, FlowNode currentFlowNode) {
switch (nodeType) {
case START_EVENT_NODE:
case APPROVE_USER_NODE: {
case START_EVENT:
case USER_TASK: {
List<SequenceFlow> outgoingFlows = currentFlowNode.getOutgoingFlows();
if (CollUtil.isNotEmpty(outgoingFlows) && outgoingFlows.size() > 1) {
throw exception(CONVERT_TO_SIMPLE_MODEL_NOT_SUPPORT);
}
validIsSupportFlowNode(outgoingFlows.get(0).getTargetFlowElement());
validIsSupportFlowNode(CollUtil.getFirst(outgoingFlows).getTargetFlowElement());
return outgoingFlows;
}
default: {

View File

@ -47,7 +47,7 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy
// TODO @芋艿这里多加了一个 name
@Override
public void createProcessInstanceCopy(Collection<Long> userIds, String processInstanceId, String taskId, String taskName) {
// 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask) TODO jason抄送节点会没有来源的 taskId
// 1.1 校验任务存在 暂时去掉这个校验. 因为任务可能仿钉钉快搭的抄送节点(ScriptTask) TODO jason抄送节点会没有来源的 taskId @芋艿 是否校验一下 传递进来的 id 不为空就行
// Task task = taskService.getTask(taskId);
// if (ObjectUtil.isNull(task)) {
// throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);

View File

@ -9,7 +9,8 @@ import org.springframework.stereotype.Service;
import java.util.Set;
/**
* 仿钉钉快搭各个节点 Service TODO @jason注释要有空行哈
* 仿钉钉快搭各个节点 Service
*
* @author jason
*/
@Service
@ -21,7 +22,8 @@ public class BpmSimpleNodeService {
private BpmProcessInstanceCopyService processInstanceCopyService;
/**
* 仿钉钉快搭抄送 TODO @jason注释要有空行哈
* 仿钉钉快搭抄送
*
* @param execution 执行的任务(ScriptTask)
*/
public Boolean copy(DelegateExecution execution) {