Commit 955ad86db45eabb9260a6aad68a81b66e3097d03

Authored by Lesan
1 parent 34c8f4ca

feat: Simple设计器-监听器

yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
... ... @@ -96,20 +96,26 @@ public class BpmSimpleModelNodeVO {
96 96 */
97 97 private AssignEmptyHandler assignEmptyHandler;
98 98  
99   - // TODO @lesan:建议改成 taskCreateListener;
100 99 /**
101 100 * 创建任务监听器
102 101 */
103   - private ListenerHandler createTaskListener;
  102 + private ListenerHandler taskCreateListener;
  103 + /**
  104 + * 指派任务监听器
  105 + */
  106 + private ListenerHandler taskAssignListener;
  107 + /**
  108 + * 完成任务监听器
  109 + */
  110 + private ListenerHandler taskCompleteListener;
104 111  
105 112 @Schema(description = "任务监听器")
106 113 @Valid
107 114 @Data
108 115 public static class ListenerHandler {
109 116  
110   - // TODO @lesan:参数校验,需要加下
111   -
112 117 @Schema(description = "是否开启任务监听器", example = "false")
  118 + @NotNull(message = "是否开启任务监听器不能为空")
113 119 private Boolean enable;
114 120  
115 121 @Schema(description = "请求路径", example = "http://xxxxx")
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
... ... @@ -6,6 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
6 6 import cn.hutool.core.util.ObjUtil;
7 7 import cn.hutool.core.util.StrUtil;
8 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
10 11 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
11 12 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
... ... @@ -56,6 +57,18 @@ public class BpmnModelUtils {
56 57 element.addExtensionElement(extensionElement);
57 58 }
58 59  
  60 + public static void addExtensionElementJson(FlowElement element, String name, Object value) {
  61 + if (value == null) {
  62 + return;
  63 + }
  64 + ExtensionElement extensionElement = new ExtensionElement();
  65 + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE);
  66 + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX);
  67 + extensionElement.setElementText(JsonUtils.toJsonString(value));
  68 + extensionElement.setName(name);
  69 + element.addExtensionElement(extensionElement);
  70 + }
  71 +
59 72 public static void addExtensionElement(FlowElement element, String name, Integer value) {
60 73 if (value == null) {
61 74 return;
... ... @@ -93,6 +106,33 @@ public class BpmnModelUtils {
93 106 return element != null ? element.getElementText() : null;
94 107 }
95 108  
  109 + public static <T> T parseExtensionElementJson(FlowElement flowElement, String elementName, Class<T> clazz) {
  110 + if (flowElement == null) {
  111 + return null;
  112 + }
  113 + ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName));
  114 + return element != null ? JsonUtils.parseObject(element.getElementText(), clazz) : null;
  115 + }
  116 +
  117 + /**
  118 + * 给节点添加Simple设计器配置Json
  119 + *
  120 + * @param userTask 节点
  121 + * @param node 节点对象
  122 + */
  123 + public static void addSimpleConfigInfo(FlowElement userTask, Object node) {
  124 + addExtensionElementJson(userTask, "config", node);
  125 + }
  126 +
  127 + /**
  128 + * 解析Simple设计器配置Json
  129 + *
  130 + * @param userTask 节点
  131 + */
  132 + public static BpmSimpleModelNodeVO parseSimpleConfigInfo(FlowElement userTask) {
  133 + return parseExtensionElementJson(userTask, "config", BpmSimpleModelNodeVO.class);
  134 + }
  135 +
