Flowable 工作流引擎实战:从 if-else 状态机到 BPMN 流程编排

SailTrack
2026-05-18
点 赞
0
热 度
11
评 论
0
  1. 首页
  2. 后端开发
  3. Flowable 工作流引擎实战:从 if-else 状态机到 BPMN 流程编排

最近在做 Workforce Hub 的审批模块时,一个需求让我头疼了好几天——请假审批流程。

说白了就是一个"员工提交 → 主管审批 → 人事复核"的流程,听着挺简单的对吧?但架不住产品经理(导师)要求多:不同假期类型走不同审批链、三天以内免人事复核、超时自动提醒、驳回原因必填……

如果全用 if-else 硬编码状态机,写到后面我估计连自己都看不懂了。

后来硬着头皮引入了 Flowable,踩了不少坑,但也确实解决了问题。今天就来聊聊这个过程。

一、为什么需要工作流引擎?——几个 if-else 引发的惨案

其实一开始我没想用工作流引擎。请假审批嘛,不就是改个状态:

// 我一开始天真的想法
if (leave.getStatus() == PENDING) {
    leave.setStatus(approved ? APPROVED : REJECTED);
    leaveMapper.updateById(leave);
}

这代码跑了一周没问题。直到需求变成了这样:

  • 年假:员工 → 主管 → 人事

  • 事假:员工 → 主管

  • 病假:员工 → 主管 → 人事(附诊断证明)

  • 三天以内:跳过人事复核

  • 主管超过 24 小时未审批 → 自动提醒

  • 驳回后允许重新提交

血泪教训:当审批链开始分叉、节点开始有动态条件时,纯写 if-else 的代码复杂度是指数级增长的。更别提后续还要支持审批历史追溯、流程可视化这些需求。

这时候工作流引擎的价值就体现出来了——它把"流程怎么走"从业务代码里抽离出来,变成可配置的 BPMN 流程图。

二、为什么选 Flowable?——一个不太成熟的选择

市面上 Java 生态的工作流引擎主要有三个:Activiti、Flowable、Camunda。

为了做出选择,我对比了一下:

特性

Activiti 7

Flowable 6.x

Camunda 7

Spring Boot 整合

✅ 原生

✅ 自动配置

✅ 社区支持

BPMN 2.0 支持

✅ 完整

✅ 完整

文档质量

一般

较好

较好

学习曲线

中等

中等

陡峭

决策表 (DMN)

中文社区

较少

有一些

更少

说实话,选 Flowable 很大程度上是因为它是 Activiti 原班人马出来做的——Activiti 5 的核心团队不满 Alfresco 的方向,出来另起炉灶。而且 Spring Boot 官方有 starter 支持,对咱们这种"能用就行"的学生项目来说最友好。

当然 Camunda 在企业级领域确实更强,但学习曲线太陡了。大三学生搞个课程项目,Flowable 够用了。

我的理解:Flowable 本质上就是把 BPMN 2.0(业务流程建模标注)这个标准规范用 Java 实现了一遍,然后包装成了 Spring Boot 能直接用的组件。

三、流程设计实例——请假审批的 BPMN 之旅

下面是我给 Workforce Hub 设计的请假审批流程,先看一张全景图:

flowchart TD S([员工提交请假]) --> A1[/AI 预审/] A1 -->|校验通过| G1{主管审批} A1 -->|校验不通过| RJ1[驳回并告知原因] G1 -->|通过| G2{请假天数 >3?} G1 -->|驳回| RJ2[驳回] G2 -->|是| H1[/人事复核/] G2 -->|否| ARC[归档] H1 -->|通过| TASKS[触发后续任务] H1 -->|驳回| RJ3[驳回] TASKS --> ARC subgraph 后续任务 T1[考勤重算] T2[余额扣减] T3[站内通知] T4[日历同步] end TASKS -.-> T1 TASKS -.-> T2 TASKS -.-> T3 TASKS -.-> T4

整个流程分为三步:

第一步:AI 预审。这个比较特别——员工提交请假后,先不直接发给主管,而是过一个 AI 校验节点。比如检查剩余年假天数够不够、有没有重叠的请假记录、病假有没有附诊断证明。不符合规则的直接驳回并告诉原因,不用等主管手动发现。

