从"复制粘贴改一天"到"5分钟出全套":我们团队的B端管理代码是怎么自动生成的
背景:我们维护一个权益中台项目,技术栈是 Spring Boot + MyBatis,代码按固定规范分
dao和management两个模块。去年一个Q,B端管理需求集中爆发,4张新表 + 3次字段变更,手写全套代码加文档花了整整两天。后来我们基于团队规范沉淀了一套生成逻辑,把同样的工作量压到了5分钟。这篇文章把背后的真实决策和完整过程摊开讲。
一、背景:我们项目的代码分层有多"规范"
我们项目的代码分层很规范,规范到每次建表都要产出十几个文件。
以一张典型的B端业务表 xxx_xxx_xxx_order_route 为例,从建表SQL到能联调,必须产出:
DAO 模块(xxx-dao):
EquityXxxOrderRoute.java—— Domain 实体类,带@Data和SerializableEquityXxxOrderRouteExample.java—— 查询条件类,内部嵌套GeneratedCriteria、Criteria、Criterion三个类EquityXxxOrderRouteMapper.java—— Mapper 接口,带@Repository、@DataSource("MYSQLXXX")、@Mapper,还要手写分页方法selectByExampleWithPageEquityXxxOrderRouteMapper.xml—— 对应的 XML,含BaseResultMap、Base_Column_List、增删改查 + 分页 SQL
Management 模块(xxx-management):
5. EquityXxxOrderRouteDto.java —— DTO,继承 RequestEntity 支持分页
6. XxxOrderRouteService.java —— Service,至少包含 create、update、delete、queryList、getById 五个方法
7. XxxOrderRouteController.java —— Controller,五个接口,每个都要打 @UmeServiceRegister、写 log.info、包 try-catch
8. XxxOrderRouteControllerTest.java —— 单测,继承 BaseTest,每个 Controller 方法对应一个 @Test 方法,只跑主流程
9. 接口文档 —— 飞书云文档格式,带服务ID(如 xxx-management;XxxOrderRouteController.createXxxOrderRoute)
一张表,9个文件。 而且命名规则是硬性的:表名去掉 xxx_xxx_ 前缀,取第一个下划线前的部分作为模块目录(xxx),Domain 类名要加 Equity 前缀,Service 和 Controller 要去掉 Equity 前缀……
这个规范本身没问题,但全靠人肉执行时,错误率极高。我统计过团队里三次手写的情况:有人把 XxxOrderRouteService 写成了 OrderRouteXxxService,Code Review 返工;有人忘了给 Mapper 加 @DataSource("MYSQLXXX"),本地能跑,测试环境连不上库;最离谱的一次,Example 类里少生成了两个字段的 andXxxEqualTo 方法,导致管理后台的筛选条件失效,上线后才发现。
去年Q3有一个真实需求:B端订单路由管理功能,涉及4张新表,中途产品又改了两次字段(varchar 扩 TEXT、加 status 字段)。我对手写全流程做了计时,一张中等复杂度的表平均耗时 4.5 小时,4张新表就是 18小时,再加上3张表的字段变更同步,总共两天半。而且这全是零业务含量的纯模板代码。
我当时的判断是:这件事的重复度超过 90%,必须自动化。
二、做法:把团队规范编码成规则,让生成逻辑自动执行
我们没有用 MyBatis Generator,原因很实际:它的输出格式和我们团队的规范差距太大,生成后几乎要全改一遍;它不生成 Management 层的 DTO、Service、Controller、单测、接口文档;也不支持 ALTER 场景的增量更新。
我们的思路是:不是"生成代码",而是"把团队规范编码成规则,自动执行"。
2.1 输入:只给 SQL,别的什么都不用填
生成逻辑只读一个 .sql 文件,里面可以混写 CREATE TABLE 和 ALTER TABLE。开发本来就要写这份 SQL 给 DBA Review,不需要额外输入。
2.2 命名规则:把团队约定硬编码进生成逻辑
这是最关键的设计。我们团队的命名规则是明确的,直接写成规则:
plain
表名:xxx_xxx_xxx_order_route
↓ 去掉前缀 xxx_xxx_
↓ 取第一个下划线前的部分作为模块名 → "xxx"
↓ 剩余部分转驼峰 → "OrderRoute"
Domain 类名:Equity + Xxx + OrderRoute → EquityXxxOrderRoute
Service 类名:Xxx + OrderRoute + Service → XxxOrderRouteService
Controller 类名:Xxx + OrderRoute + Controller → XxxOrderRouteController
目录:management/dto/xxx/、management/controller/xxx/、management/dto/xxx/service/
这个规则一旦在生成逻辑里写死,再也不会出现 OrderRouteXxxService 这种命名错误。
2.3 输出目录:按模块真实路径生成
不是按文件类型分目录(entity/、mapper/、service/),而是按项目真实包结构直接输出:
plain
output/
├── dao/
│ ├── domain/EquityXxxOrderRoute.java
│ ├── domain/EquityXxxOrderRouteExample.java
│ ├── mappers/EquityXxxOrderRouteMapper.java
│ └── resources/mappers/EquityXxxOrderRouteMapper.xml
└── management/
├── dto/xxx/EquityXxxOrderRouteDto.java
├── dto/xxx/service/XxxOrderRouteService.java
├── controller/xxx/XxxOrderRouteController.java
└── test/controller/xxx/XxxOrderRouteControllerTest.java
开发可以直接 cp -r 进项目,改一下包名声明就能编译通过。
2.4 两个场景的不同处理
新建场景(CREATE TABLE): 生成全套9个文件,Example 类里的 Criteria 方法按字段类型自动判断(String 字段生成 andXxxEqualTo + andXxxLike,数值字段生成 andXxxEqualTo + andXxxGreaterThanOrEqualTo)。
修改场景(ALTER TABLE): 生成逻辑先按表名去固定目录里定位现有文件,然后做增量修改。ADD COLUMN 时,在 Domain、Example、DTO、XML 的 BaseResultMap 和 Base_Column_List 里追加字段;MODIFY COLUMN 时,更新类型映射(如 varchar → TEXT,XML 里的 jdbcType 从 VARCHAR 改为 LONGVARCHAR);DROP COLUMN 时做全链路清理。同时自动生成 alter.sql 和回滚脚本,以及变更说明文档。
2.5 接口文档:飞书格式自动生成
这是最容易被忽略但上线前必须有的产出。生成逻辑按固定模板输出 Markdown:接口名称统一前缀"会员权益中心-";请求URL固定格式 /api/saas-manager/rest/xxxXxxController/createXxxXxx;服务ID严格按 xxx-management;类名.方法名 生成;请求参数表格从 SQL 注释自动提取。
以前手写文档,服务ID里的类名和方法名经常对不上,导致网关路由注册失败。自动生成后,这个问题彻底消失。
三、结果:同一批需求,两种做法的对比
用生成逻辑重做那批需求,4张新表加3张表字段变更,运行5秒,人工Review 3分钟,总共不到5分钟。而手写模式下,4张新表全套代码加文档大约需要18小时,3张表的字段变更同步大约需要4小时,合计两天半。
效率提升超过99%,但这还不是最重要的。更重要的是错误率的变化:手写时平均每张表会出现1到2处命名规范错误,Example类的查询方法经常遗漏,单测经常被漏写,接口文档普遍滞后一到两天,服务ID偶尔写错导致网关路由注册失败。生成逻辑跑完后,这些问题全部归零——命名规范100%正确,Example方法按字段类型自动生成不会遗漏,单测覆盖率达到100%且与代码同步产出,接口文档零滞后,服务ID严格按规则生成不再出错。
实际使用中,生成的代码拷进项目后,我只改了包名和 import 路径,其余一行未动,直接编译通过、单测跑绿。这在手写时代是不可想象的——Mapper XML 和 Example 类几乎每次都有小错误要调。
四、这件事能不能借鉴到你手上
可以。核心判断标准只有一个:你的项目里,从 SQL 到代码到文档,是不是有一条"重复度极高、规则极度固定"的流水线?
如果是,自动化收益就很大。建议分三步走:
- 统计真实时间:拿最近两次建表需求,记录手写每个环节的真实耗时和返工次数。用数据说话,说服自己(和 Leader)这件事值得投入。
- 最小化验证:不要一上来就生成全套。先只自动化 Domain + Mapper 接口,这两部分规则最固定、出错代价最高(运行期才暴露)。跑通后,再逐步加入 Service、Controller、单测、文档。
- 把规范写死,不要追求通用:不要想着做一个"万能代码生成器"。就写一段贴合你们项目包名、命名规则、注解规范的生成逻辑。代码量不大,维护成本极低,而且输出就是团队想要的格式,不需要二次调整。
五、写在最后
这套生成逻辑我们现在还在用。它没有任何高深技术——没有 AST 解析、没有模板引擎,就是读取 SQL 文本、提取表结构元数据、按规则拼接生成 Java 代码和 Markdown 文档。
但它的价值在于:把"团队规范"从"靠人记住、靠人执行"变成了"靠生成逻辑强制执行"。
规范不会因为它写在 Confluence 里就被遵守,但规范如果写在生成逻辑里,产出的代码一定是规范的。
如果你现在每次建表还在手写全套 CRUD,我建议你花一个下午试试。一个下午的投资,换以后每次需求从两天变成五分钟,这笔账怎么算都值。