低代码表单怎么和 Flowable 工作流整合?ForgeAdmin 的 3 种数据模式设计 低代码表单怎么和 Flowable 工作流整合Forge 的 3 种数据模式设计低代码搭表单、Flowable 跑审批这两件事单独做都不难。难的是合在一起表单数据要不要落业务表流程变量怎么传审批中途数据存哪这篇拆 Forge 的真实实现看它怎么用 3 种数据模式解决这个整合难题。做过审批系统的人都知道低代码表单和工作流引擎之间有一道缝低代码表单负责收集数据请假天数、报销金额、采购明细工作流引擎负责驱动流程谁审批、怎么流转、什么时候结束但这两边的数据怎么流动是最容易出问题的地方。请假审批的表单数据是只存在流程变量里还是落到业务表还是两边都存不同选择后续的查询、统计、数据权限完全不同。Forge Admin 在这块做了一个很清晰的设计用 3 种数据模式dataMode来定义表单数据和流程的关系。这篇就拆这个设计。一、先看清楚整合的 4 个核心实体Forge 的流程和低代码整合核心是 4 张表的关系。看懂这张图后面的设计就清楚了FlowForm流程表单定义 │ formSchema: JSON 格式的表单协议字段、类型、校验 │ formType: dynamic(动态表单) / external(外部表单) / builtin(内置表单) │ ▼ FlowEntry流程入口 │ modelKey: 绑定哪个流程模型Flowable BPMN │ formKey: 绑定哪个表单FlowForm │ dataMode: 数据模式PROCESS_ONLY / BUSINESS_OBJECT / HYBRID │ objectCode:业务对象编码落哪张业务表 │ ├──► FlowEntryFieldMapping字段映射 │ formField → 表单字段 │ targetField → 业务对象字段 │ flowVariable → 流程变量 │ ▼ FlowFormInstance填报实例快照 │ schemaSnapshot: 提交时的表单 schema 快照 │ formData: 用户填的数据 │ processInstanceId: 关联的流程实例 │ objectCode recordId: 关联的业务记录用一句话说清楚FlowForm定义表单长什么样低代码表单设计器产出FlowEntry是流程入口把表单和流程模型绑在一起并决定数据怎么处理FlowEntryFieldMapping定义表单字段怎么映射到业务表和流程变量FlowFormInstance是每次提交的快照保存当时填的数据和表单结构二、核心设计3 种数据模式整合的难点不在表单怎么渲染而在表单数据提交后怎么处理。Forge 定义了 3 种模式对应 3 种业务场景。模式一PROCESS_ONLY纯流程模式表单数据只存在流程变量里不落业务表。用户填表单 → 表单数据作为流程变量 → 流程流转 → 结束适用场景简单的审批不需要事后查询业务数据。比如请假审批。审批通过后人事在考勤表里手动记录就行不需要自动落一张请假记录表。优点简单不用建业务表不用写字段映射。缺点事后查数据只能查流程历史不能像查业务表那样分页、筛选、统计。模式二BUSINESS_OBJECT业务对象模式表单数据通过字段映射落到业务表里流程里不存表单数据。用户填表单 → 字段映射 → 落业务表 → 流程变量只存关键字段 → 流程流转适用场景审批完要当业务数据用的场景。比如采购审批。采购单审批通过后要变成一条采购记录后续要查询、统计、对账。这种场景必须落业务表。优点数据在业务表里可以正常查询、分页、做数据权限、做统计看板。缺点要建业务表、配字段映射、实现业务对象适配器。模式三HYBRID混合模式表单数据既落业务表又留一份快照在流程侧。用户填表单 → 字段映射 → 落业务表 ↘ 同时存一份 FlowFormInstance 快照 流程变量存关键字段 → 流程流转适用场景既要业务表查询又要追溯提交时表单长什么样。比如合同审批。合同审批通过后要落合同表BUSINESS_OBJECT但三个月后可能要查当时提交审批时填了什么——如果表单结构改过纯业务表无法还原当时的填报内容。HYBRID 模式存了一份 schemaSnapshot formData可以完整还原。优点业务表可查 快照可追溯最完整。缺点数据存两份存储成本略高。三、源码怎么实现的直接看FlowRuntimeServiceImpl.submitByMode()这是整合的核心入口privateFlowStartResultVOsubmitByMode(FlowEntryRuntimeVOruntime,ListFlowEntryFieldMappingmappings,MapString,ObjectformData,FlowEntrySubmitDTOdto,StringdataMode){FlowEntryentryruntime.getEntry();if(BUSINESS_OBJECT.equals(dataMode)){returnsubmitBusinessObject(entry,mappings,formData,dto,false);}if(HYBRID.equals(dataMode)){returnsubmitBusinessObject(entry,mappings,formData,dto,true);}returnsubmitProcessOnly(runtime,mappings,formData,dto);}就 3 个分支对应 3 种模式。BUSINESS_OBJECT 和 HYBRID 走同一个方法区别在keepSnapshot参数。PROCESS_ONLY 怎么处理纯流程模式最简单把表单数据塞进流程变量发起流程完事。privateFlowStartResultVOsubmitProcessOnly(...){// 1. 构建流程变量MapString,ObjectvariablesbuildFlowVariables(formData,mappings,dto);// 2. 创建表单实例记录只存基础信息不存快照FlowFormInstanceinstancenewFlowFormInstance();instance.setDataMode(PROCESS_ONLY);instance.setFormData(toJson(formData));formInstanceMapper.insert(instance);// 3. 发起流程StringprocessInstanceIdflowInstanceService.startProcess(entry.getModelKey(),businessKey,...,variables,...);// 4. 关联回表单实例formInstanceMapper.updateProcessInstance(instanceId,processInstanceId,RUNNING);returnresult;}BUSINESS_OBJECT / HYBRID 怎么处理这两种模式要落业务表核心靠FlowBusinessObjectRuntimeAdapter这个扩展点privateFlowStartResultVOsubmitBusinessObject(FlowEntryentry,ListFlowEntryFieldMappingmappings,MapString,ObjectformData,FlowEntrySubmitDTOdto,booleankeepSnapshot){// 1. 调用业务侧适配器把表单数据落成业务记录BusinessRecordCreateResultrecordbusinessObjectRuntimeAdapter.createBusinessRecord(entry,mappings,formData);// 2. 构建流程变量从字段映射里取MapString,ObjectvariablesbuildFlowVariables(formData,mappings,dto);// 3. HYBRID 模式额外存一份快照if(keepSnapshot){FlowFormInstanceinstancenewFlowFormInstance();instance.setSchemaSnapshot(runtime.getFormVersion().getFormSchema());instance.setFieldRegistry(runtime.getFormVersion().getFieldRegistry());instance.setFormData(toJson(formData));instance.setDataMode(HYBRID);instance.setObjectCode(objectCode);instance.setRecordId(record.getRecordId());// 关联业务记录formInstanceMapper.insert(instance);variables.put(flowFormInstanceId,String.valueOf(formInstanceId));}// 4. 发起流程StringprocessInstanceIdflowInstanceService.startProcess(...);returnresult;}四、关键解耦FlowBusinessObjectRuntimeAdapter注意上面代码里的businessObjectRuntimeAdapter.createBusinessRecord()。这是整个整合设计里最巧妙的一步。问题流程插件要把表单数据落业务表但业务表是业务模块定义的流程插件不知道你的biz_purchase表长什么样。如果让流程插件直接依赖业务模块架构就耦合了——流程插件要认识采购表、合同表、报销表……每加一个业务就要改流程插件。Forge 的解法流程插件定义一个扩展点接口由业务侧实现publicinterfaceFlowBusinessObjectRuntimeAdapter{BusinessRecordCreateResultcreateBusinessRecord(FlowEntryentry,// 流程入口配置ListFlowEntryFieldMappingmappings,// 字段映射规则MapString,ObjectformData// 用户填的表单数据);DataclassBusinessRecordCreateResult{privateStringobjectCode;// 业务对象编码privateLongrecordId;// 业务记录IDprivateStringbusinessKey;// 业务键privateMapString,Objectvariables;// 额外流程变量}}源码注释写得很明确Flow 插件不直接依赖低代码 generator 插件需要 BUSINESS_OBJECT/HYBRID 时由业务侧提供实现把表单数据映射为业务对象记录。业务侧怎么实现ComponentpublicclassPurchaseFlowAdapterimplementsFlowBusinessObjectRuntimeAdapter{AutowiredprivatePurchaseMapperpurchaseMapper;OverridepublicBusinessRecordCreateResultcreateBusinessRecord(FlowEntryentry,ListFlowEntryFieldMappingmappings,MapString,ObjectformData){// 1. 按字段映射把表单数据组装成业务对象PurchasepurchasenewPurchase();for(FlowEntryFieldMappingmapping:mappings){ObjectvalueformData.get(mapping.getFormField());// 反射或手动 set 到业务对象setFieldValue(purchase,mapping.getTargetField(),value);}// 2. 落业务表purchaseMapper.insert(purchase);// 3. 返回业务记录信息BusinessRecordCreateResultresultnewBusinessRecordCreateResult();result.setObjectCode(purchase);result.setRecordId(purchase.getId());result.setBusinessKey(purchase:purchase.getId());returnresult;}}这样流程插件和业务模块完全解耦流程插件只管调适配器、拿记录ID、发起流程业务模块只管按映射规则把表单数据落表字段映射关系在FlowEntryFieldMapping表里配置不用写代码五、字段映射表单字段怎么变成业务数据和流程变量FlowEntryFieldMapping是连接表单和流程/业务表的桥梁publicclassFlowEntryFieldMapping{privateStringformField;// 表单字段名privateStringtargetType;// 映射目标类型业务字段 / 流程变量privateStringtargetField;// 目标字段名privateStringflowVariable;// 流程变量名privateIntegerrequired;// 是否必填}一个表单字段可以同时映射到两个地方表单字段 days请假天数 ├── targetTypeBUSINESS_FIELD, targetFieldleave_days → 落业务表 └── targetTypeFLOW_VARIABLE, flowVariabledays → 流程变量条件分支用这样设计的好处是业务表存完整数据查询、统计、数据权限用流程变量只存条件判断需要的关键字段流程分支用比如days 3走老板审批映射关系可配置不用写代码六、表单类型不只是动态表单FlowForm支持三种表单类型privateStringformType;// dynamic / external / builtin类型说明适用场景dynamic动态表单formSchema 是 JSON 协议运行时渲染标准审批表单请假、报销external外部表单指向一个 URL已有业务页面接审批builtin内置表单指定前端组件路径定制化程度高的表单dynamic类型就是低代码表单和流程整合的核心低代码表单设计器产出的 JSON Schema 存到formSchema字段流程审批时运行时引擎按这个 Schema 渲染表单。external类型则给已有业务页面留了路你的采购管理页面已经做好了只需要在审批时跳过去填不用重新用低代码搭一遍。这种设计保证了整合不是强制的——你可以全用低代码动态表单也可以对接已有页面不用为了用流程引擎就把所有表单重做。七、快照机制为什么 HYBRID 要存两份HYBRID 模式下FlowFormInstance存了这些字段privateStringschemaSnapshot;// 提交时的表单 Schema 快照privateStringfieldRegistry;// 字段目录快照privateStringformData;// 用户填的数据privateStringdataMode;// HYBRIDprivateStringobjectCode;// 业务对象编码privateLongrecordId;// 业务记录ID为什么要存快照因为表单结构会变。假设 3 月你用 v1 版表单提交了采购审批表单有采购金额字段。5 月业务方加了采购用途字段表单变成 v2。如果只存业务表数据3 月的那条采购记录没有采购用途——这没问题。但如果你想在审批历史里还原3 月提交时表单长什么样没有快照就做不到。快照保存的是提交那一刻的表单结构 数据。无论表单后续怎么改历史记录都能完整还原。这对合规审计很重要——政务、金融场景经常要求可追溯当时填了什么、表单是什么样的。八、完整链路从填表到审批的 6 步把整个整合链路串一遍1. 管理员在低代码表单设计器里设计表单 → 存 FlowFormformSchema 2. 管理员创建 FlowEntry绑定 modelKey formKey dataMode 字段映射 3. 用户在待办页面点发起审批 → 运行时加载 FlowEntry FlowForm 4. 运行时引擎按 formSchema 渲染表单 → 用户填写 5. 提交 → FlowRuntimeServiceImpl.submitEntryForm() ├── PROCESS_ONLY表单数据 → 流程变量 → 发起流程 ├── BUSINESS_OBJECT表单数据 → 字段映射 → 落业务表 → 发起流程 └── HYBRID落业务表 存快照 → 发起流程 6. 流程结束 → FlowCallback 回调业务 → 更新业务状态九、和纯 Flowable 接入的关系上一篇《Spring Boot 接 Flowable用 3 个注解搭一个请假审批流程》讲的是业务侧怎么用FlowStart/FlowCallback接流程——那适合已有业务表想加审批的场景。这篇讲的是低代码表单和流程的深度整合——适合用低代码搭表单 走审批 数据落表的场景。两者的关系对比注解化接入FlowStart低代码整合FlowEntry表单来源业务模块自己的页面低代码表单设计器数据存储业务模块自己管由 dataMode 决定字段映射不需要业务方法直接处理FlowEntryFieldMapping 配置适用场景已有业务系统加审批从零搭审批表单 流程 落表侵入性加注解配置化几乎无侵入两条路按场景选已有业务表用注解从零搭用低代码整合。十、总结整合的核心是数据模式 字段映射 扩展点低代码和 Flowable 的整合核心不是表单怎么渲染那是低代码的事而是表单数据提交后怎么和流程、业务表三者协同。Forge 的设计可以总结为 3 层层机制解决什么数据模式PROCESS_ONLY / BUSINESS_OBJECT / HYBRID表单数据存哪字段映射FlowEntryFieldMapping表单字段怎么变成业务数据和流程变量扩展点FlowBusinessObjectRuntimeAdapter流程插件和业务模块解耦这 3 层做到了不耦合流程插件不认识业务表通过扩展点对接可配置字段映射在后台配不改代码可追溯HYBRID 模式存快照历史可还原可选择3 种模式按场景选不是一刀切这才是低代码和工作流整合该有的设计——不是把两边硬拼在一起而是用清晰的数据模式定义边界用扩展点解耦用字段映射做桥梁。源码自取本文所有代码片段均来自仓库可对照核验Giteehttps://gitee.com/ForgeLab/forge-adminGitHubhttps://github.com/yaomindong1996/forge-admin在线演示http://www.dlforgelab.com:8084/forge/login admin / 123456你们的审批表单和工作流是怎么整合的表单数据落业务表还是只存流程变量评论区聊聊。