Commit e114a354513140820e3d9add68d22804217a54a6

Authored by Lesan
1 parent 24261cf7

feat: 流程前后置通知

Showing 11 changed files with 348 additions and 224 deletions
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java
1 1 package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model;
2 2  
  3 +import cn.iocoder.yudao.framework.common.core.KeyValue;
3 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
4 6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmAutoApproveTypeEnum;
5 7 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
6 8 import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
... ... @@ -80,6 +82,12 @@ public class BpmModelMetaInfoVO {
80 82 @Schema(description = "摘要设置", example = "{}")
81 83 private SummarySetting summarySetting;
82 84  
  85 + @Schema(description = "流程前置通知设置", example = "{}")
  86 + private HttpRequestSetting PreProcessNotifySetting;
  87 +
  88 + @Schema(description = "流程后置通知设置", example = "{}")
  89 + private HttpRequestSetting PostProcessNotifySetting;
  90 +
83 91 @Schema(description = "流程 ID 规则")
84 92 @Data
85 93 @Valid
... ... @@ -132,4 +140,32 @@ public class BpmModelMetaInfoVO {
132 140  
133 141 }
134 142  
  143 + @Schema(description = "http 请求通知设置", example = "{}")
  144 + @Data
  145 + public static class HttpRequestSetting {
  146 +
  147 + @Schema(description = "请求路径", example = "http://127.0.0.1")
  148 + @NotEmpty(message = "请求 URL 不能为空")
  149 + @URL(message = "请求 URL 格式不正确")
  150 + private String url;
  151 +
  152 + @Schema(description = "请求头参数设置", example = "[]")
  153 + @Valid
  154 + private List<BpmSimpleModelNodeVO.HttpRequestParam> header;
  155 +
  156 + @Schema(description = "请求头参数设置", example = "[]")
  157 + @Valid
  158 + private List<BpmSimpleModelNodeVO.HttpRequestParam> body;
  159 +
  160 + /**
  161 + * 请求返回处理设置,用于修改流程表单值
  162 + * <p>
  163 + * key:表示要修改的流程表单字段名(name)
  164 + * value:接口返回的字段名
  165 + */
  166 + @Schema(description = "请求返回处理设置", example = "[]")
  167 + private List<KeyValue<String, String>> response;
  168 +
  169 + }
  170 +
135 171 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java
... ... @@ -189,4 +189,16 @@ public class BpmProcessDefinitionInfoDO extends BaseDO {
189 189 @TableField(typeHandler = JacksonTypeHandler.class)
190 190 private BpmModelMetaInfoVO.SummarySetting summarySetting;
191 191  
  192 + /**
  193 + * 流程前置通知设置
  194 + */
  195 + @TableField(typeHandler = JacksonTypeHandler.class)
  196 + private BpmModelMetaInfoVO.HttpRequestSetting PreProcessNotifySetting;
  197 +
  198 + /**
  199 + * 流程后置通知设置
  200 + */
  201 + @TableField(typeHandler = JacksonTypeHandler.class)
  202 + private BpmModelMetaInfoVO.HttpRequestSetting PostProcessNotifySetting;
  203 +
192 204 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
... ... @@ -22,6 +22,7 @@ import java.util.Set;
22 22 public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener {
23 23  
24 24 public static final Set<FlowableEngineEventType> PROCESS_INSTANCE_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
  25 + .add(FlowableEngineEventType.PROCESS_CREATED)
25 26 .add(FlowableEngineEventType.PROCESS_COMPLETED)
26 27 .add(FlowableEngineEventType.PROCESS_CANCELLED)
27 28 .build();
... ... @@ -35,6 +36,11 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
35 36 }
36 37  
37 38 @Override
  39 + protected void processCreated(FlowableEngineEntityEvent event) {
  40 + processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity());
  41 + }
  42 +
  43 + @Override
38 44 protected void processCompleted(FlowableEngineEntityEvent event) {
39 45 processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity());
40 46 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.yudao.framework.common.core.KeyValue;
  6 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  8 +import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
  9 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
  10 +import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
  11 +import com.fasterxml.jackson.core.type.TypeReference;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.flowable.engine.runtime.ProcessInstance;
  14 +import org.springframework.http.HttpEntity;
  15 +import org.springframework.http.HttpMethod;
  16 +import org.springframework.http.ResponseEntity;
  17 +import org.springframework.util.LinkedMultiValueMap;
  18 +import org.springframework.util.MultiValueMap;
  19 +import org.springframework.web.client.RestClientException;
  20 +import org.springframework.web.client.RestTemplate;
  21 +
  22 +import java.util.HashMap;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  27 +import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
  28 +import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR;
  29 +
  30 +/**
  31 + * 工作流发起 HTTP 请求工具类
  32 + *
  33 + * @author 芋道源码
  34 + */
  35 +@Slf4j
  36 +public class BpmHttpRequestUtils {
  37 +
  38 + public static void executeBpmHttpRequest(ProcessInstance processInstance,
  39 + String url,
  40 + List<BpmSimpleModelNodeVO.HttpRequestParam> headerParam,
  41 + List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParam,
  42 + Boolean handleResponse,
  43 + List<KeyValue<String, String>> response,
  44 + RestTemplate restTemplate,
  45 + BpmProcessInstanceService processInstanceService) {
  46 +
  47 + // 1.1 设置请求头
  48 + MultiValueMap<String, String> headers = BpmHttpRequestUtils.buildHttpHeaders(processInstance, headerParam);
  49 + // 1.2 设置请求体
  50 + MultiValueMap<String, String> body = BpmHttpRequestUtils.buildHttpBody(processInstance, bodyParam);
  51 +
  52 + // 2. 发起请求
  53 + ResponseEntity<String> responseEntity = BpmHttpRequestUtils.sendHttpRequest(url, headers, body, restTemplate);
  54 +
  55 + // 3. 处理返回
  56 + if (Boolean.TRUE.equals(handleResponse)) {
  57 + // 3.1 判断是否需要解析返回值
  58 + if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody())
  59 + || !responseEntity.getStatusCode().is2xxSuccessful()
  60 + || CollUtil.isEmpty(response)) {
  61 + return;
  62 + }
  63 + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。
  64 + CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
  65 + responseEntity.getBody(), new TypeReference<>() {
  66 + });
  67 + if (respResult == null || !respResult.isSuccess()) {
  68 + return;
  69 + }
  70 + // 3.3 获取需要更新的流程变量
  71 + Map<String, Object> updateVariables = BpmHttpRequestUtils.getNeedUpdatedVariablesFromResponse(respResult.getData(), response);
  72 + // 3.4 更新流程变量
  73 + if (CollUtil.isNotEmpty(updateVariables)) {
  74 + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables);
  75 + }
  76 + }
  77 +
  78 + }
  79 +
  80 + public static ResponseEntity<String> sendHttpRequest(String url,
  81 + MultiValueMap<String, String> headers,
  82 + MultiValueMap<String, String> body,
  83 + RestTemplate restTemplate) {
  84 + // 3. 发起请求
  85 + HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
  86 + ResponseEntity<String> responseEntity;
  87 + try {
  88 + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
  89 + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
  90 + } catch (RestClientException e) {
  91 + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
  92 + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR);
  93 + }
  94 + return responseEntity;
  95 + }
  96 +
  97 + public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance,
  98 + List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) {
  99 + Map<String, Object> processVariables = processInstance.getProcessVariables();
  100 + MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
  101 + headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
  102 + addHttpRequestParam(headers, headerSettings, processVariables);
  103 + return headers;
  104 + }
  105 +
  106 + public static MultiValueMap<String, String> buildHttpBody(ProcessInstance processInstance,
  107 + List<BpmSimpleModelNodeVO.HttpRequestParam> bodySettings) {
  108 + Map<String, Object> processVariables = processInstance.getProcessVariables();
  109 + MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
  110 + addHttpRequestParam(body, bodySettings, processVariables);
  111 + body.add("processInstanceId", processInstance.getId());
  112 + return body;
  113 + }
  114 +
  115 + /**
  116 + * 从请求返回值获取需要更新的流程变量
  117 + *
  118 + * @param result 请求返回结果
  119 + * @param responseSettings 返回设置
  120 + * @return 需要更新的流程变量
  121 + */
  122 + public static Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String, Object> result,
  123 + List<KeyValue<String, String>> responseSettings) {
  124 + Map<String, Object> updateVariables = new HashMap<>();
  125 + if (CollUtil.isEmpty(result)) {
  126 + return updateVariables;
  127 + }
  128 + responseSettings.forEach(responseSetting -> {
  129 + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
  130 + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
  131 + }
  132 + });
  133 + return updateVariables;
  134 + }
  135 +
  136 + /**
  137 + * 添加 HTTP 请求参数。请求头或者请求体
  138 + *
  139 + * @param params HTTP 请求参数
  140 + * @param paramSettings HTTP 请求参数设置
  141 + * @param processVariables 流程变量
  142 + */
  143 + public static void addHttpRequestParam(MultiValueMap<String, String> params,
  144 + List<BpmSimpleModelNodeVO.HttpRequestParam> paramSettings,
  145 + Map<String, Object> processVariables) {
  146 + if (CollUtil.isEmpty(paramSettings)) {
  147 + return;
  148 + }
  149 + paramSettings.forEach(item -> {
  150 + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) {
  151 + params.add(item.getKey(), item.getValue());
  152 + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) {
  153 + params.add(item.getKey(), processVariables.get(item.getValue()).toString());
  154 + }
  155 + });
  156 + }
  157 +
  158 +}
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/SimpleModelUtils.java
... ... @@ -21,7 +21,6 @@ import org.flowable.bpmn.model.Process;
21 21 import org.flowable.bpmn.model.*;
22 22 import org.flowable.engine.delegate.ExecutionListener;
23 23 import org.flowable.engine.delegate.TaskListener;
24   -import org.springframework.util.MultiValueMap;
25 24  
26 25 import java.util.*;
27 26  
... ... @@ -1002,27 +1001,4 @@ public class SimpleModelUtils {
1002 1001 return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting));
1003 1002 }
1004 1003  
1005   - // TODO @芋艿:【高】要不要优化下,抽个 HttpUtils
1006   -
1007   - /**
1008   - * 添加 HTTP 请求参数。请求头或者请求体
1009   - *
1010   - * @param params HTTP 请求参数
1011   - * @param paramSettings HTTP 请求参数设置
1012   - * @param processVariables 流程变量
1013   - */
1014   - public static void addHttpRequestParam(MultiValueMap<String, String> params,
1015   - List<BpmSimpleModelNodeVO.HttpRequestParam> paramSettings,
1016   - Map<String, Object> processVariables) {
1017   - if (CollUtil.isEmpty(paramSettings)) {
1018   - return;
1019   - }
1020   - paramSettings.forEach(item -> {
1021   - if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) {
1022   - params.add(item.getKey(), item.getValue());
1023   - } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) {
1024   - params.add(item.getKey(), processVariables.get(item.getValue()).toString());
1025   - }
1026   - });
1027   - }
1028 1004 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
... ... @@ -182,4 +182,10 @@ public interface BpmProcessInstanceService {
182 182 */
183 183 void processProcessInstanceCompleted(ProcessInstance instance);
184 184  
  185 + /**
  186 + * 处理 ProcessInstance 开始事件,例如说:流程前置通知
  187 + *
  188 + * @param instance 流程任务
  189 + */
  190 + void processProcessInstanceCreated(ProcessInstance instance);
185 191 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
... ... @@ -34,6 +34,7 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidat
34 34 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnModelConstants;
35 35 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
36 36 import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher;
  37 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
37 38 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
38 39 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
39 40 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
... ... @@ -62,6 +63,7 @@ import org.springframework.context.annotation.Lazy;
62 63 import org.springframework.stereotype.Service;
63 64 import org.springframework.transaction.annotation.Transactional;
64 65 import org.springframework.validation.annotation.Validated;
  66 +import org.springframework.web.client.RestTemplate;
65 67  
66 68 import java.util.*;
67 69  
... ... @@ -120,6 +122,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
120 122 @Resource
121 123 private BpmProcessIdRedisDAO processIdRedisDAO;
122 124  
  125 + @Resource
  126 + private RestTemplate restTemplate;
  127 +
123 128 // ========== Query 查询相关方法 ==========
124 129  
125 130 @Override
... ... @@ -148,7 +153,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
148 153 }
149 154  
150 155 private Map<String, String> getFormFieldsPermission(BpmnModel bpmnModel,
151   - String activityId, String taskId) {
  156 + String activityId, String taskId) {
152 157 // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id
153 158 if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) {
154 159 activityId = Optional.ofNullable(taskService.getHistoricTask(taskId))
... ... @@ -351,15 +356,15 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
351 356 * 主要是,拼接审批人的用户信息、部门信息
352 357 */
353 358 private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO,
354   - BpmnModel bpmnModel,
355   - ProcessDefinition processDefinition,
356   - BpmProcessDefinitionInfoDO processDefinitionInfo,
357   - HistoricProcessInstance processInstance,
358   - Integer processInstanceStatus,
359   - List<ActivityNode> endApprovalNodeInfos,
360   - List<ActivityNode> runningApprovalNodeInfos,
361   - List<ActivityNode> simulateApprovalNodeInfos,
362   - BpmTaskRespVO todoTask) {
  359 + BpmnModel bpmnModel,
  360 + ProcessDefinition processDefinition,
  361 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  362 + HistoricProcessInstance processInstance,
  363 + Integer processInstanceStatus,
  364 + List<ActivityNode> endApprovalNodeInfos,
  365 + List<ActivityNode> runningApprovalNodeInfos,
  366 + List<ActivityNode> simulateApprovalNodeInfos,
  367 + BpmTaskRespVO todoTask) {
363 368 // 1. 获取所有需要读取用户信息的 userIds
364 369 List<ActivityNode> approveNodes = newArrayList(
365 370 asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos));
... ... @@ -381,9 +386,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
381 386 * 获得【已结束】的活动节点们
382 387 */
383 388 private List<ActivityNode> getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel,
384   - BpmProcessDefinitionInfoDO processDefinitionInfo,
385   - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
386   - List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
  389 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  390 + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus,
  391 + List<HistoricActivityInstance> activities, List<HistoricTaskInstance> tasks) {
387 392 // 遍历 tasks 列表,只处理已结束的 UserTask
388 393 // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities
389 394 // 的话,它无法成为一个节点
... ... @@ -451,11 +456,11 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
451 456 * 获得【进行中】的活动节点们
452 457 */
453 458 private List<ActivityNode> getRunApproveNodeList(Long startUserId,
454   - BpmnModel bpmnModel,
455   - ProcessDefinition processDefinition,
456   - Map<String, Object> processVariables,
457   - List<HistoricActivityInstance> activities,
458   - List<HistoricTaskInstance> tasks) {
  459 + BpmnModel bpmnModel,
  460 + ProcessDefinition processDefinition,
  461 + Map<String, Object> processVariables,
  462 + List<HistoricActivityInstance> activities,
  463 + List<HistoricTaskInstance> tasks) {
459 464 // 构建运行中的任务、子流程,基于 activityId 分组
460 465 List<HistoricActivityInstance> runActivities = filterList(activities, activity -> activity.getEndTime() == null
461 466 && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY)));
... ... @@ -516,9 +521,9 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
516 521 * 获得【预测(未来)】的活动节点们
517 522 */
518 523 private List<ActivityNode> getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel,
519   - BpmProcessDefinitionInfoDO processDefinitionInfo,
520   - Map<String, Object> processVariables,
521   - List<HistoricActivityInstance> activities) {
  524 + BpmProcessDefinitionInfoDO processDefinitionInfo,
  525 + Map<String, Object> processVariables,
  526 + List<HistoricActivityInstance> activities) {
522 527 // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
523 528 // 包括了历史的操作,不是只有 startEvent 到当前节点的记录
524 529 Set<String> runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId);
... ... @@ -540,8 +545,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
540 545 }
541 546  
542 547 private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel,
543   - BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
544   - BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
  548 + BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
  549 + BpmSimpleModelNodeVO node, Set<String> runActivityIds) {
545 550 // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance
546 551 // 包括了历史的操作,不是只有 startEvent 到当前节点的记录
547 552 if (runActivityIds.contains(node.getId())) {
... ... @@ -585,8 +590,8 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
585 590 }
586 591  
587 592 private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel,
588   - BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
589   - FlowElement node, Set<String> runActivityIds) {
  593 + BpmProcessDefinitionInfoDO processDefinitionInfo, Map<String, Object> processVariables,
  594 + FlowElement node, Set<String> runActivityIds) {
590 595 if (runActivityIds.contains(node.getId())) {
591 596 return null;
592 597 }
... ... @@ -902,6 +907,46 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService
902 907 // 3. 发送流程实例的状态事件
903 908 processInstanceEventPublisher.sendProcessInstanceResultEvent(
904 909 BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status));
  910 +
  911 + // 4. 流程后置通知
  912 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) {
  913 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
  914 + getProcessDefinitionInfo(instance.getProcessDefinitionId());
  915 + if (ObjUtil.isNotNull(processDefinitionInfo) &&
  916 + ObjUtil.isNotNull(processDefinitionInfo.getPostProcessNotifySetting())) {
  917 + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPostProcessNotifySetting();
  918 +
  919 + BpmHttpRequestUtils.executeBpmHttpRequest(instance,
  920 + setting.getUrl(),
  921 + setting.getHeader(),
  922 + setting.getBody(),
  923 + true, setting.getResponse(),
  924 + restTemplate,
  925 + this);
  926 + }
  927 + }
  928 + });
  929 + }
  930 +
  931 + @Override
  932 + public void processProcessInstanceCreated(ProcessInstance instance) {
  933 + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号
  934 + FlowableUtils.execute(instance.getTenantId(), () -> {
  935 + // 流程前置通知
  936 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.
  937 + getProcessDefinitionInfo(instance.getProcessDefinitionId());
  938 + if (ObjUtil.isNotNull(processDefinitionInfo) &&
  939 + ObjUtil.isNotNull(processDefinitionInfo.getPreProcessNotifySetting())) {
  940 + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getPreProcessNotifySetting();
  941 +
  942 + BpmHttpRequestUtils.executeBpmHttpRequest(instance,
  943 + setting.getUrl(),
  944 + setting.getHeader(),
  945 + setting.getBody(),
  946 + true, setting.getResponse(),
  947 + restTemplate,
  948 + this);
  949 + }
905 950 });
906 951 }
907 952 }
... ...
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.core.util.StrUtil;
4 3 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
5   -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
  4 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
  5 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
