From 69e3afc7707d467b758858b52d3784947f7a502b Mon Sep 17 00:00:00 2001 From: 疯狂的狮子Li <15040126243@163.com> Date: 星期一, 20 五月 2024 10:25:23 +0800 Subject: [PATCH] !538 ♥️发布 5.2.0-BETA 公测版本 Merge pull request !538 from 疯狂的狮子Li/dev --- ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java | 289 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 289 insertions(+), 0 deletions(-) diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java new file mode 100644 index 0000000..7c5377e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java @@ -0,0 +1,289 @@ +package org.dromara.workflow.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.workflow.domain.vo.MultiInstanceVo; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.editor.language.json.converter.BpmnJsonConverter; +import org.flowable.engine.ProcessEngine; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.rmi.ServerException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 妯″瀷宸ュ叿 + * + * @author may + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ModelUtils { + + private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class); + + public static BpmnModel xmlToBpmnModel(String xml) throws IOException { + if (xml == null) { + throw new ServerException("xml涓嶈兘涓虹┖"); + } + try { + InputStream inputStream = new ByteArrayInputStream(StrUtil.utf8Bytes(xml)); + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLStreamReader reader = factory.createXMLStreamReader(inputStream); + return new BpmnXMLConverter().convertToBpmnModel(reader); + } catch (XMLStreamException e) { + throw new ServerException(e.getMessage()); + } + } + + /** + * bpmnModel杞负xml + * + * @param jsonBytes json + */ + public static byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException { + if (jsonBytes == null) { + return new byte[0]; + } + // 1. json瀛楄妭鐮佽浆鎴� BpmnModel 瀵硅薄 + ObjectMapper objectMapper = JsonUtils.getObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(jsonBytes); + BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode); + + if (bpmnModel.getProcesses().isEmpty()) { + return new byte[0]; + } + // 2.灏哹pmnModel杞负xml + return new BpmnXMLConverter().convertToXML(bpmnModel); + } + + /** + * xml杞负bpmnModel + * + * @param xmlBytes xml + */ + public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException { + ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes); + XMLInputFactory xif = XMLInputFactory.newInstance(); + XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream); + return new BpmnXMLConverter().convertToBpmnModel(xtr); + } + + /** + * 鏍¢獙妯″瀷 + * + * @param bpmnModel bpmn妯″瀷 + */ + public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException { + Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements(); + + checkBpmnNode(flowElements, false); + + List<SubProcess> subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList()); + if (!CollUtil.isEmpty(subProcessList)) { + for (SubProcess subProcess : subProcessList) { + Collection<FlowElement> subProcessFlowElements = subProcess.getFlowElements(); + checkBpmnNode(subProcessFlowElements, true); + } + } + List<MultiInstanceVo> multiInstanceVoList = new ArrayList<>(); + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask && ObjectUtil.isNotEmpty(((UserTask) flowElement).getLoopCharacteristics()) && StringUtils.isNotBlank(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem())) { + MultiInstanceVo multiInstanceVo = new MultiInstanceVo(); + multiInstanceVo.setAssigneeList(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem()); + multiInstanceVoList.add(multiInstanceVo); + } + } + + if (CollectionUtil.isNotEmpty(multiInstanceVoList) && multiInstanceVoList.size() > 1) { + Map<String, List<MultiInstanceVo>> assigneeListGroup = StreamUtils.groupByKey(multiInstanceVoList, MultiInstanceVo::getAssigneeList); + for (Map.Entry<String, List<MultiInstanceVo>> entry : assigneeListGroup.entrySet()) { + List<MultiInstanceVo> value = entry.getValue(); + if (CollectionUtil.isNotEmpty(value) && value.size() > 1) { + String key = entry.getKey(); + throw new ServerException("浼氱浜哄憳闆嗗悎銆�" + key + "銆戦噸澶�,璇烽噸鏂拌缃泦鍚圞EY"); + } + } + } + } + + /** + * 鏍¢獙bpmn鑺傜偣鏄惁鍚堟硶 + * + * @param flowElements 鑺傜偣闆嗗悎 + * @param subtask 鏄惁瀛愭祦绋� + */ + private static void checkBpmnNode(Collection<FlowElement> flowElements, boolean subtask) throws ServerException { + + if (CollUtil.isEmpty(flowElements)) { + throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄨ妭鐐�" : "蹇呴』瀛樺湪鑺傜偣锛�"); + } + + List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList()); + if (CollUtil.isEmpty(startEventList)) { + throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄥ紑濮嬭妭鐐�" : "蹇呴』瀛樺湪寮�濮嬭妭鐐癸紒"); + } + + if (startEventList.size() > 1) { + throw new ServerException(subtask ? "瀛愭祦绋嬪彧鑳藉瓨鍦ㄤ竴涓紑濮嬭妭鐐�" : "鍙兘瀛樺湪涓�涓紑濮嬭妭鐐癸紒"); + } + + StartEvent startEvent = startEventList.get(0); + List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows(); + if (CollUtil.isEmpty(outgoingFlows)) { + throw new ServerException(subtask ? "瀛愭祦绋嬫祦绋嬭妭鐐逛负绌猴紝璇疯嚦灏戣璁′竴鏉′富绾挎祦绋嬶紒" : "娴佺▼鑺傜偣涓虹┖锛岃鑷冲皯璁捐涓�鏉′富绾挎祦绋嬶紒"); + } + + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + if (!(targetFlowElement instanceof UserTask) && !subtask) { + throw new ServerException("寮�濮嬭妭鐐瑰悗绗竴涓妭鐐瑰繀椤绘槸鐢ㄦ埛浠诲姟锛�"); + } + //寮�濮嬭妭鐐瑰悗绗竴涓妭鐐圭敵璇蜂汉鑺傜偣 + if ((targetFlowElement instanceof UserTask) && !subtask) { + UserTask userTask = (UserTask) targetFlowElement; + if (StringUtils.isBlank(userTask.getFormKey())) { + throw new ServerException("鐢宠浜鸿妭鐐瑰繀椤婚�夋嫨琛ㄥ崟锛�"); + } + } + List<EndEvent> endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList()); + if (CollUtil.isEmpty(endEventList)) { + throw new ServerException(subtask ? "瀛愭祦绋嬪繀椤诲瓨鍦ㄧ粨鏉熻妭鐐癸紒" : "蹇呴』瀛樺湪缁撴潫鑺傜偣锛�"); + } + } + + /** + * 鑾峰彇娴佺▼鍏ㄩ儴鐢ㄦ埛鑺傜偣 + * + * @param processDefinitionId 娴佺▼瀹氫箟id + */ + public static List<UserTask> getUserTaskFlowElements(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + List<UserTask> list = new ArrayList<>(); + List<Process> processes = bpmnModel.getProcesses(); + Collection<FlowElement> flowElements = processes.get(0).getFlowElements(); + buildUserTaskFlowElements(flowElements, list); + return list; + } + + /** + * 閫掑綊鑾峰彇鎵�鏈夎妭鐐� + * + * @param flowElements 鑺傜偣淇℃伅 + * @param list 闆嗗悎 + */ + private static void buildUserTaskFlowElements(Collection<FlowElement> flowElements, List<UserTask> list) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof SubProcess) { + Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements(); + buildUserTaskFlowElements(subFlowElements, list); + } else if (flowElement instanceof UserTask) { + list.add((UserTask) flowElement); + } + } + } + + /** + * 鑾峰彇娴佺▼鍏ㄩ儴鑺傜偣 + * + * @param processDefinitionId 娴佺▼瀹氫箟id + */ + public static List<FlowElement> getFlowElements(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + List<FlowElement> list = new ArrayList<>(); + List<Process> processes = bpmnModel.getProcesses(); + Collection<FlowElement> flowElements = processes.get(0).getFlowElements(); + buildFlowElements(flowElements, list); + return list; + } + + /** + * 閫掑綊鑾峰彇鎵�鏈夎妭鐐� + * + * @param flowElements 鑺傜偣淇℃伅 + * @param list 闆嗗悎 + */ + private static void buildFlowElements(Collection<FlowElement> flowElements, List<FlowElement> list) { + for (FlowElement flowElement : flowElements) { + list.add(flowElement); + if (flowElement instanceof SubProcess) { + Collection<FlowElement> subFlowElements = ((SubProcess) flowElement).getFlowElements(); + buildFlowElements(subFlowElements, list); + } + } + } + + /** + * 鑾峰彇鍏ㄩ儴鎵╁睍淇℃伅 + * + * @param processDefinitionId 娴佺▼瀹氫箟id + */ + public static Map<String, List<ExtensionElement>> getExtensionElements(String processDefinitionId) { + Map<String, List<ExtensionElement>> map = new HashMap<>(); + List<FlowElement> flowElements = getFlowElements(processDefinitionId); + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask && CollUtil.isNotEmpty(flowElement.getExtensionElements())) { + map.putAll(flowElement.getExtensionElements()); + } + } + return map; + } + + /** + * 鑾峰彇鏌愪釜鑺傜偣鐨勬墿灞曚俊鎭� + * + * @param processDefinitionId 娴佺▼瀹氫箟id + * @param flowElementId 鑺傜偣id + */ + public static Map<String, List<ExtensionElement>> getExtensionElement(String processDefinitionId, String flowElementId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + Process process = bpmnModel.getMainProcess(); + FlowElement flowElement = process.getFlowElement(flowElementId); + return flowElement.getExtensionElements(); + } + + /** + * 鍒ゆ柇褰撳墠鑺傜偣鏄惁涓虹敤鎴蜂换鍔� + * + * @param processDefinitionId 娴佺▼瀹氫箟id + * @param taskDefinitionKey 娴佺▼瀹氫箟id + */ + public static boolean isUserTask(String processDefinitionId, String taskDefinitionKey) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + return flowNode instanceof UserTask; + } + + /** + * 鑾峰彇鐢宠浜鸿妭鐐� + * + * @param processDefinitionId 娴佺▼瀹氫箟id + * @return 缁撴灉 + */ + public static UserTask getApplyUserTask(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements(); + List<StartEvent> startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList()); + StartEvent startEvent = startEventList.get(0); + List<SequenceFlow> outgoingFlows = startEvent.getOutgoingFlows(); + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + return (UserTask) targetFlowElement; + } +} -- Gitblit v1.9.3