_model 后缀?你观察得完全对:默认 gozero 风格下,goctl 吐出来的文件名是 usermodel_gen.go,而不是 user_model.go,更不会是 xxx_model.go 这种「带 _model 后缀」的样子。这篇把「为什么没有」讲到底——核心是 goctl 怎么拼基础名、以及 --style 怎么二次格式化它。
_model 这个下划线被「风格格式化器」吃掉了goctl 并不是「给 model 文件统一加 _model 后缀」;相反,它先把「表名 + _model」拼成一个基础名,再把这个基础名整个丢进 FileNamingFormat 按 --style 重新拼写。默认 gozero 是「无分隔符连写」,于是下划线没了 → usermodel_gen.go。
记住这三条就够了:
① model 文件的基础名 = 表名 + "_model"(goctl 内部拼的),不是「文件名 + 后缀」。
② 这个基础名会整体过一遍 --style 格式化:gozero 把所有词连写 → 下划线消失;go_zero 才保留下划线。
③ 所以默认生成的文件是 usermodel_gen.go(连写),只有当你显式用 --style go_zero 时才会变成 user_model_gen.go(带下划线)。
以一张叫 user 的表、一个叫 login 的 @handler 为例,什么参数都不加(即默认 gozero) 跑出来的文件:
_model、_handler、_logic 这种「带下划线的后缀」。唯一出现的 _gen 是真后缀(详见第 ④ 节),它跟 style 无关、永远在。三个目录(model / handler / logic)的「基础名」来源完全不同,这是理解「为什么没下划线」的关键。下面三段都来自 go-zero 源码。
表名 + "_model"在 goctl model 的生成代码里,文件名是这样来的:
modelFilename, _ := format.FileNamingFormat(g.cfg.NamingFormat, fmt.Sprintf("%s_model", tn.Source())) // 表名 + "_model" 拼成基础名 name := util.SafeString(modelFilename) + "_gen.go" // 末尾再接 _gen.go
注意:goctl 是先自己把 _model 拼进基础名的,这张表叫 user,基础名就是 user_model。这个 _model 是「内部拼接」出来的,不是给文件加的「类型后缀」。
handler名 + "Handler"/"Logic"(驼峰连写)在 goctl api 的 gogen 里,handler、logic 的文件名基础串是驼峰拼接的(没有下划线):
// @handler login → 基础名 loginHandler(login + Handler 连写) // @handler login → 基础名 loginLogic (login + Logic 连写) filename := format.FileNamingFormat(style, baseName) // baseName 形如 loginHandler
所以 Handler / Logic 也是「拼进基础名的词」,不是独立后缀。gozero 下连写 → loginhandler;go_zero 下在词界插下划线 → login_handler。
同一个生成过程里,Go 标识符和文件名是分开算的。结构体名来自 ToCamel():
"upperStartCamelObject": in.Name.ToCamel(), // user → UserModel(PascalCase) "lowerStartCamelObject": stringx.From(in.Name.ToCamel()).Untitle(),
所以无论文件名变成 usermodel_gen.go 还是 user_model_gen.go,结构体始终叫 UserModel。文件名怎么拼,和代码里怎么命名是两码事。
_model 后缀」的两个原因合起来就解释了你的观察。
gozeroDefaultFormat = "gozero",即「无分隔符连写小写」。基础名 user_model 过一遍 → 词全连起来 → usermodel。下划线是风格的产物,默认风格把它吞了。网上有些旧文档说「默认是 go_zero」,那是错的。_model 不是「后缀」而是「基础名的一部分」图:基础名 user_model 经过 FileNamingFormat 后,下划线「存不存在」完全由 style 决定。
很多人以为 go-zero 给各类文件统一加了 _model / _handler / _logic 后缀。其实只有 _gen 是真正的「固定后缀」。
| 这串字符 | 性质 | 说明 |
|---|---|---|
_gen | 真后缀(固定) | 在 FileNamingFormat 之后用 + "_gen.go" 拼上,不受 style 影响,永远有。标记「这是生成物、可被覆盖」。 |
model(在基础名里) | 拼接词 | goctl 把 表名 + "_model" 拼进基础名,再整体被 style 格式化。gozero 下它和表名连写 → usermodel。 |
Handler / Logic | 拼接词 | gogen 把 handler 名和 Handler/Logic 驼峰连写拼成基础名。gozero 下连写 → loginhandler / loginlogic。 |
_gen(且它紧跟在风格化之后的名字上,如 usermodel_gen.go)。你看到没有 _model 后缀,完全正确。表 user、@handler login:
| 生成物 | gozero(默认) | go_zero |
|---|---|---|
| model 生成文件 | usermodel_gen.go | user_model_gen.go |
| model 手写扩展 | usermodel.go | user_model.go |
| handler 文件 | loginhandler.go | login_handler.go |
| logic 文件 | loginlogic.go | login_logic.go |
| 结构体 / 类型名 | UserModel / LoginLogic | UserModel / LoginLogic(一样) |
_model 只有在 go_zero 风格下才「重新现身」成 user_model_gen.go;gozero 下它和表名粘成 usermodel。而 Handler / Logic 同理——go_zero 才会在词间插下划线。_model 下划线的文件名?用 --style go_zero 显式指定即可。它是「每次生成时传参」,不会改你已经手写的代码。
# api 服务 goctl api go -api user.api -dir . --style go_zero # mysql model goctl model mysql ddl -src user.sql -dir . --style go_zero # 或写进 goctl.yaml 固定项目默认(推荐团队统一) # namingFormat: go_zero
usermodel_gen.go)和新生成规则(user_model_gen.go)会共存、冲突。所以一个项目从一开始就定死一种风格。这几篇把「命名 / 风格 / 目录」串成完整图景: