All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m32s
主要改进: 1. 新增 docs/api-documentation-guide.md 详细文档指南 2. 在 AGENTS.md 中添加路由注册规范章节 3. 更新 README.md 文档目录结构 路由注册改进: - 统一使用 Register() 函数注册路由并自动生成文档 - 所有接口必须指定 RouteSpec(Summary, Tags, Input, Output, Auth) - 修复 docs.go 和 gendocs/main.go 使用 RegisterRoutesWithDoc 统一注册 DTO 规范更新: - shop_dto.go 和 shop_account_dto.go 补充完整的 description 标签 - 所有枚举字段必须列出可能值和中文说明 文档生成优化: - admin-openapi.yaml 自动生成更新 - 健康检查和任务管理接口加入文档 - H5 认证接口完整文档化 规范文档管理: - 添加规范文档管理流程说明 - 详细文档放在 docs/ 目录 - AGENTS.md 只保留核心规则和引导链接
10 KiB
10 KiB
API 文档生成规范
版本: 1.0
最后更新: 2026-01-21
目录
核心原则
✅ 强制要求
所有 HTTP 接口必须使用统一的 Register() 函数注册,以确保自动加入 OpenAPI 文档生成。
// ✅ 正确:使用 Register() 函数
Register(router, doc, basePath, "POST", "/path", handler.Method, RouteSpec{
Summary: "操作说明",
Tags: []string{"分类"},
Input: new(model.RequestDTO),
Output: new(model.ResponseDTO),
Auth: true,
})
// ❌ 错误:直接注册(不会生成文档)
router.Post("/path", handler.Method)
为什么这样做?
- 文档自动同步:代码即文档,避免文档与实现脱节
- 前后端协作:生成标准 OpenAPI 规范,前端可直接导入
- API 测试:Swagger UI / Postman 可直接使用
- 类型安全:通过 DTO 结构体自动生成准确的字段定义
路由注册规范
1. 基本结构
所有路由注册必须在 internal/routes/ 目录中完成:
internal/routes/
├── registry.go # Register() 函数定义
├── routes.go # 总入口
├── admin.go # Admin 域路由
├── h5.go # H5 域路由
├── account.go # 账号管理路由
├── role.go # 角色管理路由
└── ...
2. 注册函数签名
func registerXxxRoutes(
api fiber.Router, // Fiber 路由组
h *admin.XxxHandler, // Handler 实例
doc *openapi.Generator, // 文档生成器(可能为 nil)
basePath string, // 基础路径(如 "/api/admin")
) {
// 路由注册逻辑
}
3. RouteSpec 结构
type RouteSpec struct {
Summary string // 操作摘要(中文,简短)
Input interface{} // 请求参数 DTO
Output interface{} // 响应结果 DTO
Tags []string // 分类标签(用于文档分组)
Auth bool // 是否需要认证
}
4. 完整示例
func registerShopRoutes(router fiber.Router, handler *admin.ShopHandler, doc *openapi.Generator, basePath string) {
shops := router.Group("/shops")
groupPath := basePath + "/shops"
Register(shops, doc, groupPath, "GET", "", handler.List, RouteSpec{
Summary: "店铺列表",
Tags: []string{"店铺管理"},
Input: new(model.ShopListRequest),
Output: new(model.ShopPageResult),
Auth: true,
})
Register(shops, doc, groupPath, "POST", "", handler.Create, RouteSpec{
Summary: "创建店铺",
Tags: []string{"店铺管理"},
Input: new(model.CreateShopRequest),
Output: new(model.ShopResponse),
Auth: true,
})
Register(shops, doc, groupPath, "PUT", "/:id", handler.Update, RouteSpec{
Summary: "更新店铺",
Tags: []string{"店铺管理"},
Input: new(model.UpdateShopParams), // 组合参数(路径 + Body)
Output: new(model.ShopResponse),
Auth: true,
})
Register(shops, doc, groupPath, "DELETE", "/:id", handler.Delete, RouteSpec{
Summary: "删除店铺",
Tags: []string{"店铺管理"},
Input: new(model.IDReq), // 仅路径参数
Output: nil,
Auth: true,
})
}
DTO 规范
1. Description 标签(必须)
所有字段必须使用 description 标签,禁止使用行内注释。
// ❌ 错误
type CreateShopRequest struct {
ShopName string `json:"shop_name" validate:"required,min=1,max=100"` // 店铺名称
}
// ✅ 正确
type CreateShopRequest struct {
ShopName string `json:"shop_name" validate:"required,min=1,max=100" required:"true" minLength:"1" maxLength:"100" description:"店铺名称"`
}
2. 枚举字段规范
必须在 description 中列出所有可能值(中文)。
type CreateShopRequest struct {
Status int `json:"status" validate:"required,oneof=0 1" required:"true" description:"状态 (0:禁用, 1:启用)"`
Level int `json:"level" validate:"required,min=1,max=7" required:"true" minimum:"1" maximum:"7" description:"店铺层级 (1-7级)"`
}
3. 验证标签与 OpenAPI 标签一致
| validate 标签 | OpenAPI 标签 | 说明 |
|---|---|---|
required |
required:"true" |
必填字段 |
min=N,max=M |
minimum:"N" maximum:"M" |
数值范围 |
min=N,max=M (字符串) |
minLength:"N" maxLength:"M" |
字符串长度 |
len=N |
minLength:"N" maxLength:"N" |
固定长度 |
oneof=A B C |
description 中说明 |
枚举值 |
4. 请求参数类型标签
// Query 参数
type ListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
}
// Path 参数
type IDReq struct {
ID uint `path:"id" description:"ID" required:"true"`
}
// Body 参数(默认)
type CreateRequest struct {
Name string `json:"name" validate:"required" required:"true" description:"名称"`
}
5. 组合参数(路径 + Body)
对于 PUT /:id 类型的端点,需要创建组合参数 DTO:
// 定义在 internal/model/common.go
type UpdateShopParams struct {
IDReq // 路径参数
UpdateShopRequest // Body 参数
}
6. 分页响应规范
type ShopPageResult struct {
Items []ShopResponse `json:"items" description:"店铺列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
Size int `json:"size" description:"每页数量"`
}
文档生成流程
1. 自动生成
# 方式1:独立生成工具
go run cmd/gendocs/main.go
# 方式2:启动 API 服务时自动生成
go run cmd/api/main.go
生成的文档位置:
docs/admin-openapi.yaml- 独立生成logs/openapi.yaml- 运行时生成
2. 验证文档
# 1. 检查生成的路径数量
python3 -c "
import yaml
with open('docs/admin-openapi.yaml', 'r', encoding='utf-8') as f:
doc = yaml.safe_load(f)
paths = list(doc.get('paths', {}).keys())
print(f'总路径数: {len(paths)}')
for p in sorted(paths):
print(f' {p}')
"
# 2. 在 Swagger UI 中测试
# 访问 https://editor.swagger.io/
# 粘贴 docs/admin-openapi.yaml 内容
3. 更新文档生成器
如果新增了 Handler,需要在 cmd/gendocs/main.go 中添加:
// 3. 创建 Handler(使用 nil 依赖,因为只需要路由结构)
newHandler := admin.NewXxxHandler(nil)
handlers := &bootstrap.Handlers{
// ... 其他 Handler
Xxx: newHandler, // 添加新 Handler
}
常见问题
Q1: 为什么我的接口没有出现在文档中?
检查清单:
-
✅ 是否使用了
Register()函数?// ❌ 错误 router.Post("/path", handler.Method) // ✅ 正确 Register(router, doc, basePath, "POST", "/path", handler.Method, RouteSpec{...}) -
✅ 路由注册函数是否接收了
doc *openapi.Generator参数?func registerXxxRoutes(router fiber.Router, handler *admin.XxxHandler, doc *openapi.Generator, basePath string) -
✅ 是否在
cmd/gendocs/main.go中创建了 Handler?handlers := &bootstrap.Handlers{ Xxx: admin.NewXxxHandler(nil), } -
✅ 是否调用了路由注册函数?
- 检查
internal/routes/admin.go中是否调用了registerXxxRoutes() - 检查
internal/routes/routes.go是否调用了RegisterAdminRoutes()
- 检查
Q2: 文档生成时报错 "undefined path parameter"?
原因:路径参数(如 /:id)的 DTO 缺少对应字段。
解决方案:创建组合参数 DTO
// ❌ 错误:直接使用 Body DTO
Register(router, doc, basePath, "PUT", "/:id", handler.Update, RouteSpec{
Input: new(model.UpdateShopRequest), // 缺少 id 参数
})
// ✅ 正确:使用组合参数
type UpdateShopParams struct {
IDReq // 包含 id 参数
UpdateShopRequest // 包含 Body 参数
}
Register(router, doc, basePath, "PUT", "/:id", handler.Update, RouteSpec{
Input: new(model.UpdateShopParams),
})
Q3: DTO 字段在文档中没有描述?
检查:
-
✅ 是否添加了
description标签?ShopName string `json:"shop_name" description:"店铺名称"` -
✅ 是否使用了行内注释(不会被识别)?
// ❌ 错误 ShopName string `json:"shop_name"` // 店铺名称 // ✅ 正确 ShopName string `json:"shop_name" description:"店铺名称"`
Q4: 如何为新模块添加路由?
步骤:
- 创建路由文件
internal/routes/xxx.go - 定义注册函数:
func registerXxxRoutes(api fiber.Router, h *admin.XxxHandler, doc *openapi.Generator, basePath string) { // 使用 Register() 注册路由 } - 在
internal/routes/admin.go中调用:if handlers.Xxx != nil { registerXxxRoutes(authGroup, handlers.Xxx, doc, basePath) } - 在
cmd/gendocs/main.go中添加 Handler - 重新生成文档验证
Q5: 如何调试文档生成?
# 1. 查看生成的 YAML 文件
cat docs/admin-openapi.yaml
# 2. 验证 YAML 格式
python3 -c "
import yaml
with open('docs/admin-openapi.yaml', 'r', encoding='utf-8') as f:
doc = yaml.safe_load(f)
print('YAML 格式正确')
"
# 3. 检查特定路径
python3 -c "
import yaml
with open('docs/admin-openapi.yaml', 'r', encoding='utf-8') as f:
doc = yaml.safe_load(f)
path = '/api/admin/shops'
if path in doc['paths']:
import json
print(json.dumps(doc['paths'][path], indent=2, ensure_ascii=False))
"