SpringBoot3+Vue3+Flowable 企业审批平台全链路:从表单设计、节点权限到移动审批怎么落地

SpringBoot3+Vue3+Flowable 企业审批平台全链路:从表单设计、节点权限到移动审批怎么落地

🌐 **文档地址**:https://ruoyioffice.com
📦 **源码1·GitHub**:https://github.com/yuqing2026/ruoyi-office
📦 **源码2·GitCode**:https://gitcode.com/zhouzhongyan/ruoyi-office
📦 **源码3·Gitee**:https://gitee.com/yqzy1688/ruoyi-office
💬 **微信**:17156169080(备注「RuoYi Office」)
企业审批项目最容易低估的工作量,是把"画流程图"误认为"审批平台已经完成"。真正上线时,实施人员还会追问:表单从哪里来?部门负责人找不到怎么办?审批人能改哪些字段?拒绝、退回、转办按钮由谁控制?审批通过后车辆、预算或资产状态谁来回写?手机端为什么看得到待办却打不开业务表单?

*▲ 审批平台的主线不是一个 Flowable API,而是设计态、运行态、业务态和移动端共同闭环*


引言:企业审批真正要交付的是"一张单走得完"

工作流引擎解决的是流程实例、任务、网关和历史轨迹;企业审批平台还要补齐引擎之外的产品层:

text 复制代码
建模型
  → 选表单
  → 配审批人
  → 配字段与按钮权限
  → 发布校验
  → 员工填写业务单
  → PC / 手机办理
  → 审批结果回写业务
  → 查询轨迹、抄送与追溯

本文不再把候选人、驳回算法、动态表单等单点各拆一遍,而是以一张"公务用车申请"为线索,把这些能力如何串成一条可交付链路讲清楚。

先说明版本:当前项目 BOM 使用 Flowable 8.0.0 ,后端为 Spring Boot 3.5 系列。部分历史资料仍写 Flowable 7.x,实际开发应以当前 ****-dependencies/pom.xml 为准。


一、平台地图:Flowable 是引擎,三端围绕同一个审批详情工作

1.1 后端、PC 和移动端各管什么

后端 ****-module-bpm 负责模型、部署、流程实例、任务、候选人计算、审批轨迹和状态事件。PC 管理端承担完整设计器、模型配置、发起大厅和审批详情;UniApp 端复用同一套 BPM API,提供待办、我的、已办、抄送和移动办理。

text 复制代码
Spring Boot 3
├─ 模型与部署:BpmModelService
├─ 流程实例:BpmProcessInstanceService
├─ 任务办理:BpmTaskService
├─ 候选人:BpmTaskCandidateInvoker
└─ 业务桥:FlowBillService + 状态事件

Vue3 / Vben
├─ BPMN 与 SIMPLE 双设计器
├─ 流程表单与业务表单配置
├─ 发起大厅、待办、已办、抄送
└─ 统一审批详情与操作按钮

UniApp
├─ 审批工作台
├─ 业务表单静态注册
└─ 同一 approval-detail 接口

三端真正共享的"单一真相源"是审批详情接口。它一次返回:

  • 流程定义与流程实例;

  • 已结束、进行中和未来活动节点;

  • 当前用户的待办任务;

  • 流程表单和业务表单字段权限;

  • 当前节点允许展示的操作按钮;

  • 退回目标和后续节点预测所需信息。

这比让 PC、App 各自拼任务、历史、表单权限可靠得多。


二、设计态第一步:BPMN 和 SIMPLE 最后都要变成可部署定义

流程模型列表不只是名称管理,它同时展示设计器类型、绑定表单和最后发布版本。

*▲ 同一平台可以让技术人员使用 BPMN,让业务实施人员使用 SIMPLE 节点树*

2.1 两种设计器解决不同复杂度

BPMN(10) 适合复杂网关、事件、子流程和标准建模;SIMPLE(20) 适合顺序审批、条件分支、会签和常见人事流程。SIMPLE 并不是另一套运行引擎,它在发布时通过 SimpleModelUtils.buildBpmnModel() 编译成 Flowable 可识别的 BpmnModel

