背景
在流程设计器里面画完图之后,后端从xml的BpmnModel解析出来的任务节点列表,其顺序是画图时节点的创建顺序。而绝大多数情况下,我们期望的任务节点顺序是流程执行顺序。这就需要进行一次排序。
排序
思路
- 拿到开始节点
- 通过开始节点的出口箭头拿到下一个节点
- 重复上面的动作,拿到下一个节点的目标节点,直到流程图结束
/**
* 获取所有排序后的用户任务节点
*
* @param model bpmnModel对象
* @return 用户任务节点列表
*/
public static List<UserTask> getAllSortedUserTaskEvent(BpmnModel model) {
final Collection<UserTask> userTasks = getAllUserTaskEvent(model);
return sortTasksByFlowPath(getStartEvent(model), userTasks);
}
/**
* 获取开始节点
*
* @param model bpmnModel对象
* @return 开始节点(未找到开始节点,返回null)
*/
public static StartEvent getStartEvent(BpmnModel model) {
Process process = model.getMainProcess();
FlowElement startElement = process.getInitialFlowElement();
if (startElement instanceof StartEvent) {
return (StartEvent) startElement;
}
return getStartEvent(process.getFlowElements());
}
/**
* 获取所有用户任务节点
*
* @param model bpmnModel对象
* @return 用户任务节点列表
*/
public static Collection<UserTask> getAllUserTaskEvent(BpmnModel model) {
Process process = model.getMainProcess();
Collection<FlowElement> flowElements = process.getFlowElements();
return getAllUserTaskEvent(flowElements, null);
}
/**
* 获取所有用户任务节点
* @param flowElements 流程元素集合
* @param allElements 所有流程元素集合
* @return 用户任务节点列表
*/
public static Collection<UserTask> getAllUserTaskEvent(Collection<FlowElement> flowElements, Collection<UserTask> allElements) {
allElements = allElements == null ? new ArrayList<>() : allElements;
for (FlowElement flowElement : flowElements) {
if (flowElement instanceof UserTask) {
allElements.add((UserTask) flowElement);
}
if (flowElement instanceof SubProcess) {
// 继续深入子流程,进一步获取子流程
allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements);
}
}
return allElements;
}
/**
* 对任务节点按照流程图顺序排序
* @param startEvent 开始节点(根节点)
* @param userTasks 所有任务节点
* @return
*/
public static List<UserTask> sortTasksByFlowPath(StartEvent startEvent, Collection<UserTask> userTasks) {
List<UserTask> sorted = new ArrayList<>(userTasks.size());
FlowElement next = startEvent.getOutgoingFlows().get(0).getTargetFlowElement();
if (next == null) {
throw new ServiceException("流程图开始节点未找到目标节点");
}
// 第一个节点
sorted.add((UserTask) next);
boolean end = false;
while (!end) {
next = findTargetNode(next);
if (next instanceof Gateway) {
continue;
}
if (next instanceof EndEvent) {
end = true;
continue;
}
if (next == null) {
end = true;
} else {
sorted.add((UserTask) next);
}
}
return sorted;
}
/**
* 查找节点在工作流中的下一个节点。如遇到网关有多个出口路径,则只搜索条件表达式包含‘pass’的路径。
*
* @param node
* @return
*/
private static FlowElement findTargetNode(FlowElement node) {
if (node instanceof Gateway) {
SequenceFlow passWay = ((Gateway) node).getOutgoingFlows().stream()
.filter(flow -> flow.getConditionExpression().contains("pass"))
.findFirst()
.orElseThrow(()-> new ServiceException("网关未正确配置流转条件"));
return passWay.getTargetFlowElement();
}
if (node instanceof EndEvent) {
return null;
}
if (node == null) {
return null;
}
final UserTask userTask = (UserTask) node;
// 用户任务节点默认只支持一个出口路径
return userTask.getOutgoingFlows().get(0).getTargetFlowElement();
}
/**
* xml转bpmnModel对象
*
* @param xml xml
* @return bpmnModel对象
*/
public static BpmnModel getBpmnModel(String xml) {
return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false);
}
public static void main(String[] args) {
String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>.......";
final BpmnModel bpmnModel = getBpmnModel(xml);
final Collection<UserTask> sortedUserTaskEvent = getAllSortedUserTaskEvent(bpmnModel);
for (UserTask userTask : sortedUserTaskEvent) {
System.out.println(userTask.getName());
}
}