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,20 +96,26 @@ public class BpmSimpleModelNodeVO {
96 */ 96 */
97 private AssignEmptyHandler assignEmptyHandler; 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 @Schema(description = "任务监听器") 112 @Schema(description = "任务监听器")
106 @Valid 113 @Valid
107 @Data 114 @Data
108 public static class ListenerHandler { 115 public static class ListenerHandler {
109 116
110 - // TODO @lesan:参数校验,需要加下  
111 -  
112 @Schema(description = "是否开启任务监听器", example = "false") 117 @Schema(description = "是否开启任务监听器", example = "false")
  118 + @NotNull(message = "是否开启任务监听器不能为空")
113 private Boolean enable; 119 private Boolean enable;
114 120
115 @Schema(description = "请求路径", example = "http://xxxxx") 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 +6,7 @@ import cn.hutool.core.util.ArrayUtil;
6 import cn.hutool.core.util.ObjUtil; 6 import cn.hutool.core.util.ObjUtil;
7 import cn.hutool.core.util.StrUtil; 7 import cn.hutool.core.util.StrUtil;
8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
  9 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
10 import cn.iocoder.yudao.framework.common.util.string.StrUtils; 11 import cn.iocoder.yudao.framework.common.util.string.StrUtils;
11 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 12 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
@@ -56,6 +57,18 @@ public class BpmnModelUtils { @@ -56,6 +57,18 @@ public class BpmnModelUtils {
56 element.addExtensionElement(extensionElement); 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 public static void addExtensionElement(FlowElement element, String name, Integer value) { 72 public static void addExtensionElement(FlowElement element, String name, Integer value) {
60 if (value == null) { 73 if (value == null) {
61 return; 74 return;
@@ -93,6 +106,33 @@ public class BpmnModelUtils { @@ -93,6 +106,33 @@ public class BpmnModelUtils {
93 return element != null ? element.getElementText() : null; 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,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.map.MapUtil; 5 import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.*; 6 import cn.hutool.core.util.*;
7 -import cn.hutool.json.JSONUtil;  
8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 8 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
10 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; 9 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups;
@@ -23,7 +22,6 @@ import java.util.*; @@ -23,7 +22,6 @@ import java.util.*;
23 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; 22 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*;
24 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; 23 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.*;
25 import static cn.iocoder.yudao.module.bpm.service.task.listener.BpmUserTaskListener.DELEGATE_EXPRESSION; 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 import static java.util.Arrays.asList; 25 import static java.util.Arrays.asList;
28 26
29 /** 27 /**
@@ -442,23 +440,38 @@ public class SimpleModelUtils { @@ -442,23 +440,38 @@ public class SimpleModelUtils {
442 userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); 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 List<FlowableListener> flowableListeners = new ArrayList<>(3); 450 List<FlowableListener> flowableListeners = new ArrayList<>(3);
446 - if (node.getCreateTaskListener().getEnable()) { 451 + if (node.getTaskCreateListener().getEnable()) {
447 FlowableListener flowableListener = new FlowableListener(); 452 FlowableListener flowableListener = new FlowableListener();
448 flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); 453 flowableListener.setEvent(TaskListener.EVENTNAME_CREATE);
449 flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); 454 flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
450 flowableListener.setImplementation(DELEGATE_EXPRESSION); 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 flowableListeners.add(flowableListener); 470 flowableListeners.add(flowableListener);
457 } 471 }
458 if (CollUtil.isNotEmpty(flowableListeners)) { 472 if (CollUtil.isNotEmpty(flowableListeners)) {
459 userTask.setTaskListeners(flowableListeners); 473 userTask.setTaskListeners(flowableListeners);
460 } 474 }
461 - return userTask;  
462 } 475 }
463 476
464 private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { 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 package cn.iocoder.yudao.module.bpm.service.task.listener; 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 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; 5 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmListenerMapType; 6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmListenerMapType;
7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; 7 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
@@ -14,12 +14,18 @@ import org.flowable.bpmn.model.FlowElement; @@ -14,12 +14,18 @@ import org.flowable.bpmn.model.FlowElement;
14 import org.flowable.engine.delegate.TaskListener; 14 import org.flowable.engine.delegate.TaskListener;
15 import org.flowable.engine.history.HistoricProcessInstance; 15 import org.flowable.engine.history.HistoricProcessInstance;
16 import org.flowable.task.service.delegate.DelegateTask; 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 import org.springframework.stereotype.Component; 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 import java.util.Map; 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 * BPM 用户任务通用监听器 31 * BPM 用户任务通用监听器
@@ -32,52 +38,62 @@ public class BpmUserTaskListener implements TaskListener { @@ -32,52 +38,62 @@ public class BpmUserTaskListener implements TaskListener {
32 38
33 public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}"; 39 public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}";
34 40
35 - public static final String EXTENSION_SUFFIX = "TaskListenerMetaInfo";  
36 -  
37 @Resource 41 @Resource
38 private BpmModelService modelService; 42 private BpmModelService modelService;
39 43
40 @Resource 44 @Resource
41 private BpmProcessInstanceService processInstanceService; 45 private BpmProcessInstanceService processInstanceService;
42 46
  47 + @Resource
  48 + private RestTemplate restTemplate;
  49 +
43 @Override 50 @Override
44 public void notify(DelegateTask delegateTask) { 51 public void notify(DelegateTask delegateTask) {
45 // 1. 获取所需基础信息 52 // 1. 获取所需基础信息
46 HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId()); 53 HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
47 BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(delegateTask.getProcessDefinitionId()); 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 // 2. 获取请求头和请求体 59 // 2. 获取请求头和请求体
55 Map<String, Object> processVariables = processInstance.getProcessVariables(); 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 if (item.getType().equals(BpmListenerMapType.FIXED_VALUE.getType())) { 82 if (item.getType().equals(BpmListenerMapType.FIXED_VALUE.getType())) {
69 - body.put(item.getKey(), item.getValue()); 83 + to.add(item.getKey(), item.getValue());
70 } else if (item.getType().equals(BpmListenerMapType.FROM_FORM.getType())) { 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 \ No newline at end of file 100 \ No newline at end of file