java 复制代码
public void deployModel(String modelId) {
    BpmModelDO model = validateModelExists(modelId);
    BpmnModel bpmnModel;
    if (BpmModelTypeEnum.BPMN.getType().equals(model.getType())) {
        bpmnModel = parseBpmnXml(model.getBpmnXml());
    } else {
        bpmnModel = SimpleModelUtils.buildBpmnModel(model.getSimpleModel());
    }
    taskCandidateInvoker.validateBpmnConfig(bpmnModel);
    deployProcessDefinition(model, bpmnModel);
}

这里最重要的不是编译代码,而是发布前校验。节点没有审批人、表达式不合法、候选人参数缺失,都应该挡在发布按钮前,不能等员工发起后才产生无人接收的任务。

2.2 用车申请怎么画

一条典型用车流程可以是:

text 复制代码
发起人 → 部门负责人 → 车管员 → 结束

部门负责人来自发起人部门,车管员来自指定岗位或用户组。若用车天数超过 3 天,还可以通过条件分支增加行政负责人审批。流程图只表达"往哪走",并不保存车辆、出发时间、同行人等业务字段。

*▲ 节点上配置谁来审批;表单和权限在模型的其它设计步骤完成*


三、设计态第二步:NORMAL 与 CUSTOM 表单怎么选

平台只有两类表单,不需要创造第三种概念。

| 表单类型 | 适合场景 | 数据来源 | 优势 | 边界 |

|:---|:---|:---|:---|:---|

| NORMAL 流程表单 | 请示、登记、简单申请 | form-create + 流程变量或物理表绑定 | 配置快、通用运行时 | 复杂主子表和资源回写较弱 |

| CUSTOM 业务表单 | 用车、用印、报销、资产领用 | 独立 Java 业务表 + Vue 页面 | 业务约束、附件、编号、回写完整 | 需要编码接入 |

公务用车必须校验车辆时段冲突、审批通过后锁定车辆、还车后释放,因此更适合 CUSTOM。模型元数据保存:

text 复制代码
formCustomCreatePath = /oa/car/car-apply-info
formCustomViewPath   = oa/car/carapply/info/index

发起路径负责创建和编辑,查看路径负责审批详情中的只读或按权限编辑。配置路径一旦与实际 Vue 文件不一致,待办详情就会空白;这类问题应修模型配置或数据,不能在通用动态加载器里为脏路径堆特殊别名。

3.1 业务单先保存,再用 businessKey 发起

CUSTOM 表单不是把所有字段塞进 Flowable 变量。通常先保存业务主表,取得业务 ID,再发起流程:

java 复制代码
@Transactional(rollbackFor = Exception.class)
public Long submitCarApplyBill(CarApplyBillSaveReqVO reqVO) {
    Long billId = saveCarApplyBill(reqVO);
    Map<String, Object> variables = buildProcessVariables(billId);
    String processInstanceId = processInstanceApi.createProcessInstance(
            getLoginUserId(), PROCESS_DEFINITION_KEY, billId.toString(), variables);
    updateProcessInstanceId(billId, processInstanceId);
    return billId;
}

businessKey 把流程实例与业务单绑定。流程变量只放网关、候选人和摘要需要的字段,不复制整张业务表。


四、设计态第三步:审批人不是一个 userId,而是一套解析策略

企业流程最容易变化的是"谁审批"。当前候选人策略覆盖角色、部门成员、部门负责人、岗位、指定用户、发起人自选、审批人自选、表单用户、表达式和空审批人处理等常见方式。

用车流程可以这样配置:

  • 部门负责人节点:根据发起人的部门向上解析负责人;

  • 车管员节点:按岗位或用户组计算;

  • 特殊长途用车:条件分支后指定行政负责人;

  • 部门负责人为空:按模型配置自动通过、拒绝或转流程管理员。

运行时由 BpmTaskCandidateInvoker.calculateUsers() 统一计算候选人,而不是每个业务模块自己查一次组织表。

4.1 为什么要保留"发起人自选"

固定角色适合稳定流程,但项目评审、跨部门会签常常需要发起人从允许范围内选人。前端选择的名单必须经过后端授权范围校验,不能把用户提交的任意 ID 直接写入流程变量。

4.2 空审批人必须有显式策略

