Skip to content

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 里怎么存。

四、部署与执行:完整链路 ​

java
@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。

xml
<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 + 运行时移动 API runtimeService.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 驱动。
xml
<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 事件自动升级催办。

📚 官方文档与参考资料 ​

✏️ 练习与自测 ​

  1. 设计一个"请假流程":3 天以下主管批,3 天以上总监批,画出 BPMN 网关与连线(ExclusiveGateway + condition)。
  2. 用 MultiInstance 实现"三人会签,全部同意才算通过",写出 completionCondition 与 isSequential 配置,说明或签如何改。
  3. 说明 RuntimeService 与 HistoryService 各自管理的表与生命周期差异,为什么流程结束 ACT_RU_* 清空而 ACT_HI_* 保留。
  4. 用 Timer Boundary Event 实现"审批超 2 小时自动升级催办",画出元素与流转。

本节小结

工作流引擎用 BPMN 把"流转规则"从代码里解放出来。核心是:流程只管流转、业务交给 ServiceTask、businessKey 关联业务表;会签靠多实例 + completionCondition(会签=全完成/或签=至少 1),驳回靠历史活动跳转、超时靠 Timer 边界事件 + JobExecutor。数据库上 ACT_RU_*(运行时) 随流程结束清空、ACT_HI_*(历史) 永久留痕。选 Flowable(API 友好、社区活跃)或 Activiti 皆可,重在建模规范与轨迹完整。