根据流程节点分支拆分,每遇到一个分支,就拆分成一条链路。
如下图所示,可拆分成3条链路:
(1)施工单位审核-->监理单位标段负责人审核-->监理单位执行总监--审核-->设计负责人转办-->项目法人建设现场指挥部负责人审核-->施工单位签字盖章-->监理单位标段负责人签字
(2)施工单位审核-->监理单位标段负责人审核-->监理单位执行总监--审核-->设计负责人转办-->施工单位签字盖章-->监理单位标段负责人签字
(3)施工单位审核-->监理单位标段负责人审核-->监理单位执行总监--审批-->监理单位总监审核-->监理单位标段负责人签字
节点信息及其关联关系存储在xml中(见下文 4. xml)。
第一步,先解析xml,取出任务、线条、判断条件;
第二步,根据线条、判断条件中的 incoming、outgoing 梳理各个任务之间的前后顺序;
第三步,根据任务之间的前后关系,拆分链路。
具体的 java 代码如下:
1. 依赖包
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.5.1</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>7.1.0.M6</version>
</dependency>
2. 解析xml、拆分链路工具类
经过测试发现,domParseBpmnXml 比 parseBpmnXml 速度快大概200毫秒。
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
/**
* 流程图设计 工具类
*
* @author
* @date 2024/10/14 星期一
* @since JDK 11
*/
@Slf4j
public class BpmActModelUtils {
/** 开始节点Key */
public static final String START_EVENT_KEY = "startEvent";
/** 用户任务key */
public static final String USER_TASK_KEY = "userTaskList";
/** 服务任务key */
public static final String SERVICE_TASK_KEY = "serviceTaskList";
/** 普通任务key */
public static final String NORMAL_TASK_KEY = "normalTaskList";
/** 用户任务连接线条Key */
public static final String SEQUENCE_FLOW_KEY = "sequenceFlowList";
/** 判断条件Key */
public static final String EXCLUSIVE_GATEWAY_KEY = "exclusiveGatewayList";
/** 并行判断条件Key */
public static final String PARALLEL_GATEWAY_KEY = "parallelGatewayList";
/** 结束节点Key */
public static final String END_EVENT_KEY = "endEvent";
/**
* 解析流程图设计节点属性
*
* @date 2024-10-14
* @param bpmnXml 流程图设计节点属性
* @return Map
* @since JDK 11
* @author
*/
public static Map<String, List<?>> parseBpmnXml(String bpmnXml) {
if (StringUtils.isBlank(bpmnXml)) {
return null;
}
Map<String, List<?>> map = new HashMap<>();
// xml转json
String jsonStr = JsonUtils.xmlToJson(bpmnXml);
String process = JsonUtils.getJsonValue(jsonStr, "bpmn2:process");
if (StringUtils.isBlank(process)) {
return null;
}
process = process.replaceAll("@camunda:", "");
process = process.replaceAll("@xmlns:", "");
process = process.replaceAll("bpmn2:", "");
process = process.replaceAll("@", "");
// 普通任务
String normalTask = JsonUtils.getJsonValue(process, "task");
if (StringUtils.isNotBlank(normalTask)) {
List<BpmnNormalTaskVo> normalTaskList = JSON.parseArray(normalTask, BpmnNormalTaskVo.class);
if (CollectionUtils.isNotEmpty(normalTaskList)) {
// 普通任务
map.put(NORMAL_TASK_KEY, normalTaskList);
}
}
// 用户任务
String userTask = JsonUtils.getJsonValue(process, "userTask");
if (StringUtils.isNotBlank(userTask)) {
// 用户任务
List<BpmnUserTaskVo> userTaskList = JSON.parseArray(userTask, BpmnUserTaskVo.class);
if (CollectionUtils.isNotEmpty(userTaskList)) {
// 用户任务
map.put(USER_TASK_KEY, userTaskList);
}
}
// 用户任务连接线条
String sequenceFlow = JsonUtils.getJsonValue(process, "sequenceFlow");
if (StringUtils.isNotBlank(sequenceFlow)) {
sequenceFlow = sequenceFlow.replaceAll("#text", "text");
sequenceFlow = sequenceFlow.replaceAll("xsi:", "");
// 用户任务连接线条
List<BpmnSequenceFlowVo> sequenceFlowList = JSON.parseArray(sequenceFlow, BpmnSequenceFlowVo.class);
if (CollectionUtils.isNotEmpty(sequenceFlowList)) {
// 用户任务
map.put(SEQUENCE_FLOW_KEY, sequenceFlowList);
}
}
// 判断条件
String exclusiveGateway = JsonUtils.getJsonValue(process, "exclusiveGateway");
if (StringUtils.isNotBlank(exclusiveGateway)) {
List<BpmnExclusiveGatewayVo> exclusiveGatewayList = JSON.parseArray(exclusiveGateway,
BpmnExclusiveGatewayVo.class);
if (CollectionUtils.isNotEmpty(exclusiveGatewayList)) {
// 判断条件
map.put(EXCLUSIVE_GATEWAY_KEY, exclusiveGatewayList);
}
}
// 服务任务
String serviceTask = JsonUtils.getJsonValue(process, "serviceTask");
if (StringUtils.isNotBlank(serviceTask)) {
List<BpmServiceTaskVo> serviceTaskList = JSON.parseArray(serviceTask, BpmServiceTaskVo.class);
if (CollectionUtils.isNotEmpty(serviceTaskList)) {
// 服务任务
map.put(SERVICE_TASK_KEY, serviceTaskList);
}
}
return map;
}
/**
* DOM解析流程图设计节点属性
*
* @date 2024-10-18
* @param bpmnXml 流程图设计节点属性
* @return Map
* @since JDK 11
* @author
*/
public static Map<String, List<?>> domParseBpmnXml(String bpmnXml) {
if (StringUtils.isBlank(bpmnXml)) {
return null;
}
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 加载xml文件
Document document = builder.parse(new InputSource(new StringReader(bpmnXml)));
Map<String, List<?>> map = new HashMap<>();
// 开始节点
BpmnStartEventVo startEvent = domParseStartEventOfBpmnXml(document);
// 普通任务
List<BpmnNormalTaskVo> normalTaskList = domParseNormalTaskOfBpmnXml(document);
// 用户任务
List<BpmnUserTaskVo> userTaskList = domParseUserTaskOfBpmnXml(document);
// 服务任务
List<BpmServiceTaskVo> serviceTaskList = domParseServiceTaskOfBpmnXml(document);
// 用户任务连接线条
List<BpmnSequenceFlowVo> sequenceFlowList = domParseSequenceFlowOfBpmnXml(document);
// 判断条件
List<BpmnExclusiveGatewayVo> exclusiveGatewayList = domParseExclusiveGatewayOfBpmnXml(document);
// 并行判断条件
List<BpmnParallelGatewayVo> parallelGatewayList = domParseParallelGatewayOfBpmnXml(document);
// 结束节点
List<BpmnEndEventVo> endEventList = domParseEndEventOfBpmnXml(document);
if (startEvent != null) {
// 开始节点
map.put(START_EVENT_KEY, new ArrayList<>(Arrays.asList(startEvent)));
}
if (CollectionUtils.isNotEmpty(normalTaskList)) {
// 普通任务
map.put(NORMAL_TASK_KEY, normalTaskList);
}
if (CollectionUtils.isNotEmpty(userTaskList)) {
// 用户任务
map.put(USER_TASK_KEY, userTaskList);
}
if (CollectionUtils.isNotEmpty(serviceTaskList)) {
// 服务任务
map.put(SERVICE_TASK_KEY, serviceTaskList);
}
if (CollectionUtils.isNotEmpty(sequenceFlowList)) {
// 用户任务连接线条
map.put(SEQUENCE_FLOW_KEY, sequenceFlowList);
}
if (CollectionUtils.isNotEmpty(exclusiveGatewayList)) {
// 判断条件
map.put(EXCLUSIVE_GATEWAY_KEY, exclusiveGatewayList);
}
if (CollectionUtils.isNotEmpty(parallelGatewayList)) {
// 并行判断条件
map.put(PARALLEL_GATEWAY_KEY, parallelGatewayList);
}
if (CollectionUtils.isNotEmpty(endEventList)) {
// 结束节点
map.put(END_EVENT_KEY, endEventList);
}
return map;
} catch (Exception e) {
log.error(String.format("dom解析流程图设计节点属性-失败.bpmnXml:%s", bpmnXml), e);
return null;
}
}
/**
* 获取流程图设计节点属性中的普通任务节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:task </p>
* @date 2024-10-18
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmnNormalTaskVo> domParseNormalTaskOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 普通任务
NodeList responsedata = document.getElementsByTagName("bpmn2:task");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 普通任务
List<BpmnNormalTaskVo> normalTaskList = new ArrayList<>();
BpmnNormalTaskVo normalTask = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个普通任务
Element element = (Element) responsedata.item(i);
// 普通任务节点属性
NamedNodeMap namedNodeMap = element.getAttributes();
normalTask = new BpmnNormalTaskVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 普通任务节点属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
normalTask.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
String nodeValue = attr.getNodeValue();
if (StringUtils.isNotBlank(nodeValue)) {
nodeValue = nodeValue.replaceAll(" ", "");
nodeValue = nodeValue.replaceAll("\n", "");
}
normalTask.setName(nodeValue);
}
}
// 普通任务子节点
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
NodeList outgoingNodeList = element.getElementsByTagName("bpmn2:outgoing");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
// 下一个节点id
List<String> outgoing = getChildrenText(outgoingNodeList);
if (CollectionUtils.isNotEmpty(incoming)) {
normalTask.setIncoming(incoming);
}
if (CollectionUtils.isNotEmpty(outgoing)) {
normalTask.setOutgoing(outgoing);
}
normalTaskList.add(normalTask);
}
return normalTaskList;
}
/**
* 获取流程图设计节点属性中的用户任务节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:userTask </p>
* @date 2024-10-18
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmnUserTaskVo> domParseUserTaskOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 用户任务
NodeList responsedata = document.getElementsByTagName("bpmn2:userTask");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 用户任务
List<BpmnUserTaskVo> userTaskList = new ArrayList<>();
BpmnUserTaskVo userTask = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个用户任务
Element element = (Element) responsedata.item(i);
// 用户任务节点属性
NamedNodeMap namedNodeMap = element.getAttributes();
userTask = new BpmnUserTaskVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 用户任务节点属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
userTask.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
String nodeValue = attr.getNodeValue();
if (StringUtils.isNotBlank(nodeValue)) {
nodeValue = nodeValue.replaceAll(" ", "");
nodeValue = nodeValue.replaceAll("\n", "");
}
userTask.setName(nodeValue);
}
}
// 用户任务子节点
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
NodeList outgoingNodeList = element.getElementsByTagName("bpmn2:outgoing");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
// 下一个节点id
List<String> outgoing = getChildrenText(outgoingNodeList);
if (CollectionUtils.isNotEmpty(incoming)) {
userTask.setIncoming(incoming);
}
if (CollectionUtils.isNotEmpty(outgoing)) {
userTask.setOutgoing(outgoing);
}
userTaskList.add(userTask);
}
return userTaskList;
}
/**
* 获取流程图设计节点属性中的服务任务节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:serviceTask </p>
* @date 2024-10-18
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmServiceTaskVo> domParseServiceTaskOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 服务任务
NodeList responsedata = document.getElementsByTagName("bpmn2:serviceTask");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 服务任务
List<BpmServiceTaskVo> sereviceTaskList = new ArrayList<>();
BpmServiceTaskVo serviceTask = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个服务任务
Element element = (Element) responsedata.item(i);
// 服务任务节点属性
NamedNodeMap namedNodeMap = element.getAttributes();
serviceTask = new BpmServiceTaskVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 服务任务节点属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
serviceTask.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
String nodeValue = attr.getNodeValue();
if (StringUtils.isNotBlank(nodeValue)) {
nodeValue = nodeValue.replaceAll(" ", "");
nodeValue = nodeValue.replaceAll("\n", "");
}
serviceTask.setName(nodeValue);
}
}
// 服务任务子节点
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
if (CollectionUtils.isNotEmpty(incoming)) {
serviceTask.setIncoming(incoming.get(0));
}
sereviceTaskList.add(serviceTask);
}
return sereviceTaskList;
}
/**
* 获取流程图设计节点属性中的用户任务连接线条
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:sequenceFlow </p>
* @date 2024-10-22
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmnSequenceFlowVo> domParseSequenceFlowOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 连接线条
NodeList responsedata = document.getElementsByTagName("bpmn2:sequenceFlow");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 连接线条
List<BpmnSequenceFlowVo> sequenceFlowList = new ArrayList<>();
BpmnSequenceFlowVo sequenceFlow = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个连接线条
Element element = (Element) responsedata.item(i);
// 连接线条属性
NamedNodeMap namedNodeMap = element.getAttributes();
sequenceFlow = new BpmnSequenceFlowVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 连接线条属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
sequenceFlow.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
sequenceFlow.setName(attr.getNodeValue());
}
if ("sourceRef".equalsIgnoreCase(attr.getNodeName())) {
// 上一个节点id
sequenceFlow.setSourceRef(attr.getNodeValue());
}
if ("targetRef".equalsIgnoreCase(attr.getNodeName())) {
// 下一个节点
sequenceFlow.setTargetRef(attr.getNodeValue());
}
}
// 判断条件表达式
NodeList condExprNodeList = element.getElementsByTagName("bpmn2:conditionExpression");
if (condExprNodeList != null && condExprNodeList.getLength() > 0) {
Node cndExprNode = condExprNodeList.item(0);
// 判断条件表达式
BpmnConditionExpressionVo condExpr = new BpmnConditionExpressionVo();
condExpr.setText(cndExprNode.getTextContent());
// 判断条件表达式属性
NamedNodeMap cndExprNamedNodeMap = cndExprNode.getAttributes();
for (int ii = 0; ii < cndExprNamedNodeMap.getLength(); ii++) {
// 判断条件表达式属性
Node attr = cndExprNamedNodeMap.item(ii);
if ("xsi:type".equalsIgnoreCase(attr.getNodeName())) {
condExpr.setType(attr.getNodeValue());
}
}
sequenceFlow.setConditionExpression(condExpr);
}
sequenceFlowList.add(sequenceFlow);
} // end of for (int i = 0; i < responsedata.getLength(); i++)
return sequenceFlowList;
}
/**
* 获取流程图设计节点属性中的判断条件
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:exclusiveGateway </p>
* @date 2024-10-22
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmnExclusiveGatewayVo> domParseExclusiveGatewayOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 判断条件
NodeList responsedata = document.getElementsByTagName("bpmn2:exclusiveGateway");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 判断条件
List<BpmnExclusiveGatewayVo> exclusiveGatewayList = new ArrayList<>();
BpmnExclusiveGatewayVo exclusiveGateway = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个判断条件
Element element = (Element) responsedata.item(i);
// 判断条件属性
NamedNodeMap namedNodeMap = element.getAttributes();
exclusiveGateway = new BpmnExclusiveGatewayVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 判断条件属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
exclusiveGateway.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
exclusiveGateway.setName(attr.getNodeValue());
}
}
// 子节点(上一个节点)
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
// 子节点(下一个节点)
NodeList outgoingNodeList = element.getElementsByTagName("bpmn2:outgoing");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
// 下一个节点
List<String> outgoing = getChildrenText(outgoingNodeList);
exclusiveGateway.setIncoming(incoming);
exclusiveGateway.setOutgoing(outgoing);
exclusiveGatewayList.add(exclusiveGateway);
} // end of for (int i = 0; i < responsedata.getLength(); i++)
return exclusiveGatewayList;
}
/**
* 获取流程图设计节点属性中的并行判断条件
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:parallelGateway </p>
* @date 2024-10-23
* @param document xml文档
* @return List
* @since JDK 11
* @author
*/
public static List<BpmnParallelGatewayVo> domParseParallelGatewayOfBpmnXml(Document document) {
/* 设计计量支付-环水保 中存在 bpmn2:parallelGateway */
if (document == null) {
return null;
}
// 判断条件
NodeList responsedata = document.getElementsByTagName("bpmn2:parallelGateway");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
// 判断条件
List<BpmnParallelGatewayVo> parallelGatewayList = new ArrayList<>();
BpmnParallelGatewayVo parallelGateway = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个判断条件
Element element = (Element) responsedata.item(i);
// 判断条件属性
NamedNodeMap namedNodeMap = element.getAttributes();
parallelGateway = new BpmnParallelGatewayVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 判断条件属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
parallelGateway.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
parallelGateway.setName(attr.getNodeValue());
}
}
// 子节点(上一个节点)
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
// 子节点(下一个节点)
NodeList outgoingNodeList = element.getElementsByTagName("bpmn2:outgoing");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
// 下一个节点
List<String> outgoing = getChildrenText(outgoingNodeList);
parallelGateway.setIncoming(incoming);
parallelGateway.setOutgoing(outgoing);
parallelGatewayList.add(parallelGateway);
} // end of for (int i = 0; i < responsedata.getLength(); i++)
return parallelGatewayList;
}
/**
* 按分支拆分任务链条
*
* @date 2024-10-24
* @param bpmnName 流程名称
* @param list 所有任务
* @return List
* @since JDK 11
* @author
*/
public static List<List<BpmActModelUserTaskVo>> splitUserTasks(String bpmnName, List<BpmActModelUserTaskVo> list) {
if (CollectionUtils.isEmpty(list)) {
log.info("按分支拆分任务链条,所有任务为空");
return new ArrayList<>();
}
// 按 activityId 分组
Map<String, List<BpmActModelUserTaskVo>> map = list.stream().collect(
Collectors.groupingBy(BpmActModelUserTaskVo::getActivityId, LinkedHashMap::new, Collectors.toList()));
// 第一个任务节点
Optional<BpmActModelUserTaskVo> firstOpt = list.stream().filter(o -> o.getSort() != null && o.getSort() == 1)
.findFirst();
BpmActModelUserTaskVo firstTask = firstOpt.orElse(null);
if (firstTask == null) {
log.info("按分支拆分任务链条,第一个任务节点不存在.所有任务:{}", com.alibaba.fastjson2.JSON.toJSONString(list));
return new ArrayList<>();
}
// 最终拆分结果
List<List<BpmActModelUserTaskVo>> resultList = new ArrayList<>();
// 用于保存当前任务节点及其之前的任务节点
List<BpmActModelUserTaskVo> initItemList = new ArrayList<>();
initItemList.add(firstTask);
recursionNextTask(resultList, firstTask, initItemList, map);
// log.info("allList:\r\n{}", JSON.toJSONString(resultList));
return resultList;
}
/**
* 根据当前任务节点递归拆分
*
* @date 2024-10-25
*
* @param result 拆分最终结果
* @param node 当前任务节点
* @param initItemList 用于保存当前任务节点及其之前的任务节点
* @param map 所有任务节点
* @since JDK 11
* @author
*/
public static void recursionNextTask(List<List<BpmActModelUserTaskVo>> result, BpmActModelUserTaskVo node,
List<BpmActModelUserTaskVo> initItemList, Map<String, List<BpmActModelUserTaskVo>> map) {
if (node.getNextList() == null || node.getNextList().size() <= 0) {
/* 最后一个任务节点 */
result.add(initItemList);
return;
}
// node的下一个节点
List<BpmActModelUserTaskVo> nextList = node.getNextList();
for (int i = 0; i < nextList.size(); i++) {
BpmActModelUserTaskVo child = nextList.get(i);
BpmActModelUserTaskVo taskVo = map.get(child.getActivityId()).get(0);
child.setNextList(taskVo.getNextList());
List<BpmActModelUserTaskVo> cPath = new ArrayList<>();
cPath.addAll(initItemList);
cPath.add(child);
recursionNextTask(result, child, cPath, map);
}
}
/**
* 获取流程图设计节点属性中的开始节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:startEvent </p>
* @date 2024-10-22
* @param document xml文档
* @return BpmnStartEventVo
* @since JDK 11
* @author
*/
private static BpmnStartEventVo domParseStartEventOfBpmnXml(Document document) {
if (document == null) {
return null;
}
// 开始节点
NodeList responsedata = document.getElementsByTagName("bpmn2:startEvent");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
BpmnStartEventVo startEvent = new BpmnStartEventVo();
// 只有一个开始节点
Element element = (Element) responsedata.item(0);
// 开始节点的属性
NamedNodeMap namedNodeMap = element.getAttributes();
for (int i = 0; i < namedNodeMap.getLength(); i++) {
// 开始节点的属性
Node attr = namedNodeMap.item(i);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
startEvent.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
String nodeValue = attr.getNodeValue();
if (StringUtils.isNotBlank(nodeValue)) {
nodeValue = nodeValue.replaceAll(" ", "");
nodeValue = nodeValue.replaceAll("\n", "");
}
startEvent.setName(nodeValue);
}
}
// 子节点
NodeList outgoingNodeList = element.getElementsByTagName("bpmn2:outgoing");
if (outgoingNodeList != null && outgoingNodeList.getLength() > 0) {
// 下一个节点
List<String> outgoing = getChildrenText(outgoingNodeList);
startEvent.setOutgoing(CollectionUtils.isEmpty(outgoing) ? "" : outgoing.get(0));
}
return startEvent;
}
/**
* 获取流程图设计节点属性中的结束节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:endEvent </p>
* @date 2024-10-22
* @param document xml文档
* @return BpmnStartEventVo
* @since JDK 11
* @author
*/
private static List<BpmnEndEventVo> domParseEndEventOfBpmnXml(Document document) {
// 结束节点
NodeList responsedata = document.getElementsByTagName("bpmn2:endEvent");
if (responsedata == null || responsedata.getLength() == 0) {
return null;
}
List<BpmnEndEventVo> endEventList = new ArrayList<>();
BpmnEndEventVo endEvent = null;
for (int i = 0; i < responsedata.getLength(); i++) {
// 每一个结束节点
Element element = (Element) responsedata.item(i);
// 结束节点的属性
NamedNodeMap namedNodeMap = element.getAttributes();
endEvent = new BpmnEndEventVo();
for (int j = 0; j < namedNodeMap.getLength(); j++) {
// 结束节点的属性
Node attr = namedNodeMap.item(j);
if ("id".equalsIgnoreCase(attr.getNodeName())) {
endEvent.setId(attr.getNodeValue());
}
if ("name".equalsIgnoreCase(attr.getNodeName())) {
String nodeValue = attr.getNodeValue();
if (StringUtils.isNotBlank(nodeValue)) {
nodeValue = nodeValue.replaceAll(" ", "");
nodeValue = nodeValue.replaceAll("\n", "");
}
endEvent.setName(nodeValue);
}
}
// 子节点
NodeList incomingNodeList = element.getElementsByTagName("bpmn2:incoming");
// 上一个节点id
List<String> incoming = getChildrenText(incomingNodeList);
if (CollectionUtils.isNotEmpty(incoming)) {
endEvent.setIncoming(incoming);
}
endEventList.add(endEvent);
}
return endEventList;
}
private static List<String> getChildrenText(NodeList nodeList) {
if (nodeList == null || nodeList.getLength() == 0) {
return null;
}
List<String> list = new ArrayList<>();
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
if (node != null && StringUtils.isNotBlank(node.getTextContent())) {
list.add(node.getTextContent());
}
}
return list;
}
}
3. 测试(JUnit 5)
执行 domParseBpmnXml()
import java.io.File;
import java.io.FileInputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeSet;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.xpath.XPathConstants;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.xerces.dom.AttributeMap;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Document;
import com.alibaba.fastjson2.JSON;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.XmlUtil;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
@Slf4j
public class TestBpmActModelUtils {
@BeforeEach
public void setUp() {
}
@Test
public void domParseBpmnXml() {
long startTime = System.currentTimeMillis();
String fileName = "党建管理--测试流程20241024";
String basePath = "F:\\xml_files\\BpmActModelInfo\\";
String filePath = basePath + fileName + ".xml";
try (FileInputStream in = new FileInputStream(filePath);) {
String xml = IOUtils.toString(in, StandardCharsets.UTF_8);
// 解析xml(上面说明中的 第一步)
Map<String, List<?>> map = BpmActModelUtils.domParseBpmnXml(xml);
System.out.println("DOM解析xml文件-结束.耗时 " + (System.currentTimeMillis() - startTime) + " 毫秒");
// 开始节点
BpmnStartEventVo startEvent = null;
// 普通任务
List<BpmnNormalTaskVo> normalTaskList = new ArrayList<>();
// 用户任务
List<BpmnUserTaskVo> userTaskList = new ArrayList<>();
// 服务任务
List<BpmServiceTaskVo> serviceTaskList = new ArrayList<>();
// 用户任务连接线条
List<BpmnSequenceFlowVo> sequenceFlowList = new ArrayList<>();
// 判断条件
List<BpmnExclusiveGatewayVo> exclusiveGatewayList = new ArrayList<>();
// 并行判断条件
List<BpmnParallelGatewayVo> parallelGatewayList = new ArrayList();
// 结束节点
List<BpmnEndEventVo> endEventList = new ArrayList<>();
if (map != null && !map.isEmpty()) {
if (map.containsKey(BpmActModelUtils.START_EVENT_KEY)) {
// 开始节点(只有一个)
List<BpmnStartEventVo> startEventList = (List<BpmnStartEventVo>) map
.get(BpmActModelUtils.START_EVENT_KEY);
startEvent = startEventList.get(0);
}
if (map.containsKey(BpmActModelUtils.USER_TASK_KEY)) {
// 用户任务
userTaskList = (List<BpmnUserTaskVo>) map.get(BpmActModelUtils.USER_TASK_KEY);
}
if (map.containsKey(BpmActModelUtils.NORMAL_TASK_KEY)) {
// 普通任务
normalTaskList = (List<BpmnNormalTaskVo>) map.get(BpmActModelUtils.NORMAL_TASK_KEY);
}
if (map.containsKey(BpmActModelUtils.SERVICE_TASK_KEY)) {
// 服务任务
serviceTaskList = (List<BpmServiceTaskVo>) map.get(BpmActModelUtils.SERVICE_TASK_KEY);
}
if (map.containsKey(BpmActModelUtils.SEQUENCE_FLOW_KEY)) {
// 用户任务连接线条
sequenceFlowList = (List<BpmnSequenceFlowVo>) map.get(BpmActModelUtils.SEQUENCE_FLOW_KEY);
}
if (map.containsKey(BpmActModelUtils.EXCLUSIVE_GATEWAY_KEY)) {
// 判断条件
exclusiveGatewayList = (List<BpmnExclusiveGatewayVo>) map.get(BpmActModelUtils.EXCLUSIVE_GATEWAY_KEY);
}
if (map.containsKey(BpmActModelUtils.PARALLEL_GATEWAY_KEY)) {
// 并行判断条件
parallelGatewayList = (List<BpmnParallelGatewayVo>) map.get(BpmActModelUtils.PARALLEL_GATEWAY_KEY);
}
if (map.containsKey(BpmActModelUtils.END_EVENT_KEY)) {
// 结束节点
endEventList = (List<BpmnEndEventVo>) map.get(BpmActModelUtils.END_EVENT_KEY);
}
}
if (startEvent == null) {
System.out.println("------ 开始节点不存在 ------");
return;
}
// 合并用户任务、服务任务
userTaskList = this.mergeUserAndServiceTask(userTaskList, serviceTaskList);
// 合并用户任务、普通任务
userTaskList = this.mergeUserAndNormalTask(userTaskList, normalTaskList);
if (CollectionUtils.isEmpty(userTaskList)) {
System.out.println("------ 任务节点为空 ------");
return;
}
// 合并判断条件、并行判断条件
exclusiveGatewayList = this.mergeGateway(exclusiveGatewayList, parallelGatewayList);
// 任务节点及之间的关系(上面说明中的 第二步)
List<BpmActModelUserTaskVo> result = new ArrayList<>();
BpmActModelUserTaskVo bpmActModelUserTaskVo = null;
// 循环任务节点
for (BpmnUserTaskVo userTask : userTaskList) {
// 当前节点id
String id = userTask.getId();
// 用户任务名称
String name = userTask.getName();
// 上一个节点id
List<String> incoming = userTask.getIncoming();
// 下一个节点id(线条)
List<String> outgoing = userTask.getOutgoing();
// 任务节点及之间的关系
bpmActModelUserTaskVo = new BpmActModelUserTaskVo();
bpmActModelUserTaskVo.setActivityId(id);
bpmActModelUserTaskVo.setActivityName(name);
if (incoming != null && incoming.contains(startEvent.getOutgoing())) {
/* 上一个节点是开始节点 */
bpmActModelUserTaskVo.setSort(1);
}
if (CollectionUtils.isNotEmpty(outgoing)) {
for (String userTaskOut : outgoing) {
// 用户任务节点的下一个是线条
Optional<BpmnSequenceFlowVo> sequenceFlowOpt = sequenceFlowList.stream()
.filter(f -> f.getId().equalsIgnoreCase(userTaskOut)).findFirst();
if (sequenceFlowOpt.isPresent()) {
/* 存在下一个节点 */
BpmnSequenceFlowVo sequenceFlow = sequenceFlowOpt.get();
// 线条的下一个节点
String targetRef = sequenceFlow.getTargetRef();
if (StringUtils.isNotBlank(targetRef)) {
// 下一个用户任务
List<BpmActModelUserTaskVo> nextList = new ArrayList<>();
BpmActModelUserTaskVo next = null;
if (targetRef.toUpperCase().contains("GATEWAY")) {
/* 线条的下一个节点是判断节点 */
this.findNextUserTaskByGateway(targetRef, userTaskList, sequenceFlowList,
exclusiveGatewayList, nextList);
} else if (targetRef.toUpperCase().contains("ACTIVITY")) {
/* 线条的下一个节点是用户任务节点 */
next = new BpmActModelUserTaskVo();
next.setActivityId(targetRef);
next.setActivityName(this.getUserTaskName(targetRef, userTaskList));
nextList.add(next);
}
bpmActModelUserTaskVo.setNextListSize(CollectionUtils.isEmpty(nextList) ? 0 : nextList.size());
bpmActModelUserTaskVo.setNextList(nextList);
} // end of if (StringUtils.isNotBlank(targetRef))
} // end fo if (sequenceFlowOpt.isPresent())
} // end of for (String userTaskOut : outgoing)
} // end of if (CollectionUtils.isNotEmpty(outgoing))
else {
bpmActModelUserTaskVo.setNextListSize(0);
}
result.add(bpmActModelUserTaskVo);
} // end of for (BpmnUserTaskVo userTask : userTaskList) 循环任务节点
// 按分支拆分任务链条
List<List<BpmActModelUserTaskVo>> splitList = BpmActModelUtils.splitUserTasks(fileName, result);
System.out.println("拆分后:" + JSON.toJSONString(splitList));
System.out.println("------ Success ------.总耗时:" + (System.currentTimeMillis() - startTime) + " 毫秒");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查找判断节点指向的用户任务
*
* @date 2024-10-16
* @param activityId 判断节点id
* @param userTaskList 用户任务节点
* @param sequenceFlowList 用户任务连接线条节点
* @param exclusiveGatewayList 判断条件节点
* @return BpmActModelUserTaskVo
* @since JDK 11
* @author wenjianhai
*/
private void findNextUserTaskByGateway(String activityId, List<BpmnUserTaskVo> userTaskList,
List<BpmnSequenceFlowVo> sequenceFlowList, List<BpmnExclusiveGatewayVo> exclusiveGatewayList,
List<BpmActModelUserTaskVo> resultList) {
if (StringUtils.isBlank(activityId)) {
return;
}
Optional<BpmnExclusiveGatewayVo> gatewayOpt = exclusiveGatewayList.stream()
.filter(g -> g.getId().equalsIgnoreCase(activityId)).findFirst();
if (gatewayOpt.isPresent()) {
BpmnExclusiveGatewayVo gateway = gatewayOpt.get();
// 判断节点的下一个节点
List<String> gateWayOutgoing = gateway.getOutgoing();
if (CollectionUtils.isNotEmpty(gateWayOutgoing)) {
// List<BpmActModelUserTaskVo> resultList = new ArrayList<>();
BpmActModelUserTaskVo resultVo = null;
for (String outgoing : gateWayOutgoing) {
/* 判断节点的下一个节点是线条(线条有可能有多条) */
Optional<BpmnSequenceFlowVo> sequenceFlowOpt = sequenceFlowList.stream()
.filter(f -> f.getId().equalsIgnoreCase(outgoing)).findFirst();
if (sequenceFlowOpt.isPresent()) {
// 线条
BpmnSequenceFlowVo sequenceFlow = sequenceFlowOpt.get();
// 判断条件表达式
BpmnConditionExpressionVo conditionExpression = sequenceFlow.getConditionExpression();
// 取 通过 的
if (conditionExpression == null || sequenceFlow.getName().contains("通过")
|| (StringUtils.isNotBlank(conditionExpression.getText())
&& conditionExpression.getText().contains("isPass==1"))) {
// 线条的下一个节点
String targetRef = sequenceFlow.getTargetRef();
if (StringUtils.isNotBlank(targetRef)) {
if (targetRef.toUpperCase().contains("GATEWAY")) {
// 线条的下一个节点是判断节点
findNextUserTaskByGateway(targetRef, userTaskList, sequenceFlowList,
exclusiveGatewayList, resultList);
} else if (targetRef.toUpperCase().contains("ACTIVITY")) {
// 线条的下一个节点是用户任务节点
String activityName = this.getUserTaskName(targetRef, userTaskList);
if (!activityName.contains("驳回")) {
resultVo = new BpmActModelUserTaskVo();
resultVo.setActivityId(targetRef);
resultVo.setActivityName(activityName);
resultList.add(resultVo);
}
}
}
}
} // end of if (sequenceFlowOpt.isPresent())
} // end of for (String outgoing : gateWayOutgoing)
// return resultList;
} // end of if (CollectionUtils.isNotEmpty(gateWayOutgoing))
} // end of if (gatewayOpt.isPresent())
// return null;
}
/**
* 获取用户任务名称
*
* @date 2024-10-16
* @param activityId 用户任务id
* @param serviceTaskList 服务任务节点
* @return String
* @since JDK 11
* @author wenjianhai
*/
private String getUserTaskName(String activityId, List<BpmnUserTaskVo> userTaskList) {
if (StringUtils.isBlank(activityId) || CollectionUtils.isEmpty(userTaskList)) {
return "";
}
Optional<BpmnUserTaskVo> opt = userTaskList.stream().filter(u -> u.getId().equalsIgnoreCase(activityId))
.findFirst();
BpmnUserTaskVo userTask = opt.orElse(null);
return userTask == null || userTask.getName() == null ? "" : userTask.getName();
}
/**
* 合并用户任务和服务任务
*
* @date 2024-10-17
* @param userTaskList 用户任务
* @param serviceTaskList 服务任务
* @return List
* @since JDK 11
* @author wenjianhai
*/
private List<BpmnUserTaskVo> mergeUserAndServiceTask(List<BpmnUserTaskVo> userTaskList,
List<BpmServiceTaskVo> serviceTaskList) {
if (CollectionUtils.isEmpty(userTaskList) && CollectionUtils.isEmpty(serviceTaskList)) {
return new ArrayList<>();
}
if (CollectionUtils.isNotEmpty(userTaskList) && CollectionUtils.isEmpty(serviceTaskList)) {
return userTaskList;
}
if (CollectionUtils.isEmpty(userTaskList) && CollectionUtils.isNotEmpty(serviceTaskList)) {
return serviceTaskList.stream().map(o -> {
BpmnUserTaskVo vo = new BpmnUserTaskVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(new ArrayList<>(Arrays.asList(o.getIncoming())));
return vo;
}).collect(Collectors.toList());
}
// System.out.println("取并集-开始。userTaskList:\r\n" +
// JSON.toJSONString(userTaskList));
// System.out.println("取并集-开始。serviceTaskList:\r\n" +
// JSON.toJSONString(serviceTaskList));
// 服务任务 转 用户任务
List<BpmnUserTaskVo> newUserTaskList = serviceTaskList.stream().map(o -> {
BpmnUserTaskVo vo = new BpmnUserTaskVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(new ArrayList<>(Arrays.asList(o.getIncoming())));
return vo;
}).collect(Collectors.toList());
// 取并集 userTaskList + serviceTaskList
List<BpmnUserTaskVo> result = Stream.concat(userTaskList.stream(), newUserTaskList.stream()).distinct()
.collect(Collectors.toList());
return result;
}
/**
* 合并用户任务和普通任务
*
* @date 2024-10-18
* @param userTaskList 用户任务
* @param normalTaskList 普通任务
* @return List
* @since JDK 11
* @author wenjianhai
*/
private List<BpmnUserTaskVo> mergeUserAndNormalTask(List<BpmnUserTaskVo> userTaskList,
List<BpmnNormalTaskVo> normalTaskList) {
if (CollectionUtils.isEmpty(userTaskList) && CollectionUtils.isEmpty(normalTaskList)) {
return new ArrayList<>();
}
if (CollectionUtils.isNotEmpty(userTaskList) && CollectionUtils.isEmpty(normalTaskList)) {
return userTaskList;
}
if (CollectionUtils.isEmpty(userTaskList) && CollectionUtils.isNotEmpty(normalTaskList)) {
return normalTaskList.stream().map(o -> {
BpmnUserTaskVo vo = new BpmnUserTaskVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(o.getIncoming());
vo.setOutgoing(o.getOutgoing());
return vo;
}).collect(Collectors.toList());
}
// 服务任务 转 用户任务
List<BpmnUserTaskVo> newUserTaskList = normalTaskList.stream().map(o -> {
BpmnUserTaskVo vo = new BpmnUserTaskVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(o.getIncoming());
vo.setOutgoing(o.getOutgoing());
return vo;
}).collect(Collectors.toList());
// 取并集 userTaskList + normalTaskList
List<BpmnUserTaskVo> result = Stream.concat(userTaskList.stream(), newUserTaskList.stream()).distinct()
.collect(Collectors.toList());
return result;
}
/**
* 合并判断条件、并行判断条件
*
* @date 2024-10-23
* @param exclusiveGatewayList 判断条件
* @param parallelGatewayList 并行判断条件
* @return List
* @since JDK 11
* @author wenjianhai
*/
private List<BpmnExclusiveGatewayVo> mergeGateway(List<BpmnExclusiveGatewayVo> exclusiveGatewayList,
List<BpmnParallelGatewayVo> parallelGatewayList) {
if (CollectionUtils.isEmpty(exclusiveGatewayList) && CollectionUtils.isEmpty(parallelGatewayList)) {
return new ArrayList<>();
}
if (CollectionUtils.isNotEmpty(exclusiveGatewayList) && CollectionUtils.isEmpty(parallelGatewayList)) {
return exclusiveGatewayList;
}
if (CollectionUtils.isEmpty(exclusiveGatewayList) && CollectionUtils.isNotEmpty(parallelGatewayList)) {
return parallelGatewayList.stream().map(o -> {
BpmnExclusiveGatewayVo vo = new BpmnExclusiveGatewayVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(o.getIncoming());
vo.setOutgoing(o.getOutgoing());
return vo;
}).collect(Collectors.toList());
}
// 并行判断条件 转 判断条件
List<BpmnExclusiveGatewayVo> newUserTaskList = parallelGatewayList.stream().map(o -> {
BpmnExclusiveGatewayVo vo = new BpmnExclusiveGatewayVo();
vo.setId(o.getId());
vo.setName(o.getName());
vo.setIncoming(o.getIncoming());
vo.setOutgoing(o.getOutgoing());
return vo;
}).collect(Collectors.toList());
// 取并集 userTaskList + serviceTaskList
List<BpmnExclusiveGatewayVo> result = Stream.concat(exclusiveGatewayList.stream(), newUserTaskList.stream())
.distinct().collect(Collectors.toList());
return result;
}
/**
* 将任务节点进行排序(按流程图设计的节点排序)
*
* @date 2024-10-22
* @param bpmnName 流程名称
* @param list 任务节点
* @since JDK 11
* @author wenjianhai
*/
private List<BpmnUserTaskVo> orderUserTasks(String bpmnName, List<BpmActModelUserTaskVo> list) {
if (CollectionUtils.isEmpty(list)) {
return null;
}
// 按 activityId 分组
Map<String, List<BpmActModelUserTaskVo>> map = list.stream().collect(
Collectors.groupingBy(BpmActModelUserTaskVo::getActivityId, LinkedHashMap::new, Collectors.toList()));
// 取第一个任务节点
List<BpmActModelUserTaskVo> firstTaskList = list.stream().filter(o -> o.getSort() != null && o.getSort() == 1)
.collect(Collectors.toList());
List<BpmnUserTaskVo> userTaskList = new ArrayList<>();
BpmnUserTaskVo userTask = null;
for (BpmActModelUserTaskVo firstTask : firstTaskList) {
/* 从第一个任务节点开始,第二个任务节点是第一个的子节点,第三个任务节点是第二个的子节点,... 依此类推 */
userTask = new BpmnUserTaskVo();
userTask.setId(firstTask.getActivityId());
userTask.setName(firstTask.getActivityName());
userTaskList.add(userTask);
// 第二个任务节点(是第一个的子节点)
List<BpmActModelUserTaskVo> secondList = firstTask.getNextList();
if (CollectionUtils.isNotEmpty(secondList)) {
/* 第二个任务节点是第一个的子节点,第三个任务节点是第二个的子节点,... 依此类推 */
List<BpmnUserTaskVo> childList = this.loopNextList(bpmnName, map, secondList);
if (CollectionUtils.isNotEmpty(childList)) {
userTaskList.addAll(childList);
}
}
}
return userTaskList;
}
/**
* 循环处理第2、3、4...任务节点
*
* @date 2024-10-23
* @param bpmnName 流程名称
* @param map 所有任务节点
* @param nextList 第2、3、4...任务节点
* @return List
* @since JDK 11
* @author wenjianhai
*/
private List<BpmnUserTaskVo> loopNextList(String bpmnName, Map<String, List<BpmActModelUserTaskVo>> map,
List<BpmActModelUserTaskVo> nextList) {
if (CollectionUtils.isEmpty(nextList)) {
return null;
}
List<BpmnUserTaskVo> userTaskList = new ArrayList<>();
BpmnUserTaskVo userTask = null;
for (BpmActModelUserTaskVo nextTask : nextList) {
userTask = new BpmnUserTaskVo();
userTask.setId(nextTask.getActivityId());
userTask.setName(nextTask.getActivityName());
userTaskList.add(userTask);
// 查找nextTask的子节点
List<BpmActModelUserTaskVo> childList = map.get(nextTask.getActivityId());
if (CollectionUtils.isNotEmpty(childList)) {
BpmActModelUserTaskVo child = childList.get(0);
// 下一个节点的下一个节点
List<BpmActModelUserTaskVo> nextChildList = child.getNextList();
if (CollectionUtils.isNotEmpty(nextChildList)) {
nextChildList = nextChildList.stream().filter(o -> !o.getActivityName().contains("驳回"))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(nextChildList)) {
// 递归调用,处理下一个节点的下一个节点
List<BpmnUserTaskVo> childExpList = this.loopNextList(bpmnName, map, nextChildList);
if (CollectionUtils.isNotEmpty(childExpList)) {
userTaskList.addAll(childExpList);
}
}
}
}
}
return userTaskList;
}
/**
* 按分支拆分任务链条
*
* @date 2024-10-24
* @param bpmnName 流程名称
* @param list 所有任务
* @return List
* @since JDK 11
* @author wenjianhai
*/
private List<List<BpmActModelUserTaskVo>> splitUserTasks(String bpmnName, List<BpmActModelUserTaskVo> list) {
if (CollectionUtils.isEmpty(list)) {
log.info("按分支拆分任务链条,所有任务为空");
return new ArrayList<>();
}
// 按 activityId 分组
Map<String, List<BpmActModelUserTaskVo>> map = list.stream().collect(
Collectors.groupingBy(BpmActModelUserTaskVo::getActivityId, LinkedHashMap::new, Collectors.toList()));
// 第一个任务节点
Optional<BpmActModelUserTaskVo> firstOpt = list.stream().filter(o -> o.getSort() != null && o.getSort() == 1)
.findFirst();
BpmActModelUserTaskVo firstTask = firstOpt.orElse(null);
if (firstTask == null) {
log.info("按分支拆分任务链条,第一个任务节点不存在.所有任务:{}", JSON.toJSONString(list));
return new ArrayList<>();
}
// 最终拆分结果
List<List<BpmActModelUserTaskVo>> resultList = new ArrayList<>();
// 用于保存当前任务节点及其之前的任务节点
List<BpmActModelUserTaskVo> initItemList = new ArrayList<>();
initItemList.add(firstTask);
recursionNextTask(resultList, firstTask, initItemList, map);
// log.info("allList:\r\n{}", JSON.toJSONString(resultList));
return resultList;
}
/**
* 根据当前任务节点递归拆分
*
* @date 2024-10-25
*
* @param result 拆分最终结果
* @param node 当前任务节点
* @param initItemList 用于保存当前任务节点及其之前的任务节点
* @param map 所有任务节点
* @since JDK 11
* @author wenjianhai
*/
private void recursionNextTask(List<List<BpmActModelUserTaskVo>> result, BpmActModelUserTaskVo node,
List<BpmActModelUserTaskVo> initItemList, Map<String, List<BpmActModelUserTaskVo>> map) {
if (node.getNextList() == null || node.getNextList().size() <= 0) {
/* 最后一个任务节点 */
result.add(initItemList);
return;
}
// node的下一个节点
List<BpmActModelUserTaskVo> nextList = node.getNextList();
for (int i = 0; i < nextList.size(); i++) {
BpmActModelUserTaskVo child = nextList.get(i);
BpmActModelUserTaskVo taskVo = map.get(child.getActivityId()).get(0);
child.setNextList(taskVo.getNextList());
List<BpmActModelUserTaskVo> cPath = new ArrayList<>();
cPath.addAll(initItemList);
cPath.add(child);
recursionNextTask(result, child, cPath, map);
}
}
private BpmnUserTaskVo changeToBpmnUserTask(BpmActModelUserTaskVo vo) {
if (vo == null) {
return null;
}
BpmnUserTaskVo userTask = new BpmnUserTaskVo();
userTask.setId(vo.getActivityId());
userTask.setName(vo.getActivityName());
return userTask;
}
}
4. xml
<?xml version="1.0" encoding="UTF-8"?>
<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" id="sample-diagram" targetNamespace="http://bpmn.io/schema/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">
<bpmn2:process id="P1849399003950776320" name="党建管理--测试流程20241024" isExecutable="false">
<bpmn2:startEvent id="Start" name="开始">
<bpmn2:outgoing>Flow_1ne0svl</bpmn2:outgoing>
</bpmn2:startEvent>
<bpmn2:sequenceFlow id="Flow_1ne0svl" sourceRef="Start" targetRef="Activity_176lx3w" />
<bpmn2:sequenceFlow id="Flow_1rcb5c9" sourceRef="Activity_0agsln1" targetRef="Activity_176lx3w" />
<bpmn2:endEvent id="Event_0ww3zjl" name="结束">
<bpmn2:incoming>Flow_1jlf6lk</bpmn2:incoming>
</bpmn2:endEvent>
<bpmn2:userTask id="Activity_176lx3w" name="施工单位审核" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:extensionElements>
<camunda:taskListener class="com.pit.jxh.bpm.listener.TaskAssigneeByBidListener" event="create" node="0" className="根据标段与角色过滤处理人">
<camunda:field name="taskListenerNodeAttr" stringValue="currentNodeListener" />
</camunda:taskListener>
</bpmn2:extensionElements>
<bpmn2:incoming>Flow_1ne0svl</bpmn2:incoming>
<bpmn2:incoming>Flow_1rcb5c9</bpmn2:incoming>
<bpmn2:outgoing>Flow_1fmkw2h</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:userTask id="Activity_0agsln1" name="驳回发起人" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:outgoing>Flow_1rcb5c9</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:userTask id="Activity_16gr09p" name="监理单位标段负责人审核">
<bpmn2:incoming>Flow_1fmkw2h</bpmn2:incoming>
<bpmn2:outgoing>Flow_162x4uh</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_1fmkw2h" sourceRef="Activity_176lx3w" targetRef="Activity_16gr09p" />
<bpmn2:exclusiveGateway id="Gateway_09feac3">
<bpmn2:incoming>Flow_162x4uh</bpmn2:incoming>
<bpmn2:outgoing>Flow_0pswyjf</bpmn2:outgoing>
<bpmn2:outgoing>Flow_0ef39iv</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="Flow_162x4uh" sourceRef="Activity_16gr09p" targetRef="Gateway_09feac3" />
<bpmn2:userTask id="Activity_067ipaf" name="监理单位执行总监--审核" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_0pswyjf</bpmn2:incoming>
<bpmn2:outgoing>Flow_1jdhsqm</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_0pswyjf" name="标段负责人->执行总监" sourceRef="Gateway_09feac3" targetRef="Activity_067ipaf">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${isPass==1 && code=="branch1"}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:userTask id="Activity_0j79q2e" name="监理单位执行总监--审批" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_0ef39iv</bpmn2:incoming>
<bpmn2:outgoing>Flow_1px82ag</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_0ef39iv" name="执行总监->总监" sourceRef="Gateway_09feac3" targetRef="Activity_0j79q2e">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${isPass==1 && code=="branch2"}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="Flow_1jdhsqm" sourceRef="Activity_067ipaf" targetRef="Activity_1htfic1" />
<bpmn2:userTask id="Activity_1htfic1" name="设计负责人转办" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_1jdhsqm</bpmn2:incoming>
<bpmn2:outgoing>Flow_0pcvprx</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:exclusiveGateway id="Gateway_1b45d7q">
<bpmn2:incoming>Flow_0pcvprx</bpmn2:incoming>
<bpmn2:outgoing>Flow_0yx4y87</bpmn2:outgoing>
<bpmn2:outgoing>Flow_08aqm45</bpmn2:outgoing>
</bpmn2:exclusiveGateway>
<bpmn2:sequenceFlow id="Flow_0pcvprx" sourceRef="Activity_1htfic1" targetRef="Gateway_1b45d7q" />
<bpmn2:userTask id="Activity_1ryfinq" name="项目法人建设现场指挥部负责人审核" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_0yx4y87</bpmn2:incoming>
<bpmn2:outgoing>Flow_1d6gvd8</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_0yx4y87" name="报指挥长" sourceRef="Gateway_1b45d7q" targetRef="Activity_1ryfinq">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${isPass==1 && code=="branch2"}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:sequenceFlow id="Flow_08aqm45" name="不报指挥长" sourceRef="Gateway_1b45d7q" targetRef="Activity_1jt3q4j">
<bpmn2:conditionExpression xsi:type="bpmn2:tFormalExpression">${isPass==1 && code=="branch1"}</bpmn2:conditionExpression>
</bpmn2:sequenceFlow>
<bpmn2:userTask id="Activity_1jt3q4j" name="施工单位签字盖章" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_08aqm45</bpmn2:incoming>
<bpmn2:incoming>Flow_1d6gvd8</bpmn2:incoming>
<bpmn2:outgoing>Flow_0boxtgp</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_1d6gvd8" sourceRef="Activity_1ryfinq" targetRef="Activity_1jt3q4j" />
<bpmn2:userTask id="Activity_1kutkrk" name="监理单位标段负责人签字" camunda:formKey="" camunda:assignee="" camunda:flowFormId="" camunda:formType="0">
<bpmn2:incoming>Flow_0boxtgp</bpmn2:incoming>
<bpmn2:incoming>Flow_0ir3eu3</bpmn2:incoming>
<bpmn2:outgoing>Flow_1jlf6lk</bpmn2:outgoing>
</bpmn2:userTask>
<bpmn2:sequenceFlow id="Flow_0boxtgp" sourceRef="Activity_1jt3q4j" targetRef="Activity_1kutkrk" />
<bpmn2:task id="Activity_1re6s41" name="监理单位总监审核" camunda:flowFormId="" camunda:formKey="" camunda:formType="0" camunda:assignee="">
<bpmn2:incoming>Flow_1px82ag</bpmn2:incoming>
<bpmn2:outgoing>Flow_0ir3eu3</bpmn2:outgoing>
</bpmn2:task>
<bpmn2:sequenceFlow id="Flow_1px82ag" sourceRef="Activity_0j79q2e" targetRef="Activity_1re6s41" />
<bpmn2:sequenceFlow id="Flow_1jlf6lk" sourceRef="Activity_1kutkrk" targetRef="Event_0ww3zjl" />
<bpmn2:sequenceFlow id="Flow_0ir3eu3" sourceRef="Activity_1re6s41" targetRef="Activity_1kutkrk" />
</bpmn2:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="P1849399003950776320">
<bpmndi:BPMNEdge id="Flow_0ir3eu3_di" bpmnElement="Flow_0ir3eu3">
<di:waypoint x="1220" y="390" />
<di:waypoint x="1620" y="390" />
<di:waypoint x="1620" y="298" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1jlf6lk_di" bpmnElement="Flow_1jlf6lk">
<di:waypoint x="1670" y="258" />
<di:waypoint x="1732" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1px82ag_di" bpmnElement="Flow_1px82ag">
<di:waypoint x="1040" y="390" />
<di:waypoint x="1120" y="390" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0boxtgp_di" bpmnElement="Flow_0boxtgp">
<di:waypoint x="1510" y="258" />
<di:waypoint x="1570" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1d6gvd8_di" bpmnElement="Flow_1d6gvd8">
<di:waypoint x="1350" y="140" />
<di:waypoint x="1460" y="140" />
<di:waypoint x="1460" y="218" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_08aqm45_di" bpmnElement="Flow_08aqm45">
<di:waypoint x="1325" y="258" />
<di:waypoint x="1410" y="258" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1341" y="240" width="54" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0yx4y87_di" bpmnElement="Flow_0yx4y87">
<di:waypoint x="1300" y="233" />
<di:waypoint x="1300" y="180" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1294" y="204" width="43" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0pcvprx_di" bpmnElement="Flow_0pcvprx">
<di:waypoint x="1220" y="258" />
<di:waypoint x="1275" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1jdhsqm_di" bpmnElement="Flow_1jdhsqm">
<di:waypoint x="1040" y="258" />
<di:waypoint x="1120" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ef39iv_di" bpmnElement="Flow_0ef39iv">
<di:waypoint x="850" y="283" />
<di:waypoint x="850" y="390" />
<di:waypoint x="940" y="390" />
<bpmndi:BPMNLabel>
<dc:Bounds x="827" y="334" width="77" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0pswyjf_di" bpmnElement="Flow_0pswyjf">
<di:waypoint x="875" y="258" />
<di:waypoint x="940" y="258" />
<bpmndi:BPMNLabel>
<dc:Bounds x="879" y="240" width="58" height="27" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_162x4uh_di" bpmnElement="Flow_162x4uh">
<di:waypoint x="770" y="258" />
<di:waypoint x="825" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1fmkw2h_di" bpmnElement="Flow_1fmkw2h">
<di:waypoint x="620" y="258" />
<di:waypoint x="670" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1rcb5c9_di" bpmnElement="Flow_1rcb5c9">
<di:waypoint x="570" y="180" />
<di:waypoint x="570" y="218" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ne0svl_di" bpmnElement="Flow_1ne0svl">
<di:waypoint x="448" y="258" />
<di:waypoint x="520" y="258" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="Start">
<dc:Bounds x="412" y="240" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="419" y="283" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0ww3zjl_di" bpmnElement="Event_0ww3zjl">
<dc:Bounds x="1732" y="240" width="36" height="36" />
<bpmndi:BPMNLabel>
<dc:Bounds x="1739" y="283" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0xgybj1_di" bpmnElement="Activity_176lx3w">
<dc:Bounds x="520" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0wf257m_di" bpmnElement="Activity_0agsln1">
<dc:Bounds x="520" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0wqior5_di" bpmnElement="Activity_16gr09p">
<dc:Bounds x="670" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_09feac3_di" bpmnElement="Gateway_09feac3" isMarkerVisible="true">
<dc:Bounds x="825" y="233" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_14xkqqt_di" bpmnElement="Activity_067ipaf">
<dc:Bounds x="940" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1tqjipe_di" bpmnElement="Activity_0j79q2e">
<dc:Bounds x="940" y="350" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_18aa89r_di" bpmnElement="Activity_1htfic1">
<dc:Bounds x="1120" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1b45d7q_di" bpmnElement="Gateway_1b45d7q" isMarkerVisible="true">
<dc:Bounds x="1275" y="233" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1kf6ios_di" bpmnElement="Activity_1ryfinq">
<dc:Bounds x="1250" y="100" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1lc44vr_di" bpmnElement="Activity_1jt3q4j">
<dc:Bounds x="1410" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1xogvs5_di" bpmnElement="Activity_1kutkrk">
<dc:Bounds x="1570" y="218" width="100" height="80" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1re6s41_di" bpmnElement="Activity_1re6s41">
<dc:Bounds x="1120" y="350" width="100" height="80" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn2:definitions>
5. 开始节点 BpmnStartEventVo
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】开始节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:startEvent </p>
* @author
* @date 2024/10/22 星期二
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnStartEventVo implements Serializable {
private static final long serialVersionUID = 3888622178629765729L;
/** 节点id(bpmn2:startEvent 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 节点名称(bpmn2:startEvent 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 下一个节点id(bpmn2:startEvent 下的 bpmn2:outgoing) */
@JSONField(ordinal = 3)
private String outgoing;
}
6. 普通任务 BpmnNormalTaskVo
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】普通任务
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:task </p>
* @author
* @date 2024/10/18 星期五
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnNormalTaskVo implements Serializable {
private static final long serialVersionUID = 592582132356281705L;
/** 当前节点id(bpmn2:task 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 用户任务名称(bpmn2:task 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:task 下的 bpmn2:incoming) */
@JSONField(ordinal = 3)
private List<String> incoming;
/** 下一个节点id(bpmn2:task 下的 bpmn2:outgoing) */
@JSONField(ordinal = 4)
private List<String> outgoing;
}
7. 用户任务
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】用户任务
* <p> 表bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:userTask </p>
* @author
* @date 2024/10/14 星期一
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnUserTaskVo implements Serializable {
private static final long serialVersionUID = -3841909171453342855L;
/** 当前节点id(bpmn2:userTask 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 用户任务名称(bpmn2:userTask 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:userTask 下的 bpmn2:incoming) */
@JSONField(ordinal = 3)
private List<String> incoming;
/** 下一个节点id(bpmn2:userTask 下的 bpmn2:outgoing) */
@JSONField(ordinal = 4)
private List<String> outgoing;
}
8. 服务任务 BpmServiceTaskVo
import java.io.Serializable;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】服务任务
* <p> 表bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:serviceTask </p>
* @author
* @date 2024/10/16 星期三
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmServiceTaskVo implements Serializable {
private static final long serialVersionUID = -799632822699624427L;
/** 当前节点id(bpmn2:serviceTask 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 当前节点名称(bpmn2:serviceTask 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:serviceTask 下的 bpmn2:incoming) */
@JSONField(ordinal = 3)
private String incoming;
}
9. 连接线条 BpmnSequenceFlowVo
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
/**
* 【流程图设计-节点】连接线条
* <p> 表bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:sequenceFlow </p>
* @author
* @date 2024/10/14 星期一
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnSequenceFlowVo implements Serializable {
private static final long serialVersionUID = 4634614840831021276L;
/** 线条id(bpmn2:sequenceFlow 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 线条名称(bpmn2:sequenceFlow 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:sequenceFlow 下的 sourceRef) */
@JSONField(ordinal = 3)
private String sourceRef;
/** 下一个节点id(bpmn2:sequenceFlow 下的 targetRef) */
@JSONField(ordinal = 4)
private String targetRef;
/** 判断条件表达式 */
@JSONField(ordinal = 5)
private BpmnConditionExpressionVo conditionExpression;
}
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import java.io.Serializable;
/**
* 【流程图设计-节点】连接线条--判断条件表达式
* @author
* @date 2024/10/14 星期一
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnConditionExpressionVo implements Serializable {
private static final long serialVersionUID = -2436384159645412119L;
/** 判断条件表达式 */
private String text;
private String type;
private String xsi;
}
10. 判断条件 BpmnExclusiveGatewayVo
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】判断条件
* <p> 表bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:exclusiveGateway </p>
* @author
* @date 2024/10/14 星期一
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnExclusiveGatewayVo implements Serializable {
private static final long serialVersionUID = 1129041486109521972L;
/** 判断节点id(bpmn2:exclusiveGateway 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 判断节点名称(bpmn2:exclusiveGateway 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:exclusiveGateway 下的 bpmn2:incoming) */
@JSONField(ordinal = 3)
private List<String> incoming;
/** 下一个节点id(bpmn2:exclusiveGateway 下的 bpmn2:outgoing) */
@JSONField(ordinal = 4)
private List<String> outgoing;
}
11. 并行判断条件 BpmnParallelGatewayVo
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】并行判断条件
* <p> 表bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:parallelGateway </p>
* @author
* @date 2024/10/23 星期三
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnParallelGatewayVo implements Serializable {
/* 设计计量支付-环水保 中存在 bpmn2:parallelGateway */
private static final long serialVersionUID = 7687664845656893652L;
/** 判断节点id(bpmn2:parallelGateway 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 判断节点名称(bpmn2:parallelGateway 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:parallelGateway 下的 bpmn2:incoming) */
@JSONField(ordinal = 3)
private List<String> incoming;
/** 下一个节点id(bpmn2:parallelGateway 下的 bpmn2:outgoing) */
@JSONField(ordinal = 4)
private List<String> outgoing;
}
12. 结束节点 BpmnEndEventVo
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 【流程图设计-节点】结束节点
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:endEvent </p>
* @author
* @date 2024/10/22 星期二
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmnEndEventVo implements Serializable {
private static final long serialVersionUID = 301741732562467606L;
/** 节点id(bpmn2:startEvent 下的 id) */
@JSONField(ordinal = 1)
private String id;
/** 节点名称(bpmn2:startEvent 下的 name) */
@JSONField(ordinal = 2)
private String name;
/** 上一个节点id(bpmn2:startEvent 下的 bpmn2:outgoing) */
@JSONField(ordinal = 3)
private List<String> incoming;
}
13. 任务节点及之间的关系 bpmActModelUserTaskVo
import java.io.Serializable;
import java.util.List;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* 流程图设计用户任务节点信息
* <p> 表 bpm_act_model_info 的字段 bpmn_xml 中的 bpmn2:userTask </p>
* @author
* @date 2024/10/16 星期三
* @since JDK 11
*/
@Data
@EqualsAndHashCode(callSuper = false)
@ToString(callSuper = true)
public class BpmActModelUserTaskVo implements Serializable {
private static final long serialVersionUID = 1320297636620632310L;
/** 当前用户任务节点id */
@JSONField(ordinal = 1)
private String activityId;
/** 当前用户任务节点名称 */
@JSONField(ordinal = 2)
private String activityName;
/** 排序 */
@JSONField(ordinal = 3)
private Integer sort;
/** 子节点长度(即 nextList.size()) */
@JSONField(ordinal = 4)
private Integer nextListSize;
/** 下一个用户任务节点 */
@JSONField(ordinal = 5)
private List<BpmActModelUserTaskVo> nextList;
}
14. 解析xml后的节点前后关系
[
{
"activityId": "Activity_176lx3w",
"activityName": "施工单位审核",
"sort": 1,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_0agsln1",
"activityName": "驳回发起人",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_176lx3w",
"activityName": "施工单位审核",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": 2,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": []
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": 2,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": []
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": 0,
"nextList": []
},
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
15. 拆分后的流程任务链路
[
[
{
"activityId": "Activity_176lx3w",
"activityName": "施工单位审核",
"sort": 1,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
}
]
},
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
],
[
{
"activityId": "Activity_176lx3w",
"activityName": "施工单位审核",
"sort": 1,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
}
]
},
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
],
[
{
"activityId": "Activity_176lx3w",
"activityName": "施工单位审核",
"sort": 1,
"nextListSize": 1,
"nextList": [
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
}
]
},
{
"activityId": "Activity_16gr09p",
"activityName": "监理单位标段负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_067ipaf",
"activityName": "监理单位执行总监--审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1htfic1",
"activityName": "设计负责人转办",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1ryfinq",
"activityName": "项目法人建设现场指挥部负责人审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1jt3q4j",
"activityName": "施工单位签字盖章",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
}
]
},
{
"activityId": "Activity_0j79q2e",
"activityName": "监理单位执行总监--审批",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
}
]
},
{
"activityId": "Activity_1re6s41",
"activityName": "监理单位总监审核",
"sort": null,
"nextListSize": null,
"nextList": [
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
},
{
"activityId": "Activity_1kutkrk",
"activityName": "监理单位标段负责人签字",
"sort": null,
"nextListSize": null,
"nextList": []
}
]
]