审批接入(业务表单)
业务表单是指使用独立的业务数据表存储数据,同时接入工作流引擎实现审批的方式。适合有复杂业务逻辑、需要数据关联查询、自定义 UI 的场景。
1. 操作步骤
1.0 第零步:开发业务表单
在创建流程之前,需要先完成业务表单的前后端开发。具体开发规范参见下文 第 3 节:业务表单开发规范。
1.1 第一步:定义流程
① 新建流程模型
与流程表单相同,进入「流程中心 → 流程模型」,点击「新建模型」按钮:

填写基本信息:
- 流程标识:唯一标识,如
oa_seal_apply_bill、hrm_employee_entry_bill - 流程名称:显示名称,如「用印申请单」「员工入职单」
- 流程分类:选择流程所属分类(OA、人力等)
- 流程图标:选择图标
- 流程描述:可选描述
② 设计表单
在「表单设计」步骤,选择「业务表单」类型(区别于流程表单),然后配置以下路由信息: 
- 表单提交路由:指向业务表单的创建/编辑页面路由,如
/oa/seal/sealapply/info - 表单查看路由:指向业务表单的详情查看页面路由,通常与提交路由相同
这两个路由告诉流程引擎:发起流程时跳转到哪个页面填写表单,审批时跳转到哪个页面查看详情。
③ 设计流程
与流程表单完全相同,选择流程设计器(BPMN 或 钉钉/飞书设计器),设计审批流程节点。

每个审批节点可配置:
- 审批人规则:指定角色、部门、岗位等
- 表单字段权限:每个字段可设为只读、可编辑、隐藏(详见 业务表单字段权限)
- 多人审批方式:会签、或签、依次审批
- 操作按钮:通过、拒绝、驳回、转办、委派等
④ 发布流程
流程设计完成后,点击「发布」按钮,流程即可使用。

1.2 第二步:发起流程
用户在业务列表页点击「新建」,填写业务表单后提交。后端同时创建业务记录并启动流程实例。

1.3 第三步:审批流程
审批人在「待办任务」中点击审批时,系统跳转到业务表单的详情页,展示业务数据和审批操作按钮。