部门负责人离职、岗位未配置、表达式返回空集合都可能产生空审批人。平台要在设计态允许配置处理方式,并在发布或运行时给出明确结果;"任务创建了但没人看到"是最难排查的流程事故之一。


五、设计态第四步:字段权限和按钮权限都属于节点

同一张用车申请:

  • 发起人可以填写车辆、时间和事由;

  • 部门负责人只能查看;

  • 车管员可能被允许修改车辆与司机;

  • 财务复核只能看到费用字段;

  • 某些节点允许退回,某些节点只允许同意和拒绝。

5.1 两种表单有两条字段权限链

NORMAL 表单把字段权限写入流程定义扩展属性,运行时返回 formFieldsPermission,form-create 根据 disabled/hidden/required 渲染。

CUSTOM 表单使用 BpmFormFieldPermissionDO 保存字段权限,业务页通过 useBusinessFieldPermissionBasicForm 应用。它允许复杂业务页继续使用自己的组件,同时由流程节点决定哪些字段可见、只读或必填。

ts 复制代码
const { fieldPermission, applyPermission } =
  useBusinessFieldPermission(processDefinitionKey);

await loadApprovalDetail(processInstanceId);
applyPermission(formSchema.value, fieldPermission.value);

5.2 操作按钮不能在页面写死

审批详情底部的通过、拒绝、抄送、转办、委派、加签、退回和取消,来自当前任务的 buttonsSetting

*▲ 同一详情页根据当前任务权限展示按钮,业务页面不需要为每个节点写一套底栏*

前端的核心判断类似:

ts 复制代码
function isShowButton(type: BpmTaskOperationButtonType) {
  const setting = runningTask.value?.buttonsSetting?.find(
    (item) => item.type === type,
  );
  return setting?.enable === true;
}

按钮显隐只能改善交互,后端办理接口仍要校验任务归属和当前状态,不能把安全边界交给 Vue。


六、运行态:一个 approval-detail 接口把表单、轨迹与权限拼起来

员工打开待办详情时,页面不能顺序调用十几个接口再猜状态。BpmProcessInstanceServiceImpl.getApprovalDetail() 聚合流程定义、实例、活动节点、任务与权限,PC 和移动端都围绕这份响应渲染。

java 复制代码
public BpmApprovalDetailRespVO getApprovalDetail(
        Long userId, String processInstanceId, String taskId) {
    BpmApprovalDetailRespVO detail = doGetApprovalDetail(
            userId, processInstanceId, taskId);
    detail.setFormFieldsPermission(
            getNormalFormPermission(detail));
    detail.setBusinessFieldsPermission(
            getBusinessFormPermission(detail));
    detail.setTodoTask(findTodoTask(userId, taskId));
    return detail;
}

它解决三个容易不一致的问题:

  1. 时间轴显示当前节点,但按钮却来自旧任务;

  2. 业务表单已经只读,底栏仍显示提交;

  3. PC 判断可退回,手机端却认为不可退回。

6.1 通过任务后,业务单必须收到结果

Flowable 完成任务只会推动流程。业务模块还需要知道流程处于审批中、已通过、已拒绝或已取消。平台通过 FlowBillService 和流程状态事件把结果回写:

java 复制代码
public interface FlowBillService {
    void updateProcessStatus(String businessKey, Integer status);
    default void onProcessApproved(String businessKey) {}
    default void onProcessRejected(String businessKey) {}
    default void onProcessCancelled(String businessKey) {}
}

用车审批通过后,业务服务锁定车辆和时段;驳回或取消后释放占用。费用报销、资产领用、用印外借也各自实现自己的业务回写。审批平台不能把所有业务规则塞进 BPM 模块,BPM 只负责发布事件与统一契约。


七、驳回、撤回、重提与抄送:四种动作不能混成"退回"

| 动作 | 发起者 | 结果 |

|:---|:---|:---|

| 拒绝 | 当前审批人 | 按模型策略终止或回到指定节点 |

| 发起人撤销 | 流程发起人 | 取消运行中的流程,业务回写取消 |

| 审批人撤回 | 已办审批人 | 在满足条件时取回已完成任务 |

| 驳回后重提 | 发起人 | 修改原业务单,复用业务关系重新进入流程 |

