一套审批流要写多少代码:13 个框架的接入 diff 我数了一遍,最少 422 行,最多 1798 行

问"加一套审批流要写多少代码",很少有人能给出可信答案。原因不是大家懒得算,而是这句问话里其实混着三件事:给现有项目接一个现成引擎、从零做一套带审批的后台、自己造一个引擎。这三档的工期差两个数量级,却被同一句话压在一起。

这次我把第一档的数拿实了:jeeflow 已经在 13 个框架上各接入过一次,每一层的 diff 都还在 git 里。我把它们一个一个数了一遍,包括我自己数错的两次。

一、先交代我数错的两次

第一轮,我用集成壳里留着的 master 引用取合并基线。结果 NestJS 报出 7,077 行------那里面大半是框架自己这两个月的演进,跟"接入审批流"没有一行关系。基准必须从隔壁基础仓重新 fetch 之后再取。

第二轮,基准对了,git diff --shortstat 吐出来 14,762 行,比第二名整整多一个数量级。我盯着这个数看了半分钟才反应过来,去翻文件列表:

bash 复制代码
package-lock.json                                    14022 +
src/modules/wf/core/wf-message.listener.ts              340 +
src/modules/wf/controller/wf.controller.ts               21 +-

14,022 行是一个锁文件,占掉 95%。它确实是这次提交写进去的,但没有任何人会在排期时把它算成"审批模块的开发量"。

第三轮才是下面用的口径:只统计新增和修改的文件(删除单独记一笔,不然"清掉旧引擎"会被记进接入成本),再剥掉锁文件、dist/ 这类产物和测试代码。

按这个口径,NestJS 那 14,762 行缩成 422 行,7 个源文件,最大的一个是 340 行的消息监听。虚报了 35 倍。

口径不先立住,后面所有数字都是噪音。所以这张文里每一个数,你都可以在任意一个集成仓里 git fetch 对应框架仓库、git diff --numstat <merge-base> HEAD 复现一遍。

二、13 个框架,422 到 1,798 行

框架 实现代码 消息监听 HTTP 转发 SPI 对接 装配与工厂 字典与元数据
NestJS 422 340 19 0* 63 0
C# ASP.NET Core 702 206 99 159 23 103
Django 735 207 50 0* 266 202
FastAPI 750 276 52 0* 414 8
Flask 793 276 49 0* 441 27
Hertz 1,013 299 222 100 365 13
Gin 1,014 299 224 100 365 13
Java boot3 1,018 317 162 395 118 26
GoFrame 1,071 298 242 103 385 0
Java boot2 1,078 317 164 441 130 26
Java boot4 1,145 317 162 441 130 95
Laravel 1,371 261 159 421 349 0
Rust Salvo 1,798 752 135 264 634 0

中位数 1,014 行,最厚与最薄差 4.3 倍。

换成原始 git diff(把测试和构建元数据算回来),同一批壳落在 702 到 1,852 行:最薄还是 C# 的 12 个文件,最厚还是 Salvo 的 21 个文件。

* 的几栏是分类假象,不是没写。Python 三栈和 NestJS 把用户、组织架构这些对接逻辑直接写进了 jeeflow_factory.py 这类装配文件里,按文件名归类就成了 0。它们确实在,只是没单独成文件------这本身就是个信息:文件怎么切,由语言生态的习惯决定;要写多少逻辑,由引擎留给你填的口子决定。

三、这一千行到底是什么

把最厚的 Java boot4 和最薄的 C# 逐文件摊开对照,会发现两边其实是同一套东西,只是切法不同。

boot4 的 1,145 行里:一个 WfMessageProcessEventListener 317 行负责听引擎事件写站内信;一个 WfFlowController 162 行把 40 多个 action 全转出去;WfProcessDictService 122 行和 MldongMetaProvider 112 行给流程设计器供字典与表单元数据;三个 provider 共 207 行回答"当前用户是谁、他部门领导是谁、怎么按名字搜人";两个 config 130 行把引擎实例挂进 Spring 容器;剩下 95 行是 5 个 pom.xml 的模块接线。

C# 的 702 行做的是同一件事:监听 206、四个 SPI 合并成一个 159 行的 WfProviders.cs、引擎服务 111、Minimal API 转发 99、字典 60、DI 注册 19。

比 boot4 少的 443 行,省的不是逻辑,是文件。 Java 要一个类一个文件、要有包名、要有独立的配置类和注册器;C# 一个文件塞四个类就行。

四、最厚的那块不是流程,是发消息和找人

把 13 个壳按职责堆起来看,绿色那段(消息监听)在每一行里都占着一大块:206 到 752 行,多数栈落在 28%~42%,NestJS 高到 81%、Laravel 低到 19%。

引擎本体那一块呢?0 行。因为流程流转的代码根本不在你仓库里。

原因很直接:引擎故意不发消息。 它的输入是"谁在哪个节点做了什么",输出是"下一个该谁做",不需要知道你们公司用站内信、企业微信还是钉钉机器人,也不该知道。所以这一层是空的,等你填。

而这一层从来填不便宜。任务创建要给办理人发一条,办理完成要给发起人回一条,抄送要按 N 个人各发一条,驳回要换文案,超时要补提醒,然后还得管已读未读、跳转链接、消息分类。写少了漏提醒被业务方追着问,写多了同一件事轰炸三遍。Salvo 那 752 行就是这么来的。

"找人"是同一个道理。boot 系和 Laravel 那 400 行 SPI 全花在这上面------引擎只肯接受一个结果:"这个节点该谁办"。至于这个人是不是副职、他领导出差了要不要往上跳一级,是你的组织树说了算。

