news 2026/4/25 4:00:28

写给做审批系统的你:状态和权限一旦没分层,后面一定越来越乱

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
写给做审批系统的你:状态和权限一旦没分层,后面一定越来越乱

Activiti/Flowable 工作流实战:业务状态和流程状态怎么保持一致?再讲清 RBAC + 数据权限

工作流项目真正难的地方,往往不是“怎么发起流程”,而是“流程跑起来之后,状态别乱、权限别乱、数据别乱”。
这个项目里我能明显看到两条很重要的主线:
一条是act_z_business.status/result这类流程侧状态管理;
另一条是sys_permissionSysPermissionDataRulejurisdiction、节点字段权限这些权限控制。
这两块如果一开始没设计好,系统前面跑得再快,后面也会越来越难维护。

文章目录

  • Activiti/Flowable 工作流实战:业务状态和流程状态怎么保持一致?再讲清 RBAC + 数据权限
    • 一、为什么“业务状态”和“流程状态”总是容易打架
    • 二、这个项目里更稳的做法:流程状态和业务状态分层
      • 2.1 流程侧状态,放在 `act_z_business`
      • 2.2 业务侧状态,放在业务表
    • 三、流程状态到底该怎么同步,才不容易乱
      • 3.1 发起时,同步一次流程主状态
      • 3.2 取消、办结、审批通过时,统一回写
      • 3.3 业务表不要直接写成“经理待审”“财务待审”
    • 四、我建议的“一致性设计原则”
      • 4.1 流程节点只保留一份真相
      • 4.2 业务表只保留业务语义状态
      • 4.3 所有状态更新都走统一服务层
      • 4.4 事务失败时,把外部副作用一起回滚
    • 五、只做 RBAC 为什么不够
    • 六、这个项目里的权限体系,其实是 4 层一起工作的
      • 6.1 第一层:菜单/功能权限
      • 6.2 第二层:数据权限
      • 6.3 第三层:页面/按钮权限
      • 6.4 第四层:节点字段权限
    • 七、RBAC + 数据权限 + 节点权限,三者到底怎么分工
    • 八、如果让我继续优化这套权限体系,我会补什么
      • 8.1 把流程状态到业务状态的映射做成统一配置
      • 8.2 让节点字段权限和按钮权限共用一套表达式能力
      • 8.3 对“流程状态一致性”增加巡检机制
    • 九、总结

一、为什么“业务状态”和“流程状态”总是容易打架

很多团队一开始做审批系统,都会这么写:

  • 业务表里写一个状态字段
  • 审批到哪个节点,就把状态改成“经理待审”“财务待审”“总监待审”
  • 前端列表也直接根据这个状态做展示

短期看很省事,长期看问题很大。

因为流程引擎本身已经有:

  • 当前任务
  • 当前节点
  • 流程实例状态
  • 历史任务轨迹

如果业务表再自己维护一套“细颗粒节点状态”,很容易出现两边不一致:

  • 引擎里已经到财务节点了,业务表还停在经理审批
  • 引擎里流程被撤销了,业务表却还是审批中
  • 引擎里任务已经办结,业务表列表仍显示待处理

这类问题在项目刚上线时不一定明显,但一旦涉及:

  • 加签
  • 退回
  • 撤销
  • 子流程
  • 条件分支

状态漂移几乎一定会出现。


二、这个项目里更稳的做法:流程状态和业务状态分层

我比较认可这个项目里的思路,因为它没有把所有状态都塞进一处,而是做了分层。

2.1 流程侧状态,放在act_z_business

ActivitiConstantActBusiness能看出,这套系统对流程状态做了统一抽象。

status负责描述流程生命周期:

  • 0:待提交
  • 1:处理中
  • 2:处理结束
  • 3:已撤销

result负责描述流程结果:

  • 0:待提交
  • 1:处理中
  • 2:通过
  • 3:驳回
  • 4:撤销
  • 5:删除
  • 6:办结

另外还有:

  • procInstId
  • nodeId
  • currTaskName
  • applyTime

这些字段一起构成了“流程侧统一真相”。

2.2 业务侧状态,放在业务表

业务表仍然可以保留自己的业务语义状态,比如:

  • 单据是否启用
  • 计划是否终止
  • 库存是否冻结
  • 合同是否到期

这个项目里DataBaseConstant也专门定义了标准字段:

  • bpm_status

我会把它理解为“业务面向报表与业务判断的状态字段”,而不是“替代流程引擎的节点状态字段”。

也就是说:

  • 流程状态负责描述流程跑到哪一步
  • 业务状态负责描述业务当前是什么语义

这两者应该映射,不应该互相覆盖。


三、流程状态到底该怎么同步,才不容易乱

这套项目里,其实已经给了一个相对标准的同步思路。

3.1 发起时,同步一次流程主状态

ActBusinessServiceImpl.apply里,发起流程成功之后,会更新:

  • procInstId
  • status = 1
  • result = 1
  • applyTime
  • currTaskName

这一步非常关键。

