fix -- 采用DFS深搜获取流程未通过的节点(修复流程跟踪查询死循环问题)

This commit is contained in:
konbai
2022-04-14 00:39:23 +08:00
parent 1b8a2ebe51
commit cf30d29f9b
2 changed files with 118 additions and 45 deletions

View File

@@ -1,5 +1,7 @@
package com.ruoyi.flowable.flow;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.*;
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
@@ -586,4 +588,117 @@ public class FlowableUtils {
log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList);
return lastHistoricTaskInstanceList;
}
/**
* 深搜递归获取流程未通过的节点
* @param bpmnModel 流程模型
* @param unfinishedTaskSet 未结束的任务节点
* @param finishedSequenceFlowSet 已经完成的连线
* @param finishedTaskSet 已完成的任务节点
* @return
*/
public static Set<String> dfsFindRejects(BpmnModel bpmnModel, Set<String> unfinishedTaskSet, Set<String> finishedSequenceFlowSet, Set<String> finishedTaskSet) {
if (ObjectUtil.isNull(bpmnModel)) {
throw new ServiceException("流程模型不存在");
}
Collection<FlowElement> allElements = getAllElements(bpmnModel.getMainProcess().getFlowElements(), null);
Set<String> rejectedSet = new HashSet<>();
for (FlowElement flowElement : allElements) {
// 用户节点且未结束元素
if (flowElement instanceof UserTask && unfinishedTaskSet.contains(flowElement.getId())) {
List<String> hasSequenceFlow = iteratorFindFinishes(flowElement, null);
List<String> rejects = iteratorFindRejects(flowElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, null);
rejectedSet.addAll(rejects);
}
}
return rejectedSet;
}
/**
* 迭代获取父级节点列表,向前找
* @param source 起始节点
* @param hasSequenceFlow 已经经过的连线的ID用于判断线路是否重复
* @return
*/
public static List<String> iteratorFindFinishes(FlowElement source, List<String> hasSequenceFlow) {
hasSequenceFlow = hasSequenceFlow == null ? new ArrayList<>() : hasSequenceFlow;
// 根据类型,获取入口连线
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
FlowElement finishedElement = sequenceFlow.getSourceFlowElement();
// 类型为子流程,则添加子流程开始节点出口处相连的节点
if (finishedElement instanceof SubProcess) {
FlowElement firstElement = (StartEvent) ((SubProcess) finishedElement).getFlowElements().toArray()[0];
// 获取子流程的连线
hasSequenceFlow.addAll(iteratorFindFinishes(firstElement, null));
}
// 继续迭代
hasSequenceFlow = iteratorFindFinishes(finishedElement, hasSequenceFlow);
}
}
return hasSequenceFlow;
}
/**
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
* @param source 起始节点
* @param finishedSequenceFlowSet 已经完成的连线
* @param finishedTaskSet 已经完成的任务节点
* @param hasSequenceFlow 已经经过的连线的 ID用于判断线路是否重复
* @param rejectedList 未通过的元素
* @return
*/
public static List<String> iteratorFindRejects(FlowElement source, Set<String> finishedSequenceFlowSet, Set<String> finishedTaskSet
, List<String> hasSequenceFlow, List<String> rejectedList) {
hasSequenceFlow = hasSequenceFlow == null ? new ArrayList<>() : hasSequenceFlow;
rejectedList = rejectedList == null ? new ArrayList<>() : rejectedList;
// 根据类型,获取出口连线
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
if (sequenceFlows != null) {
// 循环找到目标元素
for (SequenceFlow sequenceFlow: sequenceFlows) {
// 如果发现连线重复,说明循环了,跳过这个循环
if (hasSequenceFlow.contains(sequenceFlow.getId())) {
continue;
}
// 添加已经走过的连线
hasSequenceFlow.add(sequenceFlow.getId());
FlowElement targetElement = sequenceFlow.getTargetFlowElement();
// 添加未完成的节点
if (finishedTaskSet.contains(targetElement.getId())) {
rejectedList.add(targetElement.getId());
}
// 添加未完成的连线
if (finishedSequenceFlowSet.contains(sequenceFlow.getId())) {
rejectedList.add(sequenceFlow.getId());
}
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
if (targetElement instanceof SubProcess) {
FlowElement firstElement = (FlowElement) (((SubProcess) targetElement).getFlowElements().toArray()[0]);
List<String> childList = iteratorFindRejects(firstElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, null);
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
if (childList != null && childList.size() > 0) {
rejectedList.addAll(childList);
continue;
}
}
// 继续迭代
rejectedList = iteratorFindRejects(targetElement, finishedSequenceFlowSet, finishedTaskSet, hasSequenceFlow, rejectedList);
}
}
return rejectedList;
}
}

