潘志宝
3 天以前 b8a0affd03b5fa9fa33cd6f870e90394c2df86c7
提交 | 用户 | 时间
e7c126 1 package com.iailab.module.bpm.framework.flowable.core.util;
H 2
3 import cn.hutool.core.collection.CollUtil;
bb2880 4 import cn.hutool.core.map.MapUtil;
e7c126 5 import cn.hutool.core.util.ArrayUtil;
bb2880 6 import cn.hutool.core.util.ObjUtil;
H 7 import cn.hutool.core.util.StrUtil;
8 import com.google.common.collect.Maps;
9 import com.iailab.framework.common.util.collection.CollectionUtils;
e7c126 10 import com.iailab.framework.common.util.number.NumberUtils;
bb2880 11 import com.iailab.framework.common.util.string.StrUtils;
H 12 import com.iailab.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
13 import com.iailab.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
14 import com.iailab.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum;
15 import com.iailab.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum;
16 import com.iailab.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum;
17 import com.iailab.module.bpm.enums.definition.BpmUserTaskRejectHandlerType;
e7c126 18 import com.iailab.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
bb2880 19 import lombok.extern.slf4j.Slf4j;
e7c126 20 import org.flowable.bpmn.converter.BpmnXMLConverter;
H 21 import org.flowable.bpmn.model.Process;
22 import org.flowable.bpmn.model.*;
bb2880 23 import org.flowable.common.engine.api.FlowableException;
e7c126 24 import org.flowable.common.engine.impl.util.io.BytesStreamSource;
H 25
bb2880 26 import java.util.*;
H 27
28 import static com.iailab.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
29 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE;
30 import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX;
e7c126 31
H 32 /**
bb2880 33  * BPMN Model 操作工具类。目前分成三部分:
H 34  *
35  * 1. BPMN 修改 + 解析元素相关的方法
36  * 2. BPMN 简单查找相关的方法
37  * 3. BPMN 复杂遍历相关的方法
38  * 4. BPMN 流程预测相关的方法
39  *
40  * @author iailab
e7c126 41  */
bb2880 42 @Slf4j
e7c126 43 public class BpmnModelUtils {
H 44
bb2880 45     // ========== BPMN 修改 + 解析元素相关的方法 ==========
H 46
47     public static void addExtensionElement(FlowElement element, String name, String value) {
48         if (value == null) {
49             return;
50         }
51         ExtensionElement extensionElement = new ExtensionElement();
52         extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
53         extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
54         extensionElement.setElementText(value);
55         extensionElement.setName(name);
56         element.addExtensionElement(extensionElement);
e7c126 57     }
H 58
bb2880 59     public static void addExtensionElement(FlowElement element, String name, Integer value) {
H 60         if (value == null) {
61             return;
62         }
63         addExtensionElement(element, name, String.valueOf(value));
e7c126 64     }
bb2880 65
H 66     public static void addExtensionElement(FlowElement element, String name, Map<String, String> attributes) {
67         if (attributes == null) {
68             return;
69         }
70         ExtensionElement extensionElement = new ExtensionElement();
71         extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
72         extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
73         extensionElement.setName(name);
74         attributes.forEach((key, value) -> {
75             ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value);
76             extensionElement.addAttribute(extensionAttribute);
77         });
78         element.addExtensionElement(extensionElement);
79     }
80
81     /**
82      * 解析扩展元素
83      *
84      * @param flowElement 节点
85      * @param elementName 元素名称
86      * @return 扩展元素
87      */
88     public static String parseExtensionElement(FlowElement flowElement, String elementName) {
89         if (flowElement == null) {
90             return null;
91         }
92         ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));
93         return element != null ? element.getElementText() : null;
94     }
95
96     /**
97      * 给节点添加候选人元素
98      *
99      * @param candidateStrategy 候选人策略
100      * @param candidateParam 候选人参数,允许空
101      * @param flowElement 节点
102      */
103     public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) {
104         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY,
105                 candidateStrategy == null ? null : candidateStrategy.toString());
106         addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam);
107     }
108
109     /**
110      * 解析候选人策略
111      *
112      * @param userTask 任务节点
113      * @return 候选人策略
114      */
115     public static Integer parseCandidateStrategy(FlowElement userTask) {
116         Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue(
117                 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
118         // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限
119         if (candidateStrategy == null) {
120             ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY));
121             candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null;
122         }
123         return candidateStrategy;
124     }
125
126     /**
127      * 解析候选人参数
128      *
129      * @param userTask 任务节点
130      * @return 候选人参数
131      */
132     public static String parseCandidateParam(FlowElement userTask) {
133         String candidateParam = userTask.getAttributeValue(
134                 BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM);
135         if (candidateParam == null) {
136             ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM));
137             candidateParam = element != null ? element.getElementText() : null;
138         }
139         return candidateParam;
140     }
141
142     /**
143      * 解析审批类型
144      *
145      * @see BpmUserTaskApproveTypeEnum
146      * @param userTask 任务节点
147      * @return 审批类型
148      */
149     public static Integer parseApproveType(FlowElement userTask) {
150         return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE));
151     }
152
153     /**
154      * 添加任务拒绝处理元素
155      *
156      * @param rejectHandler 任务拒绝处理
157      * @param userTask 任务节点
158      */
159     public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) {
160         if (rejectHandler == null) {
161             return;
162         }
163         addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType()));
164         addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId());
165     }
166
167     /**
168      * 解析任务拒绝处理类型
169      *
170      * @param userTask 任务节点
171      * @return 任务拒绝处理类型
172      */
173     public static BpmUserTaskRejectHandlerType parseRejectHandlerType(FlowElement userTask) {
174         Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE));
175         return BpmUserTaskRejectHandlerType.typeOf(rejectHandlerType);
176     }
177
178     /**
179      * 解析任务拒绝返回任务节点 ID
180      *
181      * @param flowElement 任务节点
182      * @return 任务拒绝返回任务节点 ID
183      */
184     public static String parseReturnTaskId(FlowElement flowElement) {
185         return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID);
186     }
187
188     /**
189      * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举
190      *
191      * @see BpmUserTaskAssignStartUserHandlerTypeEnum
192      * @param assignStartUserHandlerType 发起人处理类型
193      * @param userTask 任务节点
194      */
195     public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) {
196         if (assignStartUserHandlerType == null) {
197             return;
198         }
199         addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString());
200     }
201
202     /**
203      * 给节点添加用户任务的审批人为空时,处理类型枚举
204      *
205      * @see BpmUserTaskAssignEmptyHandlerTypeEnum
206      * @param emptyHandler 空处理
207      * @param userTask 任务节点
208      */
209     public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) {
210         if (emptyHandler == null) {
211             return;
212         }
213         addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType()));
214         addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds()));
215     }
216
217     /**
218      * 解析用户任务的审批人与发起人相同时,处理类型枚举
219      *
220      * @param userTask 任务节点
221      * @return 处理类型枚举
222      */
223     public static Integer parseAssignStartUserHandlerType(FlowElement userTask) {
224         return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE));
225     }
226
227     /**
228      * 解析用户任务的审批人为空时,处理类型枚举
229      *
230      * @param userTask 任务节点
231      * @return 处理类型枚举
232      */
233     public static Integer parseAssignEmptyHandlerType(FlowElement userTask) {
234         return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE));
235     }
236
237     /**
238      * 解析用户任务的审批人为空时,处理用户 ID 数组
239      *
240      * @param userTask 任务节点
241      * @return 处理用户 ID 数组
242      */
243     public static List<Long> parseAssignEmptyHandlerUserIds(FlowElement userTask) {
244         return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ",");
245     }
246
247     /**
248      * 给节点添加表单字段权限元素
249      *
250      * @param fieldsPermissions 表单字段权限
251      * @param flowElement 节点
252      */
253     public static void addFormFieldsPermission(List<Map<String, String>> fieldsPermissions, FlowElement flowElement) {
254         if (CollUtil.isNotEmpty(fieldsPermissions)) {
255             fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item));
256         }
257     }
258
259     /**
260      * 解析表单字段权限
261      *
262      * @param bpmnModel bpmnModel 对象
263      * @param flowElementId 元素 ID
264      * @return 表单字段权限
265      */
266     public static Map<String, String> parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) {
267         if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) {
268             return null;
269         }
270         FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
271         if (flowElement == null) {
272             return null;
273         }
274         List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(FORM_FIELD_PERMISSION_ELEMENT);
275         if (CollUtil.isEmpty(extensionElements)) {
276             return null;
277         }
278         Map<String, String> fieldsPermission = MapUtil.newHashMap();
279         extensionElements.forEach(element -> {
f4f940 280             String field = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE);
H 281             String permission = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE);
bb2880 282             if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) {
H 283                 fieldsPermission.put(field, permission);
284             }
285         });
286         return fieldsPermission;
287     }
288
289     /**
290      * 给节点添加操作按钮设置元素
291      */
292     public static void addButtonsSetting(List<BpmSimpleModelNodeVO.OperationButtonSetting> buttonsSetting, UserTask userTask) {
293         if (CollUtil.isNotEmpty(buttonsSetting)) {
294             List<Map<String, String>> list = CollectionUtils.convertList(buttonsSetting, item -> {
295                 Map<String, String> settingMap = Maps.newHashMapWithExpectedSize(3);
296                 settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId()));
297                 settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName());
298                 settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable()));
299                 return settingMap;
300             });
301             list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item));
302         }
303     }
304
305     /**
306      * 解析操作按钮设置
307      *
308      * @param bpmnModel bpmnModel 对象
309      * @param flowElementId 元素 ID
310      * @return 操作按钮设置
311      */
312     public static Map<Integer, BpmTaskRespVO.OperationButtonSetting> parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) {
313         FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId);
314         if (flowElement == null) {
315             return null;
316         }
317         List<ExtensionElement> extensionElements = flowElement.getExtensionElements().get(BUTTON_SETTING_ELEMENT);
318         if (CollUtil.isEmpty(extensionElements)) {
319             return null;
320         }
321         Map<Integer, BpmTaskRespVO.OperationButtonSetting> buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size());
322         extensionElements.forEach(element -> {
f4f940 323             String id = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE);
H 324             String displayName = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE);
325             String enable = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE);
bb2880 326             if (StrUtil.isNotEmpty(id)) {
H 327                 BpmTaskRespVO.OperationButtonSetting setting = new BpmTaskRespVO.OperationButtonSetting();
328                 buttonSettings.put(Integer.valueOf(id), setting.setDisplayName(displayName).setEnable(Boolean.parseBoolean(enable)));
329             }
330         });
331         return buttonSettings;
332     }
333
334     /**
335      * 解析边界事件扩展元素
336      *
337      * @param boundaryEvent 边界事件
338      * @param customElement 元素
339      * @return 扩展元素
340      */
341     public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) {
342         if (boundaryEvent == null) {
343             return null;
344         }
345         ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement));
346         return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null);
347     }
348
349     // ========== BPM 简单查找相关的方法 ==========
e7c126 350
H 351     /**
352      * 根据节点,获取入口连线
353      *
354      * @param source 起始节点
355      * @return 入口连线列表
356      */
357     public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) {
358         if (source instanceof FlowNode) {
359             return ((FlowNode) source).getIncomingFlows();
360         }
361         return new ArrayList<>();
362     }
363
364     /**
365      * 根据节点,获取出口连线
366      *
367      * @param source 起始节点
368      * @return 出口连线列表
369      */
370     public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) {
371         if (source instanceof FlowNode) {
372             return ((FlowNode) source).getOutgoingFlows();
373         }
374         return new ArrayList<>();
375     }
376
377     /**
378      * 获取流程元素信息
379      *
380      * @param model         bpmnModel 对象
381      * @param flowElementId 元素 ID
382      * @return 元素信息
383      */
384     public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) {
385         Process process = model.getMainProcess();
386         return process.getFlowElement(flowElementId);
387     }
388
389     /**
390      * 获得 BPMN 流程中,指定的元素们
391      *
392      * @param model 模型
393      * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等
394      * @return 元素们
395      */
bb2880 396     @SuppressWarnings("unchecked")
e7c126 397     public static <T extends FlowElement> List<T> getBpmnModelElements(BpmnModel model, Class<T> clazz) {
H 398         List<T> result = new ArrayList<>();
bb2880 399         model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> {
H 400             if (flowElement.getClass().isAssignableFrom(clazz)) {
401                 result.add((T) flowElement);
402             }
403         }));
e7c126 404         return result;
H 405     }
406
407     public static StartEvent getStartEvent(BpmnModel model) {
408         Process process = model.getMainProcess();
409         // 从 initialFlowElement 找
410         FlowElement startElement = process.getInitialFlowElement();
411         if (startElement instanceof StartEvent) {
412             return (StartEvent) startElement;
413         }
414         // 从 flowElementList 找
415         return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent);
bb2880 416     }
H 417
418     public static EndEvent getEndEvent(BpmnModel model) {
419         Process process = model.getMainProcess();
420         // 从 flowElementList 找 endEvent
421         return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent);
e7c126 422     }
H 423
424     public static BpmnModel getBpmnModel(byte[] bpmnBytes) {
425         if (ArrayUtil.isEmpty(bpmnBytes)) {
426             return null;
427         }
428         BpmnXMLConverter converter = new BpmnXMLConverter();
429         // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false
430         return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false);
431     }
432
433     public static String getBpmnXml(BpmnModel model) {
434         if (model == null) {
435             return null;
436         }
437         BpmnXMLConverter converter = new BpmnXMLConverter();
bb2880 438         return StrUtil.utf8Str(converter.convertToXML(model));
e7c126 439     }
H 440
bb2880 441     public static String getBpmnXml(byte[] bpmnBytes) {
H 442         if (ArrayUtil.isEmpty(bpmnBytes)) {
443             return null;
444         }
445         return StrUtil.utf8Str(bpmnBytes);
446     }
447
448     // ========== BPMN 复杂遍历相关的方法 ==========
e7c126 449
H 450     /**
451      * 找到 source 节点之前的所有用户任务节点
452      *
453      * @param source          起始节点
454      * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
455      * @param userTaskList    已找到的用户任务节点
456      * @return 用户任务节点 数组
457      */
458     public static List<UserTask> getPreviousUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
459         userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
460         hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
461         // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
462         if (source instanceof StartEvent && source.getSubProcess() != null) {
463             userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList);
464         }
465
466         // 根据类型,获取入口连线
467         List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
468         if (sequenceFlows == null) {
469             return userTaskList;
470         }
471         // 循环找到目标元素
472         for (SequenceFlow sequenceFlow : sequenceFlows) {
473             // 如果发现连线重复,说明循环了,跳过这个循环
474             if (hasSequenceFlow.contains(sequenceFlow.getId())) {
475                 continue;
476             }
477             // 添加已经走过的连线
478             hasSequenceFlow.add(sequenceFlow.getId());
479             // 类型为用户节点,则新增父级节点
480             if (sequenceFlow.getSourceFlowElement() instanceof UserTask) {
481                 userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement());
482             }
483             // 类型为子流程,则添加子流程开始节点出口处相连的节点
484             if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) {
485                 // 获取子流程用户任务节点
486                 List<UserTask> childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null);
487                 // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
488                 if (CollUtil.isNotEmpty(childUserTaskList)) {
489                     userTaskList.addAll(childUserTaskList);
490                 }
491             }
492             // 继续迭代
493             userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList);
494         }
495         return userTaskList;
496     }
497
498     /**
499      * 迭代获取子流程用户任务节点
500      *
501      * @param source          起始节点
502      * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
503      * @param userTaskList    需要撤回的用户任务列表
504      * @return 用户任务节点
505      */
506     public static List<UserTask> findChildProcessUserTaskList(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
507         hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
508         userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
509
510         // 根据类型,获取出口连线
511         List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
512         if (sequenceFlows == null) {
513             return userTaskList;
514         }
515         // 循环找到目标元素
516         for (SequenceFlow sequenceFlow : sequenceFlows) {
517             // 如果发现连线重复,说明循环了,跳过这个循环
518             if (hasSequenceFlow.contains(sequenceFlow.getId())) {
519                 continue;
520             }
521             // 添加已经走过的连线
522             hasSequenceFlow.add(sequenceFlow.getId());
523             // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
524             if (sequenceFlow.getTargetFlowElement() instanceof UserTask) {
525                 userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
526                 continue;
527             }
528             // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
529             if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
530                 List<UserTask> childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null);
531                 // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
532                 if (CollUtil.isNotEmpty(childUserTaskList)) {
533                     userTaskList.addAll(childUserTaskList);
534                     continue;
535                 }
536             }
537             // 继续迭代
538             userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList);
539         }
540         return userTaskList;
541     }
542
543     /**
544      * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行
bb2880 545      * 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况
e7c126 546      *
H 547      * @param source          起始节点
548      * @param target          目标节点
549      * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复
550      * @return 结果
551      */
bb2880 552     @SuppressWarnings("BooleanMethodIsAlwaysInverted")
e7c126 553     public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set<String> visitedElements) {
H 554         visitedElements = visitedElements == null ? new HashSet<>() : visitedElements;
555         // 不能是开始事件和子流程
556         if (source instanceof StartEvent && isInEventSubprocess(source)) {
557             return false;
558         }
559
560         // 根据类型,获取入口连线
561         List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source);
562         if (CollUtil.isEmpty(sequenceFlows)) {
563             return true;
564         }
565         // 循环找到目标元素
566         for (SequenceFlow sequenceFlow : sequenceFlows) {
567             // 如果发现连线重复,说明循环了,跳过这个循环
568             if (visitedElements.contains(sequenceFlow.getId())) {
569                 continue;
570             }
571             // 添加已经走过的连线
572             visitedElements.add(sequenceFlow.getId());
573             // 这条线路存在目标节点,这条线路完成,进入下个线路
574             FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement();
575             if (target.getId().equals(sourceFlowElement.getId())) {
576                 continue;
577             }
578             // 如果目标节点为并行网关,则不继续
579             if (sourceFlowElement instanceof ParallelGateway) {
580                 return false;
581             }
582             // 否则就继续迭代
583             if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) {
584                 return false;
585             }
586         }
587         return true;
588     }
589
590     /**
591      * 判断当前节点是否属于不同的子流程
592      *
593      * @param flowElement 被判断的节点
594      * @return true 表示属于子流程
595      */
596     private static boolean isInEventSubprocess(FlowElement flowElement) {
597         FlowElementsContainer flowElementsContainer = flowElement.getParentContainer();
598         while (flowElementsContainer != null) {
599             if (flowElementsContainer instanceof EventSubProcess) {
600                 return true;
601             }
602
603             if (flowElementsContainer instanceof FlowElement) {
604                 flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer();
605             } else {
606                 flowElementsContainer = null;
607             }
608         }
609         return false;
610     }
611
612     /**
613      * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找
614      *
615      * @param source          起始节点
616      * @param runTaskKeyList  正在运行的任务 Key,用于校验任务节点是否是正在运行的节点
617      * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复
618      * @param userTaskList    需要撤回的用户任务列表
619      * @return 子级任务节点列表
620      */
621     public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList,
622                                                             Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
623         hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow;
624         userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList;
625         // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
626         if (source instanceof StartEvent && source.getSubProcess() != null) {
627             userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList);
628         }
629
630         // 根据类型,获取出口连线
631         List<SequenceFlow> sequenceFlows = getElementOutgoingFlows(source);
632         if (sequenceFlows == null) {
633             return userTaskList;
634         }
635         // 循环找到目标元素
636         for (SequenceFlow sequenceFlow : sequenceFlows) {
637             // 如果发现连线重复,说明循环了,跳过这个循环
638             if (hasSequenceFlow.contains(sequenceFlow.getId())) {
639                 continue;
640             }
641             // 添加已经走过的连线
642             hasSequenceFlow.add(sequenceFlow.getId());
643             // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加
644             if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) {
645                 userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement());
646                 continue;
647             }
648             // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
649             if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) {
650                 List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null);
651                 // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
652                 if (CollUtil.isNotEmpty(childUserTaskList)) {
653                     userTaskList.addAll(childUserTaskList);
654                     continue;
655                 }
656             }
657             // 继续迭代
658             userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList);
659         }
660         return userTaskList;
661     }
662
bb2880 663     // ========== BPMN 流程预测相关的方法 ==========
H 664
665     /**
666      * 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果
667      *
668      * @param bpmnModel BPMN 图
669      * @param variables 变量
670      * @return 节点元素数组
671      */
672     public static List<FlowElement> simulateProcess(BpmnModel bpmnModel, Map<String, Object> variables) {
673         List<FlowElement> resultElements = new ArrayList<>();
674         Set<FlowElement> visitElements = new HashSet<>();
675
676         // 从 StartEvent 开始遍历
677         StartEvent startEvent = getStartEvent(bpmnModel);
678         simulateNextFlowElements(startEvent, variables, resultElements, visitElements);
679
680         // 将 EndEvent 放在末尾。原因是,DFS 遍历,可能 EndEvent 在 resultElements 中
681         List<FlowElement> endEvents = CollUtil.removeWithAddIf(resultElements,
682                 flowElement -> flowElement instanceof EndEvent);
683         resultElements.addAll(endEvents);
684         return resultElements;
685     }
686
687     @SuppressWarnings("PatternVariableCanBeUsed")
688     private static void simulateNextFlowElements(FlowElement currentElement, Map<String, Object> variables,
689                                                  List<FlowElement> resultElements, Set<FlowElement> visitElements) {
690         // 如果为空,或者已经遍历过,则直接结束
691         if (currentElement == null) {
692             return;
693         }
694         if (visitElements.contains(currentElement)) {
695             return;
696         }
697         visitElements.add(currentElement);
698
699         // 情况:StartEvent/EndEvent/UserTask/ServiceTask
700         if (currentElement instanceof StartEvent
f4f940 701                 || currentElement instanceof EndEvent
H 702                 || currentElement instanceof UserTask
703                 || currentElement instanceof ServiceTask) {
bb2880 704             // 添加元素
H 705             FlowNode flowNode = (FlowNode) currentElement;
706             resultElements.add(flowNode);
707             // 遍历子节点
708             flowNode.getOutgoingFlows().forEach(
709                     nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));
710             return;
711         }
712
713         // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的
714         if (currentElement instanceof ExclusiveGateway) {
715             // 查找满足条件的 SequenceFlow 路径
716             Gateway gateway = (Gateway) currentElement;
717             SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
718                     flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
719                             && evalConditionExpress(variables, flow.getConditionExpression()));
720             if (matchSequenceFlow == null) {
721                 matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(),
f4f940 722                         flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
bb2880 723                 // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
H 724                 if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) {
725                     matchSequenceFlow = gateway.getOutgoingFlows().get(0);
726                 }
727             }
728             // 遍历满足条件的 SequenceFlow 路径
729             if (matchSequenceFlow != null) {
730                 simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements);
731             }
732             return;
733         }
734
735         // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的
736         if (currentElement instanceof InclusiveGateway) {
737             // 查找满足条件的 SequenceFlow 路径
738             Gateway gateway = (Gateway) currentElement;
739             Collection<SequenceFlow> matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
740                     flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId())
741                             && evalConditionExpress(variables, flow.getConditionExpression()));
742             if (CollUtil.isEmpty(matchSequenceFlows)) {
743                 matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(),
f4f940 744                         flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId()));
bb2880 745                 // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的
H 746                 if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) {
747                     matchSequenceFlows = gateway.getOutgoingFlows();
748                 }
749             }
750             // 遍历满足条件的 SequenceFlow 路径
751             matchSequenceFlows.forEach(
752                     flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements));
753         }
754
755         // 情况:ParallelGateway 并行,都满足,都走
756         if (currentElement instanceof ParallelGateway) {
757             Gateway gateway = (Gateway) currentElement;
758             // 遍历子节点
759             gateway.getOutgoingFlows().forEach(
760                     nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements));
761             return;
762         }
763     }
764
765     /**
766      * 计算条件表达式是否为 true 满足条件
767      *
768      * @param variables 流程实例
769      * @param express 条件表达式
770      * @return 是否满足条件
771      */
772     public static boolean evalConditionExpress(Map<String, Object> variables, String express) {
773         if (express == null) {
774             return Boolean.FALSE;
775         }
776         try {
777             Object result = FlowableUtils.getExpressionValue(variables, express);
778             return Boolean.TRUE.equals(result);
779         } catch (FlowableException ex) {
780             log.error("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错", express, variables, ex);
781             return Boolean.FALSE;
782         }
783     }
784
785     @SuppressWarnings("PatternVariableCanBeUsed")
786     public static boolean isSequentialUserTask(FlowElement flowElement) {
787         if (!(flowElement instanceof UserTask)) {
788             return false;
789         }
790         UserTask userTask = (UserTask) flowElement;
791         MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics();
792         return loopCharacteristics != null && loopCharacteristics.isSequential();
793     }
794
e7c126 795 }