Files
junhong_cmp_fiber/openspec/specs/openapi-generation/spec.md
huang 409a68d60b
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m45s
feat: OpenAPI 契约对齐与框架优化
主要变更:
1. OpenAPI 文档契约对齐
   - 统一错误响应字段名为 msg(非 message)
   - 规范 envelope 响应结构(code, msg, data, timestamp)
   - 个人客户路由纳入文档体系(使用 Register 机制)
   - 新增 BuildDocHandlers() 统一管理 handler 构造
   - 确保文档生成的幂等性

2. Service 层错误处理统一
   - 全面替换 fmt.Errorf 为 errors.New/Wrap
   - 统一错误码使用规范
   - Handler 层参数校验不泄露底层细节
   - 新增错误码验证集成测试

3. 代码质量提升
   - 删除未使用的 Task handler 和路由
   - 新增代码规范检查脚本(check-service-errors.sh)
   - 新增注释路径一致性检查(check-comment-paths.sh)
   - 更新 API 文档生成指南

4. OpenSpec 归档
   - 归档 openapi-contract-alignment 变更(63 tasks)
   - 归档 service-error-unify-core 变更
   - 归档 service-error-unify-support 变更
   - 归档 code-cleanup-docs-update 变更
   - 归档 handler-validation-security 变更
   - 同步 delta specs 到主规范文件

