先说个我见过太多次的场景。业务方提了个需求:“帮我加个请假审批流程,就是填个单子、经理审批、HR备案就行了。”于是你开始建表、写Controller、写Service、写Mapper,把表单字段写死在代码里,再写几个状态枚举,加一张审批记录表——一套流程下来半天没了。下个月业务方说“再加个报销审批”,你又把类似的事重复一遍。页面长得差不多、流程套路也差不多,唯一变的只是字段名和审批节点。
SpringBoot项目里这种重复CRUD写多了,我一直在想一个问题:能不能把“表单长什么样”和“流程怎么走”变成配置数据,而不是代码?这就是我这篇文章要聊的东西——一套基于SpringBoot的Low-Code思路,用JSON来描述表单定义和审批流定义,然后写一个通用引擎去解释和执行它们。配好之后,新的审批流不需要写一行Java代码,填Json配置就行,实测一个请假审批从零到跑通,五分钟真的够。
这篇文章适合谁看?在SpringBoot项目里写过三张以上业务表单、厌倦了复制粘贴式CRUD的后端开发,或者团队里想引入低成本低代码方案、但不想上重型平台的架构师。我会把表结构、JSON设计、核心引擎代码、完整实操步骤和踩过的坑都摊开讲,不藏着掖着。
1. 为什么重复CRUD让人难受:JSON表单引擎解决的问题
1.1 重复CRUD的尽头,是表单的“写死”
传统SpringBoot项目里的业务表单开发,流程几乎是固定的:根据需求设计数据库表,一张表对应一个实体类,然后Controller、Service、Mapper三件套,再写一个前端页面。表面上看每张表字段不同,但骨架完全一样——增删改查都是那几行代码换个字段名而已。
真正的问题是存量系统里这种表单特别多。我今天统计了一下之前维护的项目,光审批相关的表就有十一张:请假表、报销表、用章申请、采购申请、合同会签……每张表都有自己的增删改查接口,加起来几十个URL。后面想加一个权限控制或者统一日志,得挨个接口改,累得不是手,是心。
而且业务方改需求的频率远比我们想象的高。今天说请假要加一个“是否紧急”字段,明天说报销要按项目维度统计。每次改动都牵动数据库、后端代码和前端页面,哪怕是加一个下拉框选项,都要走一遍发布流程。这种模式在低代码平台出现之前是没办法的事,但现在明明有更好的方案,为什么不用?
1.2 JSON描述表单:把“界面”变成数据
核心思路其实不复杂:把表单字段的定义抽象成JSON,比如这个表单有哪些字段、每个字段是什么类型、哪个必填、可选项是什么,都放在JSON配置里。SpringBoot后端只需要一套通用的解析和校验逻辑,读取JSON配置后动态处理数据。
打个比方,传统写法像是你亲手盖每一栋房子,虽然户型不同但工序完全一样,你得重复垒砖抹灰;JSON表单引擎则是先造了一个标准框架,每栋房子只需要填写“户型图”(JSON配置),框架自己会盖好。
这意味着几件事:第一,新增表单不需要建表和写代码,只需要往配置表里插一条JSON记录;第二,表单字段变化只改JSON,不需要重新发布应用;第三,同一套校验逻辑服务所有表单,代码量大幅下降。Low-Code的核心从来不是“不用程序员”,而是让程序员把时间花在真正有挑战的事情上,而不是重复劳动。
1.3 这套方案适合谁、不适合谁
我在好几个项目里用过这套思路,经验是它适合业务表单多、结构相对固定、但经常微调的B端系统,比如OA、ERP、内部管理系统。这类系统的特点是表单字段上百个,但单个表单的字段数量一般不超过五十,性能压力不大,非常契合JSON存储和动态解析的模式。
不适合的大概有两类:一类是数据量大、对查询性能要求极高的核心业务表(比如交易流水),这种就别用JSON存了,还是老老实实建表加索引;另一类是前后端交互极其复杂的表单——比如包含嵌套表格、动态行编辑、跨字段复杂联动的高阶页面,纯JSON驱动的通用引擎实现起来成本极高,不如直接写页面。
我觉得做技术选型最忌讳“一招鲜”。JSON表单引擎是一个很好的增量方案,解决的是大量中低复杂度的表单和审批场景,而不是要推翻所有数据库设计。理解了这个边界,后面看代码和配置你就知道哪些该用、哪些不该用了。
2. 表单引擎核心设计:一份JSON定义,跑通前端界面与后端校验
2.1 表单定义的核心JSON结构
表单定义的数据结构我设计了三层:最外层是表单元信息(formKey、名称、版本),中间是字段列表,字段内部是字段属性。字段属性是核心,它既决定前端渲染成什么控件,也决定后端如何校验数据。下面是我在一套请假单上用的表单定义,基本能代表这套方案的标准写法:
{ "formKey": "leave_request", "formName": "请假申请单", "version": 1, "fields": [ { "name": "applicant", "label": "申请人", "type": "text", "required": true, "disabled": true }, { "name": "leaveType", "label": "请假类型", "type": "select", "required": true, "options": [ { "label": "事假", "value": "personal" }, { "label": "病假", "value": "sick" }, { "label": "年假", "value": "annual" } ] }, { "name": "days", "label": "请假天数", "type": "number", "required": true, "rules": { "min": 0.5, "max": 30, "precision": 1 } }, { "name": "reason", "label": "请假事由", "type": "textarea", "required": true, "rules": { "maxLength": 500 } } ] }每个字段的type决定控件类型,目前我支持text、textarea、number、select、radio、checkbox、date、datetime、file这几种,覆盖了90%的审批表单场景。rules里可以存校验规则,min和max是数值范围,maxLength是长度,后面想支持正则也可以往里加。前端拿到这个JSON直接渲染成表单,后端拿到同一个JSON做数据校验,一份配置两头通用,这是JSON Schema思路带来的最大红利。
2.2 动态表单实例:一张表存所有业务数据
表单定义存配置,表单实例存业务数据。传统方案是一个表单一张表,我的方案是字段配置在JSON里,业务数据也直接存在一个JSON字段中。form_instance表的设计如下:
CREATE TABLE form_instance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, form_key VARCHAR(64) NOT NULL, version INT NOT NULL, data_json JSON NOT NULL, status TINYINT DEFAULT 0 COMMENT '0-草稿 1-审批中 2-通过 3-驳回', process_instance_id BIGINT COMMENT '关联流程实例', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_form_key (form_key, status) );提交请假单的时候,前端POST过来就是一个普通JSON,比如{"applicant":"张三","leaveType":"personal","days":2,"reason":"家里有事"}。后端做的事情是:先根据formKey拿到表单定义,逐字段做校验,校验通过后把整个JSON塞进data_json字段。
这种设计的优点是极度的灵活,新增表单不需要动数据库表结构;缺点是查询和统计确实比普通表麻烦一点。我的解决思路是:高频查询字段用MySQL 8的JSON函数,比如JSON_EXTRACT(data_json, '$.applicant'),同时用generated column加索引;低频统计直接一次性全查出来在内存里处理,毕竟表单实例数据量通常不会膨胀到百万级。
2.3 后端通用校验逻辑:规则写在Schema里,不是写在代码里
以前每次写一个表单接收集合,都得写一段“如果是null就报错”“如果字符串长度超了就报错”的代码。有了Schema,这些逻辑全都可以自动化。我封装了一个FormSchemaValidator组件,核心逻辑大概是这样的:
@Component public class FormSchemaValidator { @Resource private FormDefinitionMapper formDefinitionMapper; public void validate(String formKey, Integer version, Map<String, Object> data) { FormDefinition formDef = formDefinitionMapper.findByKeyAndVersion(formKey, version); JSONObject schema = JSON.parseObject(formDef.getSchemaJson()); JSONArray fields = schema.getJSONArray("fields"); for (int i = 0; i < fields.size(); i++) { JSONObject field = fields.getJSONObject(i); String name = field.getString("name"); Object value = data.get(name); boolean required = field.getBooleanValue("required"); if (required && (value == null || String.valueOf(value).trim().isEmpty())) { throw new BizException(field.getString("label") + "不能为空"); } if (value == null) { continue; } String type = field.getString("type"); switch (type) { case "number" -> validateNumber(field, value); case "select", "radio" -> validateOptions(field, value); case "text", "textarea" -> validateLength(field, value); case "date", "datetime" -> validateDateTime(field, value); default -> throw new BizException("未知字段类型: " + type); } } } }我特别想强调一个坑:前端的校验只是用户体验,后端的校验才是安全底线。配置化的表单意味着前端可能被绕过,攻击者直接POST脏数据到接口。所以这套通用校验逻辑必须放在后端,而且是强制执行的,不能依赖前端判断。实际开发中我还加了一个切面,用注解@FormValidate(formKey = "leave_request")打在Controller方法上,调用前自动完成校验,代码更干净。
这里还有一个设计细节:校验规则只支持min/max/maxLength这些基础约束。跨字段的组合校验怎么办?比如“结束时间必须晚于开始时间”。我的方案是预留了一个自定义校验code字段,在规则里写"rule": "endTimeGreaterThanStartTime",后端实现一个校验函数注册表,按code分发到具体逻辑。这算是一个折中方案,低频的特殊校验走扩展点,高频的标准校验走通用逻辑,兼顾灵活性和维护成本。
3. 审批流引擎实现:节点、条件与流转
3.1 审批流本质上是一个状态机
把表单引擎搞定之后,审批流其实简单了一半。审批流的本质是状态机:数据从某个状态开始,根据操作者的动作(同意/驳回)沿着预定义好的路径迁移到下一个状态,直到终态。传统做法是在代码里硬编码状态流转——写一堆if-else,团队小还好,流程多了维护起来就是泥潭。
我采用的方案是流程驱动数据:把流程定义也做成JSON配置,节点之间通过next字段连接,通过type字段区分节点类型。审批的时候引擎只需要负责一件事——根据当前节点配置和操作结果,找到下一个该去的节点,更新表单状态。这套逻辑可以说是整个审批流引擎的全部核心。
状态机思路的优势是显式和可控:每一步的迁移都记录在案,审批历史可追溯;流程变更时只需要改配置,不用改状态枚举和流转代码;任何时刻你都能知道一条流程实例在哪个节点、经历过什么。
3.2 流程定义的JSON设计:节点类型与跳转规则
流程定义的JSON结构我反复改过几个版本,现在是这个形态:
{ "processKey": "leave_process", "processName": "请假审批流程", "version": 1, "startNode": "start", "nodes": [ { "id": "start", "type": "start", "next": "manager_approve" }, { "id": "manager_approve", "type": "approve", "name": "经理审批", "assignee": { "type": "role", "value": "role_dept_manager" }, "actions": ["agree", "reject"], "next": "branch_days", "rejectTo": "start" }, { "id": "branch_days", "type": "condition", "name": "请假天数判断", "conditions": [ { "expression": "days > 3", "next": "vp_approve" }, { "expression": "default", "next": "hr_cc" } ] }, { "id": "vp_approve", "type": "approve", "name": "分管副总审批", "assignee": { "type": "role", "value": "role_vp" }, "actions": ["agree", "reject"], "next": "hr_cc", "rejectTo": "manager_approve" }, { "id": "hr_cc", "type": "cc", "name": "人力备案", "assignee": { "type": "role", "value": "role_hr" }, "next": "end" }, { "id": "end", "type": "end" } ] }节点类型目前就这么几种:start(开始)、approve(审批)、condition(条件分支)、cc(抄送)、end(结束)。approve节点需要配置assignee决定谁能审批,actions决定支持哪些操作(同意/驳回),next指向审批通过后的下一个节点,rejectTo指向驳回后回到哪。cc节点不阻塞流程,只是记录通知,自动往下走。condition节点是纯逻辑节点,从表单数据里取字段做判断,命中哪个条件就走哪条分支。
assignee支持两种模式:type为user时指定具体人,type为role时指定角色,运行时动态解析角色的所有成员。为什么要设计成动态解析而不是直接把审批人写死在配置里?因为业务中经常要求“请假由直属上级审批”,上级是变化的,角色稳定,所以配置里绑定角色、运行时查用户信息才是正解。这一点后面讲坑的时候还会细说。
3.3 流转引擎核心实现:提交、同意、驳回
流程实例表记录每条业务的流转状态,核心字段是current_node,指向流程定义里的节点ID。整体流转服务我封装了一个ProcessEngine类,最关键的是approve方法,我简化出核心逻辑:
public void approve(Long processInstanceId, String userId, String action, String comment) { ProcessInstance pi = processInstanceMapper.findById(processInstanceId); ProcessDefinition pd = processDefinitionMapper.findByKeyAndVersion( pi.getProcessKey(), pi.getVersion()); JSONObject flow = JSON.parseObject(pd.getFlowJson()); JSONArray nodes = flow.getJSONArray("nodes"); JSONObject currentNode = findNodeById(nodes, pi.getCurrentNode()); checkAssignee(currentNode, userId); // 记录审批痕迹 nodeRecordMapper.insert(new ProcessNodeRecord( processInstanceId, currentNode.getString("id"), currentNode.getString("name"), userId, action, comment)); if ("reject".equals(action)) { moveToNode(pi, currentNode.getString("rejectTo")); formInstanceMapper.updateStatus(pi.getFormInstanceId(), 3); return; } // agree:找下一个节点 String nextNodeId = currentNode.getString("next"); JSONObject nextNode = findNodeById(nodes, nextNodeId); // 条件节点:动态计算走哪条分支 if ("condition".equals(nextNode.getType())) { JSONObject formData = loadFormData(pi.getFormInstanceId()); nextNodeId = evalCondition(nextNode, formData); } if ("end".equals(nextNodeId)) { moveToNode(pi, nextNodeId); formInstanceMapper.updateStatus(pi.getFormInstanceId(), 2); } else { moveToNode(pi, nextNodeId); formInstanceMapper.updateStatus(pi.getFormInstanceId(), 1); } }整体流程不复杂:找到当前节点,校验当前用户身份,根据动作决定去下个节点还是退回前面节点。记录审批日志是对账和追溯的基础,必须每次操作都落一条。到end节点时,表单状态同步改成2(通过),驳回时改成3(驳回)。
提交创建流程实例就走另一个方法:先校验表单数据的合法性,然后插入form_instance和process_instance,把current_node设为start节点的next,状态置为审批中。这个方法的骨架在实操章节里演示完整调用链路。
3.4 条件分支:如何安全地解析表达式
条件分支是审批流里最容易翻车的点。我上面写的condition节点里用了字符串表达式"days > 3",初步实现是自己解析这个表达式,拆分出字段名、操作符、比较值,然后从表单数据里取值做比较。这种方式对付简单的数值比较没问题,但遇到字符串等于、包含、多条件与或就力不从心了。
所以在生产环境里我推荐直接用成熟的表达式引擎,比如Aviator或者Spring自带的SpEL。Aviator的语法简单、性能好,非常适合这种配置化场景。改造后的条件节点判断逻辑大概长这样:
private String evalCondition(JSONObject conditionNode, Map<String, Object> formData) { JSONArray conditions = conditionNode.getJSONArray("conditions"); for (int i = 0; i < conditions.size(); i++) { JSONObject cond = conditions.getJSONObject(i); String expression = cond.getString("expression"); if ("default".equals(expression)) { return cond.getString("next"); } // Aviator 表达式直接求值,字段值从 formData 里取 Boolean matched = AviatorEvaluator.exec(expression, formData); if (matched != null && matched) { return cond.getString("next"); } } throw new BizException("条件节点未命中任何分支"); }用Aviator之后,表达式可以写成"days > 3 && leaveType == 'sick'",甚至支持函数调用,表达能力不知道强了多少个档次。而且Aviator的exec方法直接接收一个Map作为变量环境,表单数据天然就能喂进去,集成起来非常丝滑。这个替换成本极其低,但收益巨大,强烈建议直接上表达式引擎,别自己写字符串解析。
4. 5分钟实操:配置一套请假审批流的完整过程
4.1 准备基础表结构与工程骨架
要复现这套方案,首先把前面提到的五张核心表建好:form_definition(表单定义)、form_instance(表单实例)、process_definition(流程定义)、process_instance(流程实例)、process_node_record(节点审批记录)。我前面已经给了form_instance的建表脚本,其余几张结构类似,关键点在流程定义和表单定义都要有version字段,用(form_key, version)做联合唯一键,这样才能支持同一套配置多版本并存。
工程结构方面,SpringBoot标准分层即可:Controller层暴露提交、审批、查询接口;Service层放表单校验(FormSchemaValidator)和流程引擎(ProcessEngine);Mapper层就是普通的MyBatis Plus接口。我这里特意不用任何重量级工作流引擎,就是想让核心依赖保持最小——SpringBoot Web + MyBatis Plus + Fastjson2 + Aviator,足够了。
4.2 配置请假表单:插入一条JSON配置
把2.1节那段请假单的JSON配置塞进form_definition表,SQL就长这样:
INSERT INTO form_definition (form_key, form_name, version, schema_json, status) VALUES ('leave_request', '请假申请单', 1, '{"formKey":"leave_request","formName":"请假申请单","version":1,"fields":[...]}', 1);这一步在真实管理后台里可以做成一个“表单配置”页面,把JSON粘进去就算创建成功。我一开始是直接写SQL配置的,后来做了一个极简的可视化编辑器,本质上就是个JSON编辑器加校验提示,体验好很多。表单一旦发布,前端动态渲染组件就能根据这个配置画出页面来,不需要任何前端代码改动。
4.3 配置审批流程:插入流程定义
再把3.2节的流程定义JSON插入process_definition表:
INSERT INTO process_definition (process_key, process_name, version, flow_json, status) VALUES ('leave_process', '请假审批流程', 1, '{"processKey":"leave_process","processName":"请假审批流程","version":1,"startNode":"start","nodes":[...]}', 1);到这里“5分钟配置”的重头戏其实已经完成了——建表脚本只要执行一次,后续每次新流程都是两条INSERT语句的事。我第一次在项目里给同事演示的时候,他们盯着这两条SQL看了半天,问“就这样?”,我说对,就这样。真正让表单和流程跑起来的代码早就写好了,配置只是喂数据而已。
4.4 完整调用链路:从提交到审批通过
配置好了,我就用请假审批来演示一遍完整链路。假设业务方要两天事假,操作流程如下:
第一步:提交请假单。前端调用表单提交接口,或者直接调后端接口模拟:
curl -X POST http://localhost:8080/api/form/leave_request/submit \ -H "Content-Type: application/json" \ -d '{ "formKey": "leave_request", "data": { "applicant": "张三", "leaveType": "personal", "startTime": "2025-06-10 09:00:00", "endTime": "2025-06-11 18:00:00", "days": 2, "reason": "家里有急事" } }'后端ProcessEngine.submit方法会做三件事:加载表单定义并全字段校验、把data写入form_instance、读取流程定义并创建process_instance,初始节点指向manager_approve。接口返回processInstanceId,前端靠这个ID跳转审批进度页。
第二步:经理审批通过。经理账号登录后看到待办,点“同意”:
curl -X POST http://localhost:8080/api/process/1001/approve \ -H "Content-Type: application/json" \ -d '{"userId": "lisi", "action": "agree", "comment": "同意"}'引擎发现当前节点是manager_approve,操作是agree,就读取next指向branch_days。由于是condition节点,引擎加载表单数据,计算"days > 3"表达式,2不大于3,走了default分支,进入hr_cc抄送节点。抄送节点不需要人工操作,引擎自动继续流转到end,流程结束,表单状态变为2(通过)。
第三步:查看审批历史。
curl -X GET http://localhost:8080/api/process/1001/history返回的列表里能清楚看到:张三提交、李四同意,每条记录都有操作人、动作、意见和时间,这些全部来自process_node_record表。
第四步:演示驳回场景。如果经理点击“驳回”,引擎会把current_node改回start的rejectTo指向。这里重点是流程定义了一个rejectTo字段,明确指定驳回回到哪个节点,可以是提交节点,也可以是中间某个审批节点,配置错误的话,审批会乱套。
用这套流程跑通一遍之后,我确信两个结论:第一,中小型审批流程用JSON配置远比硬编码高效;第二,所谓Low-Code,真正要打磨的是引擎的健壮性和配置的易用性,而不是堆一堆拖拽组件。
5. 踩坑实录:动态表单与流程引擎的常见问题与排查
5.1 动态数据的查询与性能问题
表单数据存JSON字段,最直接的坑就是查询。同事第一次接我的代码,写了个“查所有事假申请单”的需求,他下意识想了一句SQL:SELECT * FROM form_instance WHERE JSON_EXTRACT(data_json, '$.leaveType') = 'personal',然后发现数据量到十万级之后这查询越来越慢。
解决办法也不复杂:把高频过滤字段提取成generated column,然后加索引。MySQL 8支持这种用法:
ALTER TABLE form_instance ADD COLUMN leave_type VARCHAR(20) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(data_json, '$.leaveType'))) STORED, ADD INDEX idx_leave_type (leave_type);创建之后,查询就可以直接WHERE leave_type = 'personal',走索引,性能跟普通字段没差别。原则是:哪些字段频繁用于筛选排序统计,就给哪些字段建generated column,别想着给所有字段建,过度设计会让表结构看起来很奇怪。
5.2 流程版本变更与存量实例的处理
业务流程改了怎么办,这是大家最关心的。比如原来请假流程是“经理审批——结束”,现在改成“经理审批——人力审批——结束”。如果直接把流程定义update掉,正在流转的实例就会出问题,因为它们记录的是旧版本节点,新旧配置对接时大概率报错。
我的解决思路是版本隔离:每次流程修改都插入一条新版本记录(version递增),而不是更新原记录。流程实例创建时把当时的版本号记录下来,之后流转就一直按这个版本走。这跟发布系统里灰度发布的意思差不多,旧实例走旧逻辑,新实例走新逻辑,互不干扰。
表单定义也一样。业务方说“请假单加一个紧急程度字段”,我就在form_definition里插入version=2的新定义,旧的请假单实例继续用version=1的Schema校验。前端动态渲染的时候要做版本匹配,别用最新的配置渲染老数据。
5.3 审批人身份校验与动态角色解析
这个坑我踩得很深。最初的设计里assignee直接写死了一个用户ID,结果业务方说“张三调部门了,以后经理审批不该是张三了”,我赶紧去找所有相关配置改了一通,气得想拍桌子。
后来统一改成assignee只配置角色代码,审批的时候先用角色代码查当前有哪些用户,再跟当前登录用户比对。角色下可能有多个人,那还要有一个策略:是任意一个角色成员审批即可,还是需要会签。我目前实现的是“任一成员即可”,会签加一个type字段区分。企业级的复杂审批(或签、会签、加签、转办)是一个无底洞,如果不是硬性需求,建议先不要往深处做。
5.4 条件表达式过于复杂的问题
有些同事看到条件节点之后非常兴奋,开始写“days > 3 && leaveType == 'sick' || applicant == 'boss'”这种六个条件的表达式。表面上看Aviator处理这些很轻松,但实际问题在于:表达式越复杂,配置的可维护性就越差,等业务方自己也想看配置的时候,没人能看懂这串逻辑。
我踩过这个坑之后定了一条规矩:条件节点只做简单判断,一到两个条件足矣;复杂的多条件判断宁可拆成多级条件节点,也不要揉在一个大表达式里。多级条件节点的好处是流程图的节点可以命名清晰,比如“天数超过3天”和“是否病假”是两个独立节点,一看就知道业务意图,排查问题的时候不知道省了多少时间。
5.5 速查表:几个高频问题一眼定位
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 提交表单时提示“XX不能为空” | 前端没传该字段 | 看表单定义required字段,对照请求json |
| 审批操作报“当前用户无权限” | assignee配置与实际角色不匹配 | 查角色表,确认该角色下是否有当前用户 |
| 流转到下一步后表单状态没变 | 条件节点表达式返回false且无default分支 | 检查表达式字段名是否与表单字段完全一致 |
| 新流程配置后老接口404 | 可能formKey或processKey发错了 | 确认配置表里的key与接口参数严格一致 |
| 驳回后状态到了结束节点 | rejectTo配置成了end | 回溯环节:检查rejectTo是否指向start或中间节点 |
排查工具我建议直接看process_node_record表,按流程实例ID倒序查一遍,每一步走到哪看得清清楚楚,比对着代码猜状态快得多。还有个小技巧,流程引擎的每个流转方法入口打一条info日志,把processInstanceId、当前节点、目标节点、操作人打出来,线上问题定位效率翻倍。
这套方案我在实际项目里用了一年多,给我最直观的感受是:业务方再提“加个审批流”的时候,我不慌了。配置模板和引擎代码都是现成的,新流程从提需求到上线跑通,真正就是一杯茶的功夫。如果有朋友正在自己的项目里纠结要不要做低代码化改造,我建议别急着买平台,先按这套JSON驱动的方式搭一个最小引擎试试,1000行代码以内你就知道它能给你省下多少事了。
最后再分享一个小技巧:表单配置和流程配置都建议做成带diff对比的版本管理,改动之前先备份一个版本。有一次我在测试环境改流程,手一抖把next指错了,整个流程直接走进死胡同,靠版本回滚才救回来。配置化的工具,数据就是人命,备份意识一定得有。