我在项目里是怎么设计工作流表的:不是只看引擎表就够了
Activiti/Flowable 工作流实战工作流表怎么设计结合项目讲清主表、业务表、表单表和节点表最近我在重新梳理这个项目里的工作流模块时一个感觉特别强烈真正决定系统能不能长期维护的往往不是 Activiti/Flowable 的 API 会不会调而是表到底怎么拆。这个项目实际使用的是 Activiti 5.22.0模块里能看到project-key-module-activiti、act_z_business、sn_flow_formmanager、sn_flow_formfield这些关键结构。但如果你后面换成 Flowable这套设计思路其实仍然成立因为真正稳定的不是“引擎名字”而是“业务表、流程表、表单表、节点配置表之间的关系”。文章目录Activiti/Flowable 工作流实战工作流表怎么设计结合项目讲清主表、业务表、表单表和节点表一、先说结论审批系统的表不能只盯着 act_ 系统表二、为什么我特别重视 act_z_business三、业务表应该预留哪些流程字段四、保存一张业务单据时流程数据是怎么挂上去的4.1 先有业务再挂流程4.2 流程桥接信息要反写回业务表五、动态表单为什么还要单独一套表六、做工作流表设计时我建议你优先守住这几个原则6.1 引擎表只做引擎该做的事6.2 业务表要有最小流程字段6.3 一定要有统一桥表6.4 节点权限不要混在业务表里6.5 父子流程关系要提前设计七、如果让我基于这个项目再优化一次我会怎么做7.1 给桥表补齐更明确的索引7.2 统一业务表的状态字段命名7.3 把桥表和业务表的同步动作做成公共能力7.4 给流程-表单绑定增加版本维度八、总结一、先说结论审批系统的表不能只盯着act_系统表很多人一做工作流就先去背act_re_*是流程定义act_ru_*是运行时act_hi_*是历史表这当然没错但如果你真要做企业审批系统只靠这三类表远远不够。因为引擎表解决的是“流程怎么跑”不是业务数据存哪里表单长什么样当前节点哪些字段可见、可编辑、只读我发起的、我待办的、我已办的怎么统一查询子流程、组合表单、多表单绑定怎么落地结合这个项目我更建议把工作流表结构理解成 4 层。层级代表表作用引擎层act_re_*、act_ru_*、act_hi_*负责流程定义、运行时任务、历史记录业务桥接层act_z_business把“流程实例”和“业务数据”连接起来业务实例层cc_dckd、cc_drkd这类业务表真正存业务字段表单配置层sn_flow_formmanager、sn_flow_formfield、sn_flow_node_procdef_relation等存动态表单、节点绑定、字段权限、节点事件真正能支撑项目落地的不是某一张表而是这 4 层一起工作。二、为什么我特别重视act_z_business这个项目里最关键的一张“桥表”其实不是 Activiti 自带表而是act_z_business。从实体ActBusiness可以看到它至少承担了下面几件事记录流程定义 IDprocDefId记录流程实例 IDprocInstId记录业务表主键tableId记录业务表名tableName记录当前节点nodeId记录当前任务名currTaskName记录流程状态status记录流程结果result记录标题、申请人、申请时间title、userId、applyTime记录父子流程关系parentTableId这张表本质上解决了一个特别现实的问题引擎知道“流程实例是谁”业务表知道“单据是谁”但系统首页、待办中心、我发起的列表、移动端审批入口需要的是“把这两边串起来的一条统一数据”。如果没有这张桥表后面你会非常痛苦查待办时要反查业务表查已发起时要拼流程实例和业务主键子流程挂父流程时很难追踪一个系统里有几十张业务表时查询入口会越来越乱所以我现在做工作流系统几乎都会保留一张类似act_z_business的流程业务桥接表。三、业务表应该预留哪些流程字段这套项目其实已经把这个规范做得很明显了。在CommonUtils.tableColumns里系统统一约定了业务表的标准字段idcreate_bycreate_timeupdate_byupdate_timenode_idprocess_idbusiness_idtrade_noversion_num这说明它不是把流程信息完全压在act_z_business里而是同时在业务表里保留最小必要字段。以cc_dckd这张待出库单为例实体里能直接看到nodeIdprocessIdbusinessIdversionNum这套设计我非常认同因为它兼顾了两件事业务表本身具备最基础的流程关联能力比如你在业务详情页里直接根据processId、nodeId做当前节点展示不需要每次都跳到引擎表或桥表里兜一圈。流程中心仍然有统一桥接层真正做待办、我发起、审批记录、子流程关联时还是以act_z_business为中心更稳。也就是说业务表保存“最小流程引用”act_z_business保存“统一流程视图”这比“所有状态都只放业务表里”要清晰得多。四、保存一张业务单据时流程数据是怎么挂上去的这个项目里有一类生成出来的服务代码非常典型比如FlowCcDckdIServiceImpl。它的保存流程大致是这样的先保存业务表数据比如先把cc_dckd保存成功拿到业务主键bussid再组装ActBusiness把tableId、tableName、procDefId、nodeId、title、parentTableId这些信息塞进去把ActBusiness落到act_z_business再反写回业务表把businessId、processId、nodeId更新回cc_dckd这套顺序看起来普通但它特别重要因为它避免了两个常见坑。4.1 先有业务再挂流程很多人会想着“先发起流程再存业务”但只要业务保存失败、主键没拿到、子表校验不过、编码回滚异常你的数据就会变得很难收拾。这个项目的思路更稳先把业务数据写完整再把流程桥接数据补齐如果中间失败就事务回滚4.2 流程桥接信息要反写回业务表如果你只把关联关系存在act_z_business业务表完全不知道自己挂了哪个流程那后面做单据详情展示流程状态业务导出专项统计移动端列表跳转都会比较绕。所以这套代码里把businessId、processId、nodeId再更新回业务表是对的。五、动态表单为什么还要单独一套表如果你的系统是固定表单那到这里其实已经能跑起来了。但这个项目明显不是只做固定审批它还有低代码/动态表单能力所以就不能只有引擎表业务表业务桥表它还需要一套表单与节点配置体系。这一层在项目里主要有几张表sn_flow_formmanager表单主表管表单名、真实表名、整体 JSON、布局、版本、组合表单标识sn_flow_formfield字段元数据管字段类型、数据类型、长度、规则、optionsJSON、页面按钮配置sn_flow_node_procdef_relation把表单和流程节点绑定起来sn_flow_node_procdef_promission配置某节点下某字段是隐藏、可写还是只读sn_flow_node_procdef_event配置节点事件、跨表写入、条件动作这意味着在这个项目里“表单”不是页面模板而已而是一套完整的元数据模型。这样做的好处非常直接同一个流程不同节点可以挂不同表单同一个表单可以反向绑定不同流程字段权限不需要写死在前端页面里节点事件不需要全部 if/else 写死在业务代码里从架构角度看这才是真正的“工作流平台化”。六、做工作流表设计时我建议你优先守住这几个原则6.1 引擎表只做引擎该做的事不要试图把所有业务字段都塞进流程变量里也不要拿act_ru_*直接当业务表用。引擎负责定义运行历史业务负责单据内容业务状态业务约束桥接层负责统一视图列表查询流程与业务关联6.2 业务表要有最小流程字段最少我会保留process_idbusiness_idnode_idversion_num如果系统要求更强的一致性我还会保留一个业务语义状态字段比如bpm_statusbiz_statusapprove_status这个项目里DataBaseConstant里也能看到标准字段bpm_status的约定。6.3 一定要有统一桥表如果一个系统里只有一两张审批单很多团队会偷懒不建桥表。但只要后面业务越来越多合同审批入库审批出库审批借用审批盘点审批没有统一桥表后面“我的待办”“我的申请”“移动端审批中心”一定会越来越难做。6.4 节点权限不要混在业务表里字段是否隐藏、是否只读、是否可写这种配置属于“节点视角”。所以它应该跟flow_idflow_node_idfield_id放在一起而不是直接拼在业务表字段注释里更不是写死在前端页面逻辑里。6.5 父子流程关系要提前设计这个项目里act_z_business有parentTableId实际上就是在为调用活动子流程组合审批做准备。很多系统一开始不考虑这个后面一做“主流程里拉起子流程”表关系就会变得很乱。七、如果让我基于这个项目再优化一次我会怎么做如果是继续往工程化方向优化我会重点做 4 件事。7.1 给桥表补齐更明确的索引像这些字段都很值得单独建索引proc_inst_idtable_nametable_iduser_idstatusresult因为待办、我发起、按业务表跳流程详情都会高频用到。7.2 统一业务表的状态字段命名项目里不同业务表会有自己的业务状态字段比如某些表叫plan_staus有些场景又会用bpm_status。如果后面要继续平台化我会推动流程统一状态字段命名业务统一状态字段命名统一枚举字典这样报表和查询条件会干净很多。7.3 把桥表和业务表的同步动作做成公共能力现在项目里很多FlowXXXServiceImpl都在做类似的事保存业务保存ActBusiness回写业务表流程字段这类代码后面很适合继续抽象成统一模板降低重复。7.4 给流程-表单绑定增加版本维度项目里已经有版本信息但如果后面要做更强的表单演进我会进一步明确表单版本流程版本绑定版本这样能更稳地处理“旧流程跑旧表单新流程跑新表单”的场景。八、总结很多人问我Activiti/Flowable 工作流表设计最容易犯的错是什么。我的回答一直是不是表太多而是表太少。只盯着引擎表你能把流程跑起来。但你做不出真正能落地、能扩展、能长期维护的业务系统。结合这个项目我更认可的一套拆法是引擎表负责流程运行业务表负责真实单据act_z_business负责统一桥接动态表单与节点配置表负责平台化能力如果只记一句话我觉得可以记住这句工作流表设计的关键不是把所有信息塞进一张表里而是让“流程运行”“业务数据”“表单配置”“节点权限”各自归位再通过桥表稳定连接起来。