1. 理解Flowable多实例任务的核心概念
第一次接触Flowable工作流引擎的多实例任务时,我完全被那些专业术语搞懵了。直到实际项目中需要实现一个OA系统的多人审批功能,才真正弄明白会签和或签的区别。简单来说,会签就像团队开会需要所有人签字确认,而或签则像紧急情况下只要有一个负责人签字就能生效。
在Flowable的可视化设计器中,多实例任务有个很直观的标识 - 活动底部的三条短线。这里有个小细节容易忽略:三条竖线代表并行执行(所有人同时收到任务),而三条横线则是顺序执行(按顺序逐个审批)。在大多数审批场景中,我们用的都是并行模式,毕竟没人希望自己的审批卡在前一个人的待办列表里。
多实例配置包含几个关键属性:
- 类型:通常选择"并行"或"顺序"
- 集合:审批人列表,可以是固定名单或动态变量
- 元素变量:循环处理集合时的当前项变量名
- 分配表达式:如何将任务分配给具体用户
- 完成条件:决定任务何时完成的表达式
2. 会签模式的完整配置指南
去年我们团队重构报销审批流程时,财务部特别强调所有部门主管必须全部审批通过。这种场景就是典型的会签需求。下面我分享下具体实现步骤:
首先在设计器中创建用户任务,右键选择"转换为多实例活动"。关键配置参数如下:
<multiInstanceLoopCharacteristics isSequential="false"> <loopCardinality>${taskUserList.size()}</loopCardinality> <loopDataInputRef>taskUserList</loopDataInputRef> <inputDataItem name="currentUser"></inputDataItem> <completionCondition>${nrOfCompletedInstances==nrOfInstances}</completionCondition> </multiInstanceLoopCharacteristics>这里有几个实战经验值得注意:
isSequential="false"确保并行发送给所有审批人loopDataInputRef指向流程变量中的审批人列表currentUser会在每次迭代时自动赋值为当前审批人
在Java代码中启动流程时,需要传入审批人列表:
List<String> approvers = Arrays.asList("user1", "user2", "user3"); runtimeService.startProcessInstanceByKey("expenseApproval", Variables.putValue("taskUserList", approvers));实际项目中我踩过一个坑:当审批人列表为空时,Flowable默认会跳过该任务。这可能导致审批流程出现漏洞,所以建议在前端做必填校验,或者在网关处添加判断逻辑。
3. 或签模式的灵活实现方案
与会签不同,或签模式只要任意一人审批即可通过。这种模式特别适合紧急事务处理,比如IT故障的应急审批。配置上与会签的主要区别在于完成条件:
<completionCondition>${nrOfCompletedInstances>=1}</completionCondition>看似简单,但在实际项目中我发现几个需要特别注意的点:
任务撤回问题:当多人同时处理时,第一个完成的人会使其他任务直接消失。这可能导致用户困惑,建议在界面添加状态提示。
审批记录完整性:虽然只需要一人审批,但最好记录所有被分配人的处理状态。可以通过监听器实现:
@EventListener public void onTaskCompleted(DelegateTask task) { if(task.getVariable("nrOfCompletedInstances") != null) { // 记录多实例任务处理日志 } }- 动态调整审批人:有时需要在流程运行中调整或签人员列表。这时可以使用
runtimeService.setVariable()方法动态更新taskUserList。
4. 深入解析多实例任务的内置变量
理解Flowable的多实例内置变量是掌握高级配置的关键。这些变量就像隐藏在引擎盖下的仪表盘,掌握它们才能精准控制流程走向。主要的内置变量包括:
| 变量名 | 描述 | 典型使用场景 |
|---|---|---|
| nrOfInstances | 总实例数 | 显示审批进度 |
| nrOfActiveInstances | 活跃实例数 | 监控未处理任务 |
| nrOfCompletedInstances | 已完成实例数 | 判断完成条件 |
| loopCounter | 当前迭代索引 | 顺序执行时使用 |
在审批列表页面,我们可以利用这些变量显示实时状态:
function getApprovalStatus(task) { const total = task.variables.nrOfInstances || 1; const completed = task.variables.nrOfCompletedInstances || 0; return `${completed}/${total}人已审批`; }有个特别实用的技巧:在会签模式下,如果想实现"超过半数同意即可通过",可以这样配置完成条件:
<completionCondition>${nrOfCompletedInstances >= (nrOfInstances/2 + 1)}</completionCondition>5. 实战中的常见问题与解决方案
在实施了十几个Flowable项目后,我整理了一些高频问题的解决方法:
问题1:审批人动态变化解决方案:使用监听器在任务创建时动态设置审批人列表
public class DynamicAssigneeListener implements TaskListener { @Override public void notify(DelegateTask task) { List<String> approvers = getApproversFromDB(task); task.setVariable("taskUserList", approvers); } }问题2:会签任务超时处理解决方案:结合边界定时器事件和补偿机制
<boundaryEvent id="timeoutEvent" attachedToRef="approvalTask"> <timerEventDefinition> <timeDuration>PT24H</timeDuration> </timerEventDefinition> </boundaryEvent>问题3:审批人重复问题解决方案:在设置审批人列表前进行去重处理
List<String> approvers = new ArrayList<>(new HashSet<>(rawApprovers));最近一个项目中,客户提出更复杂的需求:会签任务需要根据不同金额级别设置不同的审批规则。我们最终通过网关和变量判断实现了这个需求:
<sequenceFlow id="flow1" sourceRef="gateway" targetRef="approvalTask1"> <conditionExpression xsi:type="tFormalExpression"> ${amount < 10000} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow2" sourceRef="gateway" targetRef="approvalTask2"> <conditionExpression xsi:type="tFormalExpression"> ${amount >= 10000} </conditionExpression> </sequenceFlow>6. 性能优化与最佳实践
当审批人数量较多时,多实例任务可能成为性能瓶颈。根据我们的压力测试经验,以下优化措施效果显著:
批量操作:使用
runtimeService.createVariableInstanceBatch()批量设置变量,减少数据库交互异步执行:对非关键路径上的多实例任务配置异步属性
<userTask id="approvalTask" flowable:async="true"/>缓存审批人列表:对于频繁使用的审批规则,可以考虑使用缓存
分页查询:在显示待办列表时,对多实例任务进行分页处理
SELECT * FROM ACT_RU_TASK WHERE PROC_INST_ID_ = #{processInstanceId} LIMIT #{offset}, #{pageSize}有个特别实用的技巧是在流程启动时预计算所有多实例任务:
ProcessInstance instance = runtimeService.startProcessInstanceByKey(...); List<Task> tasks = taskService.createTaskQuery() .processInstanceId(instance.getId()) .list(); // 预加载所有任务避免N+1查询问题7. 扩展应用:复杂审批模式实现
基础会签和或签模式可以组合出更复杂的审批场景。比如去年我们实现的"混合审批模式"就获得了客户好评:
- 层级审批:先部门内会签,再跨部门或签
- 条件审批:根据风险等级动态调整审批模式
- 替补审批:主审批人超时未处理时自动转交
实现层级审批的关键是合理设计子流程:
<subProcess id="deptApproval" flowable:async="true"> <multiInstanceLoopCharacteristics ...> <!-- 部门内会签配置 --> </multiInstanceLoopCharacteristics> </subProcess> <subProcess id="crossDeptApproval" flowable:async="true"> <multiInstanceLoopCharacteristics ...> <!-- 跨部门或签配置 --> </multiInstanceLoopCharacteristics> </subProcess>对于条件审批,可以使用执行监听器动态调整审批模式:
public class ApprovalModeListener implements ExecutionListener { @Override public void notify(DelegateExecution execution) { String riskLevel = (String) execution.getVariable("riskLevel"); if("high".equals(riskLevel)) { execution.setVariable("approvalMode", "会签"); } else { execution.setVariable("approvalMode", "或签"); } } }