BPMN流程图分支节点没条件启动时自动扫描sequenceFlow注入路由表达式非科班野生程序员深耕政务信息化20年。从VC到PB再到Java自研框架browise也打磨了十几年。最近整理框架代码发现不少有趣的决策写出来和大家聊聊。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。问题从哪来用 Activiti 做政务工作流流程图用 BPMN 设计。一个审批节点后面可能跟多个分支同意/退回/终止每个分支用sequenceFlow连线表示。Activiti 要求分支连线必须有条件表达式比如conditionExpressionxsi:typetFormalExpression![CDATA[${nextid同意}]]/conditionExpression但政务业务人员画流程图的时候经常忘记加条件。更常见的情况是流程图先画好后面开发的时候才知道每条线走什么条件。如果让业务人员手动维护 BPMN XML 里的条件表达式几乎不可能。我的方案流程部署之前自动扫描 BPMN 文件里所有sequenceFlow找出同一个源节点发出了多条连线的情况说明是分支然后自动注入条件表达式。条件就是${nextid连线的id}。核心代码// XMLUtil.java — parse方法publicstaticListStringparse(Stringfilename)throwsTransformerException{ListStringlistnewArrayListString();StringxmlreadToString(filename);DocumentBuilderFactorydbFactoryDocumentBuilderFactory.newInstance();DocumentBuilderdbBuilderdbFactory.newDocumentBuilder();StringReadersrnewStringReader(xml);InputSourceisnewInputSource(sr);DocumentdocdbBuilder.parse(is);// 找到 process 节点NodeListnListdoc.getElementsByTagName(process);Elementprocess(Element)nList.item(0);nListprocess.getElementsByTagName(sequenceFlow);// 先收集所有 sequenceFlowListElementlist1newArrayListElement();for(inti0;inList.getLength();i){Elementnode(Element)nList.item(i);list1.add(node);}// 遍历判断哪些节点有多个出口for(inti0;ilist1.size();i){Elementnode1list1.get(i);Stringsourid1node1.getAttribute(sourceRef);Stringid1node1.getAttribute(id);for(intj0;jlist1.size();j){Elementnode2list1.get(j);Stringid2node2.getAttribute(id);Stringsourid2node2.getAttribute(sourceRef);// 同一个源节点不同的连线if(!id1.equals(id2)sourid1.equals(sourid2)){// 注入条件表达式ElementnewNodedoc.createElement(conditionExpression);newNode.setAttribute(xsi:type,tFormalExpression);Stringnodevalue![CDATA[${nextid\node1.getAttribute(id)\}]];newNode.appendChild(doc.createTextNode(nodevalue));node1.appendChild(newNode);}}}// 写回文件TransformerFactorytransformerFactoryTransformerFactory.newInstance();TransformertransformertransformerFactory.newTransformer();DOMSourcesourcenewDOMSource(doc);StreamResultresultnewStreamResult(newFile(filename));transformer.setOutputProperty(OutputKeys.INDENT,yes);transformer.transform(source,result);returnlist;}算法逻辑双重循环比较所有sequenceFlow外层循环取连线 node1拿到它的sourceRef从哪个节点出发和id连线自己的id内层循环取连线 node2同样拿到sourceRef和id如果两条不同的连线id1 ! id2来自同一个源节点sourid1 sourid2说明这里有分支给 node1 注入条件表达式${nextid连线的id}为什么比较的是连线 id 而不是目标节点 id因为流程路由的时候前端传的就是走哪条线的 idActiviti 根据条件表达式匹配。处理前的BPMNsequenceFlowidflow1sourceReftask1targetReftask2/sequenceFlowidflow2sourceReftask1targetReftask3/task1 有两个出口但没有条件表达式。处理后的BPMNsequenceFlowidflow1sourceReftask1targetReftask2conditionExpressionxsi:typetFormalExpression![CDATA[${nextidflow1}]]/conditionExpression/sequenceFlowsequenceFlowidflow2sourceReftask1targetReftask3conditionExpressionxsi:typetFormalExpression![CDATA[${nextidflow2}]]/conditionExpression/sequenceFlow附带功能Dom2MapXMLUtil 里还有一个 XML 转 Map 的方法用来解析外部系统传来的 XML 数据publicstaticMapString,ObjectDom2Map(Stringxml,Stringcharset)throwsException{SAXReaderreadernewSAXReader();reader.setIncludeExternalDTDDeclarations(false);org.dom4j.Documentdocreader.read(newByteArrayInputStream(xml.getBytes(charset)));MapString,ObjectmapnewHashMapString,Object();if(docnull)returnmap;org.dom4j.Elementrootdoc.getRootElement();for(Iteratoriteratorroot.elementIterator();iterator.hasNext();){org.dom4j.Elemente(org.dom4j.Element)iterator.next();Listliste.elements();if(list.size()0){map.put(e.getName(),Dom2Map(e));}elsemap.put(e.getName(),e.getText());}returnmap;}政务对接税务、社保等外部系统经常收到 XML 报文这个方法递归解析成 Map业务层直接用。小结整个parse方法核心逻辑就是一个双重循环找出同源多分支的 sequenceFlow自动注入条件表达式。227行的工具类里这个方法不到70行。不需要业务人员懂 BPMN 条件语法流程部署前框架自动补齐。做工作流的同学们你们的分支条件是怎么维护的评论区交流。标签#Java #BPMN #Activiti #工作流 #XML解析 #政务信息化 #自研框架