别再只会画流程图了!Flowable流程设计器里任务监听器和多实例的实战用法详解
Flowable高级流程设计:任务监听器与多实例的实战艺术
在流程自动化领域,掌握基础节点配置只是入门的第一步。真正让业务流程活起来的,是那些能够处理复杂业务场景的高级功能。本文将深入探讨Flowable中两个最具威力的特性——任务监听器和多实例配置,通过真实业务场景展示它们如何解决审批流程中的动态分配、异步触发和并行处理等核心难题。
1. 任务监听器的动态魔法
任务监听器是Flowable中最灵活的扩展点之一,它允许开发者在任务生命周期的关键节点注入自定义逻辑。不同于固定分配审批人的简单方式,监听器能够根据运行时数据动态决策,实现真正智能化的流程路由。
1.1 监听器类型与业务场景匹配
Flowable提供了四种标准事件类型,每种对应不同的业务需求:
- create监听器:当报销单金额超过部门审批权限时,自动升级到更高层级
- assignment监听器:在任务被认领时记录操作日志,用于审计追踪
- complete监听器:审批完成后自动发送通知邮件给相关干系人
- delete监听器:任务被删除时执行资源清理操作
实际项目中,最常用的是create和complete这两种监听器。前者解决"谁来处理"的问题,后者处理"完成后做什么"的需求。
1.2 动态审批人分配实战
考虑这样一个场景:销售合同的审批人需要根据合同金额和客户等级动态确定。固定分配的方式显然无法满足这种灵活需求。下面是一个典型的DeptManagerListener实现:
public class DynamicApproverListener implements TaskListener { @Override public void notify(DelegateTask task) { // 从流程变量获取业务数据 Double amount = (Double) task.getVariable("contractAmount"); String clientLevel = (String) task.getVariable("clientLevel"); // 业务规则决策 if (amount > 100000 || "VIP".equals(clientLevel)) { task.setAssignee("financialDirector"); // 大金额或VIP客户需财务总监审批 } else if (amount > 50000) { task.addCandidateUser("salesManager"); // 中等金额由销售经理候选 task.addCandidateGroup("financeTeam"); // 同时财务团队参与审批 } else { task.setAssignee("teamLeader"); // 小额合同组长直接审批 } } }在流程设计器中配置这个监听器时,需要注意几个关键点:
- 事件类型选择"create"
- 类名填写完整包路径
- 确保类在应用的classpath中可访问
提示:监听器类应当保持无状态设计,避免使用实例变量,因为Flowable可能会复用监听器实例
1.3 任务完成后的连锁反应
complete监听器常被用于处理审批后的连带操作。例如在采购审批流程中,当最后一个审批节点完成时,需要:
- 更新采购单状态为"已批准"
- 触发ERP系统的订单创建
- 通知申请人和供应商
public class ProcurementCompleteListener implements TaskListener { @Override public void notify(DelegateTask task) { // 获取流程业务ID String businessKey = task.getExecution().getProcessInstanceBusinessKey(); // 更新采购单状态 procurementService.updateStatus(businessKey, "APPROVED"); // 调用ERP接口 erpService.createPurchaseOrder( task.getVariable("items"), task.getVariable("supplierId") ); // 发送通知 notificationService.sendToApplicant(task.getVariable("applicant")); notificationService.sendToSupplier(task.getVariable("supplierContact")); } }这种设计将业务逻辑与流程引擎解耦,流程定义只关心"什么时候"触发操作,而具体"做什么"则由业务系统决定。
2. 多实例:并行处理的利器
多实例(Multi-Instance)是处理会签、并行审批等场景的核心机制。它允许单个任务节点在运行时生成多个实例,根据配置决定这些实例是顺序执行还是并行处理。
2.1 串行与并行的选择策略
在报销审批流程中,这两种模式有典型的应用场景:
- 串行(Sequential):适用于层级审批,如部门审批→财务审批→总经理审批
- 并行(Parallel):适用于同级会签,如三个部门经理同时审批跨部门项目
配置多实例时,关键的决策点是isSequential属性:
<userTask id="multiInstanceTask" name="会签审批"> <multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${approvers}" flowable:elementVariable="approver"> <completionCondition>${nrOfCompletedInstances/nrOfInstances >= 0.6}</completionCondition> </multiInstanceLoopCharacteristics> </userTask>这个配置展示了几个高级特性:
- 从流程变量
approvers集合中动态获取审批人列表 - 设置完成条件为60%通过即可继续流程
- 并行执行所有审批实例
2.2 动态参与者列表的实现
固定数量的多实例适用场景有限,更常见的需求是根据业务数据动态确定参与者。假设一个项目需要所有相关部门的负责人会签:
// 在流程启动前设置参与者列表 List<String> deptHeads = projectService.getRelatedDeptHeads(projectId); runtimeService.setVariable(processInstanceId, "approvers", deptHeads);对应的监听器配置可以这样编写:
public class DynamicApproversListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { String projectId = (String) execution.getVariable("projectId"); List<String> approvers = projectService.getRelatedDeptHeads(projectId); execution.setVariable("approvers", approvers); } }2.3 复杂完成条件配置
默认情况下,多实例节点需要所有实例都完成才能继续流程。但实际业务中往往需要更灵活的策略:
- 多数通过即可(如三分之二同意)
- 一票否决制(任何拒绝都终止流程)
- 加权投票(不同审批人有不同权重)
这些可以通过completionCondition表达式实现:
<completionCondition> ${nrOfCompletedInstances/nrOfInstances >= 0.67 && nrOfRejectedInstances == 0} </completionCondition>这个条件表示需要三分之二以上同意且没有任何拒绝才能通过。
3. 监听器与多实例的联合应用
将任务监听器和多实例结合使用,可以解决更复杂的业务场景。例如在一个采购审批流程中:
- 根据采购类型和金额,动态确定需要哪些部门会签(监听器)
- 为每个相关部门创建并行审批任务(多实例)
- 在最后一个审批完成时,自动触发采购单生成(监听器)
3.1 部门会签审批案例
public class ProcurementStartListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { String procurementType = (String) execution.getVariable("type"); Double amount = (Double) execution.getVariable("amount"); Set<String> requiredDepts = new HashSet<>(); // 基础规则:所有采购都需要财务部审批 requiredDepts.add("finance"); // 根据类型增加审批部门 if ("IT".equals(procurementType)) { requiredDepts.add("it"); if (amount > 50000) { requiredDepts.add("cio"); } } else if ("Facility".equals(procurementType)) { requiredDepts.add("facility"); } // 大额采购需要CEO审批 if (amount > 100000) { requiredDepts.add("ceo"); } execution.setVariable("requiredDepts", requiredDepts); } }对应的多实例任务配置:
<userTask id="deptApproval" name="部门审批"> <multiInstanceLoopCharacteristics isSequential="false" flowable:collection="${requiredDepts}" flowable:elementVariable="currentDept"> <completionCondition>${nrOfRejectedInstances == 0}</completionCondition> </multiInstanceLoopCharacteristics> <extensionElements> <flowable:taskListener event="create" class="com.example.DeptApproverListener"/> </extensionElements> </userTask>3.2 性能优化与错误处理
当并行实例数量较多时,需要注意:
异步执行:对于非关键路径上的操作,使用异步监听器
<flowable:taskListener event="complete" class="com.example.AsyncNotificationListener" flowable:async="true"/>事务边界:监听器中的操作与流程引擎在同一事务中,失败会导致流程回滚
超时控制:长时间运行的监听器应该实现超时机制
public class TimeoutAwareListener implements TaskListener { private static final long TIMEOUT = 30000; // 30秒超时 @Override public void notify(DelegateTask task) { Future<?> future = executor.submit(() -> { // 执行耗时操作 }); try { future.get(TIMEOUT, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { future.cancel(true); task.setVariable("timeoutError", true); } } }4. 调试与监控技巧
复杂流程的调试往往令人头疼。以下是几个实用技巧:
4.1 历史数据追踪
Flowable的历史服务(HistoryService)可以查询任务实例的完整生命周期:
HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .orderByHistoricTaskInstanceEndTime().asc(); List<HistoricTaskInstance> tasks = query.list();4.2 变量监控
流程变量的变化历史可以通过以下方式获取:
SELECT * FROM ACT_HI_VARINST WHERE PROC_INST_ID_ = #{processInstanceId} ORDER BY REV_ DESC4.3 条件断点调试
在监听器中添加调试代码:
public void notify(DelegateTask task) { if ("debug".equals(task.getVariable("debugMode"))) { // 进入调试状态 DebugUtils.enterDebugMode(task); } // 正常业务逻辑 }4.4 可视化监控
利用Flowable自带的Admin应用或集成Prometheus监控:
# application.yml flowable: metrics: enabled: true registry: prometheus在项目实践中,我们经常遇到需要动态调整审批路线的情况。有一次在实现一个跨国采购流程时,我们发现不同地区的审批规则差异很大。最终解决方案是使用监听器结合规则引擎,将业务规则外置到Drools规则文件中,这样业务人员就能自行调整规则而不需要重新部署流程。
