当前位置: 首页 > article >正文

别再手动改状态字段了!用Spring Boot + Activiti7快速搞定请假审批流程(附完整代码)

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 groupIdorg.activiti/groupId artifactIdactiviti-spring-boot-starter/artifactId version7.1.0.M6/version /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependencyapplication.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 idleaveApproval name请假审批流程 startEvent idstartEvent/ userTask idfillLeave name填写请假单 activiti:assignee${applicant}/ userTask iddeptApprove name部门经理审批 activiti:candidateGroupsdeptManager/ exclusiveGateway iddecisionGateway/ sequenceFlow idflow1 sourceRefstartEvent targetReffillLeave/ sequenceFlow idflow2 sourceReffillLeave targetRefdeptApprove/ sequenceFlow idflow3 sourceRefdeptApprove targetRefdecisionGateway/ sequenceFlow idflow4 sourceRefdecisionGateway targetRefhrArchive conditionExpression xsi:typetFormalExpression ${days 3} /conditionExpression /sequenceFlow sequenceFlow idflow5 sourceRefdecisionGateway targetRefceoApprove conditionExpression xsi:typetFormalExpression ${days 3} /conditionExpression /sequenceFlow userTask idceoApprove name总经理审批 activiti:candidateGroupsceo/ sequenceFlow idflow6 sourceRefceoApprove targetRefhrArchive/ userTask idhrArchive name人事归档 activiti:candidateGroupshr/ sequenceFlow idflow7 sourceRefhrArchive targetRefendEvent/ endEvent idendEvent/ /process3.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) { // 设置流程变量 MapString, 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 ListTaskDTO getTasksByUser(String userId, String group) { // 个人任务 ListTask personalTasks taskService.createTaskQuery() .taskAssignee(userId) .list(); // 组任务 ListTask 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); } // 设置审批结果变量 MapString, 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 iddeptApprove name部门审批 extensionElements activiti:taskListener eventcreate classcom.example.workflow.DeptManagerAssignmentListener/ /extensionElements /userTask4.2 会签实现多人并行会签场景配置userTask idmultiApprove name多部门会签 extensionElements activiti:multiInstanceLoopCharacteristics isSequentialfalse activiti:collection${approvers} activiti:elementVariableapprover activiti:completionCondition ${nrOfCompletedInstances/nrOfInstances 0.6} /activiti:completionCondition /activiti:multiInstanceLoopCharacteristics /extensionElements /userTask4.3 业务关联与历史查询业务关联查询public LeaveVO getLeaveWithProcess(String leaveId) { // 查询业务数据 Leave leave leaveRepository.findById(leaveId); // 查询关联的流程实例 ProcessInstance instance runtimeService.createProcessInstanceQuery() .processInstanceBusinessKey(leaveId) .singleResult(); // 查询审批历史 ListComment comments taskService.getProcessInstanceComments(instance.getId()); return assembleVO(leave, instance, comments); }历史流程查询public ListHistoricProcessInstance 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 ResponseEntityString handleNotFound(ActivitiObjectNotFoundException ex) { return ResponseEntity.status(404).body(流程资源不存在); } ExceptionHandler(ActivitiTaskAlreadyClaimedException.class) public ResponseEntityString 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%。特别是在应对突发性流程变更时开发团队不再需要频繁发布系统更新只需通过调整流程定义即可快速响应业务变化。

相关文章:

别再手动改状态字段了!用Spring Boot + Activiti7快速搞定请假审批流程(附完整代码)

Spring Boot Activiti7:从零构建企业级审批工作流引擎 1. 传统状态字段管理的困境与工作流引擎的价值 在传统OA/ERP系统开发中,我们经常使用状态字段(如status)来跟踪业务流程。比如请假审批流程,可能会设计这样的状态…...

Win10系统下SQL Server 2005安装避坑指南:从环境配置到成功连接全流程解析

1. 环境准备:避开Win10与SQL Server 2005的兼容性雷区 在Windows 10上安装SQL Server 2005就像让老式收音机接收数字信号——需要特殊的适配器。我经历过6次安装失败后终于发现,IIS功能组件和系统兼容性设置是两大关键。首先打开控制面板的"启用或关…...

《数字图像处理》实战:从零实现CLAHE算法,剖析OpenCV库函数性能差异

1. CLAHE算法入门:从原理到生活化理解 第一次接触CLAHE算法时,我被这个拗口的专业名词吓到了。其实拆开来看就很简单:Contrast Limited Adaptive Histogram Equalization(对比度受限的自适应直方图均衡化)。就像给照片…...

Win11Debloat:Windows系统优化工具架构解析与技术实现深度指南

Win11Debloat:Windows系统优化工具架构解析与技术实现深度指南 【免费下载链接】Win11Debloat A simple, lightweight PowerShell script that allows you to remove pre-installed apps, disable telemetry, as well as perform various other changes to declutte…...

华为昇腾Atlas 200I DK A2开发板初体验:不接显示器,如何用SSH远程登录与文件传输?

华为昇腾Atlas 200I DK A2开发板无头模式实战指南:SSH连接与文件传输全解析 当你刚拿到华为昇腾Atlas 200I DK A2开发板时,可能第一反应是找显示器、键盘鼠标来配置它——但等等,其实完全不需要这些外设。作为一名长期与各类开发板打交道的工…...

终极指南:如何用RyzenAdj释放AMD锐龙处理器全部潜能

终极指南:如何用RyzenAdj释放AMD锐龙处理器全部潜能 【免费下载链接】RyzenAdj Adjust power management settings for Ryzen APUs 项目地址: https://gitcode.com/gh_mirrors/ry/RyzenAdj 你是否曾觉得你的AMD锐龙处理器性能被厂商限制住了?你是…...

LeagueAkari实战指南:英雄联盟客户端自动化工具深度解析

LeagueAkari实战指南:英雄联盟客户端自动化工具深度解析 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit LeagueAkari是一款基于LCU…...

工业路由器PPP拨号避坑指南:从串口权限到LCP心跳配置

工业级PPP拨号稳定性实战:从硬件配置到链路维护的全方位指南 在工业物联网和远程设备管理场景中,PPP协议作为串行链路的经典解决方案,其稳定性直接关系到设备联网的可靠性。与消费级应用不同,工业现场的环境干扰、设备长期运行需求…...

终极指南:使用ide-eval-resetter重置JetBrains IDE试用期的完整教程

终极指南:使用ide-eval-resetter重置JetBrains IDE试用期的完整教程 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter 你是否曾经在关键时刻被JetBrains IDE的"评估期已结束"提示打断工作流&a…...

tcc-g15:Dell G15散热控制的轻量级革命,彻底告别臃肿的AWCC

tcc-g15:Dell G15散热控制的轻量级革命,彻底告别臃肿的AWCC 【免费下载链接】tcc-g15 Thermal Control Center for Dell G15 - open source alternative to AWCC 项目地址: https://gitcode.com/gh_mirrors/tc/tcc-g15 想象一下,当你正…...

医疗场景下的因果推断:用Dragonnet模型预测药物疗效(Python全流程)

医疗场景下的因果推断:用Dragonnet模型预测药物疗效(Python全流程) 在医疗AI领域,评估药物疗效一直是个棘手的问题。想象一下,当医生面对两种降压药时,如何判断哪种对特定患者更有效?传统随机对…...

实战技巧|用命令行彻底清除顽固文件和文件夹

1. 为什么有些文件和文件夹无法删除? 你有没有遇到过这种情况:明明已经关闭了所有程序,但某个文件就是删不掉?系统总是弹出"文件正在使用"或"需要管理员权限"的提示。这种情况在Windows系统中相当常见&#x…...

Spring Boot 异步调用性能优化

Spring Boot 异步调用性能优化 在现代高并发应用中,性能优化是开发者必须面对的挑战之一。Spring Boot作为Java生态中广泛使用的框架,其异步调用能力能够显著提升系统吞吐量,但如何高效利用这一特性仍需深入探讨。本文将从线程池配置、异步方…...

YOLO26学生课堂行为检测:6类行为mAP达0.905,使用手机与趴桌识别最准

摘要 学生课堂行为检测是智能教育监控系统中的关键任务,对于教学质量评估、学生注意力分析和课堂管理具有重要意义。本研究基于YOLO26目标检测算法,构建了一个包含6类常见课堂行为(举手、阅读、写字、使用手机、低头、趴桌)的检测…...

别再只跑Demo了!聊聊用DCGAN生成CelebA人脸时,那些影响效果的隐藏参数和调优实战

DCGAN人脸生成进阶指南:从参数调优到实战诊断 当你第一次用DCGAN跑通CelebA人脸生成时,那种兴奋感可能很快会被生成的模糊五官或重复表情所冲淡。这就像新手摄影师第一次拿到专业单反——按下快门很简单,但想要拍出杂志封面级别的作品&#x…...

YOLO26实战:吸烟/喝水/打电话检测系统,mAP达0.837

摘要 本报告基于YOLO26目标检测算法,针对监控场景下的吸烟、喝水、打电话三种行为构建了一个多类别检测系统。系统使用3157张图像进行训练,350张图像进行验证,包含三个目标类别:吸烟(smoke)、喝水&#xf…...

如何永久保存微信聊天记录:WeChatMsg终极使用指南

如何永久保存微信聊天记录:WeChatMsg终极使用指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/WeChatMsg…...

从PCIe到48V供电:手把手拆解SFF-TA-1002连接器的引脚定义与实战应用

从PCIe到48V供电:手把手拆解SFF-TA-1002连接器的引脚定义与实战应用 第一次拿到SFF-TA-1002连接器的Datasheet时,我盯着那密密麻麻的引脚图发呆了十分钟——80W的12V主电源、3.3V辅助供电、8组高速差分对,还有那些神秘的边带信号引脚&#xf…...

千峰办公助手:一站式解决30+办公场景的免费效率工具深度评测

在日常办公场景中,我们经常需要处理各种琐碎但重复性极高的任务。 从PDF格式转换到图片批量处理,从文件重命名到OCR文字识别,每一个需求都可能迫使我们安装一款专门的软件。 久而久之,电脑里充斥着各种单一功能的工具&#xff0…...

钉钉打卡风控逆向实战:从解密lbswua到绕过ddsec检测(附Frida脚本)

钉钉打卡风控逆向工程深度解析:从算法破解到环境伪装实战 早上9点整,钉钉的打卡提醒准时响起,但你的手机却显示距离公司还有3公里——这是无数打工人经历过的尴尬场景。作为国内主流办公协同平台,钉钉的风控系统经过多年迭代已形成…...

剖析若依(RuoYi)框架RBAC权限模型:从数据表到前后端联动的实战解析

1. 若依框架RBAC权限模型基础解析 第一次接触若依框架的权限系统时,我被它清晰的RBAC实现惊艳到了。这个设计完美解决了我们团队长期面临的权限管理混乱问题。RBAC(基于角色的访问控制)模型就像公司的职位体系:CEO、部门经理、普…...

CLIP模型实战:如何用Python快速实现图像与文本的跨模态搜索(附代码)

CLIP模型实战:如何用Python快速实现图像与文本的跨模态搜索(附代码) 跨模态搜索正成为AI领域的热门方向,它打破了传统搜索的单一模式限制。想象一下,在电商平台用文字描述"复古风格的皮质沙发"就能找到相关商…...

Avalonia 11.0.6与OxyPlot兼容性实战:如何解决最新版报错问题

Avalonia 11.0.6与OxyPlot兼容性深度解析:从报错排查到替代方案选择 当Avalonia跨平台UI框架升级到11.0.6版本后,许多开发者发现原本运行良好的OxyPlot图表突然无法正常显示,控制台抛出各种令人困惑的异常信息。这个问题不仅影响了项目进度&…...

保姆级教程:在Ubuntu上用Geth搭建以太坊私链,并部署你的第一个智能合约(含挖坑与填坑实录)

从零构建以太坊私链:UbuntuGeth实战指南与智能合约部署全解析 在区块链开发领域,搭建本地测试环境是每个开发者必经的成长阶梯。想象一下,你正在Ubuntu终端前,准备第一次亲手搭建以太坊私有网络——那种既兴奋又忐忑的心情&#x…...

STM32嵌入式存储方案:基于ThreadX与LevelX构建W25Q128的FileX文件系统驱动

1. 为什么需要嵌入式文件系统 在STM32这类资源受限的嵌入式设备上直接操作W25Q128 Flash芯片时,开发者常会遇到几个头疼的问题。比如每次写入前必须擦除整个扇区(4KB),频繁擦写会导致特定区块提前损坏,还有断电时数据丢…...

用Canvas与requestAnimationFrame打造沉浸式网页飘雪动画

1. 为什么选择Canvas与requestAnimationFrame? 在网页上实现动画效果有很多种方式,比如CSS动画、GIF图片、SVG动画等。但如果你想要实现高性能、可定制化的复杂动画效果,Canvas配合requestAnimationFrame绝对是首选组合。我做过不少网页动画项…...

QT (5.14.2) 编译MySQL驱动:从源码到部署的完整避坑指南

1. 为什么需要手动编译MySQL驱动? 很多新手在使用QT连接MySQL数据库时,经常会遇到一个头疼的问题:明明安装了MySQL,QT却提示"QSqlDatabase: QMYSQL driver not loaded"。这种情况通常是因为官方预编译的MySQL驱动版本不…...

Hutool数字工具进阶玩法:用NumberUtil生成抽奖号码+进制转换黑科技

Hutool数字工具进阶实战:公平抽奖系统与优惠券编码生成方案 在营销活动系统开发中,随机数生成和进制转换是两项高频需求。Hutool的NumberUtil工具类提供了简洁而强大的API,能够帮助开发者快速实现这些功能,同时保证商业计算的精确…...

一次由「 TCP半连接队列(SYN队列)溢出」导致的连接失败

**一次由TCP半连接队列溢出引发的连接故障** 在互联网通信中,TCP协议的三次握手是建立连接的基础。当服务器遭遇SYN洪泛攻击或突发高并发请求时,半连接队列(SYN队列)可能因溢出而丢弃新的SYN包,导致客户端连接失败。这…...

QQ空间导出助手终极指南:5步完整备份你的青春回忆

QQ空间导出助手终极指南:5步完整备份你的青春回忆 【免费下载链接】QZoneExport QQ空间导出助手,用于备份QQ空间的说说、日志、私密日记、相册、视频、留言板、QQ好友、收藏夹、分享、最近访客为文件,便于迁移与保存 项目地址: https://git…...