Files
junhong_cmp_fiber/docs/接入openapi.md
huang 6fc90abeb6 实现服务启动时自动生成OpenAPI文档
主要变更:
1. 新增 cmd/api/docs.go 实现文档自动生成逻辑
2. 修改 cmd/api/main.go 在服务启动时调用文档生成
3. 重构 cmd/gendocs/main.go 提取生成函数
4. 更新 .gitignore 忽略自动生成的 openapi.yaml
5. 新增 Makefile 支持 make docs 命令
6. OpenSpec 框架更新和变更归档

功能特性:
- 服务启动时自动生成 OpenAPI 文档到项目根目录
- 保留独立的文档生成工具 (make docs)
- 生成失败时记录错误但不影响服务启动
- 所有代码已通过 openspec validate --strict 验证

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-09 12:25:50 +08:00

267 lines
8.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构升级与 OpenAPI 文档接入详细实施规范
## 1. 架构调整设计 (Architecture Upgrade)
### 1.1 目录结构变更 (Directory Structure)
我们将把扁平的 Handler 层改造为按业务域Domain物理隔离的结构。
**变更前**:
```text
internal/handler/
├── account.go
├── role.go
└── ...
```
**变更后**:
```text
internal/handler/
├── admin/ # 后台管理/PC代理端 (Admin Domain)
│ ├── account.go
│ ├── role.go
│ └── ... # 现有业务逻辑全部移到这里
├── agent/ # 手机/H5代理端 (Agent Domain) - 预留
│ └── (空)
├── app/ # C端用户 (App Domain) - 预留
│ └── (空)
└── health.go # 全局健康检查 (保持在根目录)
```
### 1.2 路由注册层改造 (Routing Layer)
路由层将不再是一个巨大的 `routes.go`,而是拆分为“总线 + 分支”结构。
**文件: `internal/routes/routes.go` (总入口)**
```go
package routes
import (
"github.com/gofiber/fiber/v2"
"junhong_cmp_fiber/internal/handler" // 引用 health
"junhong_cmp_fiber/internal/middleware"
// 引入各个域的路由包 (因循环引用问题,建议直接在此文件定义 Register 函数,或拆分包)
// 最佳实践:在此文件保留 SetupRoutes调用同包下的 RegisterAdminRoutes 等
)
func SetupRoutes(app *fiber.App, deps *bootstrap.Dependencies) {
// 1. 全局路由
app.Get("/health", handler.HealthCheck)
// 2. 注册各个域的路由组
// Admin 域 (挂载在 /api/admin)
adminGroup := app.Group("/api/admin")
// 可以在这里挂载 Admin 专属中间件 (Token验证, RBAC等)
RegisterAdminRoutes(adminGroup, deps)
// App 域 (挂载在 /api/app)
appGroup := app.Group("/api/app")
RegisterAppRoutes(appGroup, deps)
// Agent 域 (挂载在 /api/agent)
agentGroup := app.Group("/api/agent")
RegisterAgentRoutes(agentGroup, deps)
}
```
**文件: `internal/routes/admin.go` (Admin 域详情)**
```go
package routes
import (
"github.com/gofiber/fiber/v2"
"junhong_cmp_fiber/internal/handler/admin" // 引用新的 handler 包
)
func RegisterAdminRoutes(router fiber.Router, deps *bootstrap.Dependencies) {
// 账号管理
account := router.Group("/accounts")
account.Post("/", admin.CreateAccount(deps.AccountService))
account.Get("/", admin.ListAccounts(deps.AccountService))
// ... 其他原有的路由逻辑,全部迁移到这里
}
```
---
## 2. OpenAPI 接入设计 (OpenAPI Integration)
我们将引入 `swaggest/openapi-go`,通过“影子路由”技术实现文档自动化。
### 2.1 基础设施: 文档生成器 (`pkg/openapi/generator.go`)
这是一个通用的工具类,用于封装 `Reflector`
```go
package openapi
import (
"github.com/swaggest/openapi-go/openapi3"
)
type Generator struct {
Reflector *openapi3.Reflector
}
func NewGenerator(title, version string) *Generator {
reflector := openapi3.Reflector{}
reflector.Spec = &openapi3.Spec{
Openapi: "3.0.3",
Info: openapi3.Info{
Title: title,
Version: version,
},
}
return &Generator{Reflector: &reflector}
}
// 核心方法:向文档中添加一个操作
func (g *Generator) AddOperation(method, path, summary string, input interface{}, output interface{}, tags ...string) {
op := openapi3.Operation{
Summary: summary,
Tags: tags,
}
// ... 反射 input/output 并添加到 Spec 中 ...
// ... 错误处理 ...
g.Reflector.Spec.AddOperation(method, path, op)
}
// 导出 YAML
func (g *Generator) Save(filepath string) error { ... }
```
### 2.2 核心机制: 影子注册器 (`internal/routes/registry.go`)
这是一个 Helper 函数,连接 Fiber 路由和 OpenAPI 生成器。
```go
package routes
import (
"github.com/gofiber/fiber/v2"
"junhong_cmp_fiber/pkg/openapi"
)
// RouteSpec 定义接口文档元数据
type RouteSpec struct {
Summary string
Input interface{} // 请求参数结构体 (Query/Path/Body)
Output interface{} // 响应参数结构体
Tags []string
Auth bool // 是否需要认证图标
}
// Register 封装后的注册函数
// router: Fiber 路由组
// doc: 文档生成器
// method, path: HTTP 方法和路径
// handler: Fiber Handler
// spec: 文档元数据
func Register(router fiber.Router, doc *openapi.Generator, method, path string, handler fiber.Handler, spec RouteSpec) {
// 1. 注册实际的 Fiber 路由
router.Add(method, path, handler)
// 2. 注册文档 (如果 doc 不为空 - 也就是在生成文档模式下)
if doc != nil {
doc.AddOperation(method, path, spec.Summary, spec.Input, spec.Output, spec.Tags...)
}
}
```
### 2.3 业务代码改造示例
**Step 1: 改造路由文件 (`internal/routes/admin.go`)**
```go
// 引入文档生成器
func RegisterAdminRoutes(router fiber.Router, deps *bootstrap.Dependencies, doc *openapi.Generator) {
// 使用 Register 替代 router.Post
registry.Register(router, doc, "POST", "/accounts",
admin.CreateAccount(deps.AccountService),
registry.RouteSpec{
Summary: "创建管理员账号",
Tags: []string{"Account"},
Input: new(model.CreateAccountReq), // 必须是结构体指针
Output: new(model.AccountResp), // 必须是结构体指针
},
)
}
```
**Step 2: 规范化 Model (`internal/model/account_dto.go`)**
必须确保 Input/Output 结构体有正确的 Tag。
```go
type CreateAccountReq struct {
// Body 参数
Username string `json:"username" required:"true" minLength:"4" description:"用户名"`
Password string `json:"password" required:"true" description:"初始密码"`
RoleID uint `json:"role_id" description:"角色ID"`
}
type AccountResp struct {
ID uint `json:"id"`
Username string `json:"username"`
// ...
}
```
### 2.4 文档生成入口 (`cmd/gendocs/main.go`)
这是一个独立的 main 函数,用于生成文档文件。
```go
package main
import (
"junhong_cmp_fiber/internal/routes"
"junhong_cmp_fiber/pkg/openapi"
"github.com/gofiber/fiber/v2"
)
func main() {
// 1. 创建生成器
adminDoc := openapi.NewGenerator("Admin API", "1.0")
// 2. 模拟 Fiber App (不需要 Start)
app := fiber.New()
// 3. 调用注册函数,传入 doc
// 注意:这里 deps 传 nil 即可,因为我们只跑路由注册逻辑,不跑实际 Handler
routes.RegisterAdminRoutes(app, nil, adminDoc)
// 4. 保存文件
adminDoc.Save("./docs/admin-openapi.yaml")
}
```
---
## 3. 详细实施步骤 (Execution Plan)
### 第一阶段:路由与目录重构 (无文档)
1. **创建目录**: `internal/handler/{admin,agent,app}`
2. **移动文件**: 将 `account.go`, `role.go` 等移入 `internal/handler/admin`
3. **修改包名**: 将移动后的文件 `package handler` 改为 `package admin`
4. **修复引用**: 使用 IDE 或 grep 查找所有引用了 `internal/handler` 的地方(主要是 `routes``bootstrap`),改为引用 `internal/handler/admin`
5. **重构路由**:
*`internal/routes` 下新建 `admin.go`,把 `routes.go` 里关于 admin 的代码剪切过去,封装成 `RegisterAdminRoutes` 函数。
*`routes.go` 中调用 `RegisterAdminRoutes`,并挂载到 `/api/admin`(注意:**路径变更**,需通知前端或暂时保持原路径)。*建议先保持原路径 `/api/v1` 以减少破坏性,等文档上齐了一起改。或者直接痛快点改成 `/api/admin`。你选择了"路由分离",我就按 `/api/admin` 改。*
### 第二阶段:文档基础设施
1. **添加依赖**: `swaggest/openapi-go`
2. **编写工具**: 实现 `pkg/openapi/generator.go`
3. **编写注册器**: 实现 `internal/routes/registry.go`
### 第三阶段:文档接入 (以 Account 模块为例)
1. **DTO 检查**: 检查 `internal/model/account_dto.go`,确保字段 Tag 完善。
2. **路由改造**: 修改 `internal/routes/admin.go`,引入 `doc` 参数,用 `registry.Register` 替换原生路由。
3. **生成测试**: 编写 `cmd/gendocs`,运行生成 YAML验证内容是否正确。
### 第四阶段:全面铺开
1. 对所有模块重复第三阶段的工作。
2. 在 Makefile 中添加 `make docs`