支持的审批操作:
- 通过:同意当前审批
- 不通过:拒绝流程
- 驳回:退回到指定节点
- 转办:转交给其他人审批
- 委派:委派给其他人审批后再返回
- 加签:临时增加审批人
2. 业务表单 vs 流程表单
| 维度 | 流程表单 | 业务表单 |
|---|---|---|
| 数据存储 | Flowable 变量表(ACT_RU_VARIABLE) | 独立业务表 |
| 表单设计 | 在线拖拽设计器(form-create) | 手写 Vue 组件(BasicForm) |
| 开发成本 | 零代码 | 需要前后端开发 |
| 灵活性 | 受限于设计器能力 | 完全自定义 |
| 数据查询 | 需要通过 Flowable API 或特殊 SQL | 标准 SQL |
| 业务逻辑 | 简单 | 可处理复杂业务 |
| 子表/明细 | 不支持 | 支持一对多明细表 |
| 附件管理 | 基础上传 | AttachmentService 统一管理 |
| 字段权限 | 设计器原生支持 | 通过动态字段权限扩展支持 |
3. 业务表单开发规范
业务表单的开发需要前后端配合完成。以下是完整的产物清单和关键规范,也可以将本节内容作为 AI 代码生成的提示词使用。
完整的代码生成规范文档:
ruoyi-office-prompt/流程表单-代码生成规范.md
3.1 完整产物清单
每个业务表单需要产出以下文件:
| 层级 | 文件 | 说明 |
|---|---|---|
| SQL | {表名}.sql | 表结构(主表 + 子表) |
| SQL | {表名}_menu.sql | 菜单与权限 |
| SQL | {字典名}_dict.sql | 新字典类型(如有) |
| 枚举 | XxxBillTypeEnum | 在模块 BillTypeEnum 中新增枚举值 |
| 枚举 | ErrorCodeConstants | 新增错误码 |
| 后端 | XxxBillDO.java | 数据对象 |
| 后端 | XxxBillSaveReqVO.java | 保存请求 VO |
| 后端 | XxxBillRespVO.java | 响应 VO |
| 后端 | XxxBillPageReqVO.java | 分页请求 VO |
| 后端 | XxxBillMapper.java | Mapper 接口 |
| 后端 | XxxBillService.java | Service 接口 |
| 后端 | XxxBillServiceImpl.java | Service 实现(含 FlowBillService) |
| 后端 | XxxBillController.java | Controller |
| 前端 | api/{模块}/{业务}/index.ts | API 定义 |
| 前端 | views/.../list/index.vue | 列表页 |
| 前端 | views/.../list/data.ts | 列表配置 |
| 前端 | views/.../info/index.vue | 表单页(含审批操作) |
| 前端 | views/.../info/data.ts | 表单配置 |
3.2 数据建模(表结构)
所有流程表单类单据主表应包含以下通用字段:
CREATE TABLE xxx_bill (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
bill_code VARCHAR(50) NOT NULL COMMENT '单据编号',
process_instance_id VARCHAR(64) DEFAULT NULL COMMENT '流程实例编号',
process_status TINYINT DEFAULT 0 COMMENT '单据状态(0草稿 1审批中 2审批通过 3审批拒绝 4已取消)',
creator_name VARCHAR(100) DEFAULT NULL COMMENT '创建者姓名',
company_id BIGINT DEFAULT NULL COMMENT '公司ID',
company_name VARCHAR(200) DEFAULT NULL COMMENT '公司名称',
dept_id BIGINT DEFAULT NULL COMMENT '部门ID',
dept_name VARCHAR(200) DEFAULT NULL COMMENT '部门名称',
remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
-- 其他业务字段 ...
tenant_id BIGINT NOT NULL DEFAULT 0 COMMENT '租户编号',
creator VARCHAR(64) DEFAULT '' COMMENT '创建者',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
updater VARCHAR(64) DEFAULT '' COMMENT '更新者',
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
deleted BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (id)
);关键约定:
- 表名:
{模块前缀}_{业务名}_bill,如oa_car_apply_bill、hrm_employee_entry_bill - 单据编号:
bill_code字段,使用BillCodeUtils自动生成 - 流程关联:
process_instance_id关联 Flowable 流程实例,process_status跟踪审批状态 - 组织维度:统一
company_id/company_name+dept_id/dept_name
3.3 后端关键实现
审批流程集成
Service 实现类必须实现 FlowBillService 接口:
public class XxxBillServiceImpl implements XxxBillService, FlowBillService<OaBillTypeEnum> {
@Override
public OaBillTypeEnum getSupportedBillType() {
return OaBillTypeEnum.XXX;
}
@Override
public void updateProcessStatus(Long billId, Integer processStatus) {
xxxBillMapper.updateById(new XxxBillDO().setId(billId)
.setProcessStatus(processStatus));
}
}发起流程
// 设置流程状态为审批中
billDO.setProcessStatus(BpmTaskStatusEnum.RUNNING.getStatus());
// 构建流程变量
Map<String, Object> variables = BpmProcessVariableUtils.buildBillVariables(saveReqVO);
// 提交流程实例
processInstanceApi.submitProcessInstance(...);单据编号
if (StringUtils.isBlank(saveReqVO.getBillCode())) {
saveReqVO.setBillCode(BillCodeUtils.generateBillCode(
SystemEnum.OA, OaBillTypeEnum.XXX));
}附件处理
// 必须使用 AttachmentService,不要用字符串字段存储
if (saveReqVO.getAttachments() != null) {
attachmentService.saveAttachmentList(
OaBillTypeEnum.XXX.getTypeCode(),
billDO.getId(),
saveReqVO.getAttachments());
}3.4 前端关键实现
页面结构
必须采用 list/ + info/ 分离结构,不使用 modal 弹窗:
views/{模块}/{功能}/
├── list/
│ ├── index.vue # 列表页(useVbenVxeGrid)
│ └── data.ts # 搜索表单 + 表格列配置
└── info/
├── index.vue # 表单页(BasicForm + 审批操作)
└── data.ts # 表单 schema 配置表单页核心结构
<template>
<Loading :spinning="loading">
<BasicForm
v-if="initialized"
:field-permission="effectiveFieldPermission"
:is-approval="props.isApproval"
:process-status="formData.processStatus"
:form-schema="formSchema"
ref="basicFormRef"
>
<template #form-extension>
<!-- 业务扩展区块 -->
<!-- 明细表格(useVbenVxeGrid) -->
<!-- 附件区块(AttachmentList) -->
</template>
</BasicForm>
</Loading>
</template>字段权限集成(新表单必须使用)
新开发的业务表单必须接入动态字段权限,在流程设计器中按节点配置字段的只读/可编辑/隐藏:
import type { BusinessFieldPermission } from '#/views/bpm/composables';
import { getProcessDefinition } from '#/api/bpm/definition';
import { getApprovalDetail } from '#/api/bpm/processInstance';
const PROCESS_DEFINITION_KEY = 'oa_xxx_bill';
const initialized = ref(false);
const localFieldPermission = ref<BusinessFieldPermission>();
const effectiveFieldPermission = computed(
() => props.businessFieldsPermission || localFieldPermission.value,
);
async function loadFieldPermission() {
if (props.viewType || props.processInstance) return;
const def = await getProcessDefinition(undefined, PROCESS_DEFINITION_KEY);
if (!def?.id) return;
const detail = await getApprovalDetail({
processDefinitionId: def.id,
activityId: 'StartUserNode',
});
if (detail?.businessFieldsPermission) {
localFieldPermission.value = detail.businessFieldsPermission;
}
}
onMounted(async () => {
initFormSchema();
loading.value = true;
try {
await Promise.all([loadData(), loadFieldPermission()]);
} finally {
loading.value = false;
initialized.value = true;
}
});审批前保存(beforeApproval)
当审批节点需要审批人回填数据时,需实现 beforeApproval:
async function beforeApproval(): Promise<boolean> {
if (!props.isApproval) return true;
const permConfig = basicFormRef.value?.hasPermissionConfig;
const editable = basicFormRef.value?.editableFields;
if (permConfig && editable?.length > 0 && basicFormRef.value) {
const { valid } = await basicFormRef.value.validateForm();
if (!valid) {
message.error('表单校验不通过,请先完善表单信息');
return false;
}
const formValues = await basicFormRef.value.getFormValues();
await saveXxxBill({ ...formData.value, ...formValues });
return true;
}
return true;
}
defineExpose({ beforeApproval, loadData, handleSaveAndSubmit });3.5 AI 代码生成
上述开发规范可以直接作为 AI 代码生成的提示词使用。推荐的提示词模板:
请参照 @参照模块 的完整实现,生成 [功能名称] 功能代码。
【强制要求】
1. 后端:严格参照参照 Service 的附件处理、审批流程、单据编号生成方式
2. 前端:严格参照参照前端的 list/info 分离结构、组件使用方式
3. 必须完整阅读参照代码后再生成,确保结构、命名、实现方式完全一致
【关键检查点】
- 附件:使用 AttachmentService(后端)/ AttachmentList(前端)
- 页面:list/info 分离,不使用 modal
- 明细:使用 useVbenVxeGrid,禁止 Ant Design Table
- 字段权限:接入 loadFieldPermission + initialized + v-if
【禁止事项】
- 不要自行简化或创新实现方式
- 不要使用字符串存储附件
- 不要使用 modal 弹窗表单完整规范文档:
ruoyi-office-prompt/流程表单-代码生成规范.md,包含表结构规范、菜单权限 SQL、后端规范、前端组件规范、明细表格规范、检查清单等全部内容。
4. 业务表单集成原理
业务表单与流程引擎的集成通过以下机制实现:
- 发起流程:业务 Service 调用
BpmProcessInstanceApi的submitProcessInstance(),传入processDefinitionKey和业务变量 - 流程变量:单据编号、公司、部门等关键信息作为流程变量存储,供流程条件分支使用
- 状态同步:通过
FlowBillService接口,流程状态变化时自动回调业务 Service 更新processStatus - 页面跳转:流程模型中配置的表单路由,控制发起和审批详情页的跳转目标
发起申请 → 创建业务记录 → 启动流程实例 → 审批人处理
↑ ↓
└──── 状态同步 ←── FlowBillService ←── 审批结果5. RuoYi Office 扩展:字段权限动态配置
RuoYi Office 在业务表单的基础上,扩展了字段权限动态配置功能。在流程设计器中按审批节点配置业务表单字段的只读、编辑、隐藏权限,无需硬编码。
详见 《业务表单字段权限》。