| 抄送 | 发起人或审批人 | 产生知会记录,不创建待办、不代表同意 |

产品文案和状态必须区分这些动作。尤其是抄送,它只让某人知情;评论只是沟通;审批意见才是任务办理结果。

批量通过也不是"勾选后循环调用通过"。意见必填、节点需改业务字段、需要签名、需要自选下一审批人或处于加签/委派中的任务,都应在资格预检中排除。


八、移动审批:API 可以同源,业务组件却不能假装自动出现

移动端审批工作台按 viewType 区分待办、我的、已办和抄送,详情页继续读取同一个审批详情接口。

*▲ 手机端同样展示业务表单、审批进度和底栏操作,但页面组件注册方式与 PC 不同*

PC 可以通过 import.meta.glob 按业务表单路径动态加载组件;小程序构建存在静态分析限制,UniApp 端采用静态 import + v-if 注册。

新增一个 CUSTOM 移动流程通常要改三处:

  1. bpm-menu-config.ts 登记 processDefinitionKey、发起路径和查看组件;

  2. form-detail.vue 静态导入并增加条件分支;

  3. 在对应 pages-oapages-hrm 等业务分包实现创建和详情页。

ts 复制代码
export const BPM_MENU_CONFIG = {
  oa_car_apply_bill: {
    mobileCreatePath: '/pages-oa/car/apply/create',
    mobileViewComponent: 'OaCarApplyDetail',
  },
};

因此,"PC 配完流程,App 自动支持所有业务表单"并不真实。NORMAL 通用表单可以更自动化,CUSTOM 复杂单据仍需要移动端适配。


九、一张用车申请的完整时序

sequenceDiagram participant U as 员工 participant B as 用车业务服务 participant P as BPM 服务 participant F as Flowable participant A as 审批人 participant M as 移动端 U->>B: 保存并提交用车申请 B->>P: createProcessInstance(businessKey) P->>F: 启动已发布定义 F->>P: 创建部门负责人任务 A->>P: getApprovalDetail P-->>A: 表单权限 + 按钮 + 轨迹 A->>P: approveTask F->>P: 推进车管员节点 M->>P: 手机端读取同一 approval-detail M->>P: approveTask F-->>P: 流程结束 P->>B: 发布审批通过事件 B->>B: 回写单据并锁定车辆时段

这条时序里没有让业务模块直接操作 Flowable 的运行表,也没有让 BPM 模块理解车辆冲突。两边通过流程实例、businessKey 和状态事件协作。


十、新增一类审批单据的接入清单

后端

  1. 创建业务主表、子表与附件关系;

  2. 定义稳定的 processDefinitionKey

  3. 保存业务单后发起流程,并记录 processInstanceId

  4. 实现 FlowBillService 的状态回写;

  5. 对审批通过、拒绝、取消分别处理资源占用;

  6. Controller 和 Service 校验租户、权限与数据范围。

PC

  1. API 类型按后端真实 JSON 定义;

  2. 创建 list/info 页面;

  3. 业务表单支持 embeddededitable 和字段权限;

  4. 暴露校验与取值方法供审批底栏调用;

  5. 模型配置正确填写 create/view 路径;

  6. 用同一实例验证发起、待办、已办和抄送视图。

移动端

  1. 注册流程 key 与移动路径;

  2. 创建业务分包页面;

  3. 在审批详情静态注册业务组件;

  4. 处理 viewType、只读态和按钮权限;

  5. 审批人特殊可编辑字段通过 beforeApproval 校验;

  6. 验证拒绝操作不会被业务必填字段错误拦截。


十一、边界:哪些能力不能宣传成"零代码"

11.1 复杂业务表单仍需要开发

动态表单可以覆盖请示、登记和中等复杂审批;车辆冲突、预算占用、资产回写、报销分摊等业务规则必须落在独立服务中。

11.2 两种设计器的能力并不完全相同

SIMPLE 面向常见审批,BPMN 面向标准模型和复杂控制流。不能因为最终都部署到 Flowable,就宣称两者表达能力完全一致。

11.3 移动端不是 PC 路由的自动镜像