它说明系统不是在业务表里随便写一句“已提交”,而是把流程实例真正跑起来之后,再统一更新桥表状态。

3.2 取消、办结、审批通过时,统一回写

cancelfinish以及任务审批相关逻辑里,又会更新:

  • 取消时:status = STATUS_CANCELED
  • 办结时:status = STATUS_FINISH
  • 通过时:result = RESULT_PASS
  • 撤回/办结时:result = RESULT_REVOKE

这说明桥表不是只在发起时写一次,而是整个流程生命周期都在维护。

3.3 业务表不要直接写成“经理待审”“财务待审”

这是我最想强调的一点。

如果你把业务表状态直接写成:

  • 采购经理待审
  • 财务负责人待审
  • 总经理待审

那你其实是在把 BPMN 的节点语义复制一份到业务库里。

一旦流程图稍微调整:

  • 增加一个会签节点
  • 某个分支跳过财务
  • 某个节点改名

你业务库里的语义就会开始变形。

更稳的做法应该是:

  • 细粒度流程节点,以引擎和act_z_business.nodeId/currTaskName为准
  • 粗粒度业务语义,以业务表自己的bpm_statusbiz_status为准

比如业务表只保留:

  • 草稿
  • 审批中
  • 已通过
  • 已驳回
  • 已撤销

这就足够了。


四、我建议的“一致性设计原则”

如果你也在做 Activiti/Flowable 项目,我建议你优先守住这 4 条。

4.1 流程节点只保留一份真相

“当前在谁手里”“当前是什么节点”“是否还有待办”,统一以:

  • 引擎运行时任务
  • act_z_business.nodeId
  • act_z_business.currTaskName

为准。

不要业务表、缓存、前端页面各维护一份。

4.2 业务表只保留业务语义状态

业务表状态字段应该回答的是:

  • 这张单据现在是不是审批中
  • 它对业务来说是否生效
  • 能不能继续后续业务动作

而不是回答:

  • 现在卡在流程图第几个节点

4.3 所有状态更新都走统一服务层

不要:

  • 控制器改一点
  • 定时任务改一点
  • SQL 脚本又改一点

流程状态同步一定要收口到统一服务层里,不然你永远查不清谁把状态改乱了。

4.4 事务失败时,把外部副作用一起回滚

这个项目里有一个我很喜欢的细节:如果保存失败,会把生成编码重新回填 Redis 队列。

这说明团队已经意识到:

事务回滚不只是数据库回滚,还包括:

  • 编码占用回滚
  • 草稿缓存清理
  • 外部状态恢复

这类意识,才是流程系统能不能做稳的关键。


五、只做 RBAC 为什么不够

很多人一说权限,第一反应就是:

  • 用户
  • 角色
  • 菜单
  • 接口

也就是标准 RBAC。

但工作流系统里,RBAC 只能解决第一层问题:

  • 你能不能进入某个功能

它解决不了:

  • 你能看哪些数据
  • 你在这个节点能编辑哪些字段
  • 你能不能看到某个按钮
  • 同一个页面里你和别人看到的是不是同一套内容

所以在审批系统里,权限至少要拆成 4 层。


六、这个项目里的权限体系,其实是 4 层一起工作的

6.1 第一层:菜单/功能权限

这一层还是经典 RBAC。

核心就是:

  • sys_role
  • sys_permission
  • sys_role_permission

它解决的是:

  • 这个角色有没有这个菜单
  • 这个用户能不能访问这个页面/接口

这是入口权限。

6.2 第二层:数据权限

这套项目里,数据权限不是口头概念,而是有完整实现的。

能看到几个很关键的点:

  • @PermissionData(pageComponent = "...")
  • SysPermissionDataRule
  • JeecgDataAutorUtils
  • QueryGenerator

这套链路的意思非常清楚:

  1. 页面或接口声明自己使用哪个菜单组件的数据权限
  2. 系统查询该菜单对应的数据规则
  3. 数据规则放进请求上下文
  4. QueryGenerator在组装QueryWrapper时自动把规则拼进去

这说明它做的不是“页面上藏一个筛选条件”,而是真正把数据权限下沉到了查询层。

这点非常重要。

因为真正可靠的数据权限,不是前端不展示,而是后端根本查不出来。

6.3 第三层:页面/按钮权限

在低代码页面这一层,项目又加了一套jurisdiction配置。

比如:

  • SysBaseApiImpl.checkUserJurisdiction
  • SnDesignServiceImpl.processButton

它会根据当前用户的:

  • 角色
  • 部门

去过滤页面按钮、操作按钮、扩展按钮。

也就是说,同一个页面不是所有人都能看到同样的操作项。

这层权限特别适合处理:

  • 某部门可见,别的部门不可见
  • 某角色能点“审核通过”,别的角色只能看详情
  • 某些快捷操作只在特定组织范围内开放

6.4 第四层:节点字段权限

这一层是审批系统最容易被忽略,但实际最重要的一层。

项目里专门有表:

  • sn_flow_node_procdef_promission

