Activiti7实战:用SpringBoot实现会签审批的‘一票否决’逻辑(附完整代码和流程图)

张开发
2026/4/11 15:11:31 15 分钟阅读

分享文章

Activiti7实战:用SpringBoot实现会签审批的‘一票否决’逻辑(附完整代码和流程图)
Activiti7会签审批实战SpringBoot工程化实现一票否决架构在OA系统开发中会签审批是典型的多人并行决策场景。当需要财务、法务、技术等多部门联合审批时如何优雅地实现一人否决则流程终止的业务逻辑本文将基于SpringBootActiviti7从工程化角度完整实现这套机制。不同于简单的功能演示我们将重点解决三个工程难题动态审批人注入、线程安全的变量传递以及监听器与Spring容器的协同。1. 会签流程的BPMN建模要点会签Counter Sign在BPMN规范中通过multiInstanceLoopCharacteristics元素实现。以下是一个生产级会签节点的XML配置示例userTask idcountersignTask name会签审批 activiti:assignee${assignee} multiInstanceLoopCharacteristics activiti:collectioncountersignList activiti:elementVariableassignee completionCondition${(pass no) || (nrOfCompletedInstances nrOfInstances)}/completionCondition /multiInstanceLoopCharacteristics /userTask关键参数说明参数名称作用域说明countersignList流程实例变量审批人列表集合通常从业务系统动态获取nrOfInstances系统自动生成总实例数等于审批人数量nrOfCompletedInstances系统自动更新已完成审批的实例数pass业务变量审批结果状态需在监听器中动态更新常见踩坑点集合变量必须通过RuntimeService启动时注入不可在任务节点临时设置完成条件表达式中的变量名区分大小写多实例任务不能直接设置assignee属性必须通过elementVariable传递2. SpringBoot工程化实现2.1 项目结构规范推荐采用分层架构避免Activiti API污染业务代码src/main/java ├── config │ └── ActivitiConfig.java # 引擎配置 ├── controller │ └── ProcessController.java # 流程接口 ├── service │ ├── impl │ │ └── CounterSignServiceImpl.java # 会签业务逻辑 │ └── TaskVariableService.java # 变量管理 ├── listener │ └── CounterSignListener.java # 执行监听器 └── util └── SecurityUtils.java # 安全工具类2.2 动态审批人注入在Service层实现审批人逻辑隔离Service public class CounterSignServiceImpl { Autowired private RuntimeService runtimeService; Transactional public ProcessInstance startCounterSign(String businessKey) { // 从业务系统获取实际审批人 ListString approvers getApproversFromHRSystem(businessKey); MapString, Object variables new HashMap(); variables.put(countersignList, approvers); variables.put(pass, yes); // 初始化审批状态 return runtimeService.startProcessInstanceByKey( counterSignProcess, businessKey, variables ); } }工程经验审批人列表建议缓存到Redis避免频繁查询业务数据库对于大规模审批超过50人应采用分批次会签策略2.3 监听器实现关键逻辑通过执行监听器实现一票否决public class CounterSignListener implements ExecutionListener { Override public void notify(DelegateExecution execution) { if (!execution.getEventName().equals(end)) return; MapString, Object vars execution.getVariables(); String formResult (String) vars.get(approvalResult); if (reject.equals(formResult)) { execution.setVariable(pass, no); // 记录否决人信息 execution.setVariable(rejectBy, SecurityUtils.getCurrentUser()); } // 更新已完成实例数 int completed (int) execution.getVariable( nrOfCompletedInstances); execution.setVariable( nrOfCompletedInstances, completed 1); } }线程安全注意事项变量操作必须放在同一个事务中对nrOfCompletedInstances的修改需要先读取当前值建议对关键变量添加乐观锁控制3. 前端交互设计规范3.1 表单数据约定前端需要与BPMN表单属性严格匹配{ formProperties: [ { id: approvalResult, type: enum, required: true, values: [ {id: agree, name: 同意}, {id: reject, name: 驳回} ] }, { id: comment, type: string, maxLength: 500 } ] }3.2 审批状态同步方案推荐采用WebSocket实现实时状态更新const socket new WebSocket(/process/notify); socket.onmessage (event) { const data JSON.parse(event.data); if (data.type COUNTERSIGN_UPDATE) { updateTaskProgress(data.completed, data.total); if (data.rejected) { showRejectAlert(data.rejectBy); } } };4. 生产环境调优策略4.1 性能优化参数在application.yml中配置关键参数activiti: async-executor: core-pool-size: 10 max-pool-size: 50 queue-capacity: 1000 process-definition-cache-limit: 100 history-level: audit # 生产环境建议级别4.2 集群部署方案多节点部署时需要特别处理共享数据库配置相同activiti.engine.idRedis分布式锁实现监听器互斥文件存储使用共享NAS或对象存储Bean public ProcessEngineConfiguration processEngineConfiguration( DataSource dataSource, PlatformTransactionManager transactionManager) { SpringProcessEngineConfiguration config ... config.setEngineName(cluster-node-1); config.setDatabaseSchemaUpdate(false); config.setAsyncExecutorActivate(true); return config; }4.3 监控与告警集成Prometheus监控指标Bean public ActivitiPrometheusReporter activitiReporter( ProcessEngine processEngine) { ActivitiPrometheusReporter reporter new ActivitiPrometheusReporter(); reporter.setProcessEngine(processEngine); reporter.register(); return reporter; }关键监控指标包括会签任务平均耗时并行实例堆积数变量操作异常次数

更多文章