diff --git a/AGENTS.md b/AGENTS.md index 2d2a5f3..4c8452f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -263,6 +263,33 @@ func (ModelName) TableName() string { - ✅ 时间字段使用 `*time.Time`(可空)或 `time.Time`(必填) - ✅ 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. ✅ 文档更新计划 9. ✅ 中文优先 -**详细规范和 OpenSpec 工作流请查看**: `@/openspec/AGENTS.md` \ No newline at end of file +**详细规范和 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 中) \ No newline at end of file diff --git a/README.md b/README.md index e512673..41353d7 100644 --- a/README.md +++ b/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)**:详细设置和测试说明 - **[限流指南](docs/rate-limiting.md)**:全面的限流配置和使用 - **[错误处理使用指南](docs/003-error-handling/使用指南.md)**:错误码参考、Handler 使用、客户端处理、最佳实践 - **[错误处理架构说明](docs/003-error-handling/架构说明.md)**:架构设计、性能优化、扩展性说明 + +### 架构设计 + - **[实现计划](specs/001-fiber-middleware-integration/plan.md)**:设计决策和架构 - **[数据模型](specs/001-fiber-middleware-integration/data-model.md)**:配置结构和 Redis 架构 diff --git a/cmd/api/docs.go b/cmd/api/docs.go index 96d53fe..25fef6f 100644 --- a/cmd/api/docs.go +++ b/cmd/api/docs.go @@ -41,13 +41,8 @@ func generateOpenAPIDocs(outputPath string, logger *zap.Logger) { ShopAccount: shopAccHandler, } - // 4. 注册后台路由到文档生成器 - adminGroup := app.Group("/api/admin") - 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") + // 4. 注册所有路由到文档生成器 + routes.RegisterRoutesWithDoc(app, handlers, &bootstrap.Middlewares{}, adminDoc) // 6. 保存规范到指定路径 if err := adminDoc.Save(outputPath); err != nil { diff --git a/cmd/gendocs/main.go b/cmd/gendocs/main.go index 5402f71..397147d 100644 --- a/cmd/gendocs/main.go +++ b/cmd/gendocs/main.go @@ -8,6 +8,7 @@ import ( "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/h5" "github.com/break/junhong_cmp_fiber/internal/routes" "github.com/break/junhong_cmp_fiber/pkg/openapi" ) @@ -34,18 +35,23 @@ func generateAdminDocs(outputPath string) error { accHandler := admin.NewAccountHandler(nil) roleHandler := admin.NewRoleHandler(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{ - Account: accHandler, - Role: roleHandler, - Permission: permHandler, - AdminAuth: authHandler, + Account: accHandler, + Role: roleHandler, + Permission: permHandler, + AdminAuth: adminAuthHandler, + H5Auth: h5AuthHandler, + Shop: shopHandler, + ShopAccount: shopAccHandler, } - // 4. 注册路由到文档生成器 - adminGroup := app.Group("/api/admin") - routes.RegisterAdminRoutes(adminGroup, handlers, &bootstrap.Middlewares{}, adminDoc, "/api/admin") + // 4. 注册所有路由到文档生成器 + routes.RegisterRoutesWithDoc(app, handlers, &bootstrap.Middlewares{}, adminDoc) // 5. 保存规范到指定路径 if err := adminDoc.Save(outputPath); err != nil { diff --git a/docs/admin-openapi.yaml b/docs/admin-openapi.yaml index 09d2246..0ad7dda 100644 --- a/docs/admin-openapi.yaml +++ b/docs/admin-openapi.yaml @@ -204,6 +204,97 @@ components: - role_name - role_type type: object + ModelCreateShopAccountRequest: + properties: + password: + description: 密码 + maxLength: 32 + minLength: 8 + type: string + phone: + description: 手机号 + maxLength: 11 + minLength: 11 + type: string + shop_id: + description: 店铺ID + minimum: 1 + type: integer + username: + description: 用户名 + maxLength: 50 + minLength: 3 + type: string + required: + - shop_id + - username + - phone + - password + type: object + ModelCreateShopRequest: + properties: + address: + description: 详细地址 + maxLength: 255 + type: string + city: + description: 城市 + maxLength: 50 + type: string + contact_name: + description: 联系人姓名 + maxLength: 50 + type: string + contact_phone: + description: 联系人电话 + maxLength: 11 + minLength: 11 + type: string + district: + description: 区县 + maxLength: 50 + type: string + init_password: + description: 初始账号密码 + maxLength: 32 + minLength: 8 + type: string + init_phone: + description: 初始账号手机号 + maxLength: 11 + minLength: 11 + type: string + init_username: + description: 初始账号用户名 + maxLength: 50 + minLength: 3 + type: string + parent_id: + description: 上级店铺ID(一级店铺可不填) + minimum: 1 + nullable: true + type: integer + province: + description: 省份 + maxLength: 50 + type: string + shop_code: + description: 店铺编号 + maxLength: 50 + minLength: 1 + type: string + shop_name: + description: 店铺名称 + maxLength: 100 + minLength: 1 + type: string + required: + - shop_name + - shop_code + - init_password + - init_username + - init_phone + type: object ModelLoginRequest: properties: device: @@ -439,6 +530,122 @@ components: minimum: 0 type: integer type: object + ModelShopAccountPageResult: + properties: + items: + description: 代理账号列表 + items: + $ref: '#/components/schemas/ModelShopAccountResponse' + nullable: true + type: array + page: + description: 当前页码 + type: integer + size: + description: 每页数量 + type: integer + total: + description: 总记录数 + type: integer + type: object + ModelShopAccountResponse: + properties: + created_at: + description: 创建时间 + type: string + id: + description: 账号ID + minimum: 0 + type: integer + phone: + description: 手机号 + type: string + shop_id: + description: 店铺ID + minimum: 0 + type: integer + shop_name: + description: 店铺名称 + type: string + status: + description: 状态 (0:禁用, 1:启用) + type: integer + updated_at: + description: 更新时间 + type: string + user_type: + description: 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号) + type: integer + username: + description: 用户名 + type: string + type: object + ModelShopPageResult: + properties: + items: + description: 店铺列表 + items: + $ref: '#/components/schemas/ModelShopResponse' + nullable: true + type: array + page: + description: 当前页码 + type: integer + size: + description: 每页数量 + type: integer + total: + description: 总记录数 + type: integer + type: object + ModelShopResponse: + properties: + address: + description: 详细地址 + type: string + city: + description: 城市 + type: string + contact_name: + description: 联系人姓名 + type: string + contact_phone: + description: 联系人电话 + type: string + created_at: + description: 创建时间 + type: string + district: + description: 区县 + type: string + id: + description: 店铺ID + minimum: 0 + type: integer + level: + description: 店铺层级 (1-7级) + type: integer + parent_id: + description: 上级店铺ID + minimum: 0 + nullable: true + type: integer + province: + description: 省份 + type: string + shop_code: + description: 店铺编号 + type: string + shop_name: + description: 店铺名称 + type: string + status: + description: 状态 (0:禁用, 1:启用) + type: integer + updated_at: + description: 更新时间 + type: string + type: object ModelUpdateAccountParams: properties: password: @@ -546,6 +753,73 @@ components: required: - status type: object + ModelUpdateShopAccountParams: + properties: + username: + description: 用户名 + maxLength: 50 + minLength: 3 + type: string + required: + - username + type: object + ModelUpdateShopAccountPasswordParams: + properties: + new_password: + description: 新密码 + maxLength: 32 + minLength: 8 + type: string + required: + - new_password + type: object + ModelUpdateShopAccountStatusParams: + properties: + status: + description: 状态 (0:禁用, 1:启用) + type: integer + required: + - status + type: object + ModelUpdateShopParams: + properties: + address: + description: 详细地址 + maxLength: 255 + type: string + city: + description: 城市 + maxLength: 50 + type: string + contact_name: + description: 联系人姓名 + maxLength: 50 + type: string + contact_phone: + description: 联系人电话 + maxLength: 11 + minLength: 11 + type: string + district: + description: 区县 + maxLength: 50 + type: string + province: + description: 省份 + maxLength: 50 + type: string + shop_name: + description: 店铺名称 + maxLength: 100 + minLength: 1 + type: string + status: + description: 状态 (0:禁用, 1:启用) + type: integer + required: + - shop_name + - status + type: object ModelUpdateStatusParams: properties: status: @@ -589,6 +863,24 @@ components: description: 用户名 type: string type: object + RoutesHealthResponse: + properties: + service: + description: 服务名称 + type: string + status: + description: 健康状态 + type: string + type: object + RoutesTaskStatusResponse: + properties: + id: + description: 任务ID + type: string + status: + description: 任务状态 (pending:待处理, running:执行中, completed:已完成, failed:失败) + type: string + type: object securitySchemes: BearerAuth: bearerFormat: JWT @@ -2428,3 +2720,759 @@ paths: summary: 移除权限 tags: - 角色 + /api/admin/shop-accounts: + get: + parameters: + - description: 页码 + in: query + name: page + schema: + description: 页码 + minimum: 1 + type: integer + - description: 每页数量 + in: query + name: page_size + schema: + description: 每页数量 + maximum: 100 + minimum: 1 + type: integer + - description: 店铺ID过滤 + in: query + name: shop_id + schema: + description: 店铺ID过滤 + minimum: 1 + nullable: true + type: integer + - description: 用户名(模糊查询) + in: query + name: username + schema: + description: 用户名(模糊查询) + maxLength: 50 + type: string + - description: 手机号(精确查询) + in: query + name: phone + schema: + description: 手机号(精确查询) + maxLength: 11 + minLength: 11 + type: string + - description: 状态 (0:禁用, 1:启用) + in: query + name: status + schema: + description: 状态 (0:禁用, 1:启用) + nullable: true + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopAccountPageResult' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 代理账号列表 + tags: + - 代理账号管理 + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCreateShopAccountRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopAccountResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 创建代理账号 + tags: + - 代理账号管理 + /api/admin/shop-accounts/{id}: + put: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelUpdateShopAccountParams' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopAccountResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 更新代理账号 + tags: + - 代理账号管理 + /api/admin/shop-accounts/{id}/password: + put: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelUpdateShopAccountPasswordParams' + responses: + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 重置代理账号密码 + tags: + - 代理账号管理 + /api/admin/shop-accounts/{id}/status: + put: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelUpdateShopAccountStatusParams' + responses: + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 启用/禁用代理账号 + tags: + - 代理账号管理 + /api/admin/shops: + get: + parameters: + - description: 页码 + in: query + name: page + schema: + description: 页码 + minimum: 1 + type: integer + - description: 每页数量 + in: query + name: page_size + schema: + description: 每页数量 + maximum: 100 + minimum: 1 + type: integer + - description: 店铺名称模糊查询 + in: query + name: shop_name + schema: + description: 店铺名称模糊查询 + maxLength: 100 + type: string + - description: 店铺编号模糊查询 + in: query + name: shop_code + schema: + description: 店铺编号模糊查询 + maxLength: 50 + type: string + - description: 上级店铺ID + in: query + name: parent_id + schema: + description: 上级店铺ID + minimum: 1 + nullable: true + type: integer + - description: 店铺层级 (1-7级) + in: query + name: level + schema: + description: 店铺层级 (1-7级) + maximum: 7 + minimum: 1 + nullable: true + type: integer + - description: 状态 (0:禁用, 1:启用) + in: query + name: status + schema: + description: 状态 (0:禁用, 1:启用) + nullable: true + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopPageResult' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 店铺列表 + tags: + - 店铺管理 + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelCreateShopRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 创建店铺 + tags: + - 店铺管理 + /api/admin/shops/{id}: + delete: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + responses: + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 删除店铺 + tags: + - 店铺管理 + put: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelUpdateShopParams' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelShopResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 更新店铺 + tags: + - 店铺管理 + /api/admin/tasks/{id}: + get: + parameters: + - description: ID + in: path + name: id + required: true + schema: + description: ID + minimum: 0 + type: integer + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/RoutesTaskStatusResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 查询任务状态 + tags: + - 任务管理 + /api/h5/login: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelLoginRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelLoginResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + summary: H5 登录 + tags: + - H5 认证 + /api/h5/logout: + post: + responses: + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 登出 + tags: + - H5 认证 + /api/h5/me: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelUserInfo' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 获取当前用户信息 + tags: + - H5 认证 + /api/h5/password: + put: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelChangePasswordRequest' + responses: + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "401": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 未认证或认证已过期 + "403": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 无权访问 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + security: + - BearerAuth: [] + summary: 修改密码 + tags: + - H5 认证 + /api/h5/refresh-token: + post: + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ModelRefreshTokenRequest' + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/ModelRefreshTokenResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + summary: 刷新 Token + tags: + - H5 认证 + /health: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/RoutesHealthResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + summary: 健康检查 + tags: + - 系统 + /ready: + get: + responses: + "200": + content: + application/json: + schema: + $ref: '#/components/schemas/RoutesHealthResponse' + description: OK + "400": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 请求参数错误 + "500": + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + description: 服务器内部错误 + summary: 就绪检查 + tags: + - 系统 diff --git a/docs/api-documentation-guide.md b/docs/api-documentation-guide.md new file mode 100644 index 0000000..6b026c7 --- /dev/null +++ b/docs/api-documentation-guide.md @@ -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) diff --git a/internal/model/shop_account_dto.go b/internal/model/shop_account_dto.go index 29c710f..2a95132 100644 --- a/internal/model/shop_account_dto.go +++ b/internal/model/shop_account_dto.go @@ -2,36 +2,35 @@ package model // ShopAccountListRequest 代理商账号列表查询请求 type ShopAccountListRequest struct { - Page int `json:"page" query:"page" validate:"omitempty,min=1"` // 页码 - PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100"` // 每页数量 - ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty,min=1"` // 店铺ID过滤 - Username string `json:"username" query:"username" validate:"omitempty,max=50"` // 用户名(模糊查询) - Phone string `json:"phone" query:"phone" validate:"omitempty,len=11"` // 手机号(精确查询) - Status *int `json:"status" query:"status" validate:"omitempty,oneof=0 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" minimum:"1" maximum:"100" description:"每页数量"` + 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" maxLength:"50" description:"用户名(模糊查询)"` + 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" description:"状态 (0:禁用, 1:启用)"` } // CreateShopAccountRequest 创建代理商账号请求 type CreateShopAccountRequest struct { - ShopID uint `json:"shop_id" validate:"required,min=1"` // 店铺ID - Username string `json:"username" validate:"required,min=3,max=50"` // 用户名 - Phone string `json:"phone" validate:"required,len=11"` // 手机号 - Password string `json:"password" validate:"required,min=8,max=32"` // 密码 + 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" required:"true" minLength:"3" maxLength:"50" description:"用户名"` + 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" required:"true" minLength:"8" maxLength:"32" description:"密码"` } -// UpdateShopAccountRequest 更新代理商账号请求 +// UpdateShopAccountRequest 更新代理商账号请求(不包含 phone 和 password,按照业务规则不允许修改) type UpdateShopAccountRequest struct { - Username string `json:"username" validate:"required,min=3,max=50"` // 用户名 - // 注意:不包含 phone 和 password,按照业务规则不允许修改 + Username string `json:"username" validate:"required,min=3,max=50" required:"true" minLength:"3" maxLength:"50" description:"用户名"` } // UpdateShopAccountPasswordRequest 修改代理商账号密码请求(管理员重置) 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 修改代理商账号状态请求 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 代理商账号响应 @@ -46,3 +45,29 @@ type ShopAccountResponse struct { CreatedAt string `json:"created_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 +} diff --git a/internal/model/shop_dto.go b/internal/model/shop_dto.go index 14b5428..e88a936 100644 --- a/internal/model/shop_dto.go +++ b/internal/model/shop_dto.go @@ -53,3 +53,18 @@ type ShopResponse struct { CreatedAt string `json:"created_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 +} diff --git a/internal/routes/health.go b/internal/routes/health.go index 714e437..609155d 100644 --- a/internal/routes/health.go +++ b/internal/routes/health.go @@ -1,25 +1,40 @@ package routes import ( - "github.com/break/junhong_cmp_fiber/pkg/response" "github.com/gofiber/fiber/v2" + + "github.com/break/junhong_cmp_fiber/pkg/openapi" + "github.com/break/junhong_cmp_fiber/pkg/response" ) -// registerHealthRoutes 注册健康检查路由 -// 不需要认证,用于负载均衡器和监控系统 -func registerHealthRoutes(app *fiber.App) { - // 健康检查 - app.Get("/health", func(c *fiber.Ctx) error { +type HealthResponse struct { + Status string `json:"status" description:"健康状态"` + Service string `json:"service,omitempty" description:"服务名称"` +} + +func registerHealthRoutes(app *fiber.App, doc *openapi.Generator) { + Register(app, doc, "", "GET", "/health", func(c *fiber.Ctx) error { return response.Success(c, fiber.Map{ "status": "healthy", "service": "junhong_cmp_fiber", }) + }, RouteSpec{ + Summary: "健康检查", + Tags: []string{"系统"}, + Input: nil, + Output: new(HealthResponse), + Auth: false, }) - // 就绪检查 - app.Get("/ready", func(c *fiber.Ctx) error { + Register(app, doc, "", "GET", "/ready", func(c *fiber.Ctx) error { return response.Success(c, fiber.Map{ "status": "ready", }) + }, RouteSpec{ + Summary: "就绪检查", + Tags: []string{"系统"}, + Input: nil, + Output: new(HealthResponse), + Auth: false, }) } diff --git a/internal/routes/routes.go b/internal/routes/routes.go index afff941..880e39f 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -4,24 +4,30 @@ import ( "github.com/gofiber/fiber/v2" "github.com/break/junhong_cmp_fiber/internal/bootstrap" + "github.com/break/junhong_cmp_fiber/pkg/openapi" ) // RegisterRoutes 路由注册总入口 // 按业务模块调用各自的路由注册函数 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. 全局路由 - registerHealthRoutes(app) + registerHealthRoutes(app, doc) // 2. Admin 域 (挂载在 /api/admin) adminGroup := app.Group("/api/admin") - RegisterAdminRoutes(adminGroup, handlers, middlewares, nil, "/api/admin") + RegisterAdminRoutes(adminGroup, handlers, middlewares, doc, "/api/admin") // 任务相关路由 (归属于 Admin 域) - registerTaskRoutes(adminGroup) + registerTaskRoutes(adminGroup, doc, "/api/admin") // 3. H5 域 (挂载在 /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) RegisterPersonalCustomerRoutes(app, handlers, middlewares.PersonalAuth) diff --git a/internal/routes/shop.go b/internal/routes/shop.go index 4b7080d..d339cb1 100644 --- a/internal/routes/shop.go +++ b/internal/routes/shop.go @@ -4,20 +4,88 @@ import ( "github.com/gofiber/fiber/v2" "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" ) func registerShopRoutes(router fiber.Router, handler *admin.ShopHandler, doc *openapi.Generator, basePath string) { - router.Get("/shops", handler.List) - router.Post("/shops", handler.Create) - router.Put("/shops/:id", handler.Update) - router.Delete("/shops/:id", handler.Delete) + 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), + 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) { - router.Get("/shop-accounts", handler.List) - router.Post("/shop-accounts", handler.Create) - router.Put("/shop-accounts/:id", handler.Update) - router.Put("/shop-accounts/:id/password", handler.UpdatePassword) - router.Put("/shop-accounts/:id/status", handler.UpdateStatus) + shopAccounts := router.Group("/shop-accounts") + groupPath := basePath + "/shop-accounts" + + Register(shopAccounts, doc, groupPath, "GET", "", handler.List, RouteSpec{ + 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, + }) } diff --git a/internal/routes/task.go b/internal/routes/task.go index dccaab3..9422889 100644 --- a/internal/routes/task.go +++ b/internal/routes/task.go @@ -1,21 +1,33 @@ package routes import ( - "github.com/break/junhong_cmp_fiber/pkg/response" "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 注册任务相关路由 -// 用于异步任务状态查询等 -func registerTaskRoutes(api fiber.Router) { - tasks := api.Group("/tasks") +type TaskStatusResponse struct { + ID string `json:"id" description:"任务ID"` + Status string `json:"status" description:"任务状态 (pending:待处理, running:执行中, completed:已完成, failed:失败)"` +} - // 获取任务状态(占位实现) - tasks.Get("/:id", func(c *fiber.Ctx) error { +func registerTaskRoutes(api fiber.Router, doc *openapi.Generator, basePath string) { + tasks := api.Group("/tasks") + groupPath := basePath + "/tasks" + + Register(tasks, doc, groupPath, "GET", "/:id", func(c *fiber.Ctx) error { taskID := c.Params("id") return response.Success(c, fiber.Map{ "id": taskID, "status": "pending", }) + }, RouteSpec{ + Summary: "查询任务状态", + Tags: []string{"任务管理"}, + Input: new(model.IDReq), + Output: new(TaskStatusResponse), + Auth: true, }) }