diff --git a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNode.java b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNode.java index 40eb9d0b4..44981ec50 100644 --- a/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNode.java +++ b/workflow-engine-common/src/main/java/cn/axzo/workflow/common/model/request/bpmn/BpmnJsonNode.java @@ -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 incoming = new ArrayList<>(); /* 内部使用, 用于 JSON 格式转换 */ private transient BpmnJsonNode preJsonNode; @@ -124,11 +123,11 @@ public class BpmnJsonNode { this.property = property; } - public Map> getIncoming() { + public List getIncoming() { return incoming; } - public void setIncoming(Map incoming) { + public void setIncoming(List incoming) { this.incoming = incoming; } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmTransformUtil.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmTransformUtil.java index 734bae371..d06468915 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmTransformUtil.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmTransformUtil.java @@ -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 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 sequenceFlows, Map childNodeMap) { - Map incomingJson = flowNode.getIncoming(); - List incoming = (List) incomingJson.get("incoming"); + List incoming = flowNode.getIncoming(); // 自动生成id // String id = id("serviceTask"); String id = flowNode.getId(); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java index 8572d0439..fcd2bbbf9 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/common/utils/BpmnJsonConverterUtil.java @@ -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, AbstractBpmnJsonConverter> converters = + private static final Map, AbstractBpmnJsonConverter> CONVERTERS = new HashMap<>(); + private static final Map 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> incoming = bpmnJsonNode.getIncoming(); - List preNodeIdList = Lists.newArrayList(preNodeIds); - incoming.put("incoming", preNodeIdList); - bpmnJsonNode.setIncoming(incoming); + bpmnJsonNode.setIncoming(Lists.newArrayList(preNodeIds)); Class 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> 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 gatewayPreIds = new ArrayList<>(); + List 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 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)); diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ExclusiveGatewayJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ExclusiveGatewayJsonConverter.java index c39858ca2..ad74ece80 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ExclusiveGatewayJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/ExclusiveGatewayJsonConverter.java @@ -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 { - // BpmnJsonNodeProperty property = branch.getProperty(); - // if (Boolean.TRUE.equals(property.getDefaultCondition())) { - // exclusiveGateway.setDefaultFlow(branch.getId()); - // } - // }); - // } return exclusiveGateway; } + + } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/NotSupportConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/NotSupportConverter.java index 82b45fd07..8d5c9a5aa 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/NotSupportConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/NotSupportConverter.java @@ -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 { @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; + } + } } diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/SequenceFlowJsonConverter.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/SequenceFlowJsonConverter.java index 1d2cb1436..f3bf1b77d 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/SequenceFlowJsonConverter.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/converter/json/SequenceFlowJsonConverter.java @@ -19,7 +19,7 @@ public class SequenceFlowJsonConverter extends AbstractBpmnJsonConverter incoming = node.getIncoming().get("incoming"); + List incoming = node.getIncoming(); SequenceFlow sequenceFlow = new SequenceFlow(); sequenceFlow.setId(id(SEQUENCE_FLOW_ID)); sequenceFlow.setSourceRef(incoming.get(0)); diff --git a/workflow-engine-server/src/main/resources/test.bpmn20.xml b/workflow-engine-server/src/main/resources/test.bpmn20.xml index 4b05a0a87..de5c7e99f 100644 --- a/workflow-engine-server/src/main/resources/test.bpmn20.xml +++ b/workflow-engine-server/src/main/resources/test.bpmn20.xml @@ -4,85 +4,85 @@ xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" - targetNamespace="customCategory"> - - 极引非东活已运王点越组油门展总想立。年深江亲联热制者条济它部即月。号线信平和者京两马何标这。 - + targetNamespace="http://www.flowable.org/test"> + + remark - - - + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - + + + - - + + - + - + + + - - + @@ -96,54 +96,61 @@ - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + + + + ${nrOfInstances == nrOfCompletedInstances} + - + - - + default="SequenceFlowId_78565be4afe34c83958b99f0a04c6e63"> + + @@ -157,55 +164,74 @@ - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - - + + + + + + + + + + + + + ${nrOfInstances == nrOfCompletedInstances} + - - - - - - + + + + + + + + + + + + + + + + + + @@ -218,11 +244,17 @@ + + + + + ${nrOfInstances == nrOfCompletedInstances} + - - + + @@ -235,130 +267,138 @@ + + + + + ${nrOfInstances == nrOfCompletedInstances} + - - - - - - + + + + + + + + + + + - - - + + - + - + - + - + - + - + - - - - - - - - - - + - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - + + + - - - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +