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