工作流引擎选型与请假审批实战:Flowable BPMN 核心机制解析
简介面向Java Web初学者的工作流入门实例围绕请假申请与审批场景演示Struts2框架结合jBPM流程定义实现任务提交、审批流转与状态管理适合想理解工作流基本概念、MVC分层及中文编码处理的学习者。压缩包共42个文件体积仅51KB包含12个xml配置struts.xml、jBPM流程定义、框架配置、8个JSP页面登录、申请、经理/老板审批视图、4个properties资源文件及Java源码、class文件、项目工程文件等目录结构完整清晰。已有250人学习下载。通过该实例可掌握请假流程从建模到落地的完整路径包括Action类业务处理、拦截器扩展、jBPM流程部署以及UTF-8编码统一设置等排错思路适合作为课堂练习或课程设计参考。 工作流这东西圈内聊得火热从Flowable到Camunda再到dify、coze这些AI工作流平台似乎什么都能往里塞。但真正落到日常业务最经典、也最锻炼人的一个场景就是请假审批。别小看这个看似简单的流程它麻雀虽小五脏俱全涵盖了条件分支、多级审批、会签或签、驳回驳回等几乎所有核心机制。把请假实例吃透你再去看那些复杂的企业级流程思路会清晰很多。这篇博文我就用实际项目里的一个请假工作流实例从场景设计、流程建模、引擎选型到具体代码实现再到排查问题完整走一遍。无论你是刚接触工作流的后端开发还是想在公司内部搭建审批系统的运维或架构师这篇内容都能给你一个可以直接落地的参考。1. 场景分析与方案选型为什么请假流程是“最佳练手项目”先别急着写代码第一步得把业务捋清楚。我在之前的项目中接过一个需求给公司内部做一个OA审批系统第一个上线的功能就是请假。需求方提了一堆要求比如请假要分类型、不同天数要不同的人审批、部门经理批完人事还要备案等等。如果直接用硬编码写if else第一版能跑但后续如果加个“请假天数超过5天需要总经理审批”改代码、重新发版效率就太低了。这就是引入工作流引擎的核心动力把业务流程从代码里剥离出来做成可配置、可热更新的模型。选型上我对比了当前主流的几个方案。第一类是重量级BPM引擎代表性的有Flowable和Camunda。这类引擎遵循BPMN 2.0规范功能非常强大支持复杂的网关、子流程、事件监听适合中大型企业级系统。Flowable因为社区活跃、文档全国内用的尤其多。第二类是轻量级状态机或自研流程框架比如Spring StateMachine或者干脆自己用数据库状态字段硬顶。这类方案轻便灵活但一旦流程复杂起来维护成本陡增不适合迭代频繁的业务。第三类是近两年很火的AI工作流平台比如dify、coze、n8n。但这里必须说清楚它们的核心优势在自动化任务编排、大模型调用和数据联动上用于处理“请假审批”这种强规则、强权重的企业流程反而显得不太合适因为审批流核心是状态流转和权限控制不是内容生成。我的结论是如果你在做企业级应用需要流程的持久化、历史追溯、复杂网关控制直接上Flowable或Camunda。这两个引擎的核心概念几乎相同学会了其中一个切换成本很低。我在项目里选了Flowable主要看重它的BPMN文件解析能力和Spring Boot集成生态接下来整个实例我都基于Flowable来演示。如果你已经在用Camunda思路完全一致只是API名称略有不同。2. 流程模型设计先画图再写代码2.1 请假流程的业务规则梳理在设计BPMN流程图之前必须先把业务规则一条条列清楚否则后面确定网关条件时会手忙脚乱。我当时的规则是请假类型分事假、病假、年假、调休四种。请假天数小于等于3天部门经理审批通过后直接结束抄送人事备案。请假天数大于3天部门经理审批通过后还需要总监审批最终抄送人事备案。任意审批节点驳回流程直接结束发起人收到驳回通知。部门经理和总监都可以进行“驳回”操作但驳回后不允许修改再提交必须重新发起流程。这里可以看到节点有“必经节点”和“条件节点”之分。天数就是一个流程变量网关根据这个变量路由到不同分支。这是一种非常典型的流程设计模式在真实的报销、采购审批中同样适用。把规则想清楚再画流程图就会非常顺利。2.2 BPMN 2.0 流程图画法与关键节点说明BPMN图我建议用Flowable提供的可视化插件直接画保存为bpmn20.xml文件。流程核心节点如下节点类型节点名称说明开始事件startEvent流程启动入口用户任务applyLeave发起人填写请假申请表单用户任务managerApprove部门经理审批节点排他网关judgeDays判断天数是否大于3天用户任务directorApprove总监审批节点仅天数大于3天时走到用户任务hrRecord人事备案节点抄送备案结束事件endEvent流程结束画图时有一个关键配置用户任务节点的assignee属性。这个决定了任务创建后分配给谁。实践中不要直接写死用户名而应该配置为流程变量比如${managerUser}这样发起流程时动态指定审批人灵活性高得多。很多人第一次用Flowable把审批人写死在流程图里结果换个部门、换个人审批就傻眼了这是新手最容易踩的坑。流程图设计完毕BPMN文件里面除了图形XML还会包含对应节点的扩展属性。如果使用Flowable的IDEA插件可视化绘制保存时插件会根据拖拽结果自动生成完整的XML描述不需要手敲但手敲也不是不行只是容易漏掉闭合标签。我建议第一次做还是用插件等熟悉BPMN结构后再手写也不迟。3. 核心代码实现从流程部署到审批通过3.1 环境依赖与基础配置先搭建项目环境。我用的是Spring Boot 2.7 Flowable 6.7.2数据库用MySQL。引入依赖后配置数据源Flowable会自己在数据库里创建ACT_开头的表这些表存放流程定义、流程实例、任务、变量等数据。dependency groupIdorg.flowable/groupId artifactIdflowable-spring-boot-starter/artifactId version6.7.2/version /dependency注意Flowable的自动建表配置是spring.flowable.database-schema-updatetrue。生产环境建议设置为false改为手动执行SQL脚本升级避免引擎版本升级时自动改动表结构造成隐患。3.2 流程部署发布BPMN模型BPMN文件画好后放到resources/processes目录下应用启动时会自动部署。这是一条非常便捷的路径因为我只需要把leave.bpmn20.xml丢进这个目录Flowable会扫描并且往ACT_RE_PROCDEF表插入流程定义记录。但如果你想在代码里动态部署比如后期通过后台管理页面上传流程图就需要手动调用API了。Autowired private RepositoryService repositoryService; public void deployProcess() { Deployment deployment repositoryService.createDeployment() .addClasspathResource(processes/leave.bpmn20.xml) .name(请假审批流程) .deploy(); System.out.println(部署ID: deployment.getId()); }部署之后每次修改BPMN文件都需要重新部署一次否则引擎运行的还是旧版本。Flowable的版本管理会保留多个版本的流程定义发起流程时默认使用最新版本也可以通过processDefinitionKey指定版本。实际项目中版本管理非常重要线上流程跑到一半如果你重新部署了新版本旧实例不受影响新发起默认走新版本这正好满足了业务流程逐步升级的需要。3.3 发起流程设置流程变量前端表单提交请假信息后后端发起一个流程实例。首先要拿到流程定义然后设置流程变量。这里我把审批人作为变量传入。Autowired private RuntimeService runtimeService; public void startLeaveProcess(LeaveApplyDTO dto) { MapString, Object variables new HashMap(); variables.put(applyUser, dto.getUserId()); variables.put(leaveType, dto.getLeaveType()); variables.put(leaveDays, dto.getLeaveDays()); variables.put(reason, dto.getReason()); variables.put(managerUser, getManagerByUserId(dto.getUserId())); variables.put(directorUser, getDirectorByUserId(dto.getUserId())); ProcessInstance processInstance runtimeService .startProcessInstanceByKey(leaveProcess, variables); System.out.println(流程实例ID: processInstance.getId()); }注意这里的getManagerByUserId和getDirectorByUserId在实际系统中需要查询组织架构关系获得对应的审批人。流程的第一个节点applyLeave在发起流程时并不会自动完成任务列表中会出现一条发起人的待办。所以发起流程之后呢还需要额外调一次完成任务的API或者干脆在开始事件后面直接跟一个服务任务来自动跳过申请节点只保留业务报备。我在实际项目中更倾向于保留申请节点作为待办让发起人确认自己填写的表单内容无误后再提交体验上更顺。3.4 查询待办并完成审批审批人的操作台通常需要列出所有待办任务。这步用得极其频繁API也比较固定。Autowired private TaskService taskService; public ListTaskInfo queryTodoList(String assignee) { ListTask tasks taskService.createTaskQuery() .taskAssignee(assignee) .orderByTaskCreateTime().desc() .list(); return tasks.stream().map(task - { TaskInfo info new TaskInfo(); info.setTaskId(task.getId()); info.setTaskName(task.getName()); info.setProcessInstanceId(task.getProcessInstanceId()); return info; }).collect(Collectors.toList()); }审批人点击“同意”或“驳回”本质上是完成当前用户任务并且设置一个审批结果变量供下一步网关或后续节点判断。这里我把“完整流程推进”封装成一个方法。public void completeTask(String taskId, boolean approved, String comment) { MapString, Object variables new HashMap(); variables.put(approved, approved); variables.put(comment, comment); taskService.complete(taskId, variables); }到这里approved变量在后续的排他网关中会被读取。如果approved为false通过网关直接跳到结束节点流程终止。如果为true那么网关继续判断leaveDays的值决定是否需要总监审批。3.5 条件分支网关与多级审批的实现我的BPMN流程里排他网关judgeDays有两个出口连线。第一条连线的条件表达式是${leaveDays 3}走到人事备案节点。第二条连线的条件表达式是${leaveDays 3}继续走到总监审批节点。Flowable的表达式是SpringEL表达式所有这些变量来源于流程实例的变量表。如果走总监审批总监完成他的用户任务后流程通向hrRecord备案节点。人事备案这个节点通常可以设置为一个“服务任务”或“接收任务”逻辑上人事不需要真的进入系统手工点击而是一个自动化的抄送提醒。我这里的做法是设置成用户任务分配给hrUser前端展示一条待办让人事确认已备案同时也可以对接企业微信或钉钉发送通知到人事的移动端保证线下操作的闭环。如果你需要更严格的多级审批控制比如某个层级需要会签全部通过或或签一人通过即可可以用Flowable的multiInstanceLoopCharacteristics特性。我在另一个项目里做过会签BPMN中配置Collection和Element Variable就能实现会签功能。核心配置如下简化userTask idmultiApprove name多人会签 flowable:assignee${assignee} multiInstanceLoopCharacteristics isSequentialfalse flowable:collectionassigneeList flowable:elementVariableassignee/multiInstanceLoopCharacteristics /userTask平行网关和多实例特性让Flowable的功能上限非常高但请假这个场景用不到简单排他网关就足够了。不过这里要提醒一下会签时业务上必须明确“全部通过才继续”还是“任一通过即可”否则流程就会卡在多人审批状态谁都不知道下一步该干嘛。4. 工具选型解析流程引擎的对比权衡前面方案选型提到了Flowable、Camunda、轻量级状态机以及AI工作流平台这里单独拉出来做一次对比。我在不同项目里都尝试过这些方案有些教训值得分享。技术方案适用场景优势突出问题Flowable中大型企业业务系统BPMN标准支持完整集成Spring Boot方便社区活跃中文文档多学习曲线较陡表结构复杂Camunda中大型企业业务系统自带操作界面友好监控强大性能优秀比Flowable更重度打包体积大Spring StateMachine状态流转简单的小项目轻量、上手快、不引入大型引擎不支持复杂的并行、会签、事件订阅要手写很多扩展自研状态字段极简单流程最简单直观流程一旦变化就要改表结构和代码dify/coze工作流AI编排、内容处理、自动化串大模型快速搭建、可视化强、适合AIGC场景不擅长有状态审批、权限控制和持久化审计现在的很多人一听到“工作流”三个字就联想到dify、coze这些AI平台其实它们解决的是另一种问题。AI工作流更多是“数据怎么流动、节点怎么处理”而BPM工作流关注的是“人的任务怎么流转、状态怎么变化、谁有权处理”。两个体系适合不同场景千万别混用。如果你做企业内部OA、合同审批、采购流程老老实实用Flowable或Camunda。如果你只是要串一串OpenAI API、定时抓取数据、生成报告那dify或者n8n的效率会高得多。只有把工具的边界搞清楚选型才不会跑偏。这也是我每次做技术方案时最强调的一点不要因为某个工具火就直接套用要去匹配业务的本质属性。5. 常见问题与排查技巧实录5.1 流程部署失败或流程图不生效新手经常会遇到明明修改了BPMN文件重启后流程却还是旧逻辑。这个问题十有八九是Flowable没有重新部署。需要检查是否把最新的bpmn20.xml文件放到了正确目录如果手动调用了deploy()是否执行成功发起流程时用的流程定义Key是否和BPMN中的process标签id一致。我在项目中还遇到过BPMN文件本身语法错误导致启动失败。常见错误包括标签漏写闭合、连线缺少sourceRef或targetRef、表达式写法不符合SpringEL语法。IDEA安装Flowable插件后打开BPMN文件会有语法校验强烈建议开启。5.2 任务一直查不到或任务卡死查询待办时返回空首先要确认当前流程实例运行到了哪个节点。排查步骤是查ACT_RU_TASK表看当前运行的任务在哪个用户任务节点以及assignee字段的值。如果没有值就看看是否配置了Candidate用户组或者变量没有传给任务。如果任务卡死多半是排他网关的条件表达式计算结果不符合预期导致没有出口连线符合条件此时需要检查变量的赋值。5.3 驳回后修改再提交的实现陷阱很多业务有“驳回后允许修改再提交”的需求这可比单纯“驳回结束流程”复杂。因为默认流程实例已经结束或者当前任务完成流程状态不可逆需要在流程设计时规划好驳回的处理方式。有一种常用设计是审批节点驳回时带一个特殊的Routing变量流程跳回到发起人的申请节点同时申请节点设置为可编辑状态。要注意的是这样实现时BPMN图中加一条从审批节点回到申请节点的连线并在连线上配条件表达式。这种设计会增加流程图的复杂度但也能满足更真实的业务需求。这个陷阱在第一次做时很容易忽略。我当时图省事直接把驳回做成结束流程上线没两周就被业务部门投诉了最后还是补上了“驳回后重新修改再提交”的循环分支才算是真正闭环。5.4 组织架构变化后审批人失效实际项目中员工会调岗、离职部门的组织结构也经常变动。如果在发起流程时一次性将审批人变量固化到流程实例中那么后续即使人员变动当前流程也不受影响这算是优点但如果流程流转时间很长中途审批人离职任务就会一直悬在那边。解决方案是在网关或服务任务中通过代码动态查询审批人或者加定时任务检测超时没有处理的任务自动重新分配。5.5 与消息通知集成的经验工作流跑通了系统还缺一个关键环节——通知。必须把Flowable的流程事件监听器结合消息队列当任务创建、流程结束、任务驳回时推送企业微信、钉钉、邮件等通知。Flowable提供了ActivitiEventListener可以监听TASK_CREATED、PROCESS_COMPLETED等事件。我在事件监听器中做了一个分发器根据事件类型发送不同模板消息整个体系才真正完整。6. 从请假实例到更复杂流程的扩展思路请假流程跑通后你已经拥有了一套完整的工作流能力。接下来扩展很有价值的方向有几个。第一个是任务超时提醒。Flowable提供定时器事件在用户任务节点上配置TIMER_EVENT可以设置任务到期时间到期后自动触发提醒或更新。对于审批时效要求严格的业务比如财务付款审批这个能力非常实用。第二个是流程历史分析与优化。ACT_HI_*系列表记录了流程的全部历史数据可以用来统计分析每个节点的平均耗时、驳回率等。用这些数据驱动流程优化比拍脑袋改流程靠谱得多。第三个是和表单引擎的深度集成。工作流引擎本身不管理表单渲染但实际业务每个节点都需要表单来承接数据。你可以自研表单引擎也可以用Flowable的FormService实现动态表单绑定让不同节点展示不同的表单字段实现流程与数据真正的联动。第四个是往多租户SaaS化演进。在Flowable中加上租户ID后每个租户的流程定义和数据完全隔离这又是另一个复杂度的提升。如果公司未来有对外输出SaaS产品的打算这一步迟早会走到。总之请假审批这个实例本质上是个引子。它的价值在于帮你理解工作流引擎的通用机制——部署、实例、任务、变量、网关。搞清楚这套机制后面面对任何流程需求你都不会再心里打鼓了。我在实际项目里踩过的最大一个坑就是最开始上来就拿着流程图去写代码忽略了流程变量和任务节点之间的对应关系导致后面各种调试焦头烂额。你先把这个请假案例完整跑通再往那些复杂场景上延伸会稳得多。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联
返回资讯列表 →