提交 | 用户 | 时间
|
e7c126
|
1 |
package com.iailab.module.bpm.framework.flowable.core.util; |
H |
2 |
|
|
3 |
import cn.hutool.core.collection.CollUtil; |
|
4 |
import cn.hutool.core.util.ArrayUtil; |
|
5 |
import com.iailab.framework.common.util.number.NumberUtils; |
|
6 |
import com.iailab.module.bpm.framework.flowable.core.enums.BpmnModelConstants; |
|
7 |
import org.flowable.bpmn.converter.BpmnXMLConverter; |
|
8 |
import org.flowable.bpmn.model.Process; |
|
9 |
import org.flowable.bpmn.model.*; |
|
10 |
import org.flowable.common.engine.impl.util.io.BytesStreamSource; |
|
11 |
|
|
12 |
import java.util.ArrayList; |
|
13 |
import java.util.HashSet; |
|
14 |
import java.util.List; |
|
15 |
import java.util.Set; |
|
16 |
|
|
17 |
/** |
|
18 |
* 流程模型转操作工具类 |
|
19 |
*/ |
|
20 |
public class BpmnModelUtils { |
|
21 |
|
|
22 |
public static Integer parseCandidateStrategy(FlowElement userTask) { |
|
23 |
return NumberUtils.parseInt(userTask.getAttributeValue( |
|
24 |
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); |
|
25 |
} |
|
26 |
|
|
27 |
public static String parseCandidateParam(FlowElement userTask) { |
|
28 |
return userTask.getAttributeValue( |
|
29 |
BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); |
|
30 |
} |
|
31 |
|
|
32 |
/** |
|
33 |
* 根据节点,获取入口连线 |
|
34 |
* |
|
35 |
* @param source 起始节点 |
|
36 |
* @return 入口连线列表 |
|
37 |
*/ |
|
38 |
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) { |
|
39 |
if (source instanceof FlowNode) { |
|
40 |
return ((FlowNode) source).getIncomingFlows(); |
|
41 |
} |
|
42 |
return new ArrayList<>(); |
|
43 |
} |
|
44 |
|
|
45 |
/** |
|
46 |
* 根据节点,获取出口连线 |
|
47 |
* |
|
48 |
* @param source 起始节点 |
|
49 |
* @return 出口连线列表 |
|
50 |
*/ |
|
51 |
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) { |
|
52 |
if (source instanceof FlowNode) { |
|
53 |
return ((FlowNode) source).getOutgoingFlows(); |
|
54 |
} |
|
55 |
return new ArrayList<>(); |
|
56 |
} |
|
57 |
|
|
58 |
/** |
|
59 |
* 获取流程元素信息 |
|
60 |
* |
|
61 |
* @param model bpmnModel 对象 |
|
62 |
* @param flowElementId 元素 ID |
|
63 |
* @return 元素信息 |
|
64 |
*/ |
|
65 |
public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { |
|
66 |
Process process = model.getMainProcess(); |
|
67 |
return process.getFlowElement(flowElementId); |
|
68 |
} |
|
69 |
|
|
70 |
/** |
|
71 |
* 获得 BPMN 流程中,指定的元素们 |
|
72 |
* |
|
73 |
* @param model 模型 |
|
74 |
* @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 |
|
75 |
* @return 元素们 |
|
76 |
*/ |
|
77 |
public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) { |
|
78 |
List<T> result = new ArrayList<>(); |
|
79 |
model.getProcesses().forEach(process -> { |
|
80 |
process.getFlowElements().forEach(flowElement -> { |
|
81 |
if (flowElement.getClass().isAssignableFrom(clazz)) { |
|
82 |
result.add((T) flowElement); |
|
83 |
} |
|
84 |
}); |
|
85 |
}); |
|
86 |
return result; |
|
87 |
} |
|
88 |
|
|
89 |
public static StartEvent getStartEvent(BpmnModel model) { |
|
90 |
Process process = model.getMainProcess(); |
|
91 |
// 从 initialFlowElement 找 |
|
92 |
FlowElement startElement = process.getInitialFlowElement(); |
|
93 |
if (startElement instanceof StartEvent) { |
|
94 |
return (StartEvent) startElement; |
|
95 |
} |
|
96 |
// 从 flowElementList 找 |
|
97 |
return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); |
|
98 |
} |
|
99 |
|
|
100 |
public static BpmnModel getBpmnModel(byte[] bpmnBytes) { |
|
101 |
if (ArrayUtil.isEmpty(bpmnBytes)) { |
|
102 |
return null; |
|
103 |
} |
|
104 |
BpmnXMLConverter converter = new BpmnXMLConverter(); |
|
105 |
// 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false |
|
106 |
return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); |
|
107 |
} |
|
108 |
|
|
109 |
public static String getBpmnXml(BpmnModel model) { |
|
110 |
if (model == null) { |
|
111 |
return null; |
|
112 |
} |
|
113 |
BpmnXMLConverter converter = new BpmnXMLConverter(); |
|
114 |
return new String(converter.convertToXML(model)); |
|
115 |
} |
|
116 |
|
|
117 |
// ========== 遍历相关的方法 ========== |
|
118 |
|
|
119 |
/** |
|
120 |
* 找到 source 节点之前的所有用户任务节点 |
|
121 |
* |
|
122 |
* @param source 起始节点 |
|
123 |
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|
124 |
* @param userTaskList 已找到的用户任务节点 |
|
125 |
* @return 用户任务节点 数组 |
|
126 |
*/ |
|
127 |
public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { |
|
128 |
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; |
|
129 |
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|
130 |
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 |
|
131 |
if (source instanceof StartEvent && source.getSubProcess() != null) { |
|
132 |
userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); |
|
133 |
} |
|
134 |
|
|
135 |
// 根据类型,获取入口连线 |
|
136 |
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source); |
|
137 |
if (sequenceFlows == null) { |
|
138 |
return userTaskList; |
|
139 |
} |
|
140 |
// 循环找到目标元素 |
|
141 |
for (SequenceFlow sequenceFlow : sequenceFlows) { |
|
142 |
// 如果发现连线重复,说明循环了,跳过这个循环 |
|
143 |
if (hasSequenceFlow.contains(sequenceFlow.getId())) { |
|
144 |
continue; |
|
145 |
} |
|
146 |
// 添加已经走过的连线 |
|
147 |
hasSequenceFlow.add(sequenceFlow.getId()); |
|
148 |
// 类型为用户节点,则新增父级节点 |
|
149 |
if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { |
|
150 |
userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); |
|
151 |
} |
|
152 |
// 类型为子流程,则添加子流程开始节点出口处相连的节点 |
|
153 |
if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { |
|
154 |
// 获取子流程用户任务节点 |
|
155 |
List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); |
|
156 |
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 |
|
157 |
if (CollUtil.isNotEmpty(childUserTaskList)) { |
|
158 |
userTaskList.addAll(childUserTaskList); |
|
159 |
} |
|
160 |
} |
|
161 |
// 继续迭代 |
|
162 |
userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); |
|
163 |
} |
|
164 |
return userTaskList; |
|
165 |
} |
|
166 |
|
|
167 |
/** |
|
168 |
* 迭代获取子流程用户任务节点 |
|
169 |
* |
|
170 |
* @param source 起始节点 |
|
171 |
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|
172 |
* @param userTaskList 需要撤回的用户任务列表 |
|
173 |
* @return 用户任务节点 |
|
174 |
*/ |
|
175 |
public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { |
|
176 |
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|
177 |
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; |
|
178 |
|
|
179 |
// 根据类型,获取出口连线 |
|
180 |
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source); |
|
181 |
if (sequenceFlows == null) { |
|
182 |
return userTaskList; |
|
183 |
} |
|
184 |
// 循环找到目标元素 |
|
185 |
for (SequenceFlow sequenceFlow : sequenceFlows) { |
|
186 |
// 如果发现连线重复,说明循环了,跳过这个循环 |
|
187 |
if (hasSequenceFlow.contains(sequenceFlow.getId())) { |
|
188 |
continue; |
|
189 |
} |
|
190 |
// 添加已经走过的连线 |
|
191 |
hasSequenceFlow.add(sequenceFlow.getId()); |
|
192 |
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 |
|
193 |
if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { |
|
194 |
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); |
|
195 |
continue; |
|
196 |
} |
|
197 |
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 |
|
198 |
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { |
|
199 |
List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); |
|
200 |
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 |
|
201 |
if (CollUtil.isNotEmpty(childUserTaskList)) { |
|
202 |
userTaskList.addAll(childUserTaskList); |
|
203 |
continue; |
|
204 |
} |
|
205 |
} |
|
206 |
// 继续迭代 |
|
207 |
userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); |
|
208 |
} |
|
209 |
return userTaskList; |
|
210 |
} |
|
211 |
|
|
212 |
|
|
213 |
/** |
|
214 |
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 |
|
215 |
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 |
|
216 |
* |
|
217 |
* @param source 起始节点 |
|
218 |
* @param target 目标节点 |
|
219 |
* @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 |
|
220 |
* @return 结果 |
|
221 |
*/ |
|
222 |
public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) { |
|
223 |
visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; |
|
224 |
// 不能是开始事件和子流程 |
|
225 |
if (source instanceof StartEvent && isInEventSubprocess(source)) { |
|
226 |
return false; |
|
227 |
} |
|
228 |
|
|
229 |
// 根据类型,获取入口连线 |
|
230 |
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source); |
|
231 |
if (CollUtil.isEmpty(sequenceFlows)) { |
|
232 |
return true; |
|
233 |
} |
|
234 |
// 循环找到目标元素 |
|
235 |
for (SequenceFlow sequenceFlow : sequenceFlows) { |
|
236 |
// 如果发现连线重复,说明循环了,跳过这个循环 |
|
237 |
if (visitedElements.contains(sequenceFlow.getId())) { |
|
238 |
continue; |
|
239 |
} |
|
240 |
// 添加已经走过的连线 |
|
241 |
visitedElements.add(sequenceFlow.getId()); |
|
242 |
// 这条线路存在目标节点,这条线路完成,进入下个线路 |
|
243 |
FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); |
|
244 |
if (target.getId().equals(sourceFlowElement.getId())) { |
|
245 |
continue; |
|
246 |
} |
|
247 |
// 如果目标节点为并行网关,则不继续 |
|
248 |
if (sourceFlowElement instanceof ParallelGateway) { |
|
249 |
return false; |
|
250 |
} |
|
251 |
// 否则就继续迭代 |
|
252 |
if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { |
|
253 |
return false; |
|
254 |
} |
|
255 |
} |
|
256 |
return true; |
|
257 |
} |
|
258 |
|
|
259 |
/** |
|
260 |
* 判断当前节点是否属于不同的子流程 |
|
261 |
* |
|
262 |
* @param flowElement 被判断的节点 |
|
263 |
* @return true 表示属于子流程 |
|
264 |
*/ |
|
265 |
private static boolean isInEventSubprocess(FlowElement flowElement) { |
|
266 |
FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); |
|
267 |
while (flowElementsContainer != null) { |
|
268 |
if (flowElementsContainer instanceof EventSubProcess) { |
|
269 |
return true; |
|
270 |
} |
|
271 |
|
|
272 |
if (flowElementsContainer instanceof FlowElement) { |
|
273 |
flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); |
|
274 |
} else { |
|
275 |
flowElementsContainer = null; |
|
276 |
} |
|
277 |
} |
|
278 |
return false; |
|
279 |
} |
|
280 |
|
|
281 |
/** |
|
282 |
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 |
|
283 |
* |
|
284 |
* @param source 起始节点 |
|
285 |
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 |
|
286 |
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|
287 |
* @param userTaskList 需要撤回的用户任务列表 |
|
288 |
* @return 子级任务节点列表 |
|
289 |
*/ |
|
290 |
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, |
|
291 |
Set<String> hasSequenceFlow, List<UserTask> userTaskList) { |
|
292 |
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|
293 |
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; |
|
294 |
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 |
|
295 |
if (source instanceof StartEvent && source.getSubProcess() != null) { |
|
296 |
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); |
|
297 |
} |
|
298 |
|
|
299 |
// 根据类型,获取出口连线 |
|
300 |
List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source); |
|
301 |
if (sequenceFlows == null) { |
|
302 |
return userTaskList; |
|
303 |
} |
|
304 |
// 循环找到目标元素 |
|
305 |
for (SequenceFlow sequenceFlow : sequenceFlows) { |
|
306 |
// 如果发现连线重复,说明循环了,跳过这个循环 |
|
307 |
if (hasSequenceFlow.contains(sequenceFlow.getId())) { |
|
308 |
continue; |
|
309 |
} |
|
310 |
// 添加已经走过的连线 |
|
311 |
hasSequenceFlow.add(sequenceFlow.getId()); |
|
312 |
// 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 |
|
313 |
if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { |
|
314 |
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); |
|
315 |
continue; |
|
316 |
} |
|
317 |
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 |
|
318 |
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { |
|
319 |
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); |
|
320 |
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 |
|
321 |
if (CollUtil.isNotEmpty(childUserTaskList)) { |
|
322 |
userTaskList.addAll(childUserTaskList); |
|
323 |
continue; |
|
324 |
} |
|
325 |
} |
|
326 |
// 继续迭代 |
|
327 |
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); |
|
328 |
} |
|
329 |
return userTaskList; |
|
330 |
} |
|
331 |
|
|
332 |
} |