影响范围:
- pkg/openapi: 新增 handlers.go,优化 generator.go
- internal/service/*: 48 个 service 文件错误处理统一
- internal/handler/admin: 优化参数校验错误提示
- internal/routes: 个人客户路由改造,删除 task 路由
- scripts: 新增 3 个代码检查脚本
- docs: 更新 OpenAPI 文档(15750+ 行)
- openspec/specs: 同步 3 个主规范文件

破坏性变更:无
向后兼容:是
2026-01-30 11:40:36 +08:00

268 lines
9.1 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-generation Specification
## Purpose
TBD - created by archiving change auto-generate-openapi-docs. Update Purpose after archive.
## Requirements
### Requirement: 服务启动时自动生成OpenAPI文档
系统启动时SHALL自动生成OpenAPI 3.0规范文档并保存到项目根目录。
#### Scenario: 服务正常启动时生成文档
- **WHEN** 服务启动流程执行到路由注册之后
- **THEN** 系统自动调用文档生成逻辑
- **AND** 在项目根目录生成 `openapi.yaml` 文件
- **AND** 文件内容包含所有已注册的API端点定义
#### Scenario: 文档生成失败时的优雅处理
- **WHEN** 文档生成过程中发生错误(如文件写入失败、权限问题)
- **THEN** 系统记录错误日志到应用日志
- **AND** 错误日志包含完整的错误信息和堆栈
- **AND** 服务启动流程继续执行,不因文档生成失败而中断
#### Scenario: 文档生成的时机控制
- **WHEN** 服务在任何环境下启动(开发、测试、生产)
- **THEN** 文档生成逻辑都会执行
- **AND** 无需额外的配置或启动参数
### Requirement: 文档输出路径规范
系统SHALL将生成的OpenAPI文档输出到固定的、可预测的位置。
#### Scenario: 文档保存到项目根目录
- **WHEN** 文档生成成功
- **THEN** 文件保存到项目根目录(相对于工作目录的 `./openapi.yaml`
- **AND** 如果文件已存在则覆盖旧版本
- **AND** 文件权限设置为 0644所有者可读写其他用户只读
#### Scenario: 确保输出目录存在
- **WHEN** 输出路径的父目录不存在
- **THEN** 系统自动创建必要的目录结构
- **AND** 目录权限设置为 0755
### Requirement: 复用现有生成逻辑
文档生成功能SHALL复用项目中已有的OpenAPI生成机制避免代码重复。
#### Scenario: 调用现有的Registry机制
- **WHEN** 执行文档生成
- **THEN** 使用 `pkg/openapi.Generator` 创建文档生成器
- **AND** 调用 `internal/routes` 中的路由注册函数
- **AND** 传入非nil的Generator实例以激活文档收集逻辑
- **AND** 使用Generator的Save方法输出YAML文件
#### Scenario: 模拟路由注册但不启动服务
- **WHEN** 生成文档时调用路由注册函数
- **THEN** 创建临时的Fiber应用实例用于路由注册
- **AND** 传入nil的依赖项因为不会执行实际的Handler逻辑
- **AND** 注册完成后丢弃Fiber应用实例不调用Listen
### Requirement: 向后兼容独立生成工具
系统SHALL保留独立的文档生成工具支持离线生成文档的用例。
#### Scenario: 通过make命令生成文档
- **WHEN** 用户执行 `make docs` 命令
- **THEN** 调用 `cmd/gendocs/main.go`
- **AND** 生成文档到指定位置(默认 `./docs/admin-openapi.yaml`
- **AND** 生成过程独立于服务运行状态
#### Scenario: 独立工具与自动生成共享代码
- **WHEN** 独立工具和自动生成都需要执行文档生成
- **THEN** 两者调用相同的底层生成函数
- **AND** 通过参数区分输出路径
- **AND** 避免逻辑重复
### Requirement: 响应格式规范
系统 SHALL 在 OpenAPI 文档中正确体现统一的响应 envelope 格式。
#### Scenario: 成功响应包裹 envelope
- **WHEN** 接口定义了 Output DTO
- **THEN** OpenAPI 文档中的成功响应包含以下结构:
```yaml
properties:
code:
type: integer
example: 0
description: 响应码
msg:
type: string
example: success
description: 响应消息
data:
$ref: '#/components/schemas/OutputDTO'
timestamp:
type: string
format: date-time
description: 时间戳
```
#### Scenario: 错误响应字段名对齐
- **WHEN** 生成错误响应 schema
- **THEN** 使用 `msg` 字段名(与真实运行时一致)
- **AND** 不使用 `message` 字段名
#### Scenario: 无返回数据的接口
- **WHEN** 接口的 Output 为 nil如删除操作
- **THEN** `data` 字段类型设为 `null`
- **AND** 保持 envelope 结构完整
#### Scenario: DTO 定义保持简洁
- **WHEN** 开发者定义 DTO
- **THEN** 只需定义 `data` 字段的内容
- **AND** 无需在 DTO 中包含 envelope 字段code、msg、timestamp
### Requirement: 错误响应字段名必须为 msg
OpenAPI 文档中的错误响应 SHALL 使用 `msg` 字段而非 `message`,与真实运行时的 Response 结构体保持一致。
#### Scenario: 错误响应使用 msg 字段
- **WHEN** 生成 OpenAPI 文档的错误响应 schema
- **THEN** ErrorResponse 包含 `msg` 字段(类型为 string
- **AND** ErrorResponse 不包含 `message` 字段
#### Scenario: 生成的文档与真实响应一致
- **WHEN** API 返回错误响应
- **THEN** 响应 JSON 包含 `msg` 字段
- **AND** OpenAPI 文档中的 schema 定义也使用 `msg` 字段
- **AND** 字段名完全匹配
### Requirement: 成功响应必须包裹在 envelope 中
所有成功响应 SHALL 包裹在统一的 envelope 结构中:`{code, msg, data, timestamp}`。
#### Scenario: 成功响应包含 envelope 结构
- **WHEN** 生成接口的 200 响应 schema
- **THEN** 响应 schema 包含以下字段:
- `code` (integer, example: 0)
- `msg` (string, example: "success")
- `data` (原始 DTO schema)
- `timestamp` (string, format: date-time)
#### Scenario: data 字段包含实际的 DTO
- **WHEN** 接口返回数据(如用户列表、详情)
- **THEN** OpenAPI 的 `data` 字段引用实际的 DTO schema
- **AND** DTO schema 不被修改(保持原结构)
#### Scenario: 无返回数据的接口 data 为 null
- **WHEN** 接口无返回数据(如删除操作)
- **THEN** OpenAPI 的 `data` 字段类型为 `null`
- **AND** 响应仍包含 `code`、`msg`、`timestamp` 字段
### Requirement: envelope 包裹适用于所有接口类型
envelope 包裹 SHALL 适用于普通接口和文件上传接口。
#### Scenario: 普通接口使用 envelope
- **WHEN** 通过 `AddOperation` 添加接口
- **THEN** 生成的 200 响应包含 envelope 结构
#### Scenario: 文件上传接口使用 envelope
- **WHEN** 通过 `AddMultipartOperation` 添加文件上传接口
- **THEN** 生成的 200 响应包含 envelope 结构
- **AND** envelope 结构与普通接口一致
### Requirement: 所有 handlers 必须在文档生成器中注册
文档生成器 SHALL 包含所有已实现的 handlers确保接口文档完整。
#### Scenario: handlers 清单完整性
- **WHEN** 生成 OpenAPI 文档
- **THEN** 所有 handler 的接口都出现在文档中
- **AND** 不存在已实现但未出现在文档的接口
#### Scenario: 新增 handler 时同步更新
- **WHEN** 新增 handler如 `PersonalCustomer`、`ShopPackageBatchAllocation`
- **THEN** 必须在 `BuildDocHandlers()` 中添加对应的构造代码
- **AND** 重新生成文档后接口出现在 OpenAPI 文件中
### Requirement: handlers 构造函数统一管理
handlers 的构造逻辑 SHALL 由公共函数 `BuildDocHandlers()` 统一管理,避免重复。
#### Scenario: cmd/api/docs.go 复用 BuildDocHandlers
- **WHEN** 在 `cmd/api/docs.go` 中需要构造 handlers
- **THEN** 调用 `openapi.BuildDocHandlers()` 获取 handlers
- **AND** 不在本文件中重复构造
#### Scenario: cmd/gendocs/main.go 复用 BuildDocHandlers
- **WHEN** 在 `cmd/gendocs/main.go` 中需要构造 handlers
- **THEN** 调用 `openapi.BuildDocHandlers()` 获取 handlers
- **AND** 不在本文件中重复构造
#### Scenario: BuildDocHandlers 传入 nil 依赖
- **WHEN** `BuildDocHandlers()` 构造 handlers
- **THEN** 所有 handler 构造函数的依赖参数传入 `nil`
- **AND** 因为文档生成不执行 handler 逻辑nil 依赖不会导致运行时错误
### Requirement: 个人客户路由必须使用 Register 机制
个人客户 API (`/api/c/v1`) SHALL 使用 `Register(...)` 机制注册,纳入 OpenAPI 文档体系。
#### Scenario: RegisterPersonalRoutes 使用 Register 机制
- **WHEN** 调用 `RegisterPersonalRoutes` 注册个人客户路由
- **THEN** 使用 `doc.Register(RouteSpec{...})` 注册每个路由
- **AND** 不直接调用 Fiber 的 `app.Get/Post` 方法
#### Scenario: 个人客户路由出现在文档中
- **WHEN** 生成 OpenAPI 文档
- **THEN** 文档包含 `/api/c/v1` 路径的接口
- **AND** 每个接口包含正确的 Summary、Tags、Auth 信息
#### Scenario: 个人客户路由的元数据完整
- **WHEN** 注册个人客户路由
- **THEN** 每个 RouteSpec 包含:
- MethodGET/POST/PUT/DELETE
- Path完整路径
- Handlerfiber.Handler
- Summary中文摘要
- Tags包含 "个人客户"
- Authtrue/false
- Input请求 DTO 或 nil
- Output响应 DTO
### Requirement: 文档生成的幂等性
文档生成 SHALL 是幂等的,相同的代码生成相同的文档。
#### Scenario: 重复生成文档内容一致
- **WHEN** 多次运行 `go run cmd/gendocs/main.go`
- **THEN** 生成的 `openapi.yaml` 内容完全一致
- **AND** 文件 hash 值相同(除 timestamp 等动态字段外)
#### Scenario: 代码未变更时文档不变
- **WHEN** 代码handlers、路由、DTO未变更
- **THEN** 重新生成的文档与之前的文档一致
- **AND** 不会因为生成逻辑的随机性导致差异