别再硬编码了!用Flowable 6.8.0实现多部门并行审批,动态分配处理人就这么简单

Flowable 6.8.0实战:动态多部门审批的架构设计与实现

上周在重构公司采购审批系统时,遇到一个典型场景:技术部需要评估设备参数,财务部审核预算,法务部检查合同条款——这三个部门的审批必须并行执行,且每个部门的处理人需要根据组织架构动态分配。传统硬编码方式不仅维护成本高,每次组织调整都要重新发布流程。本文将分享如何用Flowable 6.8.0的多实例子流程配合动态任务分配解决这个问题。

1. 多实例并行审批的核心设计

多部门并行审批的本质是流程实例的批量创建与协同。在Flowable中,这通过multiInstanceLoopCharacteristics元素实现。与简单循环任务不同,子流程级别的多实例具有完整生命周期管理能力。

关键设计参数对比:

特性单任务多实例子流程多实例
隔离性共享父执行上下文独立执行上下文
变量作用域全局可见实例私有+父流程共享
异常处理影响整个流程可单独补偿
适用场景简单会签复杂跨系统协作

采购审批流程的BPMN核心结构:

<subProcess id="departmentApproval" name="部门审批"> <multiInstanceLoopCharacteristics flowable:collection="${departmentService.getApprovalDepartments(execution)}" flowable:elementVariable="currentDept" isSequential="false"> <completionCondition>${nrOfCompletedInstances == nrOfInstances}</completionCondition> </multiInstanceLoopCharacteristics> <!-- 部门内部审批节点 --> </subProcess>

2. 动态处理人分配方案

硬编码分配方式的最大痛点在于组织变更与流程解耦。我们采用三级分配策略:

  1. 部门发现层:通过JavaDelegate获取当前有效部门

    public class DepartmentLookupDelegate implements JavaDelegate { @Override public void execute(DelegateExecution execution) { List<Department> depts = departmentService.findByProcessType( (String) execution.getVariable("processType")); execution.setVariable("approvalDepartments", depts); } }
  2. 角色映射层:使用监听器绑定部门与角色

    <userTask id="deptTask" name="部门审批"> <extensionElements> <flowable:taskListener event="create" class="com.example.DeptTaskAssignmentListener"/> </extensionElements> </userTask>
  3. 人员决策层:基于业务规则确定最终处理人

    public class DeptTaskAssignmentListener implements TaskListener { @Override public void notify(DelegateTask task) { Department dept = (Department) task.getVariable("currentDept"); String roleType = dept.getApprovalRole(); if("FINANCE".equals(dept.getType())) { task.addCandidateGroup(roleType); } else { User user = userService.findDefaultApprover( dept.getId(), roleType); task.setAssignee(user.getId()); } } }

3. 实战中的性能优化

当并发部门超过20个时,原始实现会出现性能瓶颈。我们通过以下优化使吞吐量提升300%:

数据库优化方案:

  • ACT_RU_IDENTITYLINK表添加复合索引:
    CREATE INDEX idx_identitylink_task ON ACT_RU_IDENTITYLINK(TASK_ID_, TYPE_);
  • 启用批量插入模式:
    spring.flowable.async-executor-activate=true spring.flowable.bulk-insert-enabled=true

内存优化技巧:

  • 在循环特性中使用缓存查询:
    @Cacheable("deptApprovers") public List<String> getDepartmentApprovers(String deptId) { // 数据库查询 }
  • 限制变量作用域:
    <multiInstanceLoopCharacteristics flowable:collection="${cachedDeptService.getApprovers(deptId)}" flowable:elementVariable="approver" flowable:variableAggregations="[{ 'target': 'local', 'variables': ['approvalComment'] }]">

4. 异常处理与监控

多实例流程需要特殊的异常处理策略。我们推荐采用分级错误捕获机制:

  1. 子流程级别:设置边界事件捕获业务异常

    <boundaryEvent id="deptErrorBoundary" attachedToRef="deptTask"> <errorEventDefinition errorRef="approvalRejectedError"/> </boundaryEvent>
  2. 实例级别:通过执行监听器记录异常

    public class InstanceErrorLogger implements ExecutionListener { @Override public void notify(DelegateExecution execution) { if(execution.getEventName().equals("end")) { Map<String, Object> vars = execution.getVariables(); auditService.logProcessEnd( execution.getProcessInstanceId(), vars); } } }
  3. 全局监控:自定义历史处理器

    @Component public class ApprovalHistoryHandler implements HistoryListener { @Override public void eventOccurred(HistoryEvent event) { if(event instanceof HistoricActivityInstanceEvent) { // 分析审批耗时 } } }

关键提示:在并行多实例场景下,务必设置async="true"属性避免长时间任务阻塞整体流程。

5. 与现有系统集成方案

许多企业已有HR系统或权限管理系统,需要与Flowable深度集成。我们实践出三种可靠模式:

模式一:实时API集成

@startuml participant Flowable participant HRSystem Flowable -> HRSystem : 获取部门信息(同步调用) HRSystem --> Flowable : JSON响应 @enduml

模式二:事件驱动架构

@EventListener(condition = "#event.type=='ORG_CHANGE'") public void handleOrgChange(PlatformEvent event) { cache.evict("deptApprovers"); processEngine.getRuntimeService() .createChangeDeploymentTenantIdBuilder() .sourceTenantId(event.getTenantId()) .deploy(); }

模式三:数据镜像同步

-- 定时任务将HR数据同步到Flowable专用表 MERGE INTO ACT_ID_GROUP target USING (SELECT dept_code AS ID_, dept_name AS NAME_ FROM hr_departments) source ON (target.ID_ = source.ID_) WHEN MATCHED THEN UPDATE SET target.NAME_ = source.NAME_ WHEN NOT MATCHED THEN INSERT (ID_, NAME_, TYPE_) VALUES (source.ID_, source.NAME_, 'dept');

实际项目中,我们最终采用模式二+模式三混合方案:日常变更通过事件通知实时处理,每日凌晨全量同步作为兜底。这种方案在保证实时性的同时,具备最终一致性保障。

6. 扩展性设计建议

随着业务发展,审批流程可能衍生出新的需求。我们在架构设计中预留了这些扩展点:

  1. 动态条件分支

    <sequenceFlow id="toFinance" sourceRef="approval" targetRef="financeReview"> <conditionExpression xsi:type="tFormalExpression"> ${dept.type == 'FINANCE' && amount > 10000} </conditionExpression> </sequenceFlow>
  2. 审批矩阵配置化

    # approval-matrix.yml finance: base-approver: CFO conditions: - when: amount > 100000 then: [CFO, CEO] tech: base-approver: CTO
  3. 跨流程协同

    // 在主管审批通过后触发采购流程 runtimeService.startProcessInstanceByKey( "purchaseProcess", approvalVariables);

在最近一次组织架构调整中,这套方案仅需修改部门映射配置即完成适配,无需停服或流程变更,验证了其灵活性。某个核心审批流程的平均处理时间从原来的72小时降至28小时,特别是跨部门协作效率提升显著。