Activiti / Flowable 工作流
Java 高级 · 业务引擎
审批流、订单流这类"状态多、分支多、易变更"的业务,硬编码 if-else 会迅速腐化。Activiti 与 Flowable(同源开源 BPMN 引擎)用标准流程定义解耦业务与流转。本文讲清 BPMN 2.0 元素、引擎服务架构、数据库表、会签/或签、驳回/退回与超时升级的实现机理。
一、为什么用工作流引擎
审批、工单、订单状态机,本质是"状态 + 流转规则"。硬编码的痛点:
- 状态分支多 → 大量
if(status==X)嵌套,难以维护。 - 流程变更(加一个审批人)→ 改代码、发版。
- 无法追溯"谁在何时做了什么"(审计缺失)。
工作流引擎把"流转规则"外置成 BPMN 2.0 XML,运行时由引擎驱动,业务只关心"做什么"(ServiceTask 调你的 Java 服务)。Flowable 是 Activiti 的核心开发者出走后创立的分支,API 更友好、社区更活跃,本文以 Flowable 为主(Activiti 同理)。
二、BPMN 2.0 核心元素
- Event(事件):Start / End / 定时器(Timer)/ 消息(Message)中间事件。
- Task(任务):
UserTask(人工审批,需认领/完成)、ServiceTask(自动调用 Java 类/表达式)、ReceiveTask(等待外部信号)、ScriptTask。 - Gateway(网关):
Exclusive(排他,单路,默认走第一条满足的)、Parallel(并行,多路齐发,汇合才继续)、Inclusive(包容性,满足条件的多路)、Event-based(事件网关,等第一个到达的事件)。 - Sequence Flow(流转):带
condition的连线,驱动状态流转。
三、引擎架构:ProcessEngine 与四大服务
ProcessEngine 是入口,核心是四个服务:
| 服务 | 职责 | 管理的表 |
|---|---|---|
RepositoryService | 部署/查询流程定义(BPMN) | ACT_RE_*(资源定义) |
RuntimeService | 启停流程实例、发信号 | ACT_RU_*(运行时,结束即清) |
TaskService | 任务查询/认领/完成 | ACT_RU_TASK、ACT_RU_IDENTITYLINK |
HistoryService | 历史追溯、统计 | ACT_HI_* |
另有 IdentityService(用户/组)、ManagementService(Job/定时器)、FormService(表单)。
3.1 数据库表(Activiti/Flowable 类似)
ACT_RE_*(Repository):ACT_RE_DEPLOYMENT(部署)、ACT_RE_PROCDEF(流程定义)、ACT_RE_MODEL。ACT_RU_*(Runtime):ACT_RU_EXECUTION(执行流)、ACT_RU_TASK(任务)、ACT_RU_VARIABLE(变量)、ACT_RU_JOB(定时 Job)、ACT_RU_TIMER_JOB。流程实例结束即清。ACT_HI_*(History):ACT_HI_PROCINST、ACT_HI_TASKINST、ACT_HI_ACTINST(活动实例)、ACT_HI_VARINST。永久留痕,用于追溯/审计。ACT_ID_*(Identity):用户、组、成员关系。
TIP
ACT_RU_EXECUTION 是流程运行的"指针":一个流程实例有一个根执行流,遇到并行网关会分裂出多个子执行流(PARENT_ID_ 关联),各自走到 Task 时生成 ACT_RU_TASK。理解 Execution 与 Task 的 1:N 关系,你就懂了并行/会签在 DB 里怎么存。
四、部署与执行:完整链路
@Autowired RepositoryService repositoryService;
@Autowired RuntimeService runtimeService;
@Autowired TaskService taskService;
// 1. 部署 BPMN(XML 在 resources 下)
repositoryService.createDeployment()
.addClasspathResource("process/leave.bpmn20.xml")
.deploy();
// 2. 启动实例, 传入业务变量(驱动网关)
Map<String,Object> vars = new HashMap<>();
vars.put("days", 3); vars.put("applicant", "zhangsan");
ProcessInstance pi = runtimeService.startProcessInstanceByKey("leave", "biz_1001", vars);
// 3. 查询并完成任务(带审批意见, 变量驱动网关走向)
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee("manager")
.processInstanceBusinessKey("biz_1001")
.list();
for (Task t : tasks) {
taskService.complete(t.getId(), Map.of("approved", true)); // 变量决定走哪条 Sequence Flow
}businessKey(业务键)把流程实例与你的业务表(如 leave_apply 表)关联,便于"我的待办/已办"查询与跨系统追踪。
五、会签 / 或签:多实例(MultiInstance)
"多人审批,全部通过才算过"是会签,"任一通过即可"是或签。二者都用 multiInstanceLoopCharacteristics 实现——把 UserTask 标成多实例,引擎为每个参与者生成独立 Task。
<userTask id="leaderAudit" name="会签">
<multiInstanceLoopCharacteristics isSequential="false"
activiti:collection="${auditUsers}" activiti:elementVariable="user">
<completionCondition>${nrOfCompletedInstances == nrOfInstances}</completionCondition>
<!-- 会签: 全部完成; 或签改为 ${nrOfCompletedInstances >= 1} 或 ${approvedCount >= 1} -->
</multiInstanceLoopCharacteristics>
</userTask>isSequential=false:并行会签(同时生成多个 Task);true:串行依次审批。nrOfInstances:总实例数;nrOfCompletedInstances:已完成数;nrOfActiveInstances:进行中数。completionCondition:达成即结束该多实例节点。会签=全完成;或签=至少 1 通过。
DANGER
- 把业务逻辑写死在流程定义里:流程只管"流转",业务校验/通知放 ServiceTask 调 Java 服务,便于复用与单测。
- 驳回用"新建反向流程":易丢失原轨迹。应基于历史活动跳转(Flowable 的
HistoryService+ 运行时移动 APIruntimeService.createChangeActivityStateBuilder())回退到指定节点,保持链路完整可审计。 - 运行表无限膨胀:
ACT_RU_*正常随流程结束清空,但若流程卡死/异常挂起会堆积,需定期巡检与修复(如act_ru_job堆积导致定时器不触发)。 - 并发审批数据错乱:会签变量需放在多实例集合(
elementVariable),避免共享变量相互覆盖。
六、驳回/退回与超时升级
- 驳回/退回:典型做法是用"撤回连线"回到上一节点;或更优雅地基于历史活动跳回(Flowable 支持
MoveExecutionEntity类 API 把执行流移到目标活动),保留完整轨迹。配合ACT_HI_ACTINST可还原"从哪来"。 - 超时升级(Timer 边界事件):在 UserTask 上挂
Timer Boundary Event(如timeDuration=PT2H),超时未处理则触发ServiceTask升级催办(发短信/换上级审批)。底层是ACT_RU_TIMER_JOB,由ManagementService/JobExecutor 驱动。
<userTask id="audit" name="审批"/>
<boundaryEvent id="timeout" attachedToRef="audit">
<timerEventDefinition><timeDuration>PT2H</timeDuration></timerEventDefinition>
</boundaryEvent>
<sequenceFlow sourceRef="timeout" targetRef="escalate"/>
<serviceTask id="escalate" flowable:class="com.demo.EscalateDelegate"/>七、生产落地
- 流程定义版本化管理(文件名带版本),旧实例跑旧版、新实例用新版(引擎按
ACT_RE_PROCDEF.VERSION_区分)。 businessKey关联业务表,便于待办/已办查询与跨系统追踪。ExecutionListener/TaskListener统一做日志、消息通知、权限校验。- 监控运行时表与超时任务,超时用 Timer 事件自动升级催办。
📚 官方文档与参考资料
- Flowable 用户指南:https://www.flowable.org/docs/userguide/index.html
- Activiti 官方站:https://www.activiti.org/
- BPMN 2.0 规范:https://www.omg.org/spec/BPMN/2.0/
✏️ 练习与自测
- 设计一个"请假流程":3 天以下主管批,3 天以上总监批,画出 BPMN 网关与连线(ExclusiveGateway + condition)。
- 用 MultiInstance 实现"三人会签,全部同意才算通过",写出
completionCondition与isSequential配置,说明或签如何改。 - 说明 RuntimeService 与 HistoryService 各自管理的表与生命周期差异,为什么流程结束
ACT_RU_*清空而ACT_HI_*保留。 - 用 Timer Boundary Event 实现"审批超 2 小时自动升级催办",画出元素与流转。
本节小结
工作流引擎用 BPMN 把"流转规则"从代码里解放出来。核心是:流程只管流转、业务交给 ServiceTask、businessKey 关联业务表;会签靠多实例 + completionCondition(会签=全完成/或签=至少 1),驳回靠历史活动跳转、超时靠 Timer 边界事件 + JobExecutor。数据库上 ACT_RU_*(运行时) 随流程结束清空、ACT_HI_*(历史) 永久留痕。选 Flowable(API 友好、社区活跃)或 Activiti 皆可,重在建模规范与轨迹完整。