Spring Boot 2.5 Activiti 7.1 实战从零搭建一个请假审批工作流附完整源码在当今企业级应用开发中业务流程自动化已成为提升效率的关键。想象一下当员工提交请假申请后系统能自动流转至部门主管、HR等环节审批整个过程无需人工干预——这正是工作流引擎的魔力所在。本文将带你用Spring Boot 2.5和Activiti 7.1构建一个真实的请假审批系统从流程图设计到API集成完整呈现业务与工作流引擎的深度结合。1. 环境准备与项目初始化1.1 技术栈选型说明核心框架Spring Boot 2.5.0长期支持版本工作流引擎Activiti 7.1.0.M5支持BPMN 2.0最新特性数据库MySQL 8.0兼容Activiti所有表结构辅助工具Lombok简化实体类编写MyBatis-Plus 3.3.1数据访问层增强SwaggerAPI文档生成1.2 初始化项目依赖!-- Activiti核心依赖 -- dependency groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter/artifactId version7.1.0.M5/version exclusions exclusion groupIdorg.mybatis/groupId artifactIdmybatis/artifactId /exclusion /exclusions /dependency !-- 流程设计器UI组件 -- dependency groupIdorg.activiti/groupId artifactIdactiviti-image-generator/artifactId version7.1.0.M5/version /dependency !-- 业务系统常用依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.3.1/version /dependency注意Activiti 7.x默认使用MyBatis若项目已使用MyBatis-Plus需排除冲突包1.3 数据库配置示例spring: datasource: url: jdbc:mysql://localhost:3306/activiti_demo?useSSLfalseserverTimezoneUTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver activiti: database-schema-update: true # 自动创建表结构 history-level: full # 记录完整历史数据2. 可视化流程设计实战2.1 集成Activiti Modeler设计器从官方GitHub获取Modeler资源文件将editor目录复制到resources/static下添加汉化文件zh-CN.json访问设计器的典型URL结构http://localhost:8080/editor/#/processes?modelId100012.2 设计请假审批流程图使用BPMN 2.0符号构建包含以下节点的流程开始事件员工提交申请用户任务部门审批组长人事备案HR排他网关根据请假天数分流≤3天直接归档3天需总监审批结束事件流程终止// 通过代码创建初始流程模型 Model model repositoryService.newModel(); ObjectNode modelNode objectMapper.createObjectNode(); modelNode.put(name, 请假流程); modelNode.put(description, 员工请假审批流程); model.setMetaInfo(modelNode.toString()); repositoryService.saveModel(model);2.3 流程变量设计定义审批过程需要传递的关键变量变量名类型作用applicantString申请人工号leaveDaysInteger请假天数reasonString请假原因approvalOpinionString审批意见3. 业务系统深度集成3.1 请假单实体设计Data TableName(t_leave_application) public class LeaveApplication { TableId(type IdType.ASSIGN_ID) private Long id; private String employeeId; private LocalDate startDate; private LocalDate endDate; private Integer days; private String reason; private Integer status; // 0-待审批 1-已通过 2-已拒绝 private String processInstanceId; // 关联流程实例 }3.2 核心API实现3.2.1 提交请假申请PostMapping(/leave/apply) public Result applyLeave(RequestBody LeaveApplication application) { // 1. 保存业务数据 leaveService.save(application); // 2. 启动流程实例 MapString, Object variables new HashMap(); variables.put(applicant, application.getEmployeeId()); variables.put(leaveDays, application.getDays()); RuntimeService runtimeService activitiRuntimeService; ProcessInstance instance runtimeService.startProcessInstanceByKey( leaveProcess, variables ); // 3. 关联业务与流程 application.setProcessInstanceId(instance.getId()); leaveService.updateById(application); return Result.success(instance.getId()); }3.2.2 审批任务处理PostMapping(/approve/{taskId}) public Result approveTask(PathVariable String taskId, RequestParam Boolean approved, RequestParam String comment) { TaskService taskService activitiTaskService; // 1. 完成任务 MapString, Object variables new HashMap(); variables.put(approved, approved); variables.put(approvalOpinion, comment); taskService.complete(taskId, variables); // 2. 更新业务状态 Task task taskService.createTaskQuery().taskId(taskId).singleResult(); LeaveApplication application leaveService.lambdaQuery() .eq(LeaveApplication::getProcessInstanceId, task.getProcessInstanceId()) .one(); application.setStatus(approved ? 1 : 2); leaveService.updateById(application); return Result.success(); }3.3 待办任务查询优化-- 联合查询业务数据和流程状态 SELECT t.*, l.* FROM ACT_RU_TASK t JOIN t_leave_application l ON t.PROC_INST_ID_ l.process_instance_id WHERE t.ASSIGNEE_ #{userId}4. 高级功能实现4.1 动态审批人配置通过监听器实现审批人动态分配Component public class LeaveTaskListener implements TaskListener { Override public void notify(DelegateTask delegateTask) { String eventName delegateTask.getEventName(); if (create.equals(eventName)) { String taskKey delegateTask.getTaskDefinitionKey(); String applicant (String) delegateTask.getVariable(applicant); switch (taskKey) { case deptAudit: String leader userService.getDeptLeader(applicant); delegateTask.setAssignee(leader); break; case hrRecord: delegateTask.setAssignee(hr001); break; } } } }4.2 流程版本控制策略当流程定义变更时采用语义化版本管理小版本更新v1.0 → v1.1不影响运行中实例大版本更新v1.x → v2.0新申请用新流程repositoryService.createDeployment() .name(请假流程v2) .addClasspathResource(processes/leave-v2.bpmn20.xml) .key(leaveProcess) .enableDuplicateFiltering() .deploy();4.3 审批超时自动处理配置边界定时事件实现超时自动通过boundaryEvent idtimeoutEvent attachedToRefdeptAudit timerEventDefinition timeDurationPT24H/timeDuration /timerEventDefinition /boundaryEvent5. 系统监控与优化5.1 流程效率分析HistoryService historyService activitiHistoryService; HistoricProcessInstanceQuery query historyService.createHistoricProcessInstanceQuery() .processDefinitionKey(leaveProcess); // 计算平均处理时长 double avgDuration query.averageDurationInMillis() / (1000 * 60 * 60); log.info(平均审批时长{}小时, avgDuration); // 各环节耗时统计 ListHistoricActivityInstance activities historyService .createHistoricActivityInstanceQuery() .processDefinitionId(processDefinitionId) .orderByHistoricActivityInstanceDuration().asc() .list();5.2 数据库表优化建议针对Activiti核心表的优化策略表名索引建议清理策略ACT_RU_TASKASSIGNEE_, PROC_INST_ID_流程结束立即删除ACT_HI_TASKINSTEND_TIME_, DURATION_定期归档历史数据ACT_RU_EXECUTIONPROC_INST_ID_, BUSINESS_KEY_保留最近3个月运行实例5.3 性能压测指标使用JMeter测试典型场景表现场景吞吐量(req/s)平均响应时间(ms)提交申请120045审批操作150032待办列表查询800686. 完整源码解析项目采用分层架构设计src/main/java ├── config │ ├── ActivitiConfig # 引擎配置 │ └── SwaggerConfig # 接口文档 ├── controller │ ├── LeaveController # 请假业务接口 │ └── TaskController # 任务处理接口 ├── entity │ ├── LeaveApplication # 请假单实体 │ └── ApproveHistory # 审批记录 ├── listener │ └── LeaveTaskListener # 流程监听器 └── service ├── LeaveService # 业务逻辑 └── ProcessService # 流程服务关键代码片段说明// 历史审批记录查询 public ListApproveHistory getHistory(String processInstanceId) { return historyService.createHistoricTaskInstanceQuery() .processInstanceId(processInstanceId) .orderByHistoricTaskInstanceEndTime().asc() .list() .stream() .map(task - { ApproveHistory history new ApproveHistory(); history.setTaskName(task.getName()); history.setApprover(task.getAssignee()); history.setComment(taskService.getTaskComments(task.getId()).get(0)); return history; }).collect(Collectors.toList()); }在调试过程中发现一个典型问题当并行网关分支设置不当时可能导致流程实例挂起。解决方案是在网关出口添加明确的条件表达式sequenceFlow idflow1 sourceRefparallelGateway targetRefapproval1 conditionExpression xsi:typetFormalExpression ![CDATA[${days 3}]] /conditionExpression /sequenceFlow