三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

Flowable任务监听器实战:如何动态分配审批人?以Create监听器为例

Flowable任务监听器实战:如何动态分配审批人?以Create监听器为例

Flowable任务监听器实战:动态审批人分配的艺术与陷阱

审批流程中最大的痛点莫过于"这个任务该由谁来处理?"——当审批规则涉及部门层级、项目角色或动态条件时,硬编码的assignee配置立刻显得捉襟见肘。上周我就踩了个坑:财务部重组后,原有审批链彻底失效,300多张报销单卡在系统里像堵在早高峰的高架桥上。这正是动态审批人分配技术存在的意义。

1. 解剖Create监听器:审批人分配的神经末梢

在Flowable的监听器家族中,create类型是唯一能在任务诞生的瞬间介入的"接生婆"。与assignment(签收时触发)和complete(完成后触发)不同,它的独特价值在于:

  • 时机精准:在任务对象刚创建但尚未持久化时执行
  • 权限完整:可以修改任务的所有属性,包括assignee、candidateUsers等
  • 无副作用:不会影响任务生命周期状态

典型的动态分配场景包括:

  1. 根据申请人部门匹配对应主管
  2. 按项目阶段切换审批角色
  3. 基于金额阈值触发不同审批链
  4. 节假日自动跳转值班人员
public class DynamicAssigneeListener implements TaskListener { @Override public void notify(DelegateTask task) { String eventName = task.getEventName(); if (!EVENTNAME_CREATE.equals(eventName)) return; // 从流程变量获取申请人ID String applicant = (String) task.getVariable("applicantId"); // 模拟组织架构服务调用 String departmentHead = departmentService.getDepartmentHead(applicant); task.setAssignee(departmentHead); } }

警告:在监听器中直接调用外部服务时,务必添加事务回滚和超时控制,否则可能造成流程引擎阻塞

2. Spring集成实战:当监听器遇上依赖注入

纯Java类实现的监听器就像没有GPS的出租车——能跑但不智能。与Spring整合后,才能解锁服务注入、事务管理等高级能力:

2.1 配置之道:三种集成模式对比

方式优点缺点适用场景
实现ApplicationContextAware简单直接全局静态变量有内存泄漏风险小型系统快速开发
使用DelegateExpression支持SpEL表达式调试困难需要灵活配置的场景
自定义TaskListenerFactory完全控制创建过程实现复杂度高企业级复杂系统

推荐采用DelegateExpression方案,在流程定义中这样配置:

<userTask id="leaderApproval" name="主管审批"> <extensionElements> <flowable:taskListener event="create" delegateExpression="${dynamicAssigneeListener}"/> </extensionElements> </userTask>

对应的Spring Bean配置:

@Component("dynamicAssigneeListener") public class DynamicAssigneeListener implements TaskListener { @Autowired private DepartmentService departmentService; @Override @Transactional(propagation = Propagation.REQUIRES_NEW) public void notify(DelegateTask task) { // 实现细节... } }

2.2 性能陷阱与解决方案

在压力测试中,我们发现监听器的数据库查询成为瓶颈。以下是优化前后的对比数据:

优化手段QPS提升平均响应时间降低
引入本地缓存220%65%
批量查询替代循环单查180%55%
异步日志记录150%30%

缓存实现示例:

@Cacheable(value = "deptLeaders", key = "#deptId") public String getDepartmentHead(String deptId) { // 数据库查询逻辑 }

3. 流程设计器中的隐形战场

在Flowable Modeler中配置监听器时,这些细节决定成败:

3.1 类加载的暗礁

设计器默认的类加载机制会导致:

  • 无法识别Spring Bean
  • 热部署时出现ClassCastException
  • 多模块项目中的类找不到

解决方案是在flowable.custom.properties中添加:

flowable.taskListener.delegateExpression.allow=* flowable.taskListener.spring.aware=true

3.2 版本兼容性矩阵

不同Flowable版本对监听器的支持差异:

版本Spring支持事务传播表达式语言
6.3.0部分不支持JUEL
6.5.0完整支持SpEL
6.7.2完整增强SpEL+JUEL

经验:在6.5.0+版本中,建议始终使用delegateExpression而非class属性

4. 监听器组合拳:超越Create的复杂场景

当简单的动态分配无法满足需求时,可能需要监听器联合作战:

4.1 Create + Assignment组合模式

graph TD A[任务创建] -->|Create监听器| B[设置默认审批人] B --> C[审批人修改] C -->|Assignment监听器| D[记录变更历史]

实际代码示例:

// Create阶段设置初始审批人 public class InitAssigneeListener implements TaskListener { public void notify(DelegateTask task) { task.setAssignee(getDefaultApprover()); } } // Assignment阶段记录变更 public class AuditAssigneeListener implements TaskListener { public void notify(DelegateTask task) { auditService.log( task.getOriginalAssignee(), task.getAssignee() ); } }

4.2 多监听器执行顺序控制

在复杂流程中,可能需要多个create监听器协同工作。执行顺序由它们在bpmn中的声明顺序决定:

<extensionElements> <flowable:taskListener event="create" class="com.example.ListenerA"/> <flowable:taskListener event="create" delegateExpression="${listenerB}"/> <flowable:taskListener event="create" expression="${listenerC.execute(task)}"/> </extensionElements>

常见问题排查表:

症状可能原因解决方案
监听器未触发事件类型配置错误检查event属性值
Spring注入失败未启用spring.aware配置flowable.custom.properties
事务不回滚未配置Propagation.REQUIRES_NEW添加事务注解
性能低下循环查询数据库引入缓存或批量查询

在电商退款审批系统中,我们最终实现的动态规则引擎包含:

  • 基于金额的审批链跳跃
  • 节假日自动路由
  • 审批人黑名单过滤
  • 紧急工单越级上报

这套组合拳使平均审批时效从47小时缩短到6.2小时,但最大的收获是——终于不用在凌晨三点被审批异常告警吵醒了。

← 返回列表