流程表单数据查询
RuoYi Office 扩展指南:使用 SQL 查询流程表单(NORMAL 类型)的单据数据。
1. 数据存储原理
1.1 两种表单类型对比
| 维度 | 流程表单(NORMAL) | 业务表单(CUSTOM) |
|---|---|---|
| 数据存储 | Flowable 流程变量表(ACT_HI_VARINST) | 独立业务表(如 oa_seal_apply_bill) |
| 表单定义 | bpm_form 表(JSON) | 前端 Vue 组件 |
| 查询方式 | 需特殊 SQL(EAV 行转列) | 标准 SQL |
1.2 EAV 存储模型
流程表单采用 EAV 模型(Entity-Attribute-Value):每个表单字段是变量表中的一行记录,而不是传统业务表的一列。
提交流程时:
- 前端将 form-create 表单的所有字段值打包为
variables对象 - 后端将
variables传入 Flowable 引擎 - Flowable 将每个变量存为
ACT_HI_VARINST表中的一行
1.3 涉及的数据库表
| 表 | 说明 |
|---|---|
ACT_HI_PROCINST | 历史流程实例 |
ACT_HI_VARINST | 历史流程变量(查单据数据的主要表) |
ACT_RU_VARIABLE | 运行时流程变量(仅审批中的流程) |
bpm_form | 表单定义(fields 字段包含字段名→标题的映射) |
bpm_process_definition_info | 流程定义扩展(关联表单 ID) |
1.4 ACT_HI_VARINST 表结构
| 字段 | 类型 | 说明 |
|---|---|---|
| ID_ | varchar(64) | 主键 |
| PROC_INST_ID_ | varchar(64) | 流程实例 ID |
| TASK_ID_ | varchar(64) | 任务 ID(流程级变量为 NULL) |
| NAME_ | varchar(255) | 变量名(表单字段 field name) |
| VAR_TYPE_ | varchar(100) | 类型(string/long/integer/double/boolean/serializable) |
| TEXT_ | varchar(4000) | 字符串值 |
| LONG_ | bigint | 长整型值 |
| DOUBLE_ | double | 浮点型值 |
| CREATE_TIME_ | datetime(3) | 创建时间 |
1.5 系统变量 vs 表单字段
查询时需要排除系统变量:
| 系统变量名 | 说明 |
|---|---|
| PROCESS_STATUS | 流程状态 |
| PROCESS_REASON | 审批原因 |
| PROCESS_START_USER_ID | 发起人 ID |
| _FLOWABLE_SKIP_EXPRESSION_ENABLED | 跳过表达式标记 |
| billCode | 单据编号 |
| deptId / deptName | 部门信息 |
| nrOfInstances / nrOfActiveInstances | 多实例计数 |
表单字段的变量名是 form-create 自动生成的随机 ID(如 Fcltmdk15xf0ahc),需对照 bpm_form.fields 才能知道中文标题。
2. 常用 SQL 查询模板
2.1 按流程实例 ID 查询所有变量
sql
SELECT
v.PROC_INST_ID_ AS 流程实例ID,
v.NAME_ AS 变量名,
v.VAR_TYPE_ AS 变量类型,
v.TEXT_ AS 值,
v.CREATE_TIME_ AS 创建时间
FROM ACT_HI_VARINST v
WHERE v.PROC_INST_ID_ = '指定流程实例ID'
AND v.TASK_ID_ IS NULL -- 只看流程级变量
ORDER BY v.CREATE_TIME_;2.2 按单据编号查询
sql
SELECT
p.ID_ AS 流程实例ID,
p.PROC_DEF_KEY_ AS 流程Key,
p.START_TIME_ AS 发起时间,
v.NAME_ AS 变量名,
v.TEXT_ AS 值
FROM ACT_HI_PROCINST p
JOIN ACT_HI_VARINST bill ON p.ID_ = bill.PROC_INST_ID_
AND bill.NAME_ = 'billCode'
JOIN ACT_HI_VARINST v ON p.ID_ = v.PROC_INST_ID_
AND v.TASK_ID_ IS NULL
WHERE bill.TEXT_ = '指定单据编号'
ORDER BY v.NAME_;2.3 按流程 Key 批量查询(行转列)
sql
SELECT
p.ID_ AS 流程实例ID,
p.START_TIME_ AS 发起时间,
MAX(CASE WHEN v.NAME_ = 'billCode' THEN v.TEXT_ END) AS 单据编号,
MAX(CASE WHEN v.NAME_ = 'PROCESS_STATUS' THEN v.TEXT_ END) AS 流程状态,
MAX(CASE WHEN v.NAME_ = '字段1' THEN v.TEXT_ END) AS 字段1标题,
MAX(CASE WHEN v.NAME_ = '字段2' THEN v.TEXT_ END) AS 字段2标题
FROM ACT_HI_PROCINST p
JOIN ACT_HI_VARINST v ON p.ID_ = v.PROC_INST_ID_
AND v.TASK_ID_ IS NULL
WHERE p.PROC_DEF_KEY_ = '指定流程Key'
GROUP BY p.ID_, p.START_TIME_
ORDER BY p.START_TIME_ DESC;2.4 查询表单字段定义
sql
SELECT
f.id AS 表单ID,
f.name AS 表单名称,
JSON_EXTRACT(f.fields, '$[*].field') AS 字段名列表,
JSON_EXTRACT(f.fields, '$[*].title') AS 字段标题列表
FROM bpm_form f
WHERE f.name LIKE '%关键词%';3. 万能 SQL 生成器
输入流程 Key,自动生成完整的行转列查询:
sql
-- 步骤 1:先查出表单定义的字段列表
SELECT f.id, f.name, f.fields
FROM bpm_form f
JOIN bpm_process_definition_info pdi ON pdi.form_id = f.id
JOIN ACT_RE_PROCDEF pd ON pd.ID_ = pdi.process_definition_id
WHERE pd.KEY_ = '你的流程Key'
ORDER BY pd.VERSION_ DESC
LIMIT 1;
-- 步骤 2:根据 fields JSON 中的字段,手动生成 MAX(CASE WHEN ...) 语句
-- 每个 fields 数组元素的 "field" 是变量名,"title" 是中文标题4. JSON 字段解析
bpm_form.fields 存储的是双层 JSON(JSON 字符串内嵌 JSON),使用 MySQL 的 JSON_EXTRACT 函数解析:
sql
-- 解析表单字段定义
SELECT
JSON_EXTRACT(fields, '$[0].field') AS 第一个字段名,
JSON_EXTRACT(fields, '$[0].title') AS 第一个字段标题,
JSON_LENGTH(fields) AS 字段总数
FROM bpm_form
WHERE id = 指定ID;5. 注意事项
- 流程级变量:查询时过滤
TASK_ID_ IS NULL,只看流程级变量 - 值类型:所有类型都会有
TEXT_文本表示,数值类型额外有LONG_或DOUBLE_ - JSON 值:某些字段值本身是 JSON 字符串(如数组、对象),需要用 JSON 函数进一步解析
- 性能:大量数据查询时建议加索引
PROC_INST_ID_+NAME_ - 历史 vs 运行时:优先查
ACT_HI_VARINST(包含所有数据),ACT_RU_VARIABLE仅包含运行中的流程 - 业务表单不适用:本指南仅适用于流程表单(NORMAL),业务表单(CUSTOM)直接查对应业务表即可