SpringBoot + Activiti6.0实战:手把手教你搞定请假审批系统(附完整源码)
SpringBoot与Activiti6.0深度整合构建企业级请假审批系统实战指南在当今企业数字化转型浪潮中业务流程自动化已成为提升运营效率的关键。本文将带您深入探索如何利用SpringBoot与Activiti6.0构建一个功能完备的请假审批系统从环境搭建到生产部署全程采用工程化实践方案。1. 环境准备与基础配置1.1 技术栈选型与依赖管理现代Java企业应用开发中SpringBoot已成为事实上的标准框架而Activiti作为成熟的BPMN2.0流程引擎二者的结合能够快速实现复杂业务流程的数字化。以下是核心依赖配置dependencies !-- SpringBoot基础依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Activiti集成 -- dependency groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter-basic/artifactId version6.0.0/version /dependency !-- 数据库支持 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency /dependencies1.2 数据库配置策略Activiti引擎需要特定的数据库表结构支持正确的初始化策略对系统稳定性至关重要。推荐采用以下配置组合配置项推荐值适用场景spring.activiti.database-schema-updatetrue开发环境spring.activiti.database-schema-updatefalse生产环境spring.activiti.history-levelfull需要完整审计# 开发环境配置示例 spring.datasource.urljdbc:mysql://localhost:3306/activiti_demo spring.datasource.usernameroot spring.datasource.passwordyourpassword spring.activiti.database-schema-updatetrue spring.activiti.history-levelfull提示生产环境务必关闭database-schema-update避免意外修改表结构2. 流程建模与部署2.1 BPMN2.0流程设计规范请假审批流程通常包含以下核心节点开始事件流程触发点用户任务请假申请节点排他网关根据请假天数路由用户任务部门审批节点条件天数3结束事件流程终止点!-- 简化版BPMN定义示例 -- process idvacationProcess name请假审批流程 startEvent idstartEvent/ userTask idapplyTask name请假申请/ exclusiveGateway iddecisionGateway/ sequenceFlow sourceRefstartEvent targetRefapplyTask/ sequenceFlow sourceRefapplyTask targetRefdecisionGateway/ /process2.2 流程部署最佳实践推荐采用资源目录自动部署方式将流程定义文件放置在特定目录src/main/resources/processes/ ├── vacation-process.bpmn20.xml └── department-approval.bpmn20.xml通过RepositoryService进行动态部署验证Autowired private RepositoryService repositoryService; public void deployProcess() { Deployment deployment repositoryService.createDeployment() .addClasspathResource(processes/vacation-process.bpmn20.xml) .name(请假流程部署) .deploy(); logger.info(流程部署成功ID: {}, deployment.getId()); }3. 系统集成与业务实现3.1 用户体系整合方案企业现有用户系统与Activiti身份管理的对接是关键挑战。推荐采用以下整合模式public void syncUserToActiviti(User user) { // 创建Activiti用户 User activitiUser identityService.newUser(user.getId().toString()); activitiUser.setFirstName(user.getRealName()); activitiUser.setEmail(user.getEmail()); identityService.saveUser(activitiUser); // 同步角色关系 user.getRoles().forEach(role - { Group group identityService.createGroupQuery() .groupId(role.getId().toString()) .singleResult(); if (group null) { group identityService.newGroup(role.getId().toString()); group.setName(role.getName()); group.setType(role.getType()); identityService.saveGroup(group); } // 建立用户-组关系 identityService.createMembership( user.getId().toString(), role.getId().toString() ); }); }3.2 业务数据与流程数据分离设计遵循流程数据与业务数据分离原则设计数据库表结构请假申请表结构示例字段名类型描述idBIGINT主键process_instance_idVARCHAR流程实例IDapplicant_idBIGINT申请人IDstart_dateDATE开始日期end_dateDATE结束日期reasonVARCHAR请假原因业务服务层实现流程启动逻辑public String startVacationProcess(VacationRequest request) { // 1. 保存业务数据 VacationApply apply new VacationApply(); apply.setApplicantId(request.getUserId()); apply.setDays(request.getDays()); apply vacationRepository.save(apply); // 2. 启动流程实例 MapString, Object variables new HashMap(); variables.put(days, request.getDays()); variables.put(formId, apply.getId()); ProcessInstance instance runtimeService.startProcessInstanceByKey( vacationProcess, variables ); // 3. 关联业务与流程数据 apply.setProcessInstanceId(instance.getId()); vacationRepository.save(apply); return instance.getId(); }4. 核心功能实现细节4.1 动态任务查询与分页结合Spring Data JPA实现高效的任务查询public PageTaskDTO getPendingTasks(Long userId, int page, int size) { // 获取用户所在角色组 ListString groupIds identityService.createGroupQuery() .groupMember(userId.toString()) .list() .stream() .map(Group::getId) .collect(Collectors.toList()); // 构建任务查询 TaskQuery query taskService.createTaskQuery() .taskCandidateOrAssigned(userId.toString()) .taskCandidateGroupIn(groupIds) .orderByTaskCreateTime() .desc(); // 分页处理 ListTask tasks query.listPage((page-1)*size, size); long total query.count(); // 转换为DTO ListTaskDTO dtos tasks.stream() .map(this::convertToDTO) .collect(Collectors.toList()); return new PageImpl(dtos, PageRequest.of(page-1, size), total); }4.2 审批操作完整实现审批服务需要处理多种业务场景Transactional public void processApproval(ApprovalRequest request) { // 1. 验证任务有效性 Task task taskService.createTaskQuery() .taskId(request.getTaskId()) .singleResult(); if (task null) { throw new BusinessException(任务不存在或已完成); } // 2. 保存审批意见 Approval approval new Approval(); approval.setTaskId(task.getId()); approval.setComment(request.getComment()); approval.setResult(request.getResult()); approvalRepository.save(approval); // 3. 处理审批结果 MapString, Object variables new HashMap(); variables.put(approvalResult, request.getResult()); if (request.getResult() ApprovalResult.REJECT) { // 拒绝时终止流程 runtimeService.deleteProcessInstance( task.getProcessInstanceId(), 审批拒绝 ); } else { // 认领并完成任务 taskService.claim(task.getId(), request.getUserId().toString()); taskService.complete(task.getId(), variables); } }4.3 流程可视化实现生成带高亮的流程图需要特殊处理public void generateProcessImage(String processInstanceId, HttpServletResponse response) { try { // 获取BPMN模型 ProcessInstance instance runtimeService.createProcessInstanceQuery() .processInstanceId(processInstanceId) .singleResult(); BpmnModel model repositoryService.getBpmnModel( instance.getProcessDefinitionId() ); // 获取当前活动节点 ListString activeIds runtimeService.getActiveActivityIds(processInstanceId); // 生成图像流 InputStream imageStream processEngine.getProcessEngineConfiguration() .getProcessDiagramGenerator() .generateDiagram( model, png, activeIds, Collections.emptyList(), 宋体, 宋体, 宋体, null, 1.0 ); // 输出响应 response.setContentType(image/png); IOUtils.copy(imageStream, response.getOutputStream()); } catch (Exception e) { logger.error(流程图生成失败, e); throw new RuntimeException(流程图生成失败); } }5. 生产环境注意事项5.1 性能优化建议针对高并发场景的配置调整# Activiti异步执行器配置 spring.activiti.async-executor-activatetrue spring.activiti.async-executor-core-pool-size10 spring.activiti.async-executor-max-pool-size50 spring.activiti.async-executor-queue-capacity10005.2 监控与日志方案集成Spring Boot Actuator监控端点端点功能描述/actuator/activiti流程引擎状态/actuator/processes已部署流程/actuator/tasks运行中任务统计日志记录关键操作Aspect Component public class ActivitiLogAspect { AfterReturning( pointcut execution(* org.activiti.engine..*.*(..)), returning result ) public void logActivitiOperation(JoinPoint jp, Object result) { String operation jp.getSignature().getName(); logger.info(Activiti操作 {} 执行成功结果: {}, operation, result); } }5.3 异常处理机制统一异常处理增强系统健壮性ControllerAdvice public class ActivitiExceptionHandler { ExceptionHandler(ActivitiException.class) public ResponseEntityErrorResponse handleActivitiError(ActivitiException e) { ErrorResponse response new ErrorResponse(); response.setCode(ACTIVITI_ERROR); response.setMessage(e.getMessage()); return ResponseEntity .status(HttpStatus.INTERNAL_SERVER_ERROR) .body(response); } ExceptionHandler(ActivitiObjectNotFoundException.class) public ResponseEntityErrorResponse handleNotFound(ActivitiObjectNotFoundException e) { return ResponseEntity .status(HttpStatus.NOT_FOUND) .body(new ErrorResponse(PROCESS_NOT_FOUND, e.getMessage())); } }在项目实际部署中我们发现流程定义的热更新是个常见需求。通过扩展RepositoryService可以实现不重启服务的流程版本更新这对7×24小时运行的系统尤为重要。另外建议对历史流程数据定期归档避免核心表数据膨胀影响查询性能。