aiDoc/modules/backend-layer-rules.md
Router -> API -> Service -> Model 依赖方向enter.go 作为组装与暴露入口,避免循环引用global.GVA_MODELjson 与 gorm 标签ID、CreatedAt、UpdatedAt 这些基础字段沿用项目现有约定model/request/XxxSearch,并内嵌通用的 request.PageInfoCreatedBy/UpdatedBy/DeletedBy/DeptId(列名 created_by/updated_by/deleted_by/dept_id)这组公共操作字段仅在业务表需要数据权限时才创建,对应代码生成器的 AutoCreateResource 产物,不是每张表的必备字段;手写模型需要同类语义时用同名字段,不要自造 CreatorID 等同义字段User SysUser、Leader *SysUser)必须加 form:"-":gin 的 query/form 绑定按类型树递归且会给 nil 指针自动 new,模型间互相引用(如 SysUser.Dept ⇄ SysDepartment.Leader)一旦被 ShouldBindQuery 扫到会无限递归、进程直接 stack overflow 崩溃;关联对象本来也不可能从 query string 传入DeptId(归属部门)服务于数据权限:数据权限引擎按 created_by/dept_id 两列做行级过滤与创建时自动盖章,自造字段不会被引擎识别nilgin.Contexterrorctx context.Context 为首参,数据库调用用 global.GVA_DB.WithContext(ctx) 串联请求链路(API 层传 c.Request.Context())limit, offset := info.LimitOffset()(request.PageInfo 提供,pageSize 超过 MaxPageSize=100 自动截断),不要手写 PageSize*(Page-1) 换算server/utils/datascope/):受控表(带 dept_id/created_by 列)的范围过滤与创建盖章由引擎自动完成,Service 不手写 dept_id/created_by 过滤条件、不手动赋值 CreatedBy/DeptId;更新走 Save 等全量写时用 Omit("dept_id", "created_by") 保护归属列不被表单零值覆盖updated_by(不要放进 Omit);表同时有 deleted_by 列与 gorm.DeletedAt 时,软删除的那条 UPDATE 自动并入 deleted_by(硬删除 / Unscoped 不盖);无身份 / WithSystem / UpdateColumn(SkipHooks) 不盖ErrMissingWhereClause(不会静默作用于整个数据范围);确需全量写用 Session(&gorm.Session{AllowGlobalUpdate: true}) 显式声明db.Set("data_scope:skip", true) 显式旁路,定时任务/CLI/初始化用 datascope.WithSystem(ctx),不要裸用 context.Background()service/ 下建立独立文件,并在 service/enter.go 注册multipart/form-dataShouldBindJSONShouldBindQuery、c.Query(...)、c.DefaultQuery(...)c.Param(...)c.FormFile(...)、c.DefaultPostForm(...)、c.Request.FormValue(...)c.GetHeader(...)、c.Request.Header.Get(...)c.Cookie(...)绑定方式要与真实参数来源一致
不要为了套模板,把 Header / Cookie / Query / form-data 中的数据强行改成 body
认证、追踪、网关透传等信息,很多时候本来就应该从 Header 或 Cookie 获取
上传文件时,应按上传协议从 multipart/form-data 中取文件和附带字段
必须通过 service.ServiceGroupApp 访问服务层
必须使用项目统一的 response 包输出结果
每个对外 API 都必须写完整且准确的 Swagger 注释
api.ApiGroupApp 引用 API 层router/ 下建立独立文件,并在 router/enter.go 注册插件或模块若需要初始化入口,至少关注以下职责:
gorm.go: 表结构迁移router.go: 路由注册menu.go: 菜单与权限初始化viper.go: 配置加载api.go: API 注册对外 API 的 Swagger 注释至少要准确说明:
@Success 的 data 必须反映真实返回类型,让 swag 能生成有意义的返回结构,而不是空对象:
response.Response{data=response.PageResult{list=[]xxx.Model},msg=string}
response.PageResult.List 是 interface{},只写 data=response.PageResult 会让 swag 把 list 生成成空对象,必须用嵌套覆盖把元素类型补上response.Response{data=[]xxx.Model,msg=string}response.Response{data=xxx.Model,msg=string}response.Response{msg=string}gin.H 拼装等)时,才用 data=object 或 data=[]interface{}PrivateGroup,挂 JWTAuth + Casbin)的接口才写 @Security ApiKeyAuthPublicGroup)的接口不写 @Security,否则文档与真实鉴权不符代码生成模板
resource/package/server/api/api.go.tpl、resource/plugin/server/api/api.go.tpl已按上述规范生成列表接口返回类型,手写接口遵循同一标准。