完善 API 文档生成规范:统一路由注册和 OpenAPI 文档自动生成
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 4m32s
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 只保留核心规则和引导链接
This commit is contained in:
114
AGENTS.md
114
AGENTS.md
@@ -263,6 +263,33 @@ func (ModelName) TableName() string {
|
|||||||
- ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填)
|
- ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填)
|
||||||
- ✅ JSONB 字段需要实现 `driver.Valuer` 和 `sql.Scanner` 接口
|
- ✅ JSONB 字段需要实现 `driver.Valuer` 和 `sql.Scanner` 接口
|
||||||
|
|
||||||
|
## API 路由注册规范
|
||||||
|
|
||||||
|
**核心要求:所有 HTTP 接口必须使用统一的 `Register()` 函数注册,以自动加入 OpenAPI 文档生成。**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ 正确
|
||||||
|
Register(router, doc, basePath, "POST", "/shops", handler.Create, RouteSpec{
|
||||||
|
Summary: "创建店铺",
|
||||||
|
Tags: []string{"店铺管理"},
|
||||||
|
Input: new(model.CreateShopRequest),
|
||||||
|
Output: new(model.ShopResponse),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
// ❌ 错误:直接注册不会生成文档
|
||||||
|
router.Post("/shops", handler.Create)
|
||||||
|
```
|
||||||
|
|
||||||
|
**RouteSpec 必填项:**
|
||||||
|
- `Summary` - 操作说明(中文,简短)
|
||||||
|
- `Tags` - 分类标签(用于文档分组)
|
||||||
|
- `Input` - 请求 DTO(`nil` 表示无参数)
|
||||||
|
- `Output` - 响应 DTO(`nil` 表示无返回)
|
||||||
|
- `Auth` - 是否需要认证
|
||||||
|
|
||||||
|
**完整指南**: 参见 [`docs/api-documentation-guide.md`](docs/api-documentation-guide.md)
|
||||||
|
|
||||||
## 数据库设计
|
## 数据库设计
|
||||||
|
|
||||||
**核心规则:**
|
**核心规则:**
|
||||||
@@ -520,4 +547,89 @@ make migrate-up
|
|||||||
8. ✅ 文档更新计划
|
8. ✅ 文档更新计划
|
||||||
9. ✅ 中文优先
|
9. ✅ 中文优先
|
||||||
|
|
||||||
**详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md`
|
**详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md`
|
||||||
|
|
||||||
|
## 规范文档管理
|
||||||
|
|
||||||
|
### 添加新规范的流程
|
||||||
|
|
||||||
|
当你需要为项目添加新的开发规范时,必须遵循以下流程:
|
||||||
|
|
||||||
|
#### 1. 创建详细规范文档
|
||||||
|
|
||||||
|
在 `docs/` 目录下创建详细的规范文档(Markdown 格式):
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── api-documentation-guide.md # API 文档生成规范
|
||||||
|
├── code-review-checklist.md # 代码审查清单
|
||||||
|
├── testing-guide.md # 测试规范
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
**文档内容要求**:
|
||||||
|
- ✅ 包含完整的规范说明、示例代码、常见问题
|
||||||
|
- ✅ 使用中文编写,代码示例使用英文
|
||||||
|
- ✅ 提供正确示例(✅)和错误示例(❌)的对比
|
||||||
|
- ✅ 包含故障排查和调试指南
|
||||||
|
|
||||||
|
#### 2. 在 AGENTS.md 中添加简短引导
|
||||||
|
|
||||||
|
在 `AGENTS.md` 的相关章节中添加**简短**的规范说明 + 引导链接:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## XXX 规范
|
||||||
|
|
||||||
|
**核心要求:一句话说明最重要的规则。**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ 正确示例(3-5 行)
|
||||||
|
...
|
||||||
|
|
||||||
|
// ❌ 错误示例(3-5 行)
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键要点**:
|
||||||
|
- 规则 1
|
||||||
|
- 规则 2
|
||||||
|
- 规则 3
|
||||||
|
|
||||||
|
**完整指南**: 参见 [`docs/xxx-guide.md`](docs/xxx-guide.md)
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**:
|
||||||
|
- ⚠️ AGENTS.md 中的说明不超过 20 行
|
||||||
|
- ⚠️ 只保留最核心的规则和示例
|
||||||
|
- ⚠️ 必须包含引导链接到详细文档
|
||||||
|
|
||||||
|
#### 3. 在 README.md 中添加文档链接
|
||||||
|
|
||||||
|
在 `README.md` 的"## 文档"章节中添加链接:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 文档
|
||||||
|
|
||||||
|
### 开发规范
|
||||||
|
|
||||||
|
- **[API 文档生成规范](docs/api-documentation-guide.md)**:路由注册规范、DTO 规范、OpenAPI 文档生成流程
|
||||||
|
- **[XXX 规范](docs/xxx-guide.md)**:简短的一句话说明
|
||||||
|
```
|
||||||
|
|
||||||
|
**分类规则**:
|
||||||
|
- 开发规范:代码规范、API 规范、测试规范
|
||||||
|
- 功能指南:功能使用指南、配置指南
|
||||||
|
- 架构设计:设计文档、技术选型
|
||||||
|
|
||||||
|
### 规范文档的维护
|
||||||
|
|
||||||
|
**更新规范时**:
|
||||||
|
1. 优先更新 `docs/` 下的详细文档
|
||||||
|
2. 如果核心规则变化,同步更新 AGENTS.md 中的简短说明
|
||||||
|
3. 保持 AGENTS.md 简洁,避免冗余
|
||||||
|
|
||||||
|
**删除规范时**:
|
||||||
|
1. 删除 `docs/` 下的详细文档
|
||||||
|
2. 删除 AGENTS.md 中的相关章节
|
||||||
|
3. 删除 README.md 中的链接
|
||||||
|
4. 说明删除原因(在 commit message 中)
|
||||||
10
README.md
10
README.md
@@ -812,10 +812,20 @@ rdb.Set(ctx, key, status, time.Hour)
|
|||||||
|
|
||||||
## 文档
|
## 文档
|
||||||
|
|
||||||
|
### 开发规范
|
||||||
|
|
||||||
|
- **[API 文档生成规范](docs/api-documentation-guide.md)**:路由注册规范、DTO 规范、OpenAPI 文档生成流程
|
||||||
|
- **[开发规范总览](AGENTS.md)**:完整的项目开发规范(必读)
|
||||||
|
|
||||||
|
### 功能指南
|
||||||
|
|
||||||
- **[快速开始指南](specs/001-fiber-middleware-integration/quickstart.md)**:详细设置和测试说明
|
- **[快速开始指南](specs/001-fiber-middleware-integration/quickstart.md)**:详细设置和测试说明
|
||||||
- **[限流指南](docs/rate-limiting.md)**:全面的限流配置和使用
|
- **[限流指南](docs/rate-limiting.md)**:全面的限流配置和使用
|
||||||
- **[错误处理使用指南](docs/003-error-handling/使用指南.md)**:错误码参考、Handler 使用、客户端处理、最佳实践
|
- **[错误处理使用指南](docs/003-error-handling/使用指南.md)**:错误码参考、Handler 使用、客户端处理、最佳实践
|
||||||
- **[错误处理架构说明](docs/003-error-handling/架构说明.md)**:架构设计、性能优化、扩展性说明
|
- **[错误处理架构说明](docs/003-error-handling/架构说明.md)**:架构设计、性能优化、扩展性说明
|
||||||
|
|
||||||
|
### 架构设计
|
||||||
|
|
||||||
- **[实现计划](specs/001-fiber-middleware-integration/plan.md)**:设计决策和架构
|
- **[实现计划](specs/001-fiber-middleware-integration/plan.md)**:设计决策和架构
|
||||||
- **[数据模型](specs/001-fiber-middleware-integration/data-model.md)**:配置结构和 Redis 架构
|
- **[数据模型](specs/001-fiber-middleware-integration/data-model.md)**:配置结构和 Redis 架构
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,8 @@ func generateOpenAPIDocs(outputPath string, logger *zap.Logger) {
|
|||||||
ShopAccount: shopAccHandler,
|
ShopAccount: shopAccHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 注册后台路由到文档生成器
|
// 4. 注册所有路由到文档生成器
|
||||||
adminGroup := app.Group("/api/admin")
|
routes.RegisterRoutesWithDoc(app, handlers, &bootstrap.Middlewares{}, adminDoc)
|
||||||
routes.RegisterAdminRoutes(adminGroup, handlers, &bootstrap.Middlewares{}, adminDoc, "/api/admin")
|
|
||||||
|
|
||||||
// 5. 注册 H5 路由到文档生成器
|
|
||||||
h5Group := app.Group("/api/h5")
|
|
||||||
routes.RegisterH5Routes(h5Group, handlers, &bootstrap.Middlewares{}, adminDoc, "/api/h5")
|
|
||||||
|
|
||||||
// 6. 保存规范到指定路径
|
// 6. 保存规范到指定路径
|
||||||
if err := adminDoc.Save(outputPath); err != nil {
|
if err := adminDoc.Save(outputPath); err != nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
|
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
|
||||||
|
"github.com/break/junhong_cmp_fiber/internal/handler/h5"
|
||||||
"github.com/break/junhong_cmp_fiber/internal/routes"
|
"github.com/break/junhong_cmp_fiber/internal/routes"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
||||||
)
|
)
|
||||||
@@ -34,18 +35,23 @@ func generateAdminDocs(outputPath string) error {
|
|||||||
accHandler := admin.NewAccountHandler(nil)
|
accHandler := admin.NewAccountHandler(nil)
|
||||||
roleHandler := admin.NewRoleHandler(nil)
|
roleHandler := admin.NewRoleHandler(nil)
|
||||||
permHandler := admin.NewPermissionHandler(nil)
|
permHandler := admin.NewPermissionHandler(nil)
|
||||||
authHandler := admin.NewAuthHandler(nil, nil)
|
adminAuthHandler := admin.NewAuthHandler(nil, nil)
|
||||||
|
h5AuthHandler := h5.NewAuthHandler(nil, nil)
|
||||||
|
shopHandler := admin.NewShopHandler(nil)
|
||||||
|
shopAccHandler := admin.NewShopAccountHandler(nil)
|
||||||
|
|
||||||
handlers := &bootstrap.Handlers{
|
handlers := &bootstrap.Handlers{
|
||||||
Account: accHandler,
|
Account: accHandler,
|
||||||
Role: roleHandler,
|
Role: roleHandler,
|
||||||
Permission: permHandler,
|
Permission: permHandler,
|
||||||
AdminAuth: authHandler,
|
AdminAuth: adminAuthHandler,
|
||||||
|
H5Auth: h5AuthHandler,
|
||||||
|
Shop: shopHandler,
|
||||||
|
ShopAccount: shopAccHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 注册路由到文档生成器
|
// 4. 注册所有路由到文档生成器
|
||||||
adminGroup := app.Group("/api/admin")
|
routes.RegisterRoutesWithDoc(app, handlers, &bootstrap.Middlewares{}, adminDoc)
|
||||||
routes.RegisterAdminRoutes(adminGroup, handlers, &bootstrap.Middlewares{}, adminDoc, "/api/admin")
|
|
||||||
|
|
||||||
// 5. 保存规范到指定路径
|
// 5. 保存规范到指定路径
|
||||||
if err := adminDoc.Save(outputPath); err != nil {
|
if err := adminDoc.Save(outputPath); err != nil {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
389
docs/api-documentation-guide.md
Normal file
389
docs/api-documentation-guide.md
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
# API 文档生成规范
|
||||||
|
|
||||||
|
**版本**: 1.0
|
||||||
|
**最后更新**: 2026-01-21
|
||||||
|
|
||||||
|
## 目录
|
||||||
|
|
||||||
|
- [核心原则](#核心原则)
|
||||||
|
- [路由注册规范](#路由注册规范)
|
||||||
|
- [DTO 规范](#dto-规范)
|
||||||
|
- [文档生成流程](#文档生成流程)
|
||||||
|
- [常见问题](#常见问题)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 核心原则
|
||||||
|
|
||||||
|
### ✅ 强制要求
|
||||||
|
|
||||||
|
**所有 HTTP 接口必须使用统一的 `Register()` 函数注册,以确保自动加入 OpenAPI 文档生成。**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ✅ 正确:使用 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)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 为什么这样做?
|
||||||
|
|
||||||
|
1. **文档自动同步**:代码即文档,避免文档与实现脱节
|
||||||
|
2. **前后端协作**:生成标准 OpenAPI 规范,前端可直接导入
|
||||||
|
3. **API 测试**:Swagger UI / Postman 可直接使用
|
||||||
|
4. **类型安全**:通过 DTO 结构体自动生成准确的字段定义
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 路由注册规范
|
||||||
|
|
||||||
|
### 1. 基本结构
|
||||||
|
|
||||||
|
所有路由注册必须在 `internal/routes/` 目录中完成:
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/routes/
|
||||||
|
├── registry.go # Register() 函数定义
|
||||||
|
├── routes.go # 总入口
|
||||||
|
├── admin.go # Admin 域路由
|
||||||
|
├── h5.go # H5 域路由
|
||||||
|
├── account.go # 账号管理路由
|
||||||
|
├── role.go # 角色管理路由
|
||||||
|
└── ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 注册函数签名
|
||||||
|
|
||||||
|
```go
|
||||||
|
func registerXxxRoutes(
|
||||||
|
api fiber.Router, // Fiber 路由组
|
||||||
|
h *admin.XxxHandler, // Handler 实例
|
||||||
|
doc *openapi.Generator, // 文档生成器(可能为 nil)
|
||||||
|
basePath string, // 基础路径(如 "/api/admin")
|
||||||
|
) {
|
||||||
|
// 路由注册逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. RouteSpec 结构
|
||||||
|
|
||||||
|
```go
|
||||||
|
type RouteSpec struct {
|
||||||
|
Summary string // 操作摘要(中文,简短)
|
||||||
|
Input interface{} // 请求参数 DTO
|
||||||
|
Output interface{} // 响应结果 DTO
|
||||||
|
Tags []string // 分类标签(用于文档分组)
|
||||||
|
Auth bool // 是否需要认证
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 完整示例
|
||||||
|
|
||||||
|
```go
|
||||||
|
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` 标签,禁止使用行内注释。**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ❌ 错误
|
||||||
|
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` 中列出所有可能值(中文)。**
|
||||||
|
|
||||||
|
```go
|
||||||
|
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. 请求参数类型标签
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 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:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 定义在 internal/model/common.go
|
||||||
|
type UpdateShopParams struct {
|
||||||
|
IDReq // 路径参数
|
||||||
|
UpdateShopRequest // Body 参数
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 分页响应规范
|
||||||
|
|
||||||
|
```go
|
||||||
|
type ShopPageResult struct {
|
||||||
|
Items []ShopResponse `json:"items" description:"店铺列表"`
|
||||||
|
Total int64 `json:"total" description:"总记录数"`
|
||||||
|
Page int `json:"page" description:"当前页码"`
|
||||||
|
Size int `json:"size" description:"每页数量"`
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文档生成流程
|
||||||
|
|
||||||
|
### 1. 自动生成
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 方式1:独立生成工具
|
||||||
|
go run cmd/gendocs/main.go
|
||||||
|
|
||||||
|
# 方式2:启动 API 服务时自动生成
|
||||||
|
go run cmd/api/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
生成的文档位置:
|
||||||
|
- `docs/admin-openapi.yaml` - 独立生成
|
||||||
|
- `logs/openapi.yaml` - 运行时生成
|
||||||
|
|
||||||
|
### 2. 验证文档
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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` 中添加:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 3. 创建 Handler(使用 nil 依赖,因为只需要路由结构)
|
||||||
|
newHandler := admin.NewXxxHandler(nil)
|
||||||
|
|
||||||
|
handlers := &bootstrap.Handlers{
|
||||||
|
// ... 其他 Handler
|
||||||
|
Xxx: newHandler, // 添加新 Handler
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 为什么我的接口没有出现在文档中?
|
||||||
|
|
||||||
|
**检查清单**:
|
||||||
|
|
||||||
|
1. ✅ 是否使用了 `Register()` 函数?
|
||||||
|
```go
|
||||||
|
// ❌ 错误
|
||||||
|
router.Post("/path", handler.Method)
|
||||||
|
|
||||||
|
// ✅ 正确
|
||||||
|
Register(router, doc, basePath, "POST", "/path", handler.Method, RouteSpec{...})
|
||||||
|
```
|
||||||
|
|
||||||
|
2. ✅ 路由注册函数是否接收了 `doc *openapi.Generator` 参数?
|
||||||
|
```go
|
||||||
|
func registerXxxRoutes(router fiber.Router, handler *admin.XxxHandler, doc *openapi.Generator, basePath string)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. ✅ 是否在 `cmd/gendocs/main.go` 中创建了 Handler?
|
||||||
|
```go
|
||||||
|
handlers := &bootstrap.Handlers{
|
||||||
|
Xxx: admin.NewXxxHandler(nil),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. ✅ 是否调用了路由注册函数?
|
||||||
|
- 检查 `internal/routes/admin.go` 中是否调用了 `registerXxxRoutes()`
|
||||||
|
- 检查 `internal/routes/routes.go` 是否调用了 `RegisterAdminRoutes()`
|
||||||
|
|
||||||
|
### Q2: 文档生成时报错 "undefined path parameter"?
|
||||||
|
|
||||||
|
**原因**:路径参数(如 `/:id`)的 DTO 缺少对应字段。
|
||||||
|
|
||||||
|
**解决方案**:创建组合参数 DTO
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ❌ 错误:直接使用 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 字段在文档中没有描述?
|
||||||
|
|
||||||
|
**检查**:
|
||||||
|
|
||||||
|
1. ✅ 是否添加了 `description` 标签?
|
||||||
|
```go
|
||||||
|
ShopName string `json:"shop_name" description:"店铺名称"`
|
||||||
|
```
|
||||||
|
|
||||||
|
2. ✅ 是否使用了行内注释(不会被识别)?
|
||||||
|
```go
|
||||||
|
// ❌ 错误
|
||||||
|
ShopName string `json:"shop_name"` // 店铺名称
|
||||||
|
|
||||||
|
// ✅ 正确
|
||||||
|
ShopName string `json:"shop_name" description:"店铺名称"`
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q4: 如何为新模块添加路由?
|
||||||
|
|
||||||
|
**步骤**:
|
||||||
|
|
||||||
|
1. 创建路由文件 `internal/routes/xxx.go`
|
||||||
|
2. 定义注册函数:
|
||||||
|
```go
|
||||||
|
func registerXxxRoutes(api fiber.Router, h *admin.XxxHandler, doc *openapi.Generator, basePath string) {
|
||||||
|
// 使用 Register() 注册路由
|
||||||
|
}
|
||||||
|
```
|
||||||
|
3. 在 `internal/routes/admin.go` 中调用:
|
||||||
|
```go
|
||||||
|
if handlers.Xxx != nil {
|
||||||
|
registerXxxRoutes(authGroup, handlers.Xxx, doc, basePath)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
4. 在 `cmd/gendocs/main.go` 中添加 Handler
|
||||||
|
5. 重新生成文档验证
|
||||||
|
|
||||||
|
### Q5: 如何调试文档生成?
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 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))
|
||||||
|
"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
- [OpenAPI 3.0 规范](https://swagger.io/specification/)
|
||||||
|
- [Swagger UI](https://swagger.io/tools/swagger-ui/)
|
||||||
|
- [项目 DTO 规范](../AGENTS.md#dto-规范重要)
|
||||||
|
- [已有实现示例](../internal/routes/account.go)
|
||||||
@@ -2,36 +2,35 @@ package model
|
|||||||
|
|
||||||
// ShopAccountListRequest 代理商账号列表查询请求
|
// ShopAccountListRequest 代理商账号列表查询请求
|
||||||
type ShopAccountListRequest struct {
|
type ShopAccountListRequest struct {
|
||||||
Page int `json:"page" query:"page" validate:"omitempty,min=1"` // 页码
|
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码"`
|
||||||
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100"` // 每页数量
|
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量"`
|
||||||
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty,min=1"` // 店铺ID过滤
|
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty,min=1" minimum:"1" description:"店铺ID过滤"`
|
||||||
Username string `json:"username" query:"username" validate:"omitempty,max=50"` // 用户名(模糊查询)
|
Username string `json:"username" query:"username" validate:"omitempty,max=50" maxLength:"50" description:"用户名(模糊查询)"`
|
||||||
Phone string `json:"phone" query:"phone" validate:"omitempty,len=11"` // 手机号(精确查询)
|
Phone string `json:"phone" query:"phone" validate:"omitempty,len=11" minLength:"11" maxLength:"11" description:"手机号(精确查询)"`
|
||||||
Status *int `json:"status" query:"status" validate:"omitempty,oneof=0 1"` // 状态
|
Status *int `json:"status" query:"status" validate:"omitempty,oneof=0 1" description:"状态 (0:禁用, 1:启用)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateShopAccountRequest 创建代理商账号请求
|
// CreateShopAccountRequest 创建代理商账号请求
|
||||||
type CreateShopAccountRequest struct {
|
type CreateShopAccountRequest struct {
|
||||||
ShopID uint `json:"shop_id" validate:"required,min=1"` // 店铺ID
|
ShopID uint `json:"shop_id" validate:"required,min=1" required:"true" minimum:"1" description:"店铺ID"`
|
||||||
Username string `json:"username" validate:"required,min=3,max=50"` // 用户名
|
Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"`
|
||||||
Phone string `json:"phone" validate:"required,len=11"` // 手机号
|
Phone string `json:"phone" validate:"required,len=11" required:"true" minLength:"11" maxLength:"11" description:"手机号"`
|
||||||
Password string `json:"password" validate:"required,min=8,max=32"` // 密码
|
Password string `json:"password" validate:"required,min=8,max=32" required:"true" minLength:"8" maxLength:"32" description:"密码"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateShopAccountRequest 更新代理商账号请求
|
// UpdateShopAccountRequest 更新代理商账号请求(不包含 phone 和 password,按照业务规则不允许修改)
|
||||||
type UpdateShopAccountRequest struct {
|
type UpdateShopAccountRequest struct {
|
||||||
Username string `json:"username" validate:"required,min=3,max=50"` // 用户名
|
Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"`
|
||||||
// 注意:不包含 phone 和 password,按照业务规则不允许修改
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateShopAccountPasswordRequest 修改代理商账号密码请求(管理员重置)
|
// UpdateShopAccountPasswordRequest 修改代理商账号密码请求(管理员重置)
|
||||||
type UpdateShopAccountPasswordRequest struct {
|
type UpdateShopAccountPasswordRequest struct {
|
||||||
NewPassword string `json:"new_password" validate:"required,min=8,max=32"` // 新密码
|
NewPassword string `json:"new_password" validate:"required,min=8,max=32" required:"true" minLength:"8" maxLength:"32" description:"新密码"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateShopAccountStatusRequest 修改代理商账号状态请求
|
// UpdateShopAccountStatusRequest 修改代理商账号状态请求
|
||||||
type UpdateShopAccountStatusRequest struct {
|
type UpdateShopAccountStatusRequest struct {
|
||||||
Status int `json:"status" validate:"required,oneof=0 1"` // 状态(0=禁用 1=启用)
|
Status int `json:"status" validate:"required,oneof=0 1" required:"true" description:"状态 (0:禁用, 1:启用)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShopAccountResponse 代理商账号响应
|
// ShopAccountResponse 代理商账号响应
|
||||||
@@ -46,3 +45,29 @@ type ShopAccountResponse struct {
|
|||||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShopAccountPageResult 代理账号分页响应
|
||||||
|
type ShopAccountPageResult struct {
|
||||||
|
Items []ShopAccountResponse `json:"items" description:"代理账号列表"`
|
||||||
|
Total int64 `json:"total" description:"总记录数"`
|
||||||
|
Page int `json:"page" description:"当前页码"`
|
||||||
|
Size int `json:"size" description:"每页数量"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateShopAccountParams 更新代理账号聚合参数 (用于文档生成)
|
||||||
|
type UpdateShopAccountParams struct {
|
||||||
|
IDReq
|
||||||
|
UpdateShopAccountRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateShopAccountPasswordParams 修改代理账号密码聚合参数 (用于文档生成)
|
||||||
|
type UpdateShopAccountPasswordParams struct {
|
||||||
|
IDReq
|
||||||
|
UpdateShopAccountPasswordRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateShopAccountStatusParams 修改代理账号状态聚合参数 (用于文档生成)
|
||||||
|
type UpdateShopAccountStatusParams struct {
|
||||||
|
IDReq
|
||||||
|
UpdateShopAccountStatusRequest
|
||||||
|
}
|
||||||
|
|||||||
@@ -53,3 +53,18 @@ type ShopResponse struct {
|
|||||||
CreatedAt string `json:"created_at" description:"创建时间"`
|
CreatedAt string `json:"created_at" description:"创建时间"`
|
||||||
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
UpdatedAt string `json:"updated_at" description:"更新时间"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShopPageResult 店铺分页响应
|
||||||
|
// ShopPageResult 店铺分页响应
|
||||||
|
type ShopPageResult struct {
|
||||||
|
Items []ShopResponse `json:"items" description:"店铺列表"`
|
||||||
|
Total int64 `json:"total" description:"总记录数"`
|
||||||
|
Page int `json:"page" description:"当前页码"`
|
||||||
|
Size int `json:"size" description:"每页数量"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateShopParams 更新店铺聚合参数 (用于文档生成)
|
||||||
|
type UpdateShopParams struct {
|
||||||
|
IDReq
|
||||||
|
UpdateShopRequest
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,25 +1,40 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// registerHealthRoutes 注册健康检查路由
|
type HealthResponse struct {
|
||||||
// 不需要认证,用于负载均衡器和监控系统
|
Status string `json:"status" description:"健康状态"`
|
||||||
func registerHealthRoutes(app *fiber.App) {
|
Service string `json:"service,omitempty" description:"服务名称"`
|
||||||
// 健康检查
|
}
|
||||||
app.Get("/health", func(c *fiber.Ctx) error {
|
|
||||||
|
func registerHealthRoutes(app *fiber.App, doc *openapi.Generator) {
|
||||||
|
Register(app, doc, "", "GET", "/health", func(c *fiber.Ctx) error {
|
||||||
return response.Success(c, fiber.Map{
|
return response.Success(c, fiber.Map{
|
||||||
"status": "healthy",
|
"status": "healthy",
|
||||||
"service": "junhong_cmp_fiber",
|
"service": "junhong_cmp_fiber",
|
||||||
})
|
})
|
||||||
|
}, RouteSpec{
|
||||||
|
Summary: "健康检查",
|
||||||
|
Tags: []string{"系统"},
|
||||||
|
Input: nil,
|
||||||
|
Output: new(HealthResponse),
|
||||||
|
Auth: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
// 就绪检查
|
Register(app, doc, "", "GET", "/ready", func(c *fiber.Ctx) error {
|
||||||
app.Get("/ready", func(c *fiber.Ctx) error {
|
|
||||||
return response.Success(c, fiber.Map{
|
return response.Success(c, fiber.Map{
|
||||||
"status": "ready",
|
"status": "ready",
|
||||||
})
|
})
|
||||||
|
}, RouteSpec{
|
||||||
|
Summary: "就绪检查",
|
||||||
|
Tags: []string{"系统"},
|
||||||
|
Input: nil,
|
||||||
|
Output: new(HealthResponse),
|
||||||
|
Auth: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,30 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
"github.com/break/junhong_cmp_fiber/internal/bootstrap"
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterRoutes 路由注册总入口
|
// RegisterRoutes 路由注册总入口
|
||||||
// 按业务模块调用各自的路由注册函数
|
// 按业务模块调用各自的路由注册函数
|
||||||
func RegisterRoutes(app *fiber.App, handlers *bootstrap.Handlers, middlewares *bootstrap.Middlewares) {
|
func RegisterRoutes(app *fiber.App, handlers *bootstrap.Handlers, middlewares *bootstrap.Middlewares) {
|
||||||
|
RegisterRoutesWithDoc(app, handlers, middlewares, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterRoutesWithDoc 路由注册总入口(支持文档生成)
|
||||||
|
func RegisterRoutesWithDoc(app *fiber.App, handlers *bootstrap.Handlers, middlewares *bootstrap.Middlewares, doc *openapi.Generator) {
|
||||||
// 1. 全局路由
|
// 1. 全局路由
|
||||||
registerHealthRoutes(app)
|
registerHealthRoutes(app, doc)
|
||||||
|
|
||||||
// 2. Admin 域 (挂载在 /api/admin)
|
// 2. Admin 域 (挂载在 /api/admin)
|
||||||
adminGroup := app.Group("/api/admin")
|
adminGroup := app.Group("/api/admin")
|
||||||
RegisterAdminRoutes(adminGroup, handlers, middlewares, nil, "/api/admin")
|
RegisterAdminRoutes(adminGroup, handlers, middlewares, doc, "/api/admin")
|
||||||
|
|
||||||
// 任务相关路由 (归属于 Admin 域)
|
// 任务相关路由 (归属于 Admin 域)
|
||||||
registerTaskRoutes(adminGroup)
|
registerTaskRoutes(adminGroup, doc, "/api/admin")
|
||||||
|
|
||||||
// 3. H5 域 (挂载在 /api/h5)
|
// 3. H5 域 (挂载在 /api/h5)
|
||||||
h5Group := app.Group("/api/h5")
|
h5Group := app.Group("/api/h5")
|
||||||
RegisterH5Routes(h5Group, handlers, middlewares, nil, "/api/h5")
|
RegisterH5Routes(h5Group, handlers, middlewares, doc, "/api/h5")
|
||||||
|
|
||||||
// 4. 个人客户路由 (挂载在 /api/c/v1)
|
// 4. 个人客户路由 (挂载在 /api/c/v1)
|
||||||
RegisterPersonalCustomerRoutes(app, handlers, middlewares.PersonalAuth)
|
RegisterPersonalCustomerRoutes(app, handlers, middlewares.PersonalAuth)
|
||||||
|
|||||||
@@ -4,20 +4,88 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
|
"github.com/break/junhong_cmp_fiber/internal/handler/admin"
|
||||||
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func registerShopRoutes(router fiber.Router, handler *admin.ShopHandler, doc *openapi.Generator, basePath string) {
|
func registerShopRoutes(router fiber.Router, handler *admin.ShopHandler, doc *openapi.Generator, basePath string) {
|
||||||
router.Get("/shops", handler.List)
|
shops := router.Group("/shops")
|
||||||
router.Post("/shops", handler.Create)
|
groupPath := basePath + "/shops"
|
||||||
router.Put("/shops/:id", handler.Update)
|
|
||||||
router.Delete("/shops/:id", handler.Delete)
|
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),
|
||||||
|
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,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerShopAccountRoutes(router fiber.Router, handler *admin.ShopAccountHandler, doc *openapi.Generator, basePath string) {
|
func registerShopAccountRoutes(router fiber.Router, handler *admin.ShopAccountHandler, doc *openapi.Generator, basePath string) {
|
||||||
router.Get("/shop-accounts", handler.List)
|
shopAccounts := router.Group("/shop-accounts")
|
||||||
router.Post("/shop-accounts", handler.Create)
|
groupPath := basePath + "/shop-accounts"
|
||||||
router.Put("/shop-accounts/:id", handler.Update)
|
|
||||||
router.Put("/shop-accounts/:id/password", handler.UpdatePassword)
|
Register(shopAccounts, doc, groupPath, "GET", "", handler.List, RouteSpec{
|
||||||
router.Put("/shop-accounts/:id/status", handler.UpdateStatus)
|
Summary: "代理账号列表",
|
||||||
|
Tags: []string{"代理账号管理"},
|
||||||
|
Input: new(model.ShopAccountListRequest),
|
||||||
|
Output: new(model.ShopAccountPageResult),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Register(shopAccounts, doc, groupPath, "POST", "", handler.Create, RouteSpec{
|
||||||
|
Summary: "创建代理账号",
|
||||||
|
Tags: []string{"代理账号管理"},
|
||||||
|
Input: new(model.CreateShopAccountRequest),
|
||||||
|
Output: new(model.ShopAccountResponse),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Register(shopAccounts, doc, groupPath, "PUT", "/:id", handler.Update, RouteSpec{
|
||||||
|
Summary: "更新代理账号",
|
||||||
|
Tags: []string{"代理账号管理"},
|
||||||
|
Input: new(model.UpdateShopAccountParams),
|
||||||
|
Output: new(model.ShopAccountResponse),
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Register(shopAccounts, doc, groupPath, "PUT", "/:id/password", handler.UpdatePassword, RouteSpec{
|
||||||
|
Summary: "重置代理账号密码",
|
||||||
|
Tags: []string{"代理账号管理"},
|
||||||
|
Input: new(model.UpdateShopAccountPasswordParams),
|
||||||
|
Output: nil,
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
Register(shopAccounts, doc, groupPath, "PUT", "/:id/status", handler.UpdateStatus, RouteSpec{
|
||||||
|
Summary: "启用/禁用代理账号",
|
||||||
|
Tags: []string{"代理账号管理"},
|
||||||
|
Input: new(model.UpdateShopAccountStatusParams),
|
||||||
|
Output: nil,
|
||||||
|
Auth: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,33 @@
|
|||||||
package routes
|
package routes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/break/junhong_cmp_fiber/pkg/response"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
|
"github.com/break/junhong_cmp_fiber/internal/model"
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/openapi"
|
||||||
|
"github.com/break/junhong_cmp_fiber/pkg/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// registerTaskRoutes 注册任务相关路由
|
type TaskStatusResponse struct {
|
||||||
// 用于异步任务状态查询等
|
ID string `json:"id" description:"任务ID"`
|
||||||
func registerTaskRoutes(api fiber.Router) {
|
Status string `json:"status" description:"任务状态 (pending:待处理, running:执行中, completed:已完成, failed:失败)"`
|
||||||
tasks := api.Group("/tasks")
|
}
|
||||||
|
|
||||||
// 获取任务状态(占位实现)
|
func registerTaskRoutes(api fiber.Router, doc *openapi.Generator, basePath string) {
|
||||||
tasks.Get("/:id", func(c *fiber.Ctx) error {
|
tasks := api.Group("/tasks")
|
||||||
|
groupPath := basePath + "/tasks"
|
||||||
|
|
||||||
|
Register(tasks, doc, groupPath, "GET", "/:id", func(c *fiber.Ctx) error {
|
||||||
taskID := c.Params("id")
|
taskID := c.Params("id")
|
||||||
return response.Success(c, fiber.Map{
|
return response.Success(c, fiber.Map{
|
||||||
"id": taskID,
|
"id": taskID,
|
||||||
"status": "pending",
|
"status": "pending",
|
||||||
})
|
})
|
||||||
|
}, RouteSpec{
|
||||||
|
Summary: "查询任务状态",
|
||||||
|
Tags: []string{"任务管理"},
|
||||||
|
Input: new(model.IDReq),
|
||||||
|
Output: new(TaskStatusResponse),
|
||||||
|
Auth: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user