这个设计思路来自我们项目的核心创新点——“Agent 深度嵌入业务链路”。AI 不只是聊天机器人,它实实在在地参与到了审批流程里。

第二步:部门主管审批。AI 校验通过后,流程到达主管节点。主管可以看到请假详情和 AI 预审结果,然后审批。

第三步:人事复核。如果请假天数超过 3 天,流程自动进入人事复核节点。不到 3 天的直接归档。

审批通过后,自动触发四个服务任务:考勤重算、余额扣减、站内通知、日历同步。

先看一下整体架构——Flowable 在 Spring Boot 里是怎么嵌进去的:

flowchart LR subgraph 前端 VUE[Vue3 管理后台] end subgraph Spring Boot CTRL[Controller 层] SVC[Service 层] FLOW[Flowable 引擎] AGENT[AI Agent 模块] end subgraph 存储 MYSQL[(MySQL)] REDIS[(Redis)] end VUE -->|REST API| CTRL CTRL --> SVC SVC --> FLOW SVC --> AGENT FLOW --> MYSQL FLOW --> REDIS AGENT -->|pgvector| MYSQL

四、Flowable 与 Spring Boot 的整合——到底写了多少代码?

引入 Flowable 其实不复杂,加个依赖就行:

<!-- pom.xml -->
<dependency>
    <groupId>org.flowable</groupId>
    <artifactId>flowable-spring-boot-starter</artifactId>
    <version>6.8.0</version>
</dependency>

启动 Spring Boot 后,Flowable 会自动创建 70+ 张表来管理流程定义、实例、任务、历史记录。小发现:这些表名都以 ACT_ 开头,跟 Activiti 一样的命名约定——毕竟同根同源。

核心代码其实就几个接口:

// 1. 启动一个请假流程
@PostMapping("/leave/submit")
public Result submitLeave(@RequestBody LeaveRequest req) {
    // 构建流程变量
    Map<String, Object> vars = new HashMap<>();
    vars.put("leaveType", req.getLeaveType());
    vars.put("leaveDays", req.getLeaveDays());
    vars.put("applicantId", SecurityUtils.getCurrentUserId());
    
    // 启动流程 - processDefinitionKey 对应 BPMN 文件的 process id
    ProcessInstance pi = runtimeService
        .startProcessInstanceByKey("leave-approval", vars);
    
    return Result.ok(pi.getId());
}

// 2. 查询待办任务
@GetMapping("/tasks/todo")
public Result getTodoTasks() {
    List<Task> tasks = taskService.createTaskQuery()
        .taskAssignee(SecurityUtils.getCurrentUserId())
        .list();
    return Result.ok(tasks);
}

// 3. 审批任务
@PostMapping("/tasks/{taskId}/complete")
public Result completeTask(@PathVariable String taskId, 
                           @RequestBody ApprovalDto dto) {
    Map<String, Object> vars = new HashMap<>();
    vars.put("approved", dto.isApproved());
    vars.put("comment", dto.getComment());
    
    taskService.complete(taskId, vars);
    return Result.ok();
}

踩坑经历一:一开始我直接用 taskService.complete() 就完了,没传流程变量——结果下个节点根本不知道这个任务是"通过"还是"驳回",网关判断直接报错。花了一个下午在 ACT_HI_VARINST 表里翻历史变量,才明白流程变量是 BPMN 里网关判断的唯一依据。

踩坑经历二processDefinitionKey 写错了——我在 BPMN 文件里定义的 process id="leaveApproval"(驼峰),代码里写的是 "leave-approval"(横线分隔)。Flowable 不会报"找不到",而是直接抛一个巨长的异常栈,关键信息藏在第 37 行——我是搜了半天 StackOverflow 才定位到的。

踩坑经历三:多实例任务(比如同时抄送多人)默认是串行的——我以为是并发,结果等了三分钟第一个人没处理,第二个人就一直在排队。后来加了 flowable:async="true" 才解决问题。

五、AI Agent 怎么嵌入工作流?——这个比较有意思

传统工作流引擎的节点是固定的——用户任务、服务任务、网关。但我们在 Flowable 的基础上做了一个扩展:自定义任务类型——AI 审核节点。

先看链路——AI Agent 是怎么嵌进 Flowable 的:

