package com.wflow.workflow; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.wflow.workflow.bean.process.ProcessNode; import com.wflow.workflow.bean.process.enums.ApprovalTypeEnum; import com.wflow.workflow.bean.process.enums.ConditionModeEnum; import com.wflow.workflow.bean.process.enums.NodeTypeEnum; import com.wflow.workflow.bean.process.props.*; import com.wflow.workflow.config.callActivity.WflowCallActivityBehavior; import com.wflow.workflow.config.WflowGlobalVarDef; import com.wflow.workflow.task.ApprovalTimeoutServiceTask; import com.wflow.workflow.task.CcServiceTask; import com.wflow.workflow.task.SubProcessInitTask; import com.wflow.workflow.task.TriggerServiceTask; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.BpmnAutoLayout; import org.flowable.bpmn.model.*; import org.flowable.bpmn.model.Process; import java.lang.reflect.Type; import java.util.*; import java.util.stream.Collectors; /** * json -> bpmn核心转换器 * @author : willian fu * @date : 2022/8/19 */ @Slf4j public class WFlowToBpmnCreator { //流程元素与ID映射 private final Map elementMap = new LinkedHashMap<>(); //节点Map映射,提高取效率 @Getter private final Map> nodeMap = new LinkedHashMap<>(); //分支栈 private final Stack> currentBranchStack = new Stack<>(); //支路->该支路末端节点 private final Map> footerNode = new LinkedHashMap<>(); //正常完成结束事件 private final EndEvent endNode = new EndEvent(); //被驳回的结束事件 private final EndEvent refuseEndNode = new EndEvent(); //取消流程的结束事件 private final EndEvent cancelEndNode = new EndEvent(); private boolean isSub; private final static List taskListeners = new ArrayList<>(); static { System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); FlowableListener taskListener = new FlowableListener(); taskListener.setEvent("all"); taskListener.setImplementationType("delegateExpression"); taskListener.setImplementation("${userTaskListener}"); taskListeners.add(taskListener); } /** * wflow -> bpmnModel 转换 * @param id 表单流程模型id * @param name 流程名 * @param root 根节点 * @param isSub 是否是子流程 * @return 返回转换后的bpmnModel数据 */ public BpmnModel loadBpmnFlowXmlByProcess(String id, String name, ProcessNode root, boolean isSub){ this.isSub = isSub; StartEvent startEvent = new StartEvent(); startEvent.setId("start"); startEvent.setName("开始"); loadProcess(root); loadProcessEndNode(); //构建流程 Process process = new Process(); process.setId(id); process.setName(name); process.addFlowElement(startEvent); //将组件添加到流程节点 elementMap.values().forEach(process::addFlowElement); //将结束事件连接到主干流程最后一个节点 Optional lastNode = nodeMap.values().stream() .skip(nodeMap.values().size() - 1) .map(ProcessNode::getId).findFirst(); lastNode.ifPresent(node -> { SequenceFlow line = createdConnectLine(node, endNode.getId()); FlowElement element = elementMap.get(node); if (element instanceof Gateway){ ((Gateway) element).setOutgoingFlows(CollectionUtil.newArrayList(line)); } process.addFlowElement(line); }); process.addFlowElement(this.endNode); process.addFlowElement(cancelEndNode); process.addFlowElement(refuseEndNode); //构建Bpmn模型 BpmnModel bpmnModel = new BpmnModel(); bpmnModel.addProcess(process); // Bpmn xml自动生成布局及布局节点位置 // new BpmnAutoLayout(bpmnModel).execute(); //log.info("构建审批流程[{}] 的 xml为:{}", name, xmlStr); return bpmnModel; } /** * 加载结束节点 */ private void loadProcessEndNode(){ endNode.setId("process-end"); endNode.setName("审批流程结束"); cancelEndNode.setId("cancel-end"); cancelEndNode.setName("审批流程撤消"); refuseEndNode.setId("refuse-end"); TerminateEventDefinition eventDefinition = new TerminateEventDefinition(); eventDefinition.setTerminateAll(true); cancelEndNode.setEventDefinitions(CollectionUtil.newArrayList(eventDefinition)); //强制终止流程 TerminateEventDefinition eventDefinition2 = new TerminateEventDefinition(); eventDefinition2.setTerminateAll(true); refuseEndNode.setEventDefinitions(CollectionUtil.newArrayList(eventDefinition2)); refuseEndNode.setName("审批流程被驳回"); } /** * 节点props属性强制转换 * @param node 节点 */ public synchronized static void coverProps(ProcessNode node){ if (node.getType().getTypeClass() != Object.class){ JSONObject props = (JSONObject) node.getProps(); if (props.get("listeners") instanceof JSONArray){ //兼容旧版本,旧版本是数组,新版本是对象 props.put("listeners", "{}"); } node.setProps(props.toJavaObject((Type) node.getType().getTypeClass())); } } /** * 判断并加载分支末端所有的节点 * @param node 当前节点 */ private void loadBranchEndNodes(ProcessNode node){ if (!hasChildren(node) && currentBranchStack.size() > 0){ //没有后续节点,代表该分支部分结束,塞入末端缓存 Optional.ofNullable(currentBranchStack.peek()).ifPresent(bn -> { List endNodes = footerNode.get(bn.getId()); if (CollectionUtil.isEmpty(endNodes)){ endNodes = new ArrayList<>(); footerNode.put(bn.getId(), endNodes); } if (NodeTypeEnum.EMPTY.equals(node.getType())){ currentBranchStack.pop(); if (currentBranchStack.size() > 0){ Optional.ofNullable(currentBranchStack.peek()).ifPresent(beforeBranch -> { List bfed = footerNode.get(beforeBranch.getId()); if (CollectionUtil.isEmpty(bfed)){ bfed = new ArrayList<>(); footerNode.put(beforeBranch.getId(), bfed); } bfed.add(node.getId()); }); } currentBranchStack.push(bn); }else { endNodes.add(node.getId()); //末端如果是审批节点,默认添加一条驳回的链接线 } }); } } /** * 校验是否存在后续子节点 * @param node 当前节点 * @return 1/0 */ private boolean hasChildren(ProcessNode node) { return ObjectUtil.isNotNull(node.getChildren()) && StrUtil.isNotBlank(node.getChildren().getId()); } /** * 递归加载所有流程树 * @param node 起始节点 */ public void loadProcess(ProcessNode node){ if (Objects.isNull(node) || Objects.isNull(node.getId())){ return; } loadBranchEndNodes(node); coverProps(node); nodeMap.put(node.getId(), node); FlowElement element = null; switch (node.getType()){ case ROOT: if (isSub){ //子流程的发起人节点换成初始化任务节点 element = createSubProcInitTask(node.getId()); }else { element = createInitiatorNode(node.getId(), node.getName()); } break; case TASK: //办理和审批都是同一类型操作 case APPROVAL: element = createApprovalNode((ProcessNode) node); break; case CC: element = createCcTask((ProcessNode) node); break; case DELAY: element = createDelayNode((ProcessNode) node); break; case SUBPROC: element = createSubProcessNode((ProcessNode) node); break; case INCLUSIVES: currentBranchStack.push(node); element = createInclusivesNode(node); node.getBranchs().forEach(this::loadProcess); break; case CONDITIONS: currentBranchStack.push(node); element = createConditionsNode(node); node.getBranchs().forEach(this::loadProcess); break; case CONCURRENTS: currentBranchStack.push(node); element = createConcurrentNode(node); node.getBranchs().forEach(this::loadProcess); break; case TRIGGER: element = createTriggerTask((ProcessNode) node); break; case EMPTY: if (NodeTypeEnum.CONCURRENTS.equals(node.getParentType())){ //并行网关成对存在,因此再添加一个聚合节点 element = createConcurrentNode(node); } else if (NodeTypeEnum.CONDITIONS.equals(node.getParentType())) { //构造条件网关合流点 element = createConditionsNode(node); } else if (NodeTypeEnum.INCLUSIVES.equals(node.getParentType())) { element = createInclusivesNode(node); } break; } if (Objects.nonNull(element)){ addFlowEl(element); } addAndCreateConnLine(node); loadProcess(node.getChildren()); } //构造子流程 private CallActivity createSubProcessNode(ProcessNode node) { CallActivity callActivity = new CallActivity(); callActivity.setId(node.getId()); callActivity.setName(node.getName()); callActivity.setCalledElement(node.getProps().getSubProcCode()); callActivity.setSameDeployment(Boolean.TRUE.equals(node.getProps().getSyncVersion())); callActivity.setBehavior(WflowCallActivityBehavior.class); //将主流程内子流程节点id绑定到子流程的业务key callActivity.setBusinessKey(node.getId()); if (node.getProps().getSubAll()){ callActivity.setInheritVariables(true); }else { //挨个设置输入变量 List ioParams = getIoParams(node.getProps().getInVar(), false); callActivity.setInParameters(ioParams); } //挨个设置输出变量 callActivity.setOutParameters(getIoParams(node.getProps().getOutVar(), true)); return callActivity; } //发起人节点 private UserTask createInitiatorNode(String id, String name){ UserTask userTask = new UserTask(); userTask.setId(id); userTask.setName(name); userTask.setAssignee("${" + WflowGlobalVarDef.INITIATOR + "}"); return userTask; } //触发器任务 private FlowElement createTriggerTask(ProcessNode node) { ServiceTask ccTask = new ServiceTask(); ccTask.setId(node.getId()); ccTask.setName(node.getName()); ccTask.setImplementationType("class"); ccTask.setImplementation(TriggerServiceTask.class.getName()); return ccTask; } //将当前节点向上连接,向上查找应该连接到本节点的所有父节点 private void addAndCreateConnLine(ProcessNode node){ if (NodeTypeEnum.EMPTY.equals(node.getType())){ //空节点代表一个分支结束,缓存出栈 ProcessNode branch = currentBranchStack.pop(); footerNode.get(branch.getId()).forEach(en -> { SequenceFlow connectLine = createdConnectLine(en, node.getId()); ProcessNode endNode = nodeMap.get(en); if (NodeTypeEnum.CONDITION.equals(endNode.getType()) || NodeTypeEnum.INCLUSIVE.equals(endNode.getType())){ //分支只有一个条件节点,那么就直连排他网关 connectLine.setId(endNode.getId()); connectLine.setSourceRef(endNode.getParentId()); }else if (NodeTypeEnum.EMPTY.equals(endNode.getType())){ //末端是网关,要为其构建出口 Gateway gateway = (Gateway) elementMap.get(en); gateway.setOutgoingFlows(CollectionUtil.newArrayList(connectLine)); } addFlowEl(connectLine); }); if (NodeTypeEnum.CONDITIONS.equals(node.getParentType()) || NodeTypeEnum.INCLUSIVES.equals(node.getParentType())){ //为条件网关构造出口及默认流 Gateway cdgw = (Gateway) elementMap.get(node.getParentId()); List outgoing = branch.getBranchs().stream() .map(v -> { ConditionProps props = (ConditionProps) v.getProps(); if (ConditionModeEnum.SIMPLE.equals(props.getMode()) && CollectionUtil.isEmpty(props.getGroups()) && StrUtil.isBlank(cdgw.getDefaultFlow())) { cdgw.setDefaultFlow(v.getId()); //设置默认条件 } return (SequenceFlow) elementMap.get(v.getId()); }) .collect(Collectors.toList()); cdgw.setOutgoingFlows(outgoing); } }else if (Objects.nonNull(node.getParentId())){ //非空节点,特殊处理,判断父级是啥子 ProcessNode parentNode = nodeMap.get(node.getParentId()); if (Objects.isNull(parentNode) || NodeTypeEnum.CONDITION.equals(node.getType()) || NodeTypeEnum.CONCURRENT.equals(node.getType()) || NodeTypeEnum.INCLUSIVE.equals(node.getType())){ return; } SequenceFlow line = null; switch (parentNode.getType()){ case CONCURRENT: case CONDITION: case INCLUSIVE: //并行分支、条件分支、包容分支,构造条件并连接到条件网关 line = createdConnectLine(parentNode.getParentId(), node.getId()); line.setId(parentNode.getId()); line.setName(parentNode.getName()); if (parentNode.getProps() instanceof ConditionProps){ ConditionProps props = (ConditionProps) parentNode.getProps(); if (CollectionUtil.isNotEmpty(props.getGroups())){ String conditionExplainCreator = conditionExplainCreator(line.getId(), (ConditionProps) parentNode.getProps()); if (StrUtil.isNotBlank(conditionExplainCreator)){ line.setConditionExpression(conditionExplainCreator); } } } break; case APPROVAL: case TASK: //父级节点是审批/办理节点 line = createdConnectLine(parentNode.getId(), node.getId()); line.setName(NodeTypeEnum.APPROVAL.equals(parentNode.getType()) ? "同意": "提交"); break; case EMPTY: line = createdConnectLine(parentNode.getId(), node.getId()); //为合流点构造出口流 Gateway gateway2 = (Gateway) elementMap.get(node.getParentId()); gateway2.setOutgoingFlows(CollectionUtil.newArrayList(line)); break; default: line = createdConnectLine(parentNode.getId(), node.getId()); break; } addFlowEl(line); }else if (NodeTypeEnum.ROOT.equals(node.getType())){ //发起人节点链接到开始节点 addFlowEl(createdConnectLine("start", node.getId())); } } private void addFlowEl(FlowElement element){ elementMap.put(element.getId(), element); } //审批-用户任务 private UserTask createApprovalNode(ProcessNode node) { UserTask userTask = new UserTask(); userTask.setName(node.getName()); ApprovalProps props = node.getProps(); //全部按多人审批处理 userTask.setTaskListeners(taskListeners); if(ApprovalTypeEnum.SELF.equals(props.getAssignedType())){ //发起人自己审批 userTask.setAssignee("${" + WflowGlobalVarDef.INITIATOR + "}"); }else { userTask.setAssignee("${assignee}"); userTask.setLoopCharacteristics(createAndOrMode(node.getId(), props)); } userTask.setId(node.getId()); //处理审批超时,添加定时器边界事件 ApprovalProps.TimeLimit timeLimit = props.getTimeLimit(); if (Objects.nonNull(timeLimit.getTimeout().getValue()) && timeLimit.getTimeout().getValue() > 0){ BoundaryEvent boundaryEvent = new BoundaryEvent(); boundaryEvent.setId(node.getId() + "-timeout"); boundaryEvent.setName("审批超时"); TimerEventDefinition timerEventDefinition = new TimerEventDefinition(); String timeValue = getISO8601Time(timeLimit.getTimeout().getValue(), timeLimit.getTimeout().getUnit()); timerEventDefinition.setTimeDuration(timeValue); if (ApprovalProps.TimeLimit.Handler.HandlerType.NOTIFY.equals(timeLimit.getHandler().getType())){ //根据是否循环提醒来修改定时规则 ApprovalProps.TimeLimit.Handler.Notify notify = timeLimit.getHandler().getNotify(); if (!notify.isOnce()){ timerEventDefinition.setTimeDuration(null); //默认最大10次,这里参考 https://en.wikipedia.org/wiki/ISO_8601#Repeating_intervals timerEventDefinition.setTimeCycle("R10/" + timeValue); } } boundaryEvent.addEventDefinition(timerEventDefinition); boundaryEvent.setCancelActivity(false); boundaryEvent.setAttachedToRef(userTask); //创建边界事件的出口 ServiceTask timeoutTask = new ServiceTask(); timeoutTask.setId(node.getId() + "-timeoutTask"); timeoutTask.setName(node.getName() + "超时处理"); timeoutTask.setImplementationType("class"); timeoutTask.setImplementation(ApprovalTimeoutServiceTask.class.getName()); addFlowEl(boundaryEvent); addFlowEl(timeoutTask); addFlowEl(createdConnectLine(boundaryEvent.getId(), timeoutTask.getId())); } return userTask; } //并行块节点-并行网关 private ParallelGateway createConcurrentNode(ProcessNode node) { ParallelGateway parallelGateway = new ParallelGateway(); parallelGateway.setId(node.getId()); parallelGateway.setName(NodeTypeEnum.EMPTY.equals(node.getType()) ? "并行分支聚合":"并行分支"); return parallelGateway; } //抄送任务 private ServiceTask createCcTask(ProcessNode node) { ServiceTask ccTask = new ServiceTask(); ccTask.setId(node.getId()); ccTask.setName(node.getName()); ccTask.setImplementationType("class"); ccTask.setImplementation(CcServiceTask.class.getName()); //ccTask.addAttribute(); return ccTask; } //子流程初始化任务 private ServiceTask createSubProcInitTask(String id) { ServiceTask initTask = new ServiceTask(); initTask.setId(id); initTask.setName("子流程初始化"); initTask.setImplementationType("class"); initTask.setImplementation(SubProcessInitTask.class.getName()); return initTask; } //构建延时节点 private IntermediateCatchEvent createDelayNode(ProcessNode node) { IntermediateCatchEvent catchEvent = new IntermediateCatchEvent(); TimerEventDefinition timerDefinition = new TimerEventDefinition(); DelayProps props = node.getProps(); if (props.getType().equals(DelayProps.Type.FIXED)) { timerDefinition.setTimeDuration(getISO8601Time(props.getTime(), props.getUnit())); } else { //动态计算时长 timerDefinition.setTimeDate("${uelTools.getDelayDuration(execution)}"); } catchEvent.setId(node.getId()); catchEvent.setName(node.getName()); //插入定时器捕获中间事件 catchEvent.addEventDefinition(timerDefinition); return catchEvent; } //条件块节点-排他网关 private ExclusiveGateway createConditionsNode(ProcessNode node) { ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); exclusiveGateway.setExclusive(true); exclusiveGateway.setId(node.getId()); exclusiveGateway.setName(NodeTypeEnum.EMPTY.equals(node.getType()) ? "条件分支聚合":"条件分支"); return exclusiveGateway; } //包容网关 private InclusiveGateway createInclusivesNode(ProcessNode node) { InclusiveGateway inclusiveGateway = new InclusiveGateway(); inclusiveGateway.setExclusive(true); inclusiveGateway.setId(node.getId()); inclusiveGateway.setName(NodeTypeEnum.EMPTY.equals(node.getType()) ? "包容分支聚合":"包容分支"); return inclusiveGateway; } private SequenceFlow createdConnectLine(String source, String target) { SequenceFlow flow = new SequenceFlow(); flow.setId(source + "_" + target); flow.setSourceRef(source); flow.setTargetRef(target); return flow; } //构建条件表达式 private String conditionExplainCreator(String nodeId, ConditionProps conditions) { if (CollectionUtil.isNotEmpty(conditions.getGroups())){ return "${uelTools.conditionCompare('"+ nodeId + "', execution)}"; } return null; } //多人签署设置-会签/或签 private MultiInstanceLoopCharacteristics createAndOrMode(String nodeId, ApprovalProps props) { MultiInstanceLoopCharacteristics loopCharacteristics = new MultiInstanceLoopCharacteristics(); loopCharacteristics.setId(IdUtil.randomUUID()); loopCharacteristics.setElementVariable("assignee"); loopCharacteristics.setInputDataItem("${processTaskService.getNodeApprovalUsers(execution)}"); //设置完成条件,先判断会签还是或签 String completionCondition = ""; switch (props.getMode()) { case OR: //有任意一个人处理过就结束 completionCondition = "nrOfCompletedInstances >= 1"; loopCharacteristics.setSequential(false); break; case AND: //所有任务都结束 completionCondition = "nrOfActiveInstances == 0"; loopCharacteristics.setSequential(false); break; case NEXT: completionCondition = "nrOfActiveInstances == 0"; loopCharacteristics.setSequential(true); break; } loopCharacteristics.setCompletionCondition("${" + completionCondition + "}"); return loopCharacteristics; } /** * 获取ISO8601时间 * @param time 值 * @param unit 单位 * @return 格式化时间 */ private String getISO8601Time(Integer time, String unit){ switch (unit){ case "D": return "P" + time + unit; case "H": return "PT" + time + unit; case "M": return "PT" + time + unit; } return null; } /** * 构造主子流程变量传递参数 * @param vars 参数 * @param reverse 是否为逆向传递 * @return 参数列表 */ private List getIoParams(List vars, Boolean reverse){ return vars.stream().filter(v -> StrUtil.isNotBlank(reverse ? v.getSKey() : v.getMKey())) .map(v -> { IOParameter ip = new IOParameter(); if (reverse){ ip.setSource(v.getSKey()); ip.setTarget(StrUtil.isNotBlank(v.getMKey()) ? v.getMKey() : v.getSKey()); }else { ip.setSource(v.getMKey()); ip.setTarget(StrUtil.isNotBlank(v.getSKey()) ? v.getSKey() : v.getMKey()); } return ip; }) .collect(Collectors.toList()); } }