从实体能看出来,它是按:

  • formId
  • flowId
  • flowNodeId
  • fieldId

来配置字段权限的。

promissionType语义也很清楚:

  • 0:不使用
  • 1:隐藏
  • 2:可写
  • 3:只读

这就意味着同一张表单,在不同审批节点上,可以表现成完全不同的交互形态。

这才是真正的“流程驱动表单”。


七、RBAC + 数据权限 + 节点权限,三者到底怎么分工

如果让我用一句最容易记住的话来总结,我会这么说:

  • RBAC 决定你能不能进来
  • 数据权限决定你能看哪些记录
  • 节点字段权限决定你能改哪些字段

再加上页面按钮权限,刚好构成一套比较完整的审批权限体系。

我比较反对的一种做法是:

  • 所有权限都堆到角色里

因为角色一旦负责太多事,就会开始失控:

  • 角色越来越多
  • 角色语义越来越混乱
  • 页面按钮、数据范围、字段可写性全绑在一起

最后没人敢改。

而这个项目现在的方向,至少是把这些维度分开了,这是对的。


八、如果让我继续优化这套权限体系,我会补什么

8.1 把流程状态到业务状态的映射做成统一配置

现在这套系统已经有流程统一状态和业务侧状态字段,但如果继续平台化,我会再往前走一步:

  • 用统一映射表或统一枚举策略
  • 明确“哪个流程结果映射成哪个业务状态”

这样跨业务线时就不会各写各的。

8.2 让节点字段权限和按钮权限共用一套表达式能力

现在字段权限、按钮权限、数据权限是分层的,这是优点。

但如果后面还能再抽一层统一表达式引擎,比如都支持:

  • 角色
  • 部门
  • 表单字段值
  • 当前节点

那整个系统的可配置性会更强。

8.3 对“流程状态一致性”增加巡检机制

比如定时检查:

  • act_z_business.procInstId是否存在
  • 流程已结束但桥表是否还在处理中
  • 业务表business_id/process_id/node_id是否为空

这类巡检对长期运行的系统非常有价值。


九、总结

我现在越来越觉得,审批系统做到后面,真正拉开差距的不是会不会发起流程,而是两件事:

  • 状态能不能始终对得上
  • 权限能不能分层控制好

结合这个项目,我比较认同的一套思路是:

  • 流程状态,统一放在act_z_business
  • 业务状态,留在业务表,表达业务语义
  • RBAC 管入口
  • 数据权限管记录范围
  • 页面按钮权限管操作入口
  • 节点字段权限管表单交互

如果只记一句话,我觉得可以记住这句:

工作流系统里最稳的做法,不是把所有状态和权限揉成一团,而是让“流程状态”“业务状态”“角色权限”“数据权限”“节点字段权限”各自分层,再通过统一服务把它们串起来。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 3:58:20

Pwnagotchi完全指南:从零开始构建你的WiFi安全分析利器

Pwnagotchi完全指南:从零开始构建你的WiFi安全分析利器 【免费下载链接】pwnagotchi-bookworm (⌐■_■) - Raspberry Pi instrumenting Bettercap for Wi-Fi pwning. 项目地址: https://gitcode.com/gh_mirrors/pw/pwnagotchi-bookworm Pwnagotchi是一款基于…

作者头像 李华
网站建设 2026/4/25 3:57:12

夸克自动转存API接口使用手册:构建第三方集成的完整参考

夸克自动转存API接口使用手册:构建第三方集成的完整参考 【免费下载链接】quark_auto_save 夸克网盘签到、自动转存、命名整理、发推送提醒和刷新媒体库一条龙 项目地址: https://gitcode.com/gh_mirrors/qu/quark_auto_save 夸克自动转存API接口是夸克网盘签…

作者头像 李华
网站建设 2026/4/25 3:50:21

Zip4j流式处理实战:高效处理大文件与内存优化技巧

Zip4j流式处理实战:高效处理大文件与内存优化技巧 【免费下载链接】zip4j A Java library for zip files and streams 项目地址: https://gitcode.com/gh_mirrors/zi/zip4j Zip4j是一款功能强大的Java库,专为zip文件和流操作设计,提供…

作者头像 李华
网站建设 2026/4/25 3:48:56

如何处理SQL中的NULL值_使用ISNULL或COALESCE函数

ISNULL返回首参数类型,COALESCE按优先级隐式转换;WHERE中NULL需用IS NULL判断;聚合函数自动忽略NULL;JOIN时NULL导致不匹配;NULL是缺失标记而非值。ISNULL 和 COALESCE 看起来都能填 NULL,但行为完全不同SQ…

作者头像 李华
网站建设 2026/4/25 3:47:52

Transloco 性能优化技巧:如何减少包大小并提升加载速度

Transloco 性能优化技巧:如何减少包大小并提升加载速度 【免费下载链接】transloco 🚀 😍 The internationalization (i18n) library for Angular 项目地址: https://gitcode.com/gh_mirrors/tr/transloco Transloco 作为 Angular 生态…

作者头像 李华