View File

@@ -2,7 +2,6 @@ package com.ruoyi.workflow.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -726,26 +725,6 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
// 获取流程发布Id信息
String processDefinitionId = allActivityInstanceList.get(0).getProcessDefinitionId();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
if (ObjectUtil.isNull(bpmnModel)) {
throw new ServiceException("流程模型不存在");
}
Map<String, List<String>> sequenceElementMap = new HashMap<>();
Map<String, String> sequenceFlowMap = new HashMap<>();
Collection<FlowElement> flowElements = bpmnModel.getMainProcess().getFlowElements();
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof SequenceFlow) {
SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
String sourceRef = sequenceFlow.getSourceRef();
String targetRef = sequenceFlow.getTargetRef();
List<String> targetRefList = sequenceElementMap.get(sourceRef);
if (CollUtil.isEmpty(targetRefList)) {
sequenceElementMap.put(sourceRef, ListUtil.toList(targetRef));
} else {
targetRefList.add(targetRef);
}
sequenceFlowMap.put(sourceRef + targetRef, sequenceFlow.getId());
}
}
// 查询所有已完成的元素
List<HistoricActivityInstance> finishedElementList = allActivityInstanceList.stream()
.filter(item -> ObjectUtil.isNotNull(item.getEndTime())).collect(Collectors.toList());
@@ -765,30 +744,9 @@ public class WfTaskServiceImpl extends FlowServiceFactory implements IWfTaskServ
.filter(item -> ObjectUtil.isNull(item.getEndTime()))
.map(HistoricActivityInstance::getActivityId)
.collect(Collectors.toSet());
Set<String> rejectedTaskSet = new LinkedHashSet<>();
Set<String> sourceTaskSet = unfinishedTaskSet;
while (CollUtil.isNotEmpty(sourceTaskSet)) {
Set<String> nextIdSet = new HashSet<>();
for (String previousId : sourceTaskSet) {
List<String> nextIdList = sequenceElementMap.get(previousId);
if (CollUtil.isEmpty(nextIdList)) {
continue;
}
nextIdList.forEach(nextId -> {
if (finishedTaskSet.contains(nextId)) {
nextIdSet.add(nextId);
String rejectedSequenceFlow = sequenceFlowMap.get(previousId + nextId);
if (finishedSequenceFlowSet.contains(rejectedSequenceFlow)) {
nextIdSet.add(rejectedSequenceFlow);
}
}
});
}
rejectedTaskSet.addAll(nextIdSet);
sourceTaskSet = nextIdSet;
}
return new WfViewerVo(finishedTaskSet, finishedSequenceFlowSet, unfinishedTaskSet, rejectedTaskSet);
// DFS 查询未通过的元素集合
Set<String> rejectedSet = FlowableUtils.dfsFindRejects(bpmnModel, unfinishedTaskSet, finishedSequenceFlowSet, finishedTaskSet);
return new WfViewerVo(finishedTaskSet, finishedSequenceFlowSet, unfinishedTaskSet, rejectedSet);
}
/**