2018年我讲的Activiti工作流引擎——从表结构理解流程引擎的运作

2018年我讲的Activiti工作流引擎------从表结构理解流程引擎的运作

文章目录

一、工作流引擎不是黑盒,是几十张表在协作

2018年给同事讲Activiti。来听的人大多用了半年工作流引擎,知道怎么写流程、怎么设任务、怎么审批。但出了问题------"流程卡住了"、"任务没人收"、"变量丢了"------就不知道怎么排查。

我的讲法是:不讲API,讲表结构。因为Activiti的几乎所有运行时状态都落在数据库里------弄懂了表,就弄懂了整个引擎。

二、23张核心表的四层结构

Activiti的23张表按前缀分为四层:

复制代码
ACT_RE_*   资源表(Repository)------存流程定义
ACT_RU_*   运行时表(Runtime)------存运行中的数据
ACT_HI_*   历史表(History)------存已完成的数据
ACT_ID_*   身份表(Identity)------存用户和角色

每一层有明确的职责,数据在四层之间流转------定义发布后生成运行时数据,任务完成后写入历史。

三、资源表------流程定义怎么存的

当你在流程设计器里画一个流程图,保存后,数据写入两张表:

ACT_RE_MODEL(流程定义表)

字段 含义
ID_ 编号
NAME_ 名称
KEY_ 关键字,启动流程时用这个
EDITOR_SOURCE_VALUE_ID_ 关联 ACT_GE_BYTEARRAY 的流程定义XML
EDITOR_SOURCE_EXTRA_VALUE_ID_ 关联流程图片的二进制数据

ACT_GE_BYTEARRAY(二进制数据表):存储BPMN的XML和流程图的PNG。ID关联ACT_RE_MODEL,DEPLOYMENT_ID关联ACT_RE_PROCDEF。

发布流程后,生成 ACT_RE_PROCDEF(流程发布表)

字段 含义
ID_ 流程定义编号
KEY_ 启动流程用
DEPLOYMENT_ID_ 关联 ACT_GE_BYTEARRAY
VERSION_ 版本号,每发布一次+1

同一个KEY可以有多个版本,启动流程永远取最新版本。这是"在线修改流程不中断旧流程"的机制------旧实例按旧版本跑完,新实例走新版本。

四、运行时表------一个流程实例正在跑的时候

启动流程后,数据进入运行时表:

复制代码
ACT_RU_EXECUTION     运行的流程实例
ACT_RU_TASK          运行中的任务(用户任务)
ACT_RU_IDENTITYLINK  任务和参与者的关联(谁可以处理这个任务)
ACT_RU_VARIABLE      流程变量(实例级别的变量,如申请金额、审批意见)

一个流程启动后的运行时状态:

复制代码
ACT_RU_EXECUTION: insid=1001, procDefKey=leave
  → ACT_RU_TASK: taskid=T1, assignee=张三(等待审批)
  → ACT_RU_VARIABLE: varName=days, varValue=3(请假天数)
  → ACT_RU_IDENTITYLINK: taskid=T1, userId=张三, type=assignee

任务完成后,ACT_RU_TASK 里这条记录被删除,同时写入 ACT_HI_TASKINST(历史)。这就是 "RU存活的数据,HI存死的数据"。

五、历史表------所有的"做过的事"

除了运行时表,每次操作几乎都同时写入历史表:

复制代码
ACT_HI_PROCINST      历史流程实例(包含是否已完成)
ACT_HI_TASKINST      历史任务(谁、什么时间、做了什么)
ACT_HI_VARINST       历史变量(变量名、值、修改时间)
ACT_HI_IDENTITYLINK  历史参与者(谁参与了哪个任务)

查历史不用翻日志------直接查历史表。谁在什么时间审批了什么流程、写了什么意见、流程持续了多久------都在历史表里。这就是工作流引擎的"审计能力"------不是后来补的,是设计时就落到了表结构里。

六、身份表------用户和角色不是引擎自带的

复制代码
ACT_ID_GROUP         角色表
ACT_ID_USER          用户表
ACT_ID_MEMBERSHIP    角色-用户关联表

Activiti本身不维护用户和角色数据。ACT_ID_GROUPACT_ID_USER 一般做成视图,指向系统自己的用户表和角色表。引擎只关心"当前任务的参与者是谁"------至于这个人从哪个表查出来的,引擎不关心。

七、流程变量------流程的"内存"

流程变量是Activiti最重要的概念之一。它解决了"流程在节点之间传递什么信息"的问题:

java 复制代码
// 启动时设置
variables.put("applyUserId", "user001");
variables.put("days", 3);

// 完成任务时传递
taskService.complete(taskId, variables);

// 下一个任务读变量
String userId = (String) taskService.getVariable(taskId, "applyUserId");

变量存在 ACT_RU_VARIABLE 里,任务完成时移入 ACT_HI_VARINST。关键点:变量必须在前置任务中设置,后续任务才能读取 。如果审批时需要"请假天数"这个变量,必须在前面的"申请"任务中 put("days", days)

八、自定义角色表和用户表------为什么做成视图

Activiti的 ACT_ID_GROUP 等表可以做成视图,关联系统自己的用户表。为什么要这样?

因为系统已经有了一套完整的用户-角色-权限体系。在Activiti里再建一套肯定重复。用视图把系统的用户和角色"映射"到Activiti的规则里------引擎只认 ACT_ID_USER.ID_ 这个字段的值,至于它指向哪个表的数据,不关心。

九、一个流程的完整生命周期

复制代码
1. 发布流程(RE_MODEL → RE_PROCDEF)
2. 启动流程(创建 RU_EXECUTION、RU_TASK)
3. 拾取任务(RU_TASK.ASSIGNEE_ = userId)
4. 完成任务(RU_TASK → 删除,写入 HI_TASKINST)
5. 流转到下一个节点(创建新的 RU_TASK)
6. 流程结束(删除 RU_EXECUTION,写入 HI_PROCINST)

每一步都能在对应的表里找到数据。查问题的思路就是顺着表链走 :流程卡住了?查 ACT_RU_TASK 看当前任务是哪个。变量丢了?查 ACT_RU_VARIABLE 看前置任务有没有设置。历史找不到了?查 ACT_HI_PROCINST 看是否已结束。

十、结语

用Activiti的人大部分停留在API层面------taskService.complete() 一调就完了。但出了问题,API层面是看不到答案的。只有清楚表结构------知道数据在哪张表、哪个字段、什么时候写入------才能在问题发生时准确定位。

这份培训PPT的目的就是这个:让人从"用API操作引擎"变成"从数据库理解引擎"。API文档几个月看一次就够了,表结构理解了就再也不忘。