芋道cloud(yudao-cloud)版本中,流程引擎的监听器使用心得(包括外部表插入和遇到的坑)
·
业务需求:创建一个简易审批流程,在流程结束时,用执行监听器,在监听器内遵循RPC 使用规约,远程调用另一个服务的业务进行数据入库操作
步骤一:建立新模块,并在新模块中遵循 yudao-cloud文档 微服务手册 服务调用 Feign中示例,建立api接口、实现类、监听器
API:
package cn.iocoder.yudao.module.tr.api.coursetitleform;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.tr.enums.ApiConstants;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(name = ApiConstants.NAME) // ① @FeignClient 注解
@Tag(name = "RPC 服务 - 课题") // ② Swagger 接口文档
public interface CourseTitleFormApi {
String PREFIX = ApiConstants.PREFIX + "/course-title-form";
@PostMapping(PREFIX + "/add") // ③ Spring MVC 接口注解
@Operation(summary = "通过用户 ID 查询用户") // ② Swagger 接口文档
@Parameter(name = "id", description = "部门编号", required = true, example = "1024") // ② Swagger 接口文档
CommonResult<Long> create(
@RequestParam("projectType") String projectType,
@RequestParam("courseNo") Long courseNo,
@RequestParam("courseName") String courseName,
@RequestParam("researchDirection") String researchDirection,
@RequestParam("principal") String principal,
@RequestParam("principalDegree") String principalDegree,
@RequestParam("titleForFunds") String titleForFunds,
@RequestParam("directFunds") String directFunds,
@RequestParam("indirectFunds") String indirectFunds,
@RequestParam("startTime") String startTime, // 建议使用字符串形式传输时间
@RequestParam("endTime") String endTime,
@RequestParam("fileUrl") String fileUrl
);
}
这里有个坑,不能用@RequestBody接参,否则会出现重复读取InputStream报错:java.lang.IllegalStateException: getInputStream() has already been called for this request

接口实现:
package cn.iocoder.yudao.module.tr.service.coursetitleform;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.tr.api.coursetitleform.CourseTitleFormApi;
import cn.iocoder.yudao.module.tr.api.coursetitleform.dto.CourseTitleFormRespDTO;
import cn.iocoder.yudao.module.tr.controller.admin.coursetitleform.vo.CourseTitleFormSaveReqVO;
import cn.iocoder.yudao.module.tr.dal.dataobject.coursetitleform.CourseTitleFormDO;
import cn.iocoder.yudao.module.tr.dal.mysql.coursetitleform.CourseTitleFormMapper;
import cn.iocoder.yudao.module.tr.utils.DateUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import static org.bouncycastle.asn1.cmc.CMCStatus.success;
/**
* @author 16050
* @date 2025/7/21 14:11
* @description TODO
*/
@RestController
@Validated
public class CourseTitleFormApiImpl implements CourseTitleFormApi {
@Resource
private CourseTitleFormMapper courseTitleFormMapper;
@Override
public CommonResult<Long> create(
String projectType,
Long courseNo,
String courseName,
String researchDirection,
String principal,
String principalDegree,
String titleForFunds,
String directFunds,
String indirectFunds,
String startTime,
String endTime,
String fileUrl
) {
// 构造保存 VO
CourseTitleFormSaveReqVO createReqVO = new CourseTitleFormSaveReqVO();
createReqVO.setProjectType(projectType);
createReqVO.setCourseNo(courseNo);
createReqVO.setCourseName(courseName);
createReqVO.setResearchDirection(researchDirection);
createReqVO.setPrincipal(principal);
createReqVO.setPrincipalDegree(principalDegree);
createReqVO.setTitleForFunds(titleForFunds);
createReqVO.setDirectFunds(directFunds);
createReqVO.setIndirectFunds(indirectFunds);
createReqVO.setStartTime(DateUtils.parseLocalDateTime(startTime)); // 字符串转 LocalDateTime
createReqVO.setEndTime(DateUtils.parseLocalDateTime(endTime));
createReqVO.setFileUrl(fileUrl);
// 保存数据
CourseTitleFormDO courseTitleForm = BeanUtils.toBean(createReqVO, CourseTitleFormDO.class);
courseTitleFormMapper.insert(courseTitleForm);
return CommonResult.success(courseTitleForm.getId());
}
}
bpm中监听器:
package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener.tr;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.tr.api.coursetitleform.CourseTitleFormApi;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.delegate.JavaDelegate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @author 16050
* @date 2025/7/19 16:25
* @description TODO
*/
@Component("courseSubmitListener") // 指定 Bean 名称
@Slf4j
public class CourseSubmitListener implements JavaDelegate {
@Resource
private CourseTitleFormApi courseTitleFormApi;
public void execute(DelegateExecution execution) {
try {
String projectType = (String) execution.getVariable("projectType");
String courseNo = (String) execution.getVariable("courseNo");
String startTime = (String) execution.getVariable("startTime");
String courseTitleName = (String) execution.getVariable("courseTitleName");
String researchDirection = (String) execution.getVariable("researchDirection");
String principal = (String) execution.getVariable("principal");
String principalDegree = (String) execution.getVariable("principalDegree");
String titleForFunds = (String) execution.getVariable("titleForFunds");
String directFunds = (String) execution.getVariable("directFunds");
String indirectFunds = (String) execution.getVariable("indirectFunds");
String endTime = (String) execution.getVariable("endTime");
String fileUrl = (String) execution.getVariable("fileUrl");
if (fileUrl == null) {
fileUrl = ""; // 或者给个默认值,比如 null
}
String startDateTimeStr = startTime + " 00:00:00";
String endDateTimeStr = endTime + " 23:59:59";
// 直接调用 Feign 方法(注意:必须传字符串,接口签名定义的就是字符串)
CommonResult<Long> result = courseTitleFormApi.create(
projectType,
Long.valueOf(courseNo),
courseTitleName,
researchDirection,
principal,
principalDegree,
titleForFunds,
directFunds,
indirectFunds,
startDateTimeStr, // 字符串传入即可
endDateTimeStr,
fileUrl
);
if (result.isSuccess()) {
log.info("[流程监听] 课题创建成功,ID={}", result.getData());
} else {
log.error("[流程监听] 课题创建失败,错误消息: {}", result.getMsg());
}
} catch (Exception e) {
log.error("[流程监听] 课题创建异常", e);
}
}
}
步骤二:建立流程,并引入监听器
流程:

这里一定要用全局的执行监听器,不然会重复调用两次监听器。
表单:
中记得设置字段ID,监听器中会通过这个ID来取值,如之前监听器中写的(String) execution.getVariable("projectType");

监听器:

这里用代理表达式,和监听器中的@Component("courseSubmitListener")名称一致
更多推荐

所有评论(0)