# 12.4 模块开发规约 :::tip 必须遵守 一个模块固定四个文件(dto / repository / service / handler),对外 JSON 契约(json 标签、字段名、路径、权限码、binding 标签)以表结构与前端约定为准,保持稳定。本页是规约全文要点。 ::: ## dto — internal/model/dto/{module}.go - 结构体按职责命名(UserItem、UserPageReq…),json 标签即对外 API 字段名 - 每字段补 `form:"同json名"` 标签;binding/msg 与 dc 注释按校验需求书写 - 时间字段统一 `*LocalTime`;文件字段 `*multipart.FileHeader` + UnmarshalBody - 分页公共字段内嵌 PageReq;批量删除用 `Ids []int64` - 类型别名:`type XxxListRes = []*XxxItem`、`type XxxPageRes = PageData[XxxItem]` - 路由声明在 router.go 该控制器的 RouteSpec 表(字段 Method/Path/Summary/Permission/Log/LogType/Public),新增接口补一条,写法与既有条目逐字一致 ## repository — internal/repository/{module}.go ```go type XxxRepo struct{ Base } func NewXxxRepo() *XxxRepo { return &XxxRepo{Base: NewBase("gin_xxx")} } func (r *XxxRepo) Ctx(ctx context.Context) *gorm.DB { return r.Base.Ctx(ctx) } ``` 表名 = gin_ + 模块名;关联表同样建仓储。铁律见 [12.3 数据访问铁律](/zh/standards/data-access.html)。 ## service — internal/service/{module}.go - 三段式:`type sXxx struct{}` + init 注册 + `var Xxx *sXxx` - 标准七方法委托 ModuleSpec;复杂逻辑(导入导出、登录、分配菜单)手写方法 - 跨模块同包直调(无需 import);dict 相关模块 init 调 SetDictLookup - 数据权限需要的模块声明 `DataScope: ApplyDataScope("dept_id")` - 事务:`db.WithTrans(ctx, func(ctx context.Context) error { ... })` - 业务错误:`consts.CodeXxx` 哨兵或 `apperr.Newf(consts.CodeXxx.Code(), "fmt", a...)` - 钩子禁 HTTP/文件 IO、禁 append;错误原样上抛 ## handler — internal/handler/{module}.go - 极薄委托:`type CXxx struct{}` + 与 dto 一一对应的方法 - 签名固定 `func (c *CXxx) Method(ctx context.Context, req *dto.XxxReq) (*dto.XxxRes, error)` - 非标准方法(导入/导出/重置密码/分配菜单等)保留为 handler 方法并注释入参/返回/副作用 ## 通用禁则 - 注释一律按当前功能直述 - 文件头不加版权横幅,仅包注释一句说明模块职责 - binding/msg 标签逐字保持(本项目 validator 读取该标签) - 新增配置键必须同步 .env.example ## 相关 - [第 3 章 模块开发实战](/zh/guide/overview.html) - [3.11 完整代码清单](/zh/guide/code-list.html)