5.18 gmeta 注解系统总览
概述
gmeta 注解是本项目的"声明即事实"机制:请求结构体上的一个 gmeta.Meta 标签同时驱动路由注册、权限校验、操作日志、OpenAPI 文档四件事。解析与分发在 internal/router/bind.go(反射绑定)与 meta/(路由元数据表)。
一个标签,四份事实
go
type UserPageReq struct {
gmeta.Meta `method:"get" path:"/api/user/page" summary:"用户分页"
permission:"sys:user:page" log:"用户管理-查询分页记录" logType:"query"`
// ...
}text
解析后同时产出:
① 路由注册 GET /api/user/page → CUser.Page
② 权限校验 HasPerm("sys:user:page")(包装层每请求判定)
③ 操作日志 标题"用户管理-查询分页记录"、类型 query
④ OpenAPI 文档 /api.json 的 path/summary/参数说明不存在"代码改了忘改路由表/权限表/文档"——标签是唯一真相。
标签字段语义
| 标签 | 语义 |
|---|---|
method | HTTP 方法(get/post/put/delete) |
path | 完整路由模板(含 /api 前缀;:id 为路由参数,req 同名字段承接) |
summary | OpenAPI 摘要 |
permission | 权限节点 sys:{module小驼峰}:{action};空 = 登录即可 |
log | 操作日志标题 "{模块中文名}-{动作}";空 = 不记录 |
logType | 日志类型字符串(query/add/update/delete/import/export/reset/…),经 LogTypeMap 转 41 个枚举值 |
public | "true" = 免登录(仍需与 middleware/public.go 白名单一致) |
Bind 绑定流程(internal/router/bind.go)
text
Bind(s, new(controller.CXxx)) 扫描控制器全部导出方法:
1) 校验签名:func (c *Xxx) Page(ctx, req *v1.XxxReq) (*v1.XxxRes, error)
(签名不符静默跳过——排障注意)
2) 解析请求结构体内嵌 gmeta.Meta 扩展标签
3) 按 method:path 注册路由 + meta.Register(rm) 写入路由元数据表
(键 "METHOD /path",读写锁保护;Lookup 供调试,All 供文档生成)
4) 补注册进 gf goai(闭包路由 gf 不会自动收集)→ /api.json 原生 OpenAPI
5) 生成统一包装 handler:
路由元数据写入 ctx(操作日志中间件读取)
→ 权限校验(403 短路;不设独立 Permission 中间件)
→ r.Parse(req)(路由参数+query+body,含 gvalid 校验)
→ 调用业务方法 → response.Ok/Fail 信封写出BodyUnmarshaler 逃生舱
text
当请求体顶层形态无法用结构体标签表达(如平铺 map)时,
Req 实现 UnmarshalBody(r *ghttp.Request) error 接口:
→ 跳过默认 Parse,实现内自行完成解析与 gvalid 校验注册验证
sh
# OpenAPI 检查(gmeta 补注册后全量可用)
curl http://127.0.0.1:8000/api.json | grep "user/page"
# 启动路由表
# config.yaml: server.dumpRouterMap: true开发规约
text
* 每个接口必须有 gmeta——没有标签的方法不会注册路由
* permission 新码必须同步菜单节点 + 角色勾选(否则 403)
* public:"true" 必须同时出现在 middleware/public.go 白名单(三处一致)
* log 空 = 不记录(有意不记录才留空);logType 缺省按 HTTP 方法自动推断(0)
* path 一律 /api 开头、无 /v1 版本段
* 改 path/permission 等标签后重启生效(路由与元数据表启动期构建)