96 136 /**
97 137 * 给节点添加候选人元素
98 138 *
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.map.MapUtil;
6 6 import cn.hutool.core.util.*;
7   -import cn.hutool.json.JSONUtil;
8 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
10 9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
... ... @@ -23,7 +22,6 @@ import java.util.*;
23 22 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
24 23 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
25 24 import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION;
26   -import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.EXTENSION_SUFFIX;
27 25 import static java.util.Arrays.asList;
28 26  
29 27 /**
... ... @@ -442,23 +440,38 @@ public class SimpleModelUtils {
442 440 userTask.setDueDate(node.getTimeoutHandler().getTimeDuration());
443 441 }
444 442 // 设置监听器
  443 + addUserTaskListener(node, userTask);
  444 + // 设置Simple设计器节点配置
  445 + addSimpleConfigInfo(userTask, node);
  446 + return userTask;
  447 + }
  448 +
  449 + private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) {
445 450 List<FlowableListener> flowableListeners = new ArrayList<>(3);
446   - if (node.getCreateTaskListener().getEnable()) {
  451 + if (node.getTaskCreateListener().getEnable()) {
447 452 FlowableListener flowableListener = new FlowableListener();
448 453 flowableListener.setEvent(TaskListener.EVENTNAME_CREATE);
449 454 flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
450 455 flowableListener.setImplementation(DELEGATE_EXPRESSION);
451   - // TODO @lesan:可以加个 addExtensionElementJson() 方法;
452   - // TODO @lesan:是不是不用带 "create" + EXTENSION_SUFFIX 这种,直接给个 "config" 就完事了!
453   - addExtensionElement(userTask, "create" + EXTENSION_SUFFIX,
454   - // TODO @lesan:默认使用项目里的 JsonUtils 方法
455   - JSONUtil.toJsonStr(node.getCreateTaskListener()));
  456 + flowableListeners.add(flowableListener);
  457 + }
  458 + if (node.getTaskAssignListener().getEnable()) {
  459 + FlowableListener flowableListener = new FlowableListener();
  460 + flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT);
  461 + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
  462 + flowableListener.setImplementation(DELEGATE_EXPRESSION);
  463 + flowableListeners.add(flowableListener);
  464 + }
  465 + if (node.getTaskCompleteListener().getEnable()) {
  466 + FlowableListener flowableListener = new FlowableListener();
  467 + flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE);
  468 + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
  469 + flowableListener.setImplementation(DELEGATE_EXPRESSION);
456 470 flowableListeners.add(flowableListener);
457 471 }
458 472 if (CollUtil.isNotEmpty(flowableListeners)) {
459 473 userTask.setTaskListeners(flowableListeners);
460 474 }
461   - return userTask;
462 475 }
463 476  
464 477 private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) {
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/listener/BpmUserTaskListener.java
1 1 package cn.iocoder.yudao.module.bpm.service.task.listener;
2 2  
3   -import cn.hutool.http.HttpRequest;
4   -import cn.hutool.json.JSONUtil;
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.lang.Assert;
5 5 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
6 6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmListenerMapType;
7 7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
... ... @@ -14,12 +14,18 @@ import org.flowable.bpmn.model.FlowElement;
14 14 import org.flowable.engine.delegate.TaskListener;
15 15 import org.flowable.engine.history.HistoricProcessInstance;
16 16 import org.flowable.task.service.delegate.DelegateTask;
  17 +import org.springframework.http.HttpEntity;
  18 +import org.springframework.http.HttpMethod;
  19 +import org.springframework.http.ResponseEntity;
17 20 import org.springframework.stereotype.Component;
  21 +import org.springframework.util.LinkedMultiValueMap;
  22 +import org.springframework.util.MultiValueMap;
  23 +import org.springframework.web.client.RestTemplate;
18 24  
19   -import java.util.HashMap;
  25 +import java.util.List;
20 26 import java.util.Map;
21 27  
22   -import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseExtensionElement;
  28 +import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseSimpleConfigInfo;
23 29  
24 30 /**
25 31 * BPM 用户任务通用监听器
... ... @@ -32,52 +38,62 @@ public class BpmUserTaskListener implements TaskListener {
32 38  
33 39 public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}";
34 40  
35   - public static final String EXTENSION_SUFFIX = "TaskListenerMetaInfo";
36   -
37 41 @Resource
38 42 private BpmModelService modelService;
39 43  
40 44 @Resource
41 45 private BpmProcessInstanceService processInstanceService;
42 46  
  47 + @Resource
  48 + private RestTemplate restTemplate;
  49 +
43 50 @Override
44 51 public void notify(DelegateTask delegateTask) {
45 52 // 1. 获取所需基础信息
46 53 HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
47 54 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(delegateTask.getProcessDefinitionId());
48   - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, delegateTask.getTaskDefinitionKey());
49   - // TODO @lesan:可以写到 FlowableUtils 里,简化解析逻辑!
50   - BpmSimpleModelNodeVO.ListenerHandler listenerHandler = JSONUtil.toBean(
51   - parseExtensionElement(userTaskElement, delegateTask.getEventName() + EXTENSION_SUFFIX),
52   - BpmSimpleModelNodeVO.ListenerHandler.class);
  55 + FlowElement userTask = BpmnModelUtils.getFlowElementById(bpmnModel, delegateTask.getTaskDefinitionKey());
  56 + BpmSimpleModelNodeVO node = parseSimpleConfigInfo(userTask);
  57 + BpmSimpleModelNodeVO.ListenerHandler listenerHandler = getListenerHandlerByEvent(delegateTask.getEventName(), node);
53 58  
54 59 // 2. 获取请求头和请求体
55 60 Map<String, Object> processVariables = processInstance.getProcessVariables();
56   - Map<String, String> headers = new HashMap<>();
57   - Map<String, Object> body = new HashMap<>();
58   - listenerHandler.getHeader().forEach(item -> {
59   - // TODO @lesan:可以写个统一的方法,解析参数。然后非空,put 到 headers 或者 body 里!
60   - if (item.getType().equals(BpmListenerMapType.FIXED_VALUE.getType())) {
61   - headers.put(item.getKey(), item.getValue());
62   - } else if (item.getType().equals(BpmListenerMapType.FROM_FORM.getType())) {
63   - headers.put(item.getKey(), processVariables.getOrDefault(item.getValue(), "").toString());
64   - }
65   - });
66   - // TODO @lesan:header 里面,需要添加下 tenant-id!
67   - listenerHandler.getBody().forEach(item -> {
  61 + MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
  62 + MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
  63 + parseListenerMap(listenerHandler.getHeader(), processVariables, headers);
  64 + headers.add("tenant-id", delegateTask.getTenantId());
  65 + parseListenerMap(listenerHandler.getBody(), processVariables, body);
  66 +
  67 + // 3. 异步发起请求
  68 + HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
  69 + ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST,
  70 + requestEntity, String.class);
  71 + log.info("[BpmUserTaskListener][的响应结果({})]", responseEntity);
  72 + // 4. 是否需要后续操作?TODO 芋艿:待定!
  73 + }
  74 +
  75 + private void parseListenerMap(List<BpmSimpleModelNodeVO.ListenerHandler.ListenerMap> list,
  76 + Map<String, Object> processVariables,
  77 + MultiValueMap<String, String> to) {
  78 + if (CollUtil.isEmpty(list)) {
  79 + return;
  80 + }
  81 + list.forEach(item -> {
68 82 if (item.getType().equals(BpmListenerMapType.FIXED_VALUE.getType())) {
69   - body.put(item.getKey(), item.getValue());
  83 + to.add(item.getKey(), item.getValue());
70 84 } else if (item.getType().equals(BpmListenerMapType.FROM_FORM.getType())) {
71   - body.put(item.getKey(), processVariables.getOrDefault(item.getValue(), ""));
  85 + to.add(item.getKey(), processVariables.get(item.getValue()).toString());
72 86 }
73 87 });
  88 + }
74 89  
75   - // 3. 异步发起请求
76   - // TODO @lesan:最好打印下日志!
77   - HttpRequest.post(listenerHandler.getPath())
78   - .addHeaders(headers).form(body).executeAsync();
79   -
80   - // 4. 是否需要后续操作?TODO 芋艿:待定!
  90 + private BpmSimpleModelNodeVO.ListenerHandler getListenerHandlerByEvent(String eventName, BpmSimpleModelNodeVO node) {
  91 + return switch (eventName) {
  92 + case TaskListener.EVENTNAME_CREATE -> node.getTaskCreateListener();
  93 + case TaskListener.EVENTNAME_ASSIGNMENT -> node.getTaskAssignListener();
  94 + case TaskListener.EVENTNAME_COMPLETE -> node.getTaskCompleteListener();
  95 + default -> null;
  96 + };
81 97 }
82 98  
83 99 }
84 100 \ No newline at end of file
... ...