fix:【bpm 工作流】跨进程调用,无法回调 crm 的工作流结果的问题

pull/150/MERGE
YunaiV 2025-07-30 18:46:32 +08:00
parent b46f29e45f
commit 8eea97add4
7 changed files with 100 additions and 15 deletions

View File

@ -15,6 +15,7 @@ import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfigu
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -124,6 +125,7 @@ public class YudaoWebAutoConfiguration implements WebMvcConfigurer {
*/ */
@Bean @Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder.build(); return restTemplateBuilder.build();
} }

View File

@ -34,6 +34,11 @@ public class BpmProcessInstanceStatusEvent extends ApplicationEvent {
*/ */
private String businessKey; private String businessKey;
public BpmProcessInstanceStatusEvent() {
// new Object() 保证非空
super(new Object());
}
public BpmProcessInstanceStatusEvent(Object source) { public BpmProcessInstanceStatusEvent(Object source) {
super(source); super(source);
} }

View File

@ -40,7 +40,7 @@ public interface ErrorCodeConstants {
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败");
ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置");
ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消");

View File

@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.validation.Valid;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
/**
*
*
* @author
*/
public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener {
@Override
public String getProcessDefinitionKey() {
return "crm-receivable-audit";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://crm-server/rpc-api/crm/receivable/update-audit-status");
}
}

View File

@ -6,15 +6,15 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils; import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService; import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.http.HttpEntity; import org.springframework.http.*;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestClientException;
@ -26,7 +26,7 @@ import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR;
/** /**
* HTTP * HTTP
@ -42,7 +42,6 @@ public class BpmHttpRequestUtils {
List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams, List<BpmSimpleModelNodeVO.HttpRequestParam> bodyParams,
Boolean handleResponse, Boolean handleResponse,
List<KeyValue<String, String>> response) { List<KeyValue<String, String>> response) {
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
// 1.1 设置请求头 // 1.1 设置请求头
@ -51,6 +50,7 @@ public class BpmHttpRequestUtils {
MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams); MultiValueMap<String, String> body = buildHttpBody(processInstance, bodyParams);
// 2. 发起请求 // 2. 发起请求
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate); ResponseEntity<String> responseEntity = sendHttpRequest(url, headers, body, restTemplate);
// 3. 处理返回 // 3. 处理返回
@ -78,27 +78,55 @@ public class BpmHttpRequestUtils {
} }
} }
public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event,
String url) {
// 1.1 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
if (TenantContextHolder.getTenantId() != null) {
headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
} else {
BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId());
if (processInstance != null) {
headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
}
}
// 1.2 设置请求体
// MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
// body.add("id", event.getId());
// body.add("processDefinitionKey", event.getProcessDefinitionKey());
// body.add("status", event.getStatus().toString());
// if (StrUtil.isNotEmpty(event.getBusinessKey())) {
// body.add("businessKey", event.getBusinessKey());
// }
// 2. 发起请求
RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
sendHttpRequest(url, headers, event, restTemplate);
}
public static ResponseEntity<String> sendHttpRequest(String url, public static ResponseEntity<String> sendHttpRequest(String url,
MultiValueMap<String, String> headers, MultiValueMap<String, String> headers,
MultiValueMap<String, String> body, Object body,
RestTemplate restTemplate) { RestTemplate restTemplate) {
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(body, headers); HttpEntity<Object> requestEntity = new HttpEntity<>(body, headers);
ResponseEntity<String> responseEntity; ResponseEntity<String> responseEntity;
try { try {
responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); log.info("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity);
} catch (RestClientException e) { } catch (RestClientException e) {
log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); log.error("[sendHttpRequest][HTTP 请求,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage());
throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); throw exception(PROCESS_INSTANCE_HTTP_CALL_ERROR);
} }
return responseEntity; return responseEntity;
} }
public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance, public static MultiValueMap<String, String> buildHttpHeaders(ProcessInstance processInstance,
List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) { List<BpmSimpleModelNodeVO.HttpRequestParam> headerSettings) {
Map<String, Object> processVariables = processInstance.getProcessVariables();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); headers.add(HEADER_TENANT_ID, processInstance.getTenantId());
Map<String, Object> processVariables = processInstance.getProcessVariables();
addHttpRequestParam(headers, headerSettings, processVariables); addHttpRequestParam(headers, headerSettings, processVariables);
return headers; return headers;
} }

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.framework.rpc.config; package cn.iocoder.yudao.module.bpm.framework.rpc.config;
import cn.iocoder.yudao.module.bpm.api.event.CrmReceivableStatusListener;
import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.PostApi; import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi; import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
@ -7,11 +8,22 @@ import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.RoleApi; import cn.iocoder.yudao.module.system.api.permission.RoleApi;
import cn.iocoder.yudao.module.system.api.sms.SmsSendApi; import cn.iocoder.yudao.module.system.api.sms.SmsSendApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) @Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false)
@EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, @EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class,
PermissionApi.class}) PermissionApi.class})
public class RpcConfiguration { public class RpcConfiguration {
// ========== 特殊:解决微 yudao-cloud 微服务场景下,跨服务(进程)无法 Listener 的问题 ==========
@Bean
@ConditionalOnMissingBean(name = "crmReceivableStatusListener")
public CrmReceivableStatusListener crmReceivableStatusListener() {
return new CrmReceivableStatusListener();
}
} }

View File

@ -1,20 +1,30 @@
package cn.iocoder.yudao.module.crm.service.receivable.listener; package cn.iocoder.yudao.module.crm.service.receivable.listener;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener;
import cn.iocoder.yudao.module.crm.enums.ApiConstants;
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService;
import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableServiceImpl; import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.stereotype.Component; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/** /**
* *
* *
* @author HUIHUI * @author HUIHUI
*/ */
@Component @RestController
@Validated
@FeignClient(name = ApiConstants.NAME) // TODO 芋艿fallbackFactory =
public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener { public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventListener {
private static final String PREFIX = ApiConstants.PREFIX + "/receivable";
@Resource @Resource
private CrmReceivableService receivableService; private CrmReceivableService receivableService;
@ -24,7 +34,8 @@ public class CrmReceivableStatusListener extends BpmProcessInstanceStatusEventLi
} }
@Override @Override
public void onEvent(BpmProcessInstanceStatusEvent event) { @PostMapping(PREFIX + "/update-audit-status") // 目的:提供给 bpm-server rpc 调用
public void onEvent(@RequestBody BpmProcessInstanceStatusEvent event) {
receivableService.updateReceivableAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus()); receivableService.updateReceivableAuditStatus(Long.parseLong(event.getBusinessKey()), event.getStatus());
} }