001/* 002 * Copyright 2024-2025, Warm-Flow (290631660@qq.com). 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.dromara.warm.flow.core.utils; 017 018import org.dromara.warm.flow.core.FlowFactory; 019import org.dromara.warm.flow.core.constant.ExceptionCons; 020import org.dromara.warm.flow.core.dto.FlowCombine; 021import org.dromara.warm.flow.core.entity.Definition; 022import org.dromara.warm.flow.core.entity.Node; 023import org.dromara.warm.flow.core.entity.Skip; 024import org.dromara.warm.flow.core.enums.ActivityStatus; 025import org.dromara.warm.flow.core.enums.NodeType; 026import org.dromara.warm.flow.core.enums.SkipType; 027import org.dom4j.Document; 028import org.dom4j.DocumentHelper; 029import org.dom4j.Element; 030import org.dom4j.io.SAXReader; 031 032import java.io.IOException; 033import java.io.InputStream; 034import java.math.BigDecimal; 035import java.util.*; 036import java.util.concurrent.atomic.AtomicInteger; 037 038/** 039 * 流程配置帮助类 040 * 041 * @author zhoukai 042 */ 043public class FlowConfigUtil { 044 /** 045 * 无参构造 046 */ 047 private FlowConfigUtil() { 048 049 } 050 051 /** 052 * 读取配置 053 * 054 * @param is 055 * @throws Exception 056 */ 057 public static FlowCombine readConfig(InputStream is) throws Exception { 058 Definition definition = readDocument(is); 059 return structureFlow(definition); 060 } 061 062 @SuppressWarnings("unchecked") 063 public static Definition readDocument(InputStream is) throws Exception { 064 AssertUtil.isNull(is, "文件不存在!"); 065 // 获取流程节点 066 Element definitionElement = new SAXReader().read(is).getRootElement(); 067 AssertUtil.isNull(definitionElement, "流程为空!"); 068 069 // 读取流程定义 070 Definition definition = FlowFactory.newDef(); 071 definition.setFlowCode(definitionElement.attributeValue("flowCode")); 072 definition.setFlowName(definitionElement.attributeValue("flowName")); 073 definition.setVersion(definitionElement.attributeValue("version")); 074 definition.setCategory(definitionElement.attributeValue("category")); 075 definition.setFormCustom(definitionElement.attributeValue("formCustom")); 076 definition.setFormPath(definitionElement.attributeValue("formPath")); 077 definition.setExt(definitionElement.attributeValue("ext")); 078 definition.setActivityStatus(ActivityStatus.ACTIVITY.getKey()); 079 definition.setListenerType(definitionElement.attributeValue("listenerType")); 080 definition.setListenerPath(definitionElement.attributeValue("listenerPath")); 081 082 List<Element> nodesElement = definitionElement.elements(); 083 // 遍历一个流程中的各个节点 084 List<Node> nodeList = definition.getNodeList(); 085 for (Element nodeElement : nodesElement) { 086 Node node = initNodeAndCondition(nodeElement); 087 nodeList.add(node); 088 } 089 try { 090 if (is != null) { 091 is.close(); 092 } 093 } catch (IOException ex) { 094 ex.printStackTrace(); 095 } 096 return definition; 097 } 098 099 /** 100 * 读取工作节点和跳转条件 101 * 102 * @param nodeElement 103 * @return 104 */ 105 private static Node initNodeAndCondition(Element nodeElement) { 106 Node node = FlowFactory.newNode(); 107 node.setNodeType(NodeType.getKeyByValue(nodeElement.attributeValue("nodeType"))); 108 node.setNodeCode(nodeElement.attributeValue("nodeCode")); 109 node.setNodeName(nodeElement.attributeValue("nodeName")); 110 node.setPermissionFlag(nodeElement.attributeValue("permissionFlag")); 111 if (StringUtils.isNotEmpty(nodeElement.attributeValue("nodeRatio"))) { 112 node.setNodeRatio(new BigDecimal(nodeElement.attributeValue("nodeRatio"))); 113 } else { 114 node.setNodeRatio(new BigDecimal("0")); 115 } 116 node.setCoordinate(nodeElement.attributeValue("coordinate")); 117 node.setSkipAnyNode(nodeElement.attributeValue("skipAnyNode")); 118 node.setListenerType(nodeElement.attributeValue("listenerType")); 119 node.setListenerPath(nodeElement.attributeValue("listenerPath")); 120 node.setHandlerType(nodeElement.attributeValue("handlerType")); 121 node.setHandlerPath(nodeElement.attributeValue("handlerPath")); 122 node.setFormCustom(nodeElement.attributeValue("formCustom")); 123 node.setFormPath(nodeElement.attributeValue("formPath")); 124 FlowFactory.dataFillHandler().idFill(node); 125 126 List<Element> skipsElement = nodeElement.elements(); 127 List<Skip> skips = node.getSkipList(); 128 // 遍历节点下的跳转条件 129 for (Element skipElement : skipsElement) { 130 Skip skip = FlowFactory.newSkip(); 131 if ("skip".equals(skipElement.getName())) { 132 skip.setNowNodeCode(node.getNodeCode()); 133 skip.setNowNodeType(node.getNodeType()); 134 skip.setNextNodeCode(skipElement.getText()); 135 // 条件约束 136 skip.setSkipName(skipElement.attributeValue("skipName")); 137 skip.setSkipType(skipElement.attributeValue("skipType")); 138 skip.setCoordinate(skipElement.attributeValue("coordinate")); 139 skip.setSkipCondition(skipElement.attributeValue("skipCondition")); 140 skips.add(skip); 141 } 142 } 143 return node; 144 } 145 146 @SuppressWarnings("unchecked") 147 public static Document createDocument(Definition definition) { 148 // 创建document对象 149 Document document = DocumentHelper.createDocument(); 150 // 创建根节点bookRoot 151 Element definitionElement = document.addElement("definition"); 152 // 向子节点中添加属性 153 definitionElement.addAttribute("flowCode", definition.getFlowCode()); 154 definitionElement.addAttribute("flowName", definition.getFlowName()); 155 definitionElement.addAttribute("version", definition.getVersion()); 156 definitionElement.addAttribute("category", definition.getCategory()); 157 definitionElement.addAttribute("formCustom", definition.getFormCustom()); 158 definitionElement.addAttribute("formPath", definition.getFormPath()); 159 definitionElement.addAttribute("listenerType", definition.getListenerType()); 160 definitionElement.addAttribute("listenerPath", definition.getListenerPath()); 161 definitionElement.addAttribute("ext", definition.getExt()); 162 163 List<Node> nodeList = definition.getNodeList(); 164 for (Node node : nodeList) { 165 // 向节点中添加子节点 166 Element nodeElement = definitionElement.addElement("node"); 167 nodeElement.addAttribute("nodeType", NodeType.getValueByKey(node.getNodeType())); 168 nodeElement.addAttribute("nodeCode", node.getNodeCode()); 169 nodeElement.addAttribute("nodeName", node.getNodeName()); 170 nodeElement.addAttribute("permissionFlag", node.getPermissionFlag()); 171 if (Objects.nonNull(node.getNodeRatio())) { 172 nodeElement.addAttribute("nodeRatio", node.getNodeRatio().toString()); 173 } 174 nodeElement.addAttribute("coordinate", node.getCoordinate()); 175 nodeElement.addAttribute("skipAnyNode", node.getSkipAnyNode()); 176 nodeElement.addAttribute("listenerType", node.getListenerType()); 177 nodeElement.addAttribute("listenerPath", node.getListenerPath()); 178 nodeElement.addAttribute("handlerType", node.getHandlerType()); 179 nodeElement.addAttribute("handlerPath", node.getHandlerPath()); 180 nodeElement.addAttribute("formCustom", node.getFormCustom()); 181 nodeElement.addAttribute("formPath", node.getFormPath()); 182 183 List<Skip> skipList = node.getSkipList(); 184 if (CollUtil.isNotEmpty(skipList)) { 185 for (Skip skip : skipList) { 186 Element skipElement = nodeElement.addElement("skip"); 187 skipElement.addAttribute("coordinate", skip.getCoordinate()); 188 if (StringUtils.isNotEmpty(skip.getSkipType())) { 189 AssertUtil.isFalse(StringUtils.isNotEmpty(skip.getNextNodeCode()), "下一个流程节点编码为空"); 190 skipElement.addAttribute("skipType", skip.getSkipType()); 191 } 192 if (StringUtils.isNotEmpty(skip.getSkipName())) { 193 skipElement.addAttribute("skipName", skip.getSkipName()); 194 } 195 if (StringUtils.isNotEmpty(skip.getSkipCondition())) { 196 skipElement.addAttribute("skipCondition", skip.getSkipCondition()); 197 } 198 skipElement.addText(skip.getNextNodeCode()); 199 } 200 } 201 } 202 return document; 203 } 204 205 private static FlowCombine structureFlow(Definition definition) { 206 // 获取流程 207 FlowCombine combine = new FlowCombine(); 208 // 流程定义 209 combine.setDefinition(definition); 210 // 所有的流程节点 211 List<Node> allNodes = combine.getAllNodes(); 212 // 所有的流程连线 213 List<Skip> allSkips = combine.getAllSkips(); 214 215 String flowName = definition.getFlowName(); 216 AssertUtil.isEmpty(definition.getFlowCode(), "【" + flowName + "】流程flowCode为空!"); 217 // 发布 218 definition.setIsPublish(0); 219 definition.setUpdateTime(new Date()); 220 FlowFactory.dataFillHandler().idFill(definition); 221 222 List<Node> nodeList = definition.getNodeList(); 223 // 每一个流程的开始节点个数 224 int startNum = 0; 225 Set<String> nodeCodeSet = new HashSet<>(); 226 // 便利一个流程中的各个节点 227 for (Node node : nodeList) { 228 initNodeAndCondition(node, definition.getId(), definition.getVersion()); 229 if (NodeType.isStart(node.getNodeType())) { 230 startNum++; 231 AssertUtil.isTrue(startNum > 1, "[" + flowName + "]" + ExceptionCons.MUL_START_NODE); 232 } 233 // 保证不存在重复的nodeCode 234 AssertUtil.contains(nodeCodeSet, node.getNodeCode(), 235 "【" + flowName + "】" + ExceptionCons.SAME_NODE_CODE); 236 nodeCodeSet.add(node.getNodeCode()); 237 allNodes.add(node); 238 allSkips.addAll(node.getSkipList()); 239 } 240 Map<String, Integer> skipMap = StreamUtils.toMap(allNodes, Node::getNodeCode, Node::getNodeType); 241 allSkips.forEach(allSkip -> allSkip.setNextNodeType(skipMap.get(allSkip.getNextNodeCode()))); 242 AssertUtil.isTrue(startNum == 0, "[" + flowName + "]" + ExceptionCons.LOST_START_NODE); 243 // 校验跳转节点的合法性 244 checkSkipNode(allSkips); 245 // 校验所有目标节点是否都存在 246 validaIsExistDestNode(allSkips, nodeCodeSet); 247 return combine; 248 } 249 250 /** 251 * 校验跳转节点的合法性 252 * 253 * @param allSkips 254 */ 255 private static void checkSkipNode(List<Skip> allSkips) { 256 Map<String, List<Skip>> allSkipMap = StreamUtils.groupByKey(allSkips, Skip::getNowNodeCode); 257 // 不可同时通过或者退回到多个中间节点,必须先流转到网关节点 258 allSkipMap.forEach((key, values) -> { 259 AtomicInteger passNum = new AtomicInteger(); 260 AtomicInteger rejectNum = new AtomicInteger(); 261 for (Skip value : values) { 262 if (NodeType.isBetween(value.getNowNodeType()) && NodeType.isBetween(value.getNextNodeType())) { 263 if (SkipType.isPass(value.getSkipType())) { 264 passNum.getAndIncrement(); 265 } else { 266 rejectNum.getAndIncrement(); 267 } 268 } 269 } 270 AssertUtil.isTrue(passNum.get() > 1 || rejectNum.get() > 1, ExceptionCons.MUL_SKIP_BETWEEN); 271 }); 272 } 273 274 /** 275 * 校验所有的目标节点是否存在 276 * 277 * @param allSkips 278 * @param nodeCodeSet 279 */ 280 private static void validaIsExistDestNode(List<Skip> allSkips, Set<String> nodeCodeSet) { 281 for (int i = 0; i < allSkips.size(); i++) { 282 String nextNodeCode = allSkips.get(i).getNextNodeCode(); 283 AssertUtil.isTrue(!nodeCodeSet.contains(nextNodeCode), "【" + nextNodeCode + "】" + ExceptionCons.NULL_NODE_CODE); 284 } 285 } 286 287 288 /** 289 * 读取工作节点和跳转条件 290 * 291 * @param node 292 * @param definitionId 293 * @param version 294 * @return 295 */ 296 private static void initNodeAndCondition(Node node, Long definitionId, String version) { 297 String nodeName = node.getNodeName(); 298 String nodeCode = node.getNodeCode(); 299 List<Skip> skipList = node.getSkipList(); 300 if (!NodeType.isEnd(node.getNodeType())) { 301 AssertUtil.isEmpty(skipList, "开始和中间节点必须有跳转规则"); 302 } 303 AssertUtil.isEmpty(nodeCode, "[" + nodeName + "]" + ExceptionCons.LOST_NODE_CODE); 304 305 node.setVersion(version); 306 node.setDefinitionId(definitionId); 307 node.setUpdateTime(new Date()); 308 309 // 中间节点的集合, 跳转类型和目标节点不能重复 310 Set<String> betweenSet = new HashSet<>(); 311 // 网关的集合 跳转条件和下目标节点不能重复 312 Set<String> gateWaySet = new HashSet<>(); 313 int skipNum = 0; 314 // 遍历节点下的跳转条件 315 for (Skip skip : skipList) { 316 if (NodeType.isStart(node.getNodeType())) { 317 skipNum++; 318 AssertUtil.isTrue(skipNum > 1, "[" + node.getNodeName() + "]" + ExceptionCons.MUL_START_SKIP); 319 } 320 AssertUtil.isEmpty(skip.getNextNodeCode(), "【" + nodeName + "】" + ExceptionCons.LOST_DEST_NODE); 321 FlowFactory.dataFillHandler().idFill(skip); 322 // 流程id 323 skip.setDefinitionId(definitionId); 324 // 节点id 325 skip.setNodeId(node.getId()); 326 if (NodeType.isGateWaySerial(node.getNodeType())) { 327 String target = skip.getSkipCondition() + ":" + skip.getNextNodeCode(); 328 AssertUtil.contains(gateWaySet, target, "[" + nodeName + "]" + ExceptionCons.SAME_CONDITION_NODE); 329 gateWaySet.add(target); 330 } else if (NodeType.isGateWayParallel(node.getNodeType())) { 331 String target = skip.getNextNodeCode(); 332 AssertUtil.contains(gateWaySet, target, "[" + nodeName + "]" + ExceptionCons.SAME_DEST_NODE); 333 gateWaySet.add(target); 334 } else { 335 String value = skip.getSkipType() + ":" + skip.getNextNodeCode(); 336 AssertUtil.contains(betweenSet, value, "[" + nodeName + "]" + ExceptionCons.SAME_CONDITION_VALUE); 337 betweenSet.add(value); 338 } 339 } 340 } 341 342}