1. 从零开始理解 yudao-cloud 工作流监听器如果你正在用 yudao-cloud 做项目肯定遇到过这样的场景一个审批流程走完了你需要自动通知相关人或者自动更新数据库里的某个状态又或者触发下一个业务流程。手动去处理这些“后事”不仅麻烦还容易出错。这时候工作流监听器就是你最好的帮手。简单来说监听器就像是你安插在工作流关键节点上的“耳朵”和“手”。当流程流转到某个特定时刻比如任务创建了、任务完成了、整个流程结束了这个“耳朵”就会立刻听到然后你预先写好的逻辑那只“手”就会自动执行。在 yudao-cloud 这个基于 RuoYi 的微服务开源平台上用好监听器能让你从繁琐的流程后续处理中彻底解放出来实现真正的自动化。我刚开始接触的时候也犯迷糊官方文档虽然给了方向但具体怎么配、代码怎么写、坑在哪里还得自己摸索。这篇文章我就把我这几年在实战中积累的经验用最直白的话分享给你。咱们不聊晦涩的理论直接上手从环境准备、两种设计器的不同配置方法到代码怎么写、常见的坑怎么避一步步带你搞定。无论你是刚接触 yudao-cloud 的新手还是想优化现有流程的老手这篇指南都能让你有实实在在的收获。2. 环境准备与基础概念扫盲在动手配置之前咱们先把“战场”准备好顺便理清几个关键概念这样后面操作起来才不会云里雾里。2.1 你的开发环境准备好了吗首先确保你的 yudao-cloud 项目已经能在本地跑起来。你需要有后端代码通常从 Gitee 的 yudao-cloud 仓库克隆和前端代码ruoyi-vue-pro。我建议你用一个现成的、能运行的流程来测试监听器比如系统自带的请假流程。这样你修改和测试的效果能立刻看到比凭空造一个流程要直观得多。后端项目启动后重点关注网关服务gateway-server。因为后面我们会看到在一种配置方式下监听器是通过 HTTP 调用后端接口的这个调用的地址就和网关服务息息相关。你可以在application.yml文件里找到它的spring.application.name和端口号记下来后面会用到。2.2 监听器类型执行监听器 vs. 任务监听器这是两个核心概念一定要分清楚它们监听的“时机”完全不同。执行监听器是挂在流程实例Process Instance或活动Activity比如一个用户任务节点上的。它关心的是流程本身的“生命事件”。主要监听三个时机start一个活动开始执行时。比如流程流转到了“部门经理审批”这个节点该节点刚开始被激活的那一刻。end一个活动执行完毕时。比如“部门经理审批”这个节点已经完成即将离开的那一刻。take专门针对顺序流Sequence Flow当流程沿着某条线流转时触发。这个用得相对少一些。任务监听器则是挂在用户任务User Task上的。它关心的是“人的任务”的生命周期。主要监听四个时机create一个审批任务刚被创建出来还没到任何人待办列表里的时候。assignment任务被分配给具体处理人的时候。注意在 yudao-cloud 里任务创建后通常会自动分配所以create和assignment可能紧接着发生。complete任务被审批人提交完成的时候。这是最常用、最最重要的时机比如经理点击了“同意”在流程继续往下走之前你想干点啥如更新业务状态就是在这里做。delete任务被删除时比如流程被中止了。简单类比执行监听器像公司的监控摄像头盯着整个办公室流程和各会议室活动的进出。任务监听器像你办公桌上的电话专门响应对你个人任务的指派和完成。在 yudao-cloud 的实际业务中complete任务监听器是你打交道最多的。3. 实战配置BPMN 设计器下的监听器yudao-cloud 的流程设计器有两种功能强大的 BPMN 设计器和简洁的 SIMPLE 设计器。我们先看更标准、更灵活的 BPMN 设计器配置方式。这种方式是通过实现特定的 Java 接口来编写监听逻辑的。3.1 在流程设计器中添加监听器首先我们从前端操作开始。登录系统进入【工作流程】-【流程管理】-【流程模型】。选择一个已有的模型或新建一个记得选择“流程类型”为BPMN 设计器。在图形化设计界面点击一个用户任务节点比如“经理审批”。右侧会弹出属性面板找到“监听器”选项卡。点击“添加”选择监听器类型。这里我们以最常用的“任务监听器”为例。在“事件类型”下拉框中选择“complete”任务完成。关键的“监听器类型”下拉框选择“Java 类”。在“Java 类”输入框中填写你即将要编写的监听器类的全限定名。例如cn.iocoder.yudao.module.bpm.service.listener.MyTaskCompleteListener。这一步相当于在流程图纸上做了一个标记“当这个任务完成时去调用这个 Java 类”。接下来我们就去实现这个 Java 类。3.2 编写 Java 监听器类在后端项目中你需要创建一个类来实现org.flowable.engine.delegate.TaskListener接口。我习惯在module-bpm模块下建立一个listener包来统一管理。package cn.iocoder.yudao.module.bpm.service.listener; import org.flowable.engine.delegate.DelegateTask; import org.flowable.engine.delegate.TaskListener; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; /** * 自定义任务完成监听器 */ Component // 务必加入Spring容器管理 Slf4j public class MyTaskCompleteListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { // 1. 这里可以获取到丰富的流程上下文信息 String processInstanceId delegateTask.getProcessInstanceId(); // 流程实例ID String taskDefinitionKey delegateTask.getTaskDefinitionKey(); // 当前任务定义Key如 managerAudit String eventName delegateTask.getEventName(); // 事件名称这里是 complete // 2. 获取业务关联键通常在启动流程时传入 String businessKey delegateTask.getExecution().getProcessInstanceBusinessKey(); log.info([任务完成监听器] 流程实例ID: {}, 任务Key: {}, 业务Key: {}, processInstanceId, taskDefinitionKey, businessKey); // 3. 根据任务Key执行不同的业务逻辑 if (managerAudit.equals(taskDefinitionKey)) { // 经理审批通过了执行特定逻辑 log.info(经理审批节点完成开始更新业务状态...); // 调用你的业务Service更新订单、合同等状态 // yourBusinessService.updateStatus(businessKey, APPROVED); } else if (financeAudit.equals(taskDefinitionKey)) { // 财务审批逻辑 log.info(财务审批节点完成...); } // 4. 你还可以通过 delegateTask 设置或获取流程变量 // delegateTask.setVariable(auditResult, pass); // Object someVar delegateTask.getVariable(someVariable); } }几个实战要点Component注解必不可少这样 Spring 才能管理这个类Flowable 引擎才能实例化它。善用DelegateTask对象它是你的信息宝库几乎能拿到流程当前所有的上下文数据。通过taskDefinitionKey区分节点这是最常用的方法针对不同审批节点执行不同逻辑。业务关联键businessKey这是连接工作流和业务数据的生命线。启动流程时一定要把业务主键如订单ID传进去这里才能取到并进行后续业务处理。3.3 执行监听器的配置与编写执行监听器的配置步骤类似只是在属性面板里选择“执行监听器”事件类型选start或end。对应的 Java 类需要实现org.flowable.engine.delegate.ExecutionListener接口。package cn.iocoder.yudao.module.bpm.service.listener; import org.flowable.engine.delegate.DelegateExecution; import org.flowable.engine.delegate.ExecutionListener; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; Component Slf4j public class ProcessEndListener implements ExecutionListener { Override public void notify(DelegateExecution execution) { String eventName execution.getEventName(); // start 或 end if (EVENTNAME_END.equals(eventName)) { String processInstanceId execution.getProcessInstanceId(); String businessKey execution.getProcessInstanceBusinessKey(); log.info([流程结束监听器] 流程实例 {} 已结束业务Key: {}, processInstanceId, businessKey); // 流程整体结束后的清理工作或最终状态更新 // yourBusinessService.finalizeProcess(businessKey); } } }使用场景执行监听器更适合做流程级别的全局事件处理比如流程开始时初始化一些变量流程结束时发送一个总的通知或进行归档。4. 实战配置SIMPLE 设计器下的 HTTP 监听器如果你觉得写 Java 类、部署重启服务有点重或者你的监听逻辑很简单比如只是调用一个已有的 HTTP API那么 SIMPLE 设计器提供的 HTTP 监听器方式就非常轻量、快捷。它的原理是在任务完成时由流程引擎自动向一个你配置的 HTTP 接口发起调用。4.1 前端配置步骤详解在流程模型列表新建或编辑一个模型这次选择流程类型为SIMPLE 设计器。设计界面相对简单拖出一个用户任务节点后你需要切换到“流程设计”标签页这是一个 JSON 配置视图。在这里找到对应任务的配置。监听器配置通常如下所示{ tasks: [ { id: userTask1, name: 经理审批, assignee: ${manager}, listeners: [ { event: complete, // 监听完成事件 type: http, config: { url: http://gateway-server:48080/admin-api/bpm/task/complete-callback, method: POST, headers: { Content-Type: application/json, id: {{id}} // 这个id是任务ID由引擎自动替换 }, body: {\processInstanceId\: \{{processInstanceId}}\, \taskId\: \{{id}}\} } } ] } ] }关键配置解析url这是最重要的参数。格式为http://{网关服务名}:{端口}/{你的业务接口路径}。gateway-server必须替换成你实际网关服务的spring.application.name。48080是网关端口根据你的环境调整。/admin-api/bpm/task/complete-callback是你需要在后端编写的接收回调的接口地址。headers可以传递请求头。这里id: {{id}}是一个模板变量引擎在调用时会自动替换为当前任务的 ID。注意根据原始文章提示processInstanceId已经在BpmUserTaskListener.notify中设置到请求头了所以你可能不需要再单独配置。body请求体。你可以用{{variableName}}的格式引用流程变量引擎会替换为实际值。这里我们把流程实例ID和任务ID都传过去。4.2 编写后端接收接口前端配置好了“呼叫地址”后端自然要有一个“接电话”的接口。这个接口就是一个普通的 Spring MVC Controller。package cn.iocoder.yudao.module.bpm.controller.admin.task; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; Tag(name 管理后台 - 任务回调) RestController RequestMapping(/bpm/task) Slf4j public class TaskCallbackController { PostMapping(/complete-callback) Operation(summary 任务完成回调接口) // 注意参数名需要和HTTP请求中传递的名字对应 public CommonResultBoolean completeCallback( RequestHeader(id) String taskId, // 从请求头获取任务ID RequestBody(required false) TaskCompleteRequest request) { // 从请求体获取JSON参数 log.info([HTTP任务完成回调] 任务ID: {}, 流程实例ID: {}, taskId, request.getProcessInstanceId()); // 1. 参数校验 (建议) if (taskId null) { return CommonResult.error(400, 任务ID不能为空); } // 2. 根据 processInstanceId 或 taskId查询业务数据 // String businessKey yourQueryService.getBusinessKeyByProcessInstanceId(request.getProcessInstanceId()); // 3. 执行你的核心业务逻辑例如更新状态 // yourBusinessService.handleTaskComplete(businessKey, managerAudit); // 4. 返回成功 return CommonResult.success(true); } // 内部请求体定义 Data public static class TaskCompleteRequest { private String processInstanceId; private String taskId; } }重要提醒接口幂等性HTTP 调用可能会因为网络问题重试所以你的接口逻辑要保证幂等即同一任务多次回调结果应该一致。可以在接口里先判断该任务是否已处理过。超时与重试Flowable 对 HTTP 调用有超时和重试机制可在引擎配置中调整如果你的业务逻辑很耗时需要考虑异步处理避免阻塞流程引擎。错误处理如果你的接口返回非 2xx 状态码Flowable 会将此任务视为“处理失败”可能导致流程挂起。务必做好异常捕获和日志记录。5. 避坑指南与高级技巧配置听起来不难但实际踩的坑才让人印象深刻。下面分享几个我趟过的雷和提升效率的技巧。5.1 常见问题排查问题一监听器的 Java 类不生效。检查点1类上有没有加Component注解确保 Spring 能扫描到这个类。检查点2流程模型有没有部署并激活修改监听器配置后必须重新部署流程定义新的实例才会生效。检查点3全限定类名是否拼写正确在流程设计器里配置时一个字母都不能错。检查点4查看应用日志。监听器逻辑里的日志输出是否打印了如果没有说明根本没执行到。如果有异常会在这里看到堆栈信息。问题二HTTP 监听器调用失败。检查点1url地址是否正确特别是gateway-server这个服务名在 Docker 或 K8s 环境内是否可解析本地测试可以用localhost但要注意端口。检查点2网络是否通畅网关服务是否正在运行可以用 Postman 手动构造一个请求模拟调用一下你的回调接口看能否通。检查点3查看 Flowable 引擎日志。通常会有 HTTP 调用失败的具体原因如连接超时、404找不到等。问题三获取不到业务数据。根源十有八九是businessKey没传对。确保在启动流程实例的代码里正确设置了processInstanceBusinessKey。ProcessInstance instance runtimeService.startProcessInstanceByKey(yourProcessKey, businessKey);技巧除了businessKey也可以在启动时或任务完成前通过runtimeService.setVariable()把更多业务数据存入流程变量在监听器里再用delegateTask.getVariable()取出来。5.2 性能与设计建议监听器逻辑要轻量监听器是同步执行的会阻塞流程的推进。如果你的业务逻辑复杂比如调用外部系统、处理大量数据强烈建议在监听器里只做发消息如发 MQ 消息或提交异步任务的操作将耗时的业务逻辑转移到后台异步执行。善用全局监听器如果你有很多流程都需要在任务完成时做同一件事比如发站内信写很多个类似的监听器类很冗余。可以考虑实现一个全局的TaskListener然后在notify方法里根据taskDefinitionKey进行分发处理。不过这种方式需要更精细的设计避免耦合。SIMPLE 设计器的优势对于简单的、通用的回调逻辑如通知一个统一的审计服务HTTP 方式无需修改后端代码、无需重启服务直接修改流程JSON配置并重新部署即可在动态性要求高的场景下非常方便。5.3 一个综合实战案例假设我们有一个“用章申请”流程包含“部门经理审批”和“法务部审批”两个节点。需求是经理审批通过后自动锁定用章的印章状态整个流程结束后无论通过与否都发送一条汇总消息给申请人。实现方案针对“部门经理审批”任务在 BPMN 设计器中配置一个任务监听器complete事件指向一个SealLockListener。在该监听器中通过businessKey申请单ID调用印章服务锁定对应印章。针对流程结束在流程图的结束事件上配置一个执行监听器end事件指向一个ProcessEndNotificationListener。在这个监听器中获取流程变量中的申请单ID和最终结果调用消息服务发送通知。这样通过两种监听器的组合我们就清晰、优雅地完成了业务自动化。整个过程审批者完全无感数据却准确无误地同步了。