flowchart LR FLOW[Flowable 引擎] -->|触发 Service Task| DGT[JavaDelegate 代理] DGT -->|调用| AI[AI Agent 服务] AI -->|检索| VEC[(pgvector)] AI -->|推理| LLM[DeepSeek 大模型] LLM -->|返回结果| DGT DGT -->|写入流程变量| FLOW FLOW --> GW{排他网关} GW -->|aiCheckPassed=true| PASS[进入主管审批] GW -->|aiCheckPassed=false| FAIL[驳回 + 申诉通道]

实现方式是在 BPMN 流程里定义一个 Service Task,通过 JavaDelegate 调用 AI Agent:

@Component("aiLeaveChecker")
public class AILeaveCheckerDelegate implements JavaDelegate {
    
    @Override
    public void execute(DelegateExecution execution) {
        // 获取流程变量
        String applicantId = (String) execution.getVariable("applicantId");
        int leaveDays = (int) execution.getVariable("leaveDays");
        String leaveType = (String) execution.getVariable("leaveType");
        
        // 调用 AI Agent 校验规则
        AILeaveCheckResult result = aiAgentService.checkLeaveRules(
            applicantId, leaveType, leaveDays);
        
        // 将校验结果写入流程变量,供网关判断
        execution.setVariable("aiCheckPassed", result.isPassed());
        execution.setVariable("aiCheckReason", result.getReason());
    }
}

然后在 BPMN 里这样引用:

<serviceTask id="aiCheck" name="AI预审" 
    flowable:delegateExpression="${aiLeaveChecker}"/>

网关会根据 aiCheckPassed 变量自动路由到"主管审批"或"驳回"分支——全程不需要写 if-else。

说实话,这个设计一开始我心里也没底——把 AI 的判断直接作为流程的路由依据,万一 AI 判断错了呢?

我在测试的时候遇到过:AI 预审说员工"年假余额不足",但员工截图过来显示还剩 3 天。排查了半天发现是缓存问题——余额扣减后 Redis 更新了,但 pgvector 里旧的员工文档还写着旧余额,AI 查到了旧数据。

后来加了两层兜底:

  1. 数据一致性:AI 检索时强制绕过缓存,直查 MySQL 最新余额

  2. 人工申诉:AI 校验不通过的情况下,员工可以走"人工申诉"通道,绕过 AI 节点直接到主管

申诉通道的流程大概是这样的:

小发现:加了申诉通道之后,流程图的复杂度直接翻倍。这让我意识到一个问题——BPMN 图不是越复杂越好,每个兜底分支都是在给维护埋坑。后来我把申诉逻辑抽成了独立的子流程(callActivity),主流程才瘦回来。

六、流程可视化——调试利器

Flowable 自带了一个流程设计器和监控面板,开发阶段可以直接看到流程跑到哪了:

flowchart TD S([员工提交请假]) --> AI[/AI 预审节点/] AI -->|校验通过| MGR[主管审批] AI -->|校验不通过| APPEAL{员工申诉?} APPEAL -->|是| MGR APPEAL -->|否| END1([驳回]) MGR -->|通过| HR{>3天?} MGR -->|驳回| END2([驳回]) HR -->|是| HRNODE[人事复核] HR -->|否| END3([归档]) HRNODE -->|通过| END3 HRNODE -->|驳回| END2

当然生产环境一般不会开这个面板。我在项目里自己画了一个轻量的流程跟踪页面,让用户能看到自己提交的请假当前在哪个环节、谁在审批、预计多久能完成。

总结

我的收获

  1. 工作流引擎的核心价值是"流程与代码解耦"——不是说用上 Flowable 代码就变少了,而是流程变可维护了。加一个新审批节点,改 BPMN 图就行,不用翻几千行 Java 代码

  2. Flowable 的学习曲线比我想象中平——不用上来就啃 BPMN 2.0 完整规范,看懂开始/结束/任务/网关/连线这五个元素就够走完一个请假流程了

  3. AI + 工作流的化学反应比我预期的强——Agent 从"聊天机器人"变成了"流程的守门员",这个体验很不一样


让我们忠于理想,让我们面对显示

SailTrack

entp 辩论家

站长

具有版权性

请您在转载、复制时注明本文 作者、链接及内容来源信息。 若涉及转载第三方内容,还需一同注明。

具有时效性

目录

欢迎来到SailTrack的站点,为您导航全站动态

37 文章数
11 分类数
3 评论数
49标签数