CUSTOM 表单需要静态注册和分包页面。新增 PC 页面后不做移动适配,手机端只能看到任务壳,不能完成业务办理。

11.4 批量审批有资格边界

需要填业务字段、签名、附加任务表单或自选下一审批人的任务应回到单条详情办理。


常见问题(FAQ)

Flowable 已经提供任务 API,为什么还要做 BPM 模块?

引擎 API 不负责业务表单、组织候选人、字段权限、按钮配置、业务状态回写和多端产品体验。BPM 模块的价值就是把这些企业能力围绕引擎组织起来。

SIMPLE 设计器是不是绕开了 BPMN?

不是。SIMPLE 保存的是更适合业务人员理解的节点结构,发布时仍会编译为 BPMN 模型并交给 Flowable 运行。

NORMAL 动态表单和 CUSTOM 业务表单怎么选?

简单登记、请示优先 NORMAL;存在复杂主子表、资源冲突、金额计算、附件规则或审批后回写时优先 CUSTOM。

字段隐藏和只读只在前端做可以吗?

不可以。前端权限用于展示,后端仍要校验当前用户、任务归属和允许修改的业务字段,敏感字段不能只靠 disabled

PC 能打开业务表单,为什么 App 打不开?

PC 支持按路径动态加载;UniApp 小程序端需要静态 import 和条件注册。应检查 bpm-menu-config.tsform-detail.vue 和业务分包页面。


结语

企业审批平台的完成标准,不是流程设计器能拖出几个节点,而是任意一张真实业务单都能走完:模型可发布、审批人找得到、不同节点看到正确字段和按钮、PC 与手机办理同一个实例、流程结果最终回到业务资源。

RuoYi Office 用 Flowable 8 承载流程运行,用 Vue3 设计态和审批详情组织产品能力,再通过 businessKeyFlowBillService 与状态事件连接用车、用印、资产、报销等业务。把这条端到端主线打通后,条件网关、会签、加签、抄送和批量办理才不是一堆孤立功能。


💡 **想要体验 RuoYi Office 的强大功能?**

>

🌐 **在线演示**:https://ruoyioffice.com/web/(账号 admin / admin123)

>

📦 **源码仓库**:GitHub:https://github.com/yuqing2026/ruoyi-office | GitCode:https://gitcode.com/zhouzhongyan/ruoyi-office | Gitee:https://gitee.com/yqzy1688/ruoyi-office

>

💬 **技术咨询**:添加微信 **17156169080**,备注「RuoYi Office」

>

⭐ **如果觉得不错,请给个 Star 支持一下!**

相关推荐
Bs_MoneyMagnet2 小时前
基于springboot+vue的图书馆预约系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·毕业设计·图书管理系统·计算机毕业设计
小蒜学长2 小时前
基于小程序的绘画作品创作与分享社区系统的设计与实现(代码+数据库+LW)
java·spring boot·后端·绘画作品·创作分享社区
RuoyiOffice2 小时前
SpringBoot3+Vue3 企业级管理平台架构:多租户、RBAC、数据权限、工作流与微服务一次讲透
spring boot·vue3·springboot3·rbac·多租户·数据权限·ruoyi office
砍材农夫3 小时前
spring|spring web|拦截器、过滤器、aop
java·spring boot·spring·spring cloud
weixin_BYSJ198712 小时前
【计算机毕设】基于SpringBoot与Vue的文物保护档案管理系统08621
vue.js·spring boot·spring cloud·微服务·架构·django·课程设计
Bs_MoneyMagnet12 小时前
基于springboot+vue的个人健康管理系统的设计与实现 源码+文档
java·vue.js·spring boot·后端·vue3·springboot3·计算机毕业设计
Bs_MoneyMagnet15 小时前
基于springboot+vue的心理咨询预约与随访平台的设计与实现 源码+文档
vue.js·spring boot·后端·spring·毕业设计·旅游·计算机毕业设计
陌シ未央ゞ17 小时前
基于BM25算法和RRF实现的混合索引(java版)
人工智能·spring boot·后端·算法
user_admin_god20 小时前
第 01 篇:OpenAI 兼容 API 初探 —— 用 curl 跑通第一次对话
java·人工智能·spring boot·语言模型·devops