——动态任务分配与审批流优化实战)
1. 动态任务分配的核心思路在传统的审批流程中任务办理人通常是固定写死在流程定义里的。比如某个审批节点指定必须由财务总监处理这种硬编码方式在实际业务中会遇到很多问题。当财务总监离职或调岗时整个流程就会卡住。我在去年实施的一个OA项目中就遇到过这种情况最后不得不手动修改数据库记录来解决问题。动态任务分配的核心在于将办理人规则从流程定义中解耦出来。Activiti提供了三种主要实现方式候选用户/组表达式通过${expression}动态计算候选人任务监听器在任务创建时通过Java代码指定办理人自定义AssignmentHandler实现更复杂的分配逻辑先看一个最简单的动态分配例子。假设我们需要根据申请金额决定审批人// 在流程定义中配置 userTask idapproveTask name审批 activiti:candidateUsers${approvalService.getApprovers(execution)}/ // 对应的服务方法 public ListString getApprovers(DelegateExecution execution) { Double amount (Double) execution.getVariable(amount); return amount 10000 ? Arrays.asList(financeManager, ceo) : Collections.singletonList(departmentManager); }这种方式的优势在于审批规则变更只需修改Java代码无需重新部署流程可以基于业务数据动态决策支持从外部系统获取办理人信息2. 会签与或签的实现技巧会签ALL签和或签ANY签是审批流中常见的两种多人处理模式。会签需要所有指定人员都审批通过才算完成或签则是任意一人处理即可。Activiti原生支持这两种模式但实际使用时有些坑需要注意。2.1 基础配置方法在BPMN文件中配置多实例任务userTask idmultiApproval name会签审批 multiInstanceLoopCharacteristics isSequentialfalse activiti:collection${approvalService.getApprovers(execution)} activiti:elementVariableapprover completionCondition${nrOfCompletedInstances/nrOfInstances 0.6}/completionCondition /multiInstanceLoopCharacteristics /userTask关键参数说明isSequentialfalse表示并行处理true表示串行collection指定办理人集合的表达式completionCondition完成条件示例表示60%通过即可2.2 实际项目中的经验在电商退款审批流程中我们遇到过这样的需求3人评审小组中至少2人同意才能通过。实现时要注意变量作用域问题每个实例有自己的局部变量共享变量要通过execution设置历史记录查看使用historyService.createHistoricTaskInstanceQuery()查询各实例状态超时处理结合边界定时事件实现逾期未处理自动跳过// 获取会签任务的完成情况 ListHistoricTaskInstance tasks historyService .createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .taskDefinitionKey(multiApproval) .list(); long approvedCount tasks.stream() .filter(t - approved.equals(t.getVariable(result))) .count();3. 条件分支的优化策略复杂的审批流往往需要根据不同的业务场景走不同的分支路径。常见的实现方式有三种3.1 网关条件表达式最直接的方式是在排他网关上配置条件sequenceFlow idtoReject sourceRefgateway targetRefrejectTask conditionExpression xsi:typetFormalExpression ![CDATA[${status rejected}]] /conditionExpression /sequenceFlow3.2 动态决策服务对于复杂条件建议封装到服务方法中public class RoutingService { public String determinePath(DelegateExecution execution) { Order order orderService.findById( (String) execution.getVariable(orderId)); if (order.isSpecial()) { return specialApproval; } return normalApproval; } } // BPMN中配置 sequenceFlow iddynamicFlow sourceRefgateway targetRef${routingService.determinePath(execution)}/3.3 基于规则的路径选择对于特别复杂的场景可以集成Drools等规则引擎KieSession kieSession kieContainer.newKieSession(); kieSession.insert(execution); kieSession.fireAllRules();我在金融风控系统中采用这种方案将200多条审批规则全部配置在规则库中业务变更时只需更新规则文件完全不用修改流程定义。4. 审批流性能优化实战当审批流程涉及多人、多部门时性能问题会逐渐显现。以下是几个经过验证的优化方案4.1 人员查询缓存动态分配时频繁查询组织架构会导致性能瓶颈。我们可以在流程启动时预加载人员信息// 启动流程时缓存数据 ProcessInstance instance runtimeService.startProcessInstanceByKey( approvalFlow, variables, true // 启用缓存模式 ); // 在表达式中使用缓存 public ListString getApprovers(DelegateExecution execution) { MapString, Object cache (MapString, Object) execution.getVariable(_cache); if (cache.containsKey(approvers)) { return (ListString) cache.get(approvers); } // ...查询逻辑 }4.2 异步任务处理对于非关键操作如发送通知、更新统计信息可以使用异步执行serviceTask idasyncService activiti:expression${notificationService.sendAsync(task)} activiti:asynctrue/4.3 历史数据归档长时间运行的流程会产生大量历史数据建议配置定期归档# application.properties activiti.history-levelaudit activiti.async-executor-activatetrue activiti.async-history.enabledtrue在银行项目中通过异步历史处理使流程引擎吞吐量提升了3倍。具体实施时要特别注意历史数据一致性保证归档周期设置建议非高峰期执行查询接口的兼容性处理5. 与SpringBoot的深度集成SpringBoot的自动配置特性可以极大简化Activiti的集成工作但要做到生产级应用还需要一些定制5.1 自动部署配置Configuration public class ProcessAutoDeploy { Bean public SpringProcessEngineConfiguration processEngineConfiguration( DataSource dataSource, PlatformTransactionManager transactionManager) { SpringProcessEngineConfiguration config new SpringProcessEngineConfiguration(); config.setDataSource(dataSource); config.setTransactionManager(transactionManager); config.setDatabaseSchemaUpdate(true); config.setAsyncExecutorActivate(true); config.setDeploymentResources(new Resource[]{ new ClassPathResource(processes/approval.bpmn20.xml) }); return config; } }5.2 事务管理要点审批流中经常需要同时操作业务数据和流程数据必须保证事务一致性Transactional public void completeTask(String taskId, MapString, Object variables) { // 更新业务状态 orderService.updateStatus(variables); // 完成任务 taskService.complete(taskId, variables); // 发送消息 eventPublisher.publishEvent(new ApprovalEvent(variables)); }特别注意事务传播行为设置异常处理与回滚逻辑长事务的性能影响5.3 安全集成方案将审批流与Spring Security集成Service public class ActivitiUserService implements UserGroupManager { Override public ListString getUserGroups(String userId) { return securityService.getUserRoles(userId); } }这样就能在流程表达式中使用安全上下文信息userTask idauditTask name审计 activiti:candidateGroups${ROLE_AUDIT}/