签入版本

This commit is contained in:
小蚂蚁云
2026-10-20 21:53:26 +08:00
parent 5cc3da657c
commit b33799aa63
427 changed files with 32547 additions and 1 deletions
@@ -0,0 +1,115 @@
# 3.7 服务注册与路由
:::tip 概述
本项目没有传统的路由表文件逐条登记——路由由 Bind 反射绑定从 gmeta 注解自动生成。手工需要维护的只有两处:service 接口文件(手维护)和 router.go 的一行 Bind 注册(生成器自动写入)。
:::
## service 接口文件(手维护,真实代码摘自 internal/service/example.go)
```go
// ============================== 案例演示服务接口 ==============================
// IExample 案例演示服务接口
// 实现位于 internal/logic/example.go
type IExample interface {
// Page 案例演示分页
// 按查询条件分页返回案例演示列表
Page(ctx context.Context, req *v1.ExamplePageReq) (res *v1.ExamplePageRes, err error)
// List 案例演示平铺列表
// data 直接为数组,树形结构由前端构建
List(ctx context.Context, req *v1.ExampleListReq) (res v1.ExampleListRes, err error)
// Detail 案例演示详情
// 按主键ID查询详情
Detail(ctx context.Context, req *v1.ExampleDetailReq) (res *v1.ExampleDetailRes, err error)
// Add 新增案例演示
// 新增成功后返回新记录ID
Add(ctx context.Context, req *v1.ExampleAddReq) (res *v1.ExampleIdRes, err error)
// Update 修改案例演示
// 按主键ID更新记录
Update(ctx context.Context, req *v1.ExampleUpdateReq) (res *v1.ExampleUpdateRes, err error)
// Status 案例演示状态
// 设置记录的状态字段
Status(ctx context.Context, req *v1.ExampleStatusReq) (res *v1.ExampleStatusRes, err error)
// Delete 删除案例演示
// 按主键ID删除单条记录
Delete(ctx context.Context, req *v1.ExampleDeleteReq) (res *v1.ExampleDeleteRes, err error)
// BatchDelete 批量删除案例演示
// 按主键ID列表批量删除记录
BatchDelete(ctx context.Context, req *v1.ExampleBatchDeleteReq) (res *v1.ExampleBatchDeleteRes, err error)
// Import 导入案例演示
// 接收上传的 xls/xlsx 文件并批量导入数据
Import(ctx context.Context, req *v1.ExampleImportReq) (res *v1.ExampleImportRes, err error)
// Export 导出案例演示
// 按查询条件导出数据并返回文件访问地址
Export(ctx context.Context, req *v1.ExampleExportReq) (res *v1.ExampleExportRes, err error)
}
// localExample 案例演示服务实现实例
var localExample IExample
// Example 获取案例演示服务实例
func Example() IExample {
if localExample == nil {
panic("implement not found for interface IExample, forgot register?")
}
return localExample
}
// RegisterExample 注册案例演示服务实现
func RegisterExample(i IExample) {
localExample = i
}
```
三件套缺一不可:**接口 IXxx**(controller 依赖)+ **取用函数 Xxx()**(controller 调用入口)+ **RegisterXxx()**(logic init 注册)。未注册时取用会 panic(启动即暴露配置错误,而非运行时空指针)。
::: warning 禁止 gf gen service
service 接口文件是手工重组维护的(每模块一文件、中文注释齐全)。**禁止整体运行 `gf gen service`**——会按域重新生成并覆盖手工结构。logic 方法签名变更时手工同步接口文件。
:::
## 路由注册(internal/router/router.go)
```go
// 按域顺序注册(摘自 registerControllers)
Bind(s, new(controller.CAuth)) // 认证
Bind(s, new(controller.CIndex)) // 个人中心
Bind(s, new(controller.CUser)) // system 域
Bind(s, new(controller.CRole))
// …
Bind(s, new(controller.CExample)) // 代码生成器注册
```
新增模块时在最后一个 `Bind` 行后追加一行(代码生成器 `registerRouter` 副作用自动写入,已存在则跳过,幂等)。
## 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 扩展标签:
method / path / summary / permission / log / logType / public
3. 按 method:path 注册路由 + 写入路由元数据表 meta.Register
4. 补注册进 gf goai(闭包路由 gf 不会自动收集)→ /api.json 原生 OpenAPI
5. 生成统一包装 handler:
元数据入 ctx → 权限校验(403 短路)→ r.Parse 参数解析(含 gvalid)
→ 调用业务方法 → response.Ok/Fail 信封写出
```
::: tip 注解即事实
权限校验与操作日志共用同一份路由元数据(meta 表),gmeta 标签是唯一真相。不存在"代码改了忘改路由表/权限表"的可能。
:::
## URL 约定
+ 全部路由统一 `/api` 前缀,**URL 中无 `/v1` 版本段**(`api/v1` 只是 Go 包名)。例:`GET /api/user/page`。
+ 公开白名单(`middleware/public.go`,精确匹配):`/api/login`、`/api/register`(白名单放行,路由未注册)、`/api/captcha`、`/api/oauth2/token`、`/api/health`;另放行 `/api.json` 与 `/swagger*`。
+ **三处必须一致**:public.go 白名单 / gmeta `public:"true"` 标注 / router.go 手工注册。
## 注册验证
```sh
go build ./... && ./bin/admin-server.exe
# 启动后检查:
curl http://127.0.0.1:8000/api.json | grep "/api/example" # OpenAPI 已含新路由
# 或配置 server.dumpRouterMap: true 启动看路由表
```