update - 调整 BPMN 协议转换逻辑, 解决针对部分情况的转换异常

This commit is contained in:
wangli 2023-11-16 18:58:05 +08:00
parent 76422ee9cc
commit f04abb0960
7 changed files with 376 additions and 312 deletions

View File

@ -9,9 +9,8 @@ import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* JSON 版本的 BPMN 协议模型
@ -64,7 +63,7 @@ public class BpmnJsonNode {
/* 内部使用,不需要外界传 */
private transient Map incoming = new HashMap();
private transient List<String> incoming = new ArrayList<>();
/* 内部使用, 用于 JSON 格式转换 */
private transient BpmnJsonNode preJsonNode;
@ -124,11 +123,11 @@ public class BpmnJsonNode {
this.property = property;
}
public Map<String, List<String>> getIncoming() {
public List<String> getIncoming() {
return incoming;
}
public void setIncoming(Map incoming) {
public void setIncoming(List<String> incoming) {
this.incoming = incoming;
}

View File

@ -11,16 +11,34 @@ import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowableListener;
import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.bpmn.model.SequenceFlow;
import org.flowable.bpmn.model.ServiceTask;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.TaskListener;
import org.flowable.engine.repository.Model;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.constant.BpmnConstants.*;
import static cn.axzo.workflow.common.constant.BpmnConstants.AND_SIGN_EXPRESSION;
import static cn.axzo.workflow.common.constant.BpmnConstants.END_EVENT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.OR_SIGN_EXPRESSION;
import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.CONVERTOR_META_DATA_FORMAT_ERROR;
import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.CONVERTOR_UNKNOW_NODE_TYPE;
import static org.flowable.bpmn.model.ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION;
@ -154,8 +172,7 @@ public class BpmTransformUtil {
return createExclusiveGatewayBuilder(fromId, flowNode, process, bpmnModel, sequenceFlows, childNodeMap);
} else if (BpmnFlowNodeType.NODE_TASK.isEqual(nodeType)) {
childNodeMap.put(flowNode.getId(), flowNode);
Map incoming = flowNode.getIncoming();
incoming.put("incoming", Collections.singletonList(fromId));
flowNode.setIncoming(Collections.singletonList(fromId));
String id = createTask(process, flowNode, sequenceFlows, childNodeMap);
// 如果当前任务还有后续任务则遍历创建后续任务
BpmnJsonNode children = flowNode.getChildren();
@ -166,8 +183,7 @@ public class BpmTransformUtil {
}
} else if (BpmnFlowNodeType.NODE_STARTER.isEqual(nodeType)) {
childNodeMap.put(flowNode.getId(), flowNode);
Map incoming = flowNode.getIncoming();
incoming.put("incoming", Collections.singletonList(fromId));
flowNode.setIncoming(Collections.singletonList(fromId));
String id = createTask(process, flowNode, sequenceFlows, childNodeMap);
// 如果当前任务还有后续任务则遍历创建后续任务
BpmnJsonNode children = flowNode.getChildren();
@ -236,8 +252,7 @@ public class BpmTransformUtil {
continue;
}
// 只生成一个任务同时设置当前任务的条件
Map incomingObj = children.getIncoming();
incomingObj.put("incoming", Collections.singletonList(exclusiveGatewayId));
children.setIncoming(Collections.singletonList(exclusiveGatewayId));
String identifier = create(exclusiveGatewayId, children, process, bpmnModel, sequenceFlows, childNodeMap);
List<SequenceFlow> flows = sequenceFlows.stream().filter(flow -> StringUtils.equals(exclusiveGatewayId,
flow.getSourceRef()))
@ -273,9 +288,8 @@ public class BpmTransformUtil {
return create(exclusiveGatewayId, childNode, process, bpmnModel, sequenceFlows,
childNodeMap);
} else {
Map incomingObj = childNode.getIncoming();
// 所有 service task 连接 end exclusive gateway
incomingObj.put("incoming", incoming);
childNode.setIncoming(incoming);
FlowElement flowElement = bpmnModel.getFlowElement(incoming.get(0));
// 1.0 先进行边连接, 暂存 nextNode
BpmnJsonNode nextNode = childNode.getChildren();
@ -332,8 +346,7 @@ public class BpmTransformUtil {
private static String createTask(Process process, BpmnJsonNode flowNode, List<SequenceFlow> sequenceFlows,
Map<String, BpmnJsonNode> childNodeMap) {
Map incomingJson = flowNode.getIncoming();
List<String> incoming = (List<String>) incomingJson.get("incoming");
List<String> incoming = flowNode.getIncoming();
// 自动生成id
// String id = id("serviceTask");
String id = flowNode.getId();

View File

@ -1,5 +1,6 @@
package cn.axzo.workflow.core.common.utils;
import cn.axzo.workflow.common.enums.ApprovalMethodEnum;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonConf;
import cn.axzo.workflow.common.model.request.bpmn.BpmnButtonMetaInfo;
import cn.axzo.workflow.common.model.request.bpmn.BpmnFieldConf;
@ -51,6 +52,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -82,6 +84,7 @@ import static cn.axzo.workflow.common.constant.BpmnConstants.START_EVENT_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_NOTICE_MESSAGE_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_PENDING_MESSAGE_ID;
import static cn.axzo.workflow.common.constant.BpmnConstants.TEMPLATE_SMS_MESSAGE_ID;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CONDITION;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.CONVERTOR_COMMON_ERROR;
@ -96,19 +99,21 @@ public final class BpmnJsonConverterUtil {
}
private static final Logger log = LoggerFactory.getLogger(BpmnJsonConverterUtil.class);
private static Map<Class<? extends BaseElement>, AbstractBpmnJsonConverter<? extends BaseElement>> converters =
private static final Map<Class<? extends BaseElement>, AbstractBpmnJsonConverter<? extends BaseElement>> CONVERTERS =
new HashMap<>();
private static final Map<String, BpmnJsonNode> FLAT_NODE_MAP = new HashMap<>();
static {
converters.put(StartEvent.class, new StartEventJsonConverter());
converters.put(SequenceFlow.class, new SequenceFlowJsonConverter());
converters.put(EndEvent.class, new EndEventJsonConverter());
CONVERTERS.put(NotSupportConverter.NotSupportFlowElement.class, new NotSupportConverter());
CONVERTERS.put(StartEvent.class, new StartEventJsonConverter());
CONVERTERS.put(SequenceFlow.class, new SequenceFlowJsonConverter());
CONVERTERS.put(EndEvent.class, new EndEventJsonConverter());
converters.put(ExclusiveGateway.class, new ExclusiveGatewayJsonConverter());
converters.put(ParallelGateway.class, new ParallelGatewayJsonConverter());
converters.put(UserTask.class, new UserTaskJsonConverter());
converters.put(ServiceTask.class, new ServiceTaskJsonConverter());
converters.put(ReceiveTask.class, new ReceiveTaskJsonConverter());
CONVERTERS.put(ExclusiveGateway.class, new ExclusiveGatewayJsonConverter());
CONVERTERS.put(ParallelGateway.class, new ParallelGatewayJsonConverter());
CONVERTERS.put(UserTask.class, new UserTaskJsonConverter());
CONVERTERS.put(ServiceTask.class, new ServiceTaskJsonConverter());
CONVERTERS.put(ReceiveTask.class, new ReceiveTaskJsonConverter());
}
/**
@ -338,11 +343,9 @@ public final class BpmnJsonConverterUtil {
*/
private static String create(BpmnJsonNode bpmnJsonNode, Process mainProcess,
BpmnModel bpmnModel, String... preNodeIds) {
FLAT_NODE_MAP.put(bpmnJsonNode.getId(), bpmnJsonNode);
// 设置来源节点
Map<String, List<String>> incoming = bpmnJsonNode.getIncoming();
List<String> preNodeIdList = Lists.newArrayList(preNodeIds);
incoming.put("incoming", preNodeIdList);
bpmnJsonNode.setIncoming(incoming);
bpmnJsonNode.setIncoming(Lists.newArrayList(preNodeIds));
Class<? extends BaseElement> clz;
switch (bpmnJsonNode.getType()) {
@ -358,6 +361,7 @@ public final class BpmnJsonConverterUtil {
clz = ParallelGateway.class;
break;
case NODE_CONDITION:
// 这个节点非常特殊,整个协议转换完全不会走到这里
clz = SequenceFlow.class;
break;
case NODE_BUSINESS:
@ -365,7 +369,7 @@ public final class BpmnJsonConverterUtil {
// 一种是: 可以不配置人, 当运行到该节点时, 需要由外部发送信号来触发(这个信号的发送方通常是程序应用), 流程继续运行.
// 另外一种是: 可以配置人, 当运行到该节点时, 有需要人为来处理. 这种情况下, 该节点就是一个普通的任务节点.
clz = ReceiveTask.class;
if (!Objects.equals("nobody", bpmnJsonNode.getProperty().getApprovalMethod())) {
if (!Objects.equals(ApprovalMethodEnum.nobody, bpmnJsonNode.getProperty().getApprovalMethod())) {
clz = UserTask.class;
}
break;
@ -376,49 +380,49 @@ public final class BpmnJsonConverterUtil {
clz = ServiceTask.class;
break;
default:
clz = UserTask.class;
clz = NotSupportConverter.NotSupportFlowElement.class;
break;
}
// 根据 BpmnJsonNode 创建节点
FlowElement flowElement = convertJsonToElement(clz, bpmnJsonNode, mainProcess);
mainProcess.addFlowElement(flowElement);
// 通用链接当前节点和前一个节点的 SequenceFlow
if (preNodeIdList.size() > 1) {
// 如果涉及到网关,那么 incoming 就是大于 1
preNodeIdList.stream()
// 过滤掉自己
.filter(income -> !Objects.equals(bpmnJsonNode.getId(), income))
.forEach(income -> {
Map<String, List<String>> tmpIncomeMap = new HashMap<>();
tmpIncomeMap.put("incoming", Lists.newArrayList(income));
bpmnJsonNode.setIncoming(tmpIncomeMap);
mainProcess.addFlowElement(convertJsonToElement(SequenceFlow.class, bpmnJsonNode, mainProcess));
});
} else {
FlowElement sequenceFlow = convertJsonToElement(SequenceFlow.class, bpmnJsonNode, mainProcess);
mainProcess.addFlowElement(sequenceFlow);
String preNodeId = preNodeIds[0];
FlowElement preFlowNode;
if (Objects.nonNull(preFlowNode = mainProcess.getFlowElement(preNodeId, true))
&& preFlowNode instanceof Gateway) {
BpmnJsonNode preJsonNode = bpmnJsonNode.getPreJsonNode();
if (Boolean.TRUE.equals(preJsonNode.getProperty().getDefaultBranch())) {
Gateway gateway = (Gateway) preFlowNode;
gateway.setDefaultFlow(sequenceFlow.getId());
}
}
if (Lists.newArrayList(preNodeIds).isEmpty()) {
// first time entrance, nothing to do.
} else if (Lists.newArrayList(preNodeIds).size() == 1 && !NODE_CONDITION.equals(bpmnJsonNode.getType())) {
mainProcess.addFlowElement(convertJsonToElement(SequenceFlow.class, bpmnJsonNode, mainProcess));
} else {
// 网关的 SequenceFlow, 需要在这里设置网关的 DefaultFlow, 同时也需要解析这些 SequenceFlow 的条件
Arrays.stream(preNodeIds).forEach(income -> {
bpmnJsonNode.setIncoming(Lists.newArrayList(income));
FlowElement sequenceFlow = convertJsonToElement(SequenceFlow.class, bpmnJsonNode, mainProcess);
mainProcess.addFlowElement(sequenceFlow);
setDefaultFlow(bpmnJsonNode, mainProcess, sequenceFlow);
});
}
// 只有网关才会涉及到 branch
List<String> gatewayPreIds = new ArrayList<>();
List<String> conditions = new ArrayList<>();
if (!CollectionUtils.isEmpty(bpmnJsonNode.getBranches())) {
for (BpmnJsonNode branch : bpmnJsonNode.getBranches()) {
BpmnJsonNode children = Objects.isNull(branch.getChildren()) ? bpmnJsonNode.getChildren() :
branch.getChildren();
if (Objects.nonNull(children)) {
children.setPreJsonNode(branch);
gatewayPreIds.add(create(children, mainProcess, bpmnModel, flowElement.getId()));
// branch == node_condition
if (Objects.nonNull(branch.getChildren())) {
conditions.add(create(branch.getChildren(), mainProcess, bpmnModel, flowElement.getId()));
} else {
bpmnJsonNode.getChildren().setIncoming(Lists.newArrayList(bpmnJsonNode.getId()));
FlowElement sequenceFlow = convertJsonToElement(SequenceFlow.class, bpmnJsonNode.getChildren(),
mainProcess);
mainProcess.addFlowElement(sequenceFlow);
// 判断这个是不是默认流
if (Objects.nonNull(branch.getProperty()) && Boolean.TRUE.equals(branch.getProperty().getDefaultBranch())) {
Gateway gateway = (Gateway) flowElement;
gateway.setDefaultFlow(sequenceFlow.getId());
}
}
}
}
@ -428,11 +432,26 @@ public final class BpmnJsonConverterUtil {
if (Objects.isNull(children) || Objects.equals(NODE_EMPTY, children.getType()) || !StringUtils.hasLength(children.getId())) {
return flowElement.getId();
} else {
String[] preIds = new String[]{flowElement.getId()};
if (!CollectionUtils.isEmpty(gatewayPreIds)) {
preIds = gatewayPreIds.toArray(new String[0]);
if (!CollectionUtils.isEmpty(conditions)) {
// 由于不是通过 create 方法解析 node_condition, 所以单独用一个字段来传递它
children.setPreJsonNode(bpmnJsonNode);
return create(children, mainProcess, bpmnModel, conditions.toArray(new String[0]));
}
return create(children, mainProcess, bpmnModel, flowElement.getId());
}
}
private static void setDefaultFlow(BpmnJsonNode bpmnJsonNode, Process mainProcess, FlowElement sequenceFlow) {
// 查找并设置网关的默认流
if (Objects.nonNull(bpmnJsonNode.getPreJsonNode()) && Objects.nonNull(bpmnJsonNode.getPreJsonNode().getProperty())
&& Boolean.TRUE.equals(bpmnJsonNode.getPreJsonNode().getProperty().getDefaultBranch())) {
FlowElement preFlowNode;
if (Objects.nonNull(preFlowNode =
mainProcess.getFlowElement(bpmnJsonNode.getPreJsonNode().getId(), true))
&& preFlowNode instanceof Gateway) {
Gateway gateway = (Gateway) preFlowNode;
gateway.setDefaultFlow(sequenceFlow.getId());
}
return create(children, mainProcess, bpmnModel, preIds);
}
}
@ -443,7 +462,7 @@ public final class BpmnJsonConverterUtil {
private static FlowElement convertJsonToElement(Class<? extends BaseElement> clz, BpmnJsonNode bpmnJsonNode,
Process process) {
AbstractBpmnJsonConverter converter = converters.getOrDefault(clz, new NotSupportConverter());
AbstractBpmnJsonConverter converter = CONVERTERS.getOrDefault(clz, new NotSupportConverter());
FlowElement flowElement = converter.convertJsonToElement(bpmnJsonNode, process);
if (Objects.nonNull(bpmnJsonNode)) {
converter.addNodeTypeAttribute(flowElement, copy(bpmnJsonNode));

View File

@ -3,9 +3,6 @@ package cn.axzo.workflow.core.converter.json;
import cn.axzo.workflow.common.model.request.bpmn.BpmnJsonNode;
import org.flowable.bpmn.model.ExclusiveGateway;
import org.flowable.bpmn.model.Process;
import org.springframework.util.StringUtils;
import java.util.Objects;
/**
* 排它网关
@ -20,19 +17,8 @@ public class ExclusiveGatewayJsonConverter extends AbstractBpmnJsonConverter<Exc
ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
exclusiveGateway.setId(node.getId());
exclusiveGateway.setName(node.getName());
if ((Objects.isNull(node.getChildren()) || !StringUtils.hasLength(node.getId()))
&& Objects.isNull(node.getBranches())) {
return exclusiveGateway;
}
// 条件分支, 只有一个默认流
// if (!CollectionUtils.isEmpty(node.getBranches())) {
// node.getBranches().forEach(branch -> {
// BpmnJsonNodeProperty property = branch.getProperty();
// if (Boolean.TRUE.equals(property.getDefaultCondition())) {
// exclusiveGateway.setDefaultFlow(branch.getId());
// }
// });
// }
return exclusiveGateway;
}
}

View File

@ -13,10 +13,17 @@ import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.CONVERTOR_NODE_TY
* @author wangli
* @since 2023/10/13 15:13
*/
public class NotSupportConverter extends AbstractBpmnJsonConverter {
public class NotSupportConverter extends AbstractBpmnJsonConverter<NotSupportConverter.NotSupportFlowElement> {
@Override
public FlowElement convertJsonToElement(BpmnJsonNode node, Process process) {
public NotSupportFlowElement convertJsonToElement(BpmnJsonNode node, Process process) {
throw new WorkflowEngineException(CONVERTOR_NODE_TYPE_NOT_SUPPORT, node.getType().getType(), node.getId());
}
public static class NotSupportFlowElement extends FlowElement {
@Override
public FlowElement clone() {
return null;
}
}
}

View File

@ -19,7 +19,7 @@ public class SequenceFlowJsonConverter extends AbstractBpmnJsonConverter<Sequenc
@Override
public SequenceFlow convertJsonToElement(BpmnJsonNode node, Process process) {
List<String> incoming = node.getIncoming().get("incoming");
List<String> incoming = node.getIncoming();
SequenceFlow sequenceFlow = new SequenceFlow();
sequenceFlow.setId(id(SEQUENCE_FLOW_ID));
sequenceFlow.setSourceRef(incoming.get(0));

File diff suppressed because one or more lines are too long