上面那一千多行换到界面上,就是这张图:一个 Go 后端,六条待办排在那儿,会签、一票否决、按比例通过、报销各占一行,点「办理」进去填表单写意见。这张截图来自公开演示站 jeeflow-demo.mldong.com/?lang=go,右上角切到 Java、Python、Node、PHP、Rust、MoonBit、C# 任何一格,左边这一列待办和中间这个办理抽屉都不用改一行代码------因为那 13 个壳里的转发层,最终都收敛到同一个门面入口。

五、第二笔账:换成引擎之后少了 11,226 行

boot2 和 boot3 这两个壳特殊:它们本来自带一套内置工作流模块,切到 jeeflow 做的是减法

复制代码
新增 9 个、修改 3 个   →   +1,078 行
删除 180 个文件        →  -11,226 行

git diff --shortstat 报 -11,236,多出的 10 行来自修改文件里的删除,按被删文件累加是 11,226。)

删掉的东西值得逐类看一眼,因为它说明了"自研一套审批流"的真实成本在哪:

删掉的类别 文件 行数
Service 层 19 3,494
引擎内核与拦截 27 1,732
模型与解析器 27 1,233
实体与枚举 19 1,034
VO 与 DTO 31 1,006
Controller 8 945
Mapper 与 XML 18 649
其它(工具、调度) 22 580
处理人解析器 9 553

引擎本体只占 3,518 行(31%),剩下 7,708 行(69%)全是围着它写的脚手架------Service、Mapper、VO、DTO、实体、枚举。

这才是"自研很贵"的准确说法。贵的不是那台状态机,是它周围必须长出来的一整圈数据访问和接口皮。而这圈东西,boot2 和 boot3 当年是各自维护一份的(boot3 那笔删除是 -11,265 行),同一套语义写两遍,还得保证行为一致、两边一起修 bug。

六、第三档:自己造一台引擎要多久

我把 jeeflow-csharp 的提交时间线拉出来当尺子。那是我最省的一次,因为前面已经有七种语言垫着:

复制代码
09-06 01:07  M0  仓骨架 + 45 个 action 清单 + 5 个技术验证
09-06 01:33  M1  引擎核心 + 内存仓储,100 个用例绿
09-06 02:00  M2  MySQL 仓储 + 事务模板,真库 120 用例绿
09-06 02:53  M3  持久化与门面 45 个 action,153 用例全绿
09-06 03:07  M4  demo 起服务 + 冒烟 20/20
09-06 03:28  M5  发版就绪收口

2 小时 21 分,一台新语言的工作流引擎。

然后从 09:31 到 12:01,又花掉两个半小时处理跟引擎一点关系都没有的事:发布流水线里 API Key 的输出名大小写写错导致 401、宿主机内存紧张 CoreCLR 起不来要压 GC 上限、老版本 Docker 的 seccomp 拦掉运行时系统调用、CI 机器上没有 Python 逼着把冒烟脚本改成纯 shell。

所以第三档的账要这么算:引擎本体两小时能立起来,把它变成别人能直接用的东西再搭半天进去,而这还只是一台引擎。 流程设计器、审批前端、移动端、八种语言之间的契约对齐和文档,都是另外的账。

七、三档工期,以及怎么自己动手验

你在哪一档 量级 真正花时间的是
① 已有项目接现成引擎 千行级,一到两天 消息监听和组织架构对接,不是流程
② 从零要一套带审批的后台 一周级 权限、组织、表单、附件这些引擎之外的东西
③ 自己造引擎 月级起,且要一直养 八种语义对齐、跨版本回归、别人提的 issue

第一档最实在的验证方式不是读我这篇文章,是在你自己的机器上跑一遍。十三套后端都配了一键部署脚本,挑你熟的栈,一条命令起一个带完整审批流的环境:

bash 复制代码
curl -fsSL https://www.mldong.com/deploy/mldong-boot4-jeeflow/deploy.sh | bash

Go 系换 mldong-goframe-jeeflow / mldong-gin-jeeflow / mldong-hertz-jeeflow,Python 系 mldong-fastapi-jeeflow / mldong-flask-jeeflow / mldong-django-jeeflow,其余 mldong-nestjs-jeeflowmldong-laravel-jeeflowmldong-salvo-jeeflowmldong-csharp-jeeflowmldong-boot2-jeeflowmldong-boot3-jeeflow 同名同规则。起来之后登录进去,流程设计、发起、待办、审批、抄送、统计全都在------就是第四节那张图的那一屏。

参考资料

相关推荐
GreenTea10 小时前
GrokBot 核心成员 Lauren Tan:每月交付 2000 个 PR 的人,是怎么用 AI 的
前端·后端·架构
码事漫谈10 小时前
FDE:一个缩写,两种命运
后端
Hrain-AI10 小时前
多 Agent 并行不打架:worktree 隔离与反馈回流落地(附脚本)
网络·数据库·人工智能·架构
名字还没想好☜11 小时前
Go context.AfterFunc 实战(Go 1.21):context 一取消就自动跑清理,告别手写 goroutine 监听 Done
后端·golang·go
明月_清风12 小时前
为什么最近开始关注 JEV?几个实战案例告诉你答案
人工智能·后端
GetcharZp12 小时前
5 分钟拥有自己的 S3:RustFS 上手(MinIO 的 Rust 替代,Apache 2.0 可商用)
后端
行百里er13 小时前
Redis 版本演进、新特性与协议那些事儿
redis·后端
Joy T14 小时前
Spring AI 2.0 Agent 进阶:Memory、State 与 Context Engineering 常见技术全景
java·人工智能·后端·spring·agent入门·agent state