package com.iailab.module.bpm.framework.flowable.core.util; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import com.iailab.framework.common.util.number.NumberUtils; import com.iailab.module.bpm.framework.flowable.core.enums.BpmnModelConstants; import org.flowable.bpmn.converter.BpmnXMLConverter; import org.flowable.bpmn.model.Process; import org.flowable.bpmn.model.*; 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; /** * 流程模型转操作工具类 */ public class BpmnModelUtils { public static Integer parseCandidateStrategy(FlowElement userTask) { return NumberUtils.parseInt(userTask.getAttributeValue( BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); } public static String parseCandidateParam(FlowElement userTask) { return userTask.getAttributeValue( BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); } /** * 根据节点,获取入口连线 * * @param source 起始节点 * @return 入口连线列表 */ public static List getElementIncomingFlows(FlowElement source) { if (source instanceof FlowNode) { return ((FlowNode) source).getIncomingFlows(); } return new ArrayList<>(); } /** * 根据节点,获取出口连线 * * @param source 起始节点 * @return 出口连线列表 */ public static List getElementOutgoingFlows(FlowElement source) { if (source instanceof FlowNode) { return ((FlowNode) source).getOutgoingFlows(); } return new ArrayList<>(); } /** * 获取流程元素信息 * * @param model bpmnModel 对象 * @param flowElementId 元素 ID * @return 元素信息 */ public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { Process process = model.getMainProcess(); return process.getFlowElement(flowElementId); } /** * 获得 BPMN 流程中,指定的元素们 * * @param model 模型 * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 * @return 元素们 */ public static List getBpmnModelElements(BpmnModel model, Class clazz) { List result = new ArrayList<>(); model.getProcesses().forEach(process -> { process.getFlowElements().forEach(flowElement -> { if (flowElement.getClass().isAssignableFrom(clazz)) { result.add((T) flowElement); } }); }); return result; } public static StartEvent getStartEvent(BpmnModel model) { Process process = model.getMainProcess(); // 从 initialFlowElement 找 FlowElement startElement = process.getInitialFlowElement(); if (startElement instanceof StartEvent) { return (StartEvent) startElement; } // 从 flowElementList 找 return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); } public static BpmnModel getBpmnModel(byte[] bpmnBytes) { if (ArrayUtil.isEmpty(bpmnBytes)) { return null; } BpmnXMLConverter converter = new BpmnXMLConverter(); // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); } public static String getBpmnXml(BpmnModel model) { if (model == null) { return null; } BpmnXMLConverter converter = new BpmnXMLConverter(); return new String(converter.convertToXML(model)); } // ========== 遍历相关的方法 ========== /** * 找到 source 节点之前的所有用户任务节点 * * @param source 起始节点 * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 * @param userTaskList 已找到的用户任务节点 * @return 用户任务节点 数组 */ public static List getPreviousUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 if (source instanceof StartEvent && source.getSubProcess() != null) { userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); } // 根据类型,获取入口连线 List sequenceFlows = getElementIncomingFlows(source); if (sequenceFlows == null) { return userTaskList; } // 循环找到目标元素 for (SequenceFlow sequenceFlow : sequenceFlows) { // 如果发现连线重复,说明循环了,跳过这个循环 if (hasSequenceFlow.contains(sequenceFlow.getId())) { continue; } // 添加已经走过的连线 hasSequenceFlow.add(sequenceFlow.getId()); // 类型为用户节点,则新增父级节点 if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); } // 类型为子流程,则添加子流程开始节点出口处相连的节点 if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { // 获取子流程用户任务节点 List childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 if (CollUtil.isNotEmpty(childUserTaskList)) { userTaskList.addAll(childUserTaskList); } } // 继续迭代 userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); } return userTaskList; } /** * 迭代获取子流程用户任务节点 * * @param source 起始节点 * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 * @param userTaskList 需要撤回的用户任务列表 * @return 用户任务节点 */ public static List findChildProcessUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; // 根据类型,获取出口连线 List sequenceFlows = getElementOutgoingFlows(source); if (sequenceFlows == null) { return userTaskList; } // 循环找到目标元素 for (SequenceFlow sequenceFlow : sequenceFlows) { // 如果发现连线重复,说明循环了,跳过这个循环 if (hasSequenceFlow.contains(sequenceFlow.getId())) { continue; } // 添加已经走过的连线 hasSequenceFlow.add(sequenceFlow.getId()); // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); continue; } // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { List childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 if (CollUtil.isNotEmpty(childUserTaskList)) { userTaskList.addAll(childUserTaskList); continue; } } // 继续迭代 userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); } return userTaskList; } /** * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 * * @param source 起始节点 * @param target 目标节点 * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 * @return 结果 */ public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set visitedElements) { visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; // 不能是开始事件和子流程 if (source instanceof StartEvent && isInEventSubprocess(source)) { return false; } // 根据类型,获取入口连线 List sequenceFlows = getElementIncomingFlows(source); if (CollUtil.isEmpty(sequenceFlows)) { return true; } // 循环找到目标元素 for (SequenceFlow sequenceFlow : sequenceFlows) { // 如果发现连线重复,说明循环了,跳过这个循环 if (visitedElements.contains(sequenceFlow.getId())) { continue; } // 添加已经走过的连线 visitedElements.add(sequenceFlow.getId()); // 这条线路存在目标节点,这条线路完成,进入下个线路 FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); if (target.getId().equals(sourceFlowElement.getId())) { continue; } // 如果目标节点为并行网关,则不继续 if (sourceFlowElement instanceof ParallelGateway) { return false; } // 否则就继续迭代 if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { return false; } } return true; } /** * 判断当前节点是否属于不同的子流程 * * @param flowElement 被判断的节点 * @return true 表示属于子流程 */ private static boolean isInEventSubprocess(FlowElement flowElement) { FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); while (flowElementsContainer != null) { if (flowElementsContainer instanceof EventSubProcess) { return true; } if (flowElementsContainer instanceof FlowElement) { flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); } else { flowElementsContainer = null; } } return false; } /** * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 * * @param source 起始节点 * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 * @param userTaskList 需要撤回的用户任务列表 * @return 子级任务节点列表 */ public static List iteratorFindChildUserTasks(FlowElement source, List runTaskKeyList, Set hasSequenceFlow, List userTaskList) { hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 if (source instanceof StartEvent && source.getSubProcess() != null) { userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); } // 根据类型,获取出口连线 List sequenceFlows = getElementOutgoingFlows(source); if (sequenceFlows == null) { return userTaskList; } // 循环找到目标元素 for (SequenceFlow sequenceFlow : sequenceFlows) { // 如果发现连线重复,说明循环了,跳过这个循环 if (hasSequenceFlow.contains(sequenceFlow.getId())) { continue; } // 添加已经走过的连线 hasSequenceFlow.add(sequenceFlow.getId()); // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); continue; } // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { List childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 if (CollUtil.isNotEmpty(childUserTaskList)) { userTaskList.addAll(childUserTaskList); continue; } } // 继续迭代 userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); } return userTaskList; } }