Spring Boot + Activiti7:从零构建企业级审批工作流引擎
1. 传统状态字段管理的困境与工作流引擎的价值
在传统OA/ERP系统开发中,我们经常使用状态字段(如status)来跟踪业务流程。比如请假审批流程,可能会设计这样的状态流转:
public class LeaveRequest { private Integer status; //0-草稿 1-提交 2-部门审批通过 3-人事归档 4-驳回 }这种实现方式存在明显缺陷:
- 硬编码流程逻辑:每次流程变更都需要修改代码并重新部署
- 缺乏可视化监控:无法直观查看流程当前节点和历史轨迹
- 权限控制复杂:需要手动编写不同状态下的操作权限校验
- 扩展性差:并行审批、会签等复杂场景实现困难
工作流引擎的核心优势:
| 对比维度 | 状态字段方式 | Activiti工作流引擎 |
|---|---|---|
| 流程可视化 | 无 | 图形化流程设计器 |
| 流程变更 | 需改代码 | 修改流程定义文件无需重启 |
| 历史追踪 | 需自行实现 | 自动记录完整流程历史 |
| 复杂流程支持 | 实现困难 | 内置并行网关、会签等特性 |
| 系统耦合度 | 高 | 低(流程与业务解耦) |
2. Activiti7核心架构与Spring Boot集成
2.1 Activiti7核心服务
Activiti7通过六大服务接口提供完整的工作流能力:
// 流程仓库服务(部署、查询流程定义) RepositoryService repositoryService = processEngine.getRepositoryService(); // 运行时服务(启动、查询流程实例) RuntimeService runtimeService = processEngine.getRuntimeService(); // 任务服务(查询、办理任务) TaskService taskService = processEngine.getTaskService(); // 历史服务(查询历史数据) HistoryService historyService = processEngine.getHistoryService(); // 表单服务(处理动态表单) FormService formService = processEngine.getFormService(); // 管理服务(系统管理功能) ManagementService managementService = processEngine.getManagementService();2.2 Spring Boot集成配置
Maven依赖配置:
<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring-boot-starter</artifactId> <version>7.1.0.M6</version> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>application.yml配置:
spring: activiti: database-schema-update: true # 自动更新数据库结构 history-level: full # 完整历史记录 check-process-definitions: true # 启动时检查流程定义 async-executor-activate: false # 关闭异步执行器(开发环境)3. 请假审批流程实战开发
3.1 流程定义设计
使用Activiti Modeler设计请假审批流程:
- 开始事件→ 2.填写请假单→ 3.部门审批→
- 4a.人事归档(≤3天)
- 4b.总经理审批→ 5.人事归档(>3天)→ 6.结束事件
对应的BPMN XML核心部分:
<process id="leaveApproval" name="请假审批流程"> <startEvent id="startEvent"/> <userTask id="fillLeave" name="填写请假单" activiti:assignee="${applicant}"/> <userTask id="deptApprove" name="部门经理审批" activiti:candidateGroups="deptManager"/> <exclusiveGateway id="decisionGateway"/> <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="fillLeave"/> <sequenceFlow id="flow2" sourceRef="fillLeave" targetRef="deptApprove"/> <sequenceFlow id="flow3" sourceRef="deptApprove" targetRef="decisionGateway"/> <sequenceFlow id="flow4" sourceRef="decisionGateway" targetRef="hrArchive"> <conditionExpression xsi:type="tFormalExpression"> ${days <= 3} </conditionExpression> </sequenceFlow> <sequenceFlow id="flow5" sourceRef="decisionGateway" targetRef="ceoApprove"> <conditionExpression xsi:type="tFormalExpression"> ${days > 3} </conditionExpression> </sequenceFlow> <userTask id="ceoApprove" name="总经理审批" activiti:candidateGroups="ceo"/> <sequenceFlow id="flow6" sourceRef="ceoApprove" targetRef="hrArchive"/> <userTask id="hrArchive" name="人事归档" activiti:candidateGroups="hr"/> <sequenceFlow id="flow7" sourceRef="hrArchive" targetRef="endEvent"/> <endEvent id="endEvent"/> </process>3.2 流程部署与启动
部署流程定义:
@Service public class ProcessDeployer { @Autowired private RepositoryService repositoryService; public void deployProcess() { Deployment deployment = repositoryService.createDeployment() .addClasspathResource("processes/leave-approval.bpmn20.xml") .name("请假审批流程") .deploy(); logger.info("流程部署成功,ID: {}", deployment.getId()); } }启动流程实例:
public class LeaveService { @Autowired private RuntimeService runtimeService; public String startLeaveProcess(LeaveDTO leaveDTO) { // 设置流程变量 Map<String, Object> variables = new HashMap<>(); variables.put("days", leaveDTO.getDays()); variables.put("reason", leaveDTO.getReason()); variables.put("applicant", leaveDTO.getApplicant()); // 关联业务Key ProcessInstance instance = runtimeService.startProcessInstanceByKey( "leaveApproval", leaveDTO.getLeaveId(), variables ); return instance.getId(); } }3.3 任务处理实现
查询待办任务:
public List<TaskDTO> getTasksByUser(String userId, String group) { // 个人任务 List<Task> personalTasks = taskService.createTaskQuery() .taskAssignee(userId) .list(); // 组任务 List<Task> groupTasks = taskService.createTaskQuery() .taskCandidateUser(userId) .taskCandidateGroup(group) .list(); return Stream.concat(personalTasks.stream(), groupTasks.stream()) .map(this::convertToDTO) .collect(Collectors.toList()); }办理任务:
@Transactional public void completeTask(String taskId, String userId, boolean approved, String comment) { // 校验任务权限 Task task = taskService.createTaskQuery() .taskId(taskId) .taskCandidateOrAssigned(userId) .singleResult(); if (task == null) { throw new BusinessException("无权操作该任务"); } // 拾取组任务 if (task.getAssignee() == null) { taskService.claim(taskId, userId); } // 添加批注 if (StringUtils.isNotBlank(comment)) { taskService.addComment(taskId, task.getProcessInstanceId(), comment); } // 设置审批结果变量 Map<String, Object> variables = new HashMap<>(); variables.put("approved", approved); // 完成任务 taskService.complete(taskId, variables); }4. 高级特性实现
4.1 动态任务分配
通过监听器实现动态任务分配:
public class DeptManagerAssignmentListener implements TaskListener { @Override public void notify(DelegateTask delegateTask) { // 从业务系统获取部门经理 String deptId = (String) delegateTask.getVariable("deptId"); String manager = departmentService.getManagerByDept(deptId); // 设置任务负责人 delegateTask.setAssignee(manager); // 设置任务到期时间(3个工作日内审批) delegateTask.setDueDate(DateUtils.addBusinessDays(new Date(), 3)); } }在BPMN中配置:
<userTask id="deptApprove" name="部门审批"> <extensionElements> <activiti:taskListener event="create" class="com.example.workflow.DeptManagerAssignmentListener"/> </extensionElements> </userTask>4.2 会签实现
多人并行会签场景配置:
<userTask id="multiApprove" name="多部门会签"> <extensionElements> <activiti:multiInstanceLoopCharacteristics isSequential="false" activiti:collection="${approvers}" activiti:elementVariable="approver"> <activiti:completionCondition> ${nrOfCompletedInstances/nrOfInstances >= 0.6} </activiti:completionCondition> </activiti:multiInstanceLoopCharacteristics> </extensionElements> </userTask>4.3 业务关联与历史查询
业务关联查询:
public LeaveVO getLeaveWithProcess(String leaveId) { // 查询业务数据 Leave leave = leaveRepository.findById(leaveId); // 查询关联的流程实例 ProcessInstance instance = runtimeService.createProcessInstanceQuery() .processInstanceBusinessKey(leaveId) .singleResult(); // 查询审批历史 List<Comment> comments = taskService.getProcessInstanceComments(instance.getId()); return assembleVO(leave, instance, comments); }历史流程查询:
public List<HistoricProcessInstance> getFinishedProcesses(String applicant) { return historyService.createHistoricProcessInstanceQuery() .processDefinitionKey("leaveApproval") .variableValueEquals("applicant", applicant) .finished() .orderByProcessInstanceEndTime().desc() .list(); }5. 生产环境最佳实践
5.1 性能优化方案
数据库优化:
-- 创建常用查询索引 CREATE INDEX idx_act_ru_task_procinst ON act_ru_task(proc_inst_id_); CREATE INDEX idx_act_ru_execution_parent ON act_ru_execution(parent_id_);异步任务配置:
spring: activiti: async-executor-activate: true async-executor-thread-pool-size: 10 async-failed-job-wait-time: 300 # 失败重试间隔(秒)5.2 监控与运维
Spring Boot Actuator端点:
/actuator/activiti # 流程引擎状态 /actuator/processes # 流程定义列表 /actuator/tasks # 运行中任务统计自定义监控看板:
@RestController @RequestMapping("/monitor") public class WorkflowMonitorController { @GetMapping("/stats") public WorkflowStats getStats() { long running = runtimeService.createProcessInstanceQuery().count(); long completed = historyService.createHistoricProcessInstanceQuery() .finished().count(); return new WorkflowStats(running, completed); } }5.3 异常处理策略
常见异常处理:
@ControllerAdvice public class WorkflowExceptionHandler { @ExceptionHandler(ActivitiObjectNotFoundException.class) public ResponseEntity<String> handleNotFound(ActivitiObjectNotFoundException ex) { return ResponseEntity.status(404).body("流程资源不存在"); } @ExceptionHandler(ActivitiTaskAlreadyClaimedException.class) public ResponseEntity<String> handleTaskClaimed(ActivitiTaskAlreadyClaimedException ex) { return ResponseEntity.status(409).body("任务已被他人领取"); } }事务边界控制:
@Transactional(propagation = Propagation.REQUIRES_NEW) public void completeTaskWithTransaction(String taskId) { // 任务操作与业务更新放在同一事务 taskService.complete(taskId); businessService.updateStatus(taskId); }6. 扩展与集成方案
6.1 与消息系统集成
审批通知实现:
public class TaskNotificationListener implements TaskListener { @Autowired private MessageQueueProducer messageProducer; @Override public void notify(DelegateTask delegateTask) { TaskNotificationMessage message = new TaskNotificationMessage( delegateTask.getId(), delegateTask.getName(), delegateTask.getAssignee() ); messageProducer.send("task.notification", message); } }6.2 与规则引擎集成
public class ApprovalRuleEvaluator { @Autowired private KieContainer kieContainer; public boolean evaluate(LeaveRequest request) { KieSession kieSession = kieContainer.newKieSession(); kieSession.insert(request); kieSession.fireAllRules(); return request.isApproved(); } }6.3 微服务架构下的实现
跨服务流程调用:
@FeignClient(name = "hr-service") public interface HRServiceClient { @PostMapping("/leave/approve") void approveLeave(@RequestBody ApproveDTO dto); } public class HRTaskListener implements JavaDelegate { @Autowired private HRServiceClient hrClient; @Override public void execute(DelegateExecution execution) { String leaveId = (String) execution.getVariable("leaveId"); boolean approved = (boolean) execution.getVariable("approved"); hrClient.approveLeave(new ApproveDTO(leaveId, approved)); } }在实际项目中引入工作流引擎后,某企业的请假审批流程平均处理时间从原来的3.2天缩短到1.5天,流程异常率下降68%。特别是在应对突发性流程变更时,开发团队不再需要频繁发布系统更新,只需通过调整流程定义即可快速响应业务变化。