6 6 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
7 7 import jakarta.annotation.Resource;
8 8 import lombok.Setter;
9 9 import lombok.extern.slf4j.Slf4j;
10 10 import org.flowable.engine.delegate.TaskListener;
11   -import org.flowable.engine.history.HistoricProcessInstance;
12 11 import org.flowable.engine.impl.el.FixedValue;
  12 +import org.flowable.engine.runtime.ProcessInstance;
13 13 import org.flowable.task.service.delegate.DelegateTask;
14 14 import org.springframework.context.annotation.Scope;
15   -import org.springframework.http.HttpEntity;
16   -import org.springframework.http.HttpMethod;
17   -import org.springframework.http.ResponseEntity;
18 15 import org.springframework.stereotype.Component;
19   -import org.springframework.util.LinkedMultiValueMap;
20   -import org.springframework.util.MultiValueMap;
21   -import org.springframework.web.client.RestClientException;
22 16 import org.springframework.web.client.RestTemplate;
23 17  
24   -import java.util.Map;
25   -
26   -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
27 18 import static cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig;
28 19  
29 20 /**
... ... @@ -50,46 +41,35 @@ public class BpmUserTaskListener implements TaskListener {
50 41 @Override
51 42 public void notify(DelegateTask delegateTask) {
52 43 // 1. 获取所需基础信息
53   - HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(delegateTask.getProcessInstanceId());
  44 + ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId());
54 45 BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig);
55 46  
56   - // 2. 获取请求头和请求体
57   - Map<String, Object> processVariables = processInstance.getProcessVariables();
58   - MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
59   - MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
60   - SimpleModelUtils.addHttpRequestParam(headers, listenerHandler.getHeader(), processVariables);
61   - SimpleModelUtils.addHttpRequestParam(body, listenerHandler.getBody(), processVariables);
62   - // 2.1 请求头默认参数
63   - if (StrUtil.isNotEmpty(delegateTask.getTenantId())) {
64   - headers.add(HEADER_TENANT_ID, delegateTask.getTenantId());
65   - }
66   - // 2.2 请求体默认参数
  47 + // 2. 发起请求
67 48 // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去;
68   - body.add("processInstanceId", delegateTask.getProcessInstanceId());
69   - body.add("assignee", delegateTask.getAssignee());
70   - body.add("taskDefinitionKey", delegateTask.getTaskDefinitionKey());
71   - body.add("taskId", delegateTask.getId());
  49 + listenerHandler.getBody()
  50 + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId")
  51 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())
  52 + .setValue(delegateTask.getProcessInstanceId()));
  53 + listenerHandler.getBody()
  54 + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee")
  55 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())
  56 + .setValue(delegateTask.getAssignee()));
  57 + listenerHandler.getBody()
  58 + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey")
  59 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())
  60 + .setValue(delegateTask.getTaskDefinitionKey()));
  61 + listenerHandler.getBody()
  62 + .add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId")
  63 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())
  64 + .setValue(delegateTask.getId()));
  65 + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,
  66 + listenerHandler.getPath(),
  67 + listenerHandler.getHeader(),
  68 + listenerHandler.getBody(),
  69 + false, null,
  70 + restTemplate,
  71 + processInstanceService);
72 72  
73   - // 3. 异步发起请求
74   - // TODO @芋艿:确认要同步,还是异步
75   - HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
76   - try {
77   - ResponseEntity<String> responseEntity = restTemplate.exchange(listenerHandler.getPath(), HttpMethod.POST,
78   - requestEntity, String.class);
79   - log.info("[notify][监听器:{},事件类型:{},请求头:{},请求体:{},响应结果:{}]",
80   - DELEGATE_EXPRESSION,
81   - delegateTask.getEventName(),
82   - headers,
83   - body,
84   - responseEntity);
85   - } catch (RestClientException e) {
86   - log.error("[error][监听器:{},事件类型:{},请求头:{},请求体:{},请求出错:{}]",
87   - DELEGATE_EXPRESSION,
88   - delegateTask.getEventName(),
89   - headers,
90   - body,
91   - e.getMessage());
92   - }
93   - // 4. 是否需要后续操作?TODO 芋艿:待定!
  73 + // 3. 是否需要后续操作?TODO 芋艿:待定!
94 74 }
95 75 }
96 76 \ No newline at end of file
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java
1 1 package cn.iocoder.yudao.module.bpm.service.task.trigger.http;
2 2  
3   -import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
4   -import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.SimpleModelUtils;
5 3 import cn.iocoder.yudao.module.bpm.service.task.trigger.BpmTrigger;
6   -import jakarta.annotation.Resource;
7 4 import lombok.extern.slf4j.Slf4j;
8   -import org.flowable.engine.runtime.ProcessInstance;
9   -import org.springframework.http.HttpEntity;
10   -import org.springframework.http.HttpMethod;
11   -import org.springframework.http.ResponseEntity;
12   -import org.springframework.util.LinkedMultiValueMap;
13   -import org.springframework.util.MultiValueMap;
14   -import org.springframework.web.client.RestClientException;
15   -import org.springframework.web.client.RestTemplate;
16   -
17   -import java.util.List;
18   -import java.util.Map;
19   -
20   -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
21   -import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
22   -import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR;
23 5  
24 6 /**
25 7 * BPM 发送 HTTP 请求触发器抽象类
... ... @@ -29,42 +11,4 @@ import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTA
29 11 @Slf4j
30 12 public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger {
31 13  
32   - @Resource
33   - private RestTemplate restTemplate;
34   -
35   - protected ResponseEntity<String> sendHttpRequest(String url,
36   - MultiValueMap<String, String> headers,
37   - MultiValueMap<String, String> body) {
38   - // TODO @芋艿:要不要抽象一个 Http 请求的工具类,方便复用呢?
39   - // 3. 发起请求
40   - HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers);
41   - ResponseEntity<String> responseEntity;
42   - try {
43   - responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
44   - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
45   - } catch (RestClientException e) {
46   - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
47   - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR);
48   - }
49   - return responseEntity;
50   - }
51   -
52   - protected MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance,
53   - List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) {
54   - Map<String, Object> processVariables = processInstance.getProcessVariables();
55   - MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
56   - headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
57   - SimpleModelUtils.addHttpRequestParam(headers, headerSettings, processVariables);
58   - return headers;
59   - }
60   -
61   - protected MultiValueMap<String, String> buildHttpBody(ProcessInstance processInstance,
62   - List<BpmSimpleModelNodeVO.HttpRequestParam> bodySettings) {
63   - Map<String, Object> processVariables = processInstance.getProcessVariables();
64   - MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
65   - SimpleModelUtils.addHttpRequestParam(body, bodySettings, processVariables);
66   - body.add("processInstanceId", processInstance.getId());
67   - return body;
68   - }
69   -
70 14 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java
... ... @@ -2,13 +2,15 @@ package cn.iocoder.yudao.module.bpm.service.task.trigger.http;
2 2  
3 3 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
4 4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
  5 +import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
5 6 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
  7 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
6 8 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
7 9 import jakarta.annotation.Resource;
8 10 import lombok.extern.slf4j.Slf4j;
9 11 import org.flowable.engine.runtime.ProcessInstance;
10 12 import org.springframework.stereotype.Component;
11   -import org.springframework.util.MultiValueMap;
  13 +import org.springframework.web.client.RestTemplate;
12 14  
13 15 /**
14 16 * BPM HTTP 回调触发器
... ... @@ -20,6 +22,9 @@ import org.springframework.util.MultiValueMap;
20 22 public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger {
21 23  
22 24 @Resource
  25 + private RestTemplate restTemplate;
  26 +
  27 + @Resource
23 28 private BpmProcessInstanceService processInstanceService;
24 29  
25 30 @Override
... ... @@ -36,16 +41,19 @@ public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger {
36 41 log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId);
37 42 return;
38 43 }
39   -
40   - // 2.1 设置请求头
  44 + // 2. 发起请求
41 45 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
42   - MultiValueMap<String, String> headers = buildHttpHeaders(processInstance, setting.getHeader());
43   - // 2.2 设置请求体
44   - MultiValueMap<String, String> body = buildHttpBody(processInstance, setting.getBody());
45 46 // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行
46   - body.add("taskDefineKey", setting.getCallbackTaskDefineKey());
47   -
48   - // 3. 发起请求
49   - sendHttpRequest(setting.getUrl(), headers, body);
  47 + setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam()
  48 + .setKey("taskDefineKey")
  49 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())
  50 + .setValue(setting.getCallbackTaskDefineKey()));
  51 + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,
  52 + setting.getUrl(),
  53 + setting.getHeader(),
  54 + setting.getBody(),
  55 + false, null,
  56 + restTemplate,
  57 + processInstanceService);
50 58 }
51 59 }
... ...
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java
1 1 package cn.iocoder.yudao.module.bpm.service.task.trigger.http;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4   -import cn.hutool.core.util.StrUtil;
5   -import cn.iocoder.yudao.framework.common.core.KeyValue;
6   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 3 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 4 import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting;
9 5 import cn.iocoder.yudao.module.bpm.enums.definition.BpmTriggerTypeEnum;
  6 +import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
10 7 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
11   -import com.fasterxml.jackson.core.type.TypeReference;
12 8 import jakarta.annotation.Resource;
13 9 import lombok.extern.slf4j.Slf4j;
14 10 import org.flowable.engine.runtime.ProcessInstance;
15   -import org.springframework.http.ResponseEntity;
16 11 import org.springframework.stereotype.Component;
17   -import org.springframework.util.MultiValueMap;
18   -
19   -import java.util.HashMap;
20   -import java.util.List;
21   -import java.util.Map;
  12 +import org.springframework.web.client.RestTemplate;
22 13  
23 14 /**
24 15 * BPM 发送同步 HTTP 请求触发器
... ... @@ -30,6 +21,9 @@ import java.util.Map;
30 21 public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger {
31 22  
32 23 @Resource
  24 + private RestTemplate restTemplate;
  25 +
  26 + @Resource
33 27 private BpmProcessInstanceService processInstanceService;
34 28  
35 29 @Override
... ... @@ -45,56 +39,15 @@ public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger {
45 39 log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId);
46 40 return;
47 41 }
48   -
49   - // 2.1 设置请求头
  42 + // 2. 发起请求
50 43 ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId);
51   - MultiValueMap<String, String> headers = buildHttpHeaders(processInstance, setting.getHeader());
52   - // 2.2 设置请求体
53   - MultiValueMap<String, String> body = buildHttpBody(processInstance, setting.getBody());
54   -
55   - // 3. 发起请求
56   - ResponseEntity<String> responseEntity = sendHttpRequest(setting.getUrl(), headers, body);
57   -
58   - // 4.1 判断是否需要解析返回值
59   - if (responseEntity == null || StrUtil.isEmpty(responseEntity.getBody())
60   - || !responseEntity.getStatusCode().is2xxSuccessful()
61   - || CollUtil.isEmpty(setting.getResponse())) {
62   - return;
63   - }
64   - // 4.2 解析返回值, 返回值必须符合 CommonResult 规范。
65   - CommonResult<Map<String, Object>> respResult = JsonUtils.parseObjectQuietly(
66   - responseEntity.getBody(), new TypeReference<>() {
67   - });
68   - if (respResult == null || !respResult.isSuccess()) {
69   - return;
70   - }
71   - // 4.3 获取需要更新的流程变量
72   - Map<String, Object> updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), setting.getResponse());
73   - // 4.4 更新流程变量
74   - if (CollUtil.isNotEmpty(updateVariables)) {
75   - processInstanceService.updateProcessInstanceVariables(processInstanceId, updateVariables);
76   - }
77   - }
78   -
79   - /**
80   - * 从请求返回值获取需要更新的流程变量
81   - *
82   - * @param result 请求返回结果
83   - * @param responseSettings 返回设置
84   - * @return 需要更新的流程变量
85   - */
86   - private Map<String, Object> getNeedUpdatedVariablesFromResponse(Map<String, Object> result,
87   - List<KeyValue<String, String>> responseSettings) {
88   - Map<String, Object> updateVariables = new HashMap<>();
89   - if (CollUtil.isEmpty(result)) {
90   - return updateVariables;
91   - }
92   - responseSettings.forEach(responseSetting -> {
93   - if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) {
94   - updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue()));
95   - }
96   - });
97   - return updateVariables;
  44 + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance,
  45 + setting.getUrl(),
  46 + setting.getHeader(),
  47 + setting.getBody(),
  48 + true, setting.getResponse(),
  49 + restTemplate,
  50 + processInstanceService);
98 51 }
99 52  
100 53 }
... ...