Files
junhong_cmp_fiber/openspec/changes/archive/2026-01-30-openapi-contract-alignment/proposal.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

6.5 KiB
Raw Blame History

Change: OpenAPI 文档契约对齐

Why

确保 OpenAPI 文档描述的响应结构与真实运行时一致,避免 SDK 生成和接口对接问题。

当前问题

  1. 响应字段名不一致

    • OpenAPI 错误响应定义为 message 字段
    • 真实运行时返回为 msg 字段
  2. 成功响应缺少 envelope

    • OpenAPI 文档直接返回 DTO schema
    • 真实运行时包裹在 {code, data, msg, timestamp}
  3. handlers 清单不完整

    • cmd/api/docs.gocmd/gendocs/main.go 清单不一致
    • 缺少部分 handlerPersonalCustomer、ShopPackageBatchAllocation、ShopPackageBatchPricing
  4. 个人客户路由未纳入文档

    • /api/c/v1 路由未使用 Register(...) 机制
    • 不在 OpenAPI 文档体系中

What Changes

4.1 响应字段名对齐

修改 OpenAPI 错误响应 schema

# ❌ 当前
components:
  schemas:
    ErrorResponse:
      properties:
        code: { type: integer }
        message: { type: string }  # 错误:应为 msg
        data: { type: object }
        timestamp: { type: string }

# ✅ 修复后
components:
  schemas:
    ErrorResponse:
      properties:
        code: { type: integer, example: 0 }
        msg: { type: string, example: "success" }  # 对齐真实字段名
        data: { type: object }
        timestamp: { type: string, format: date-time }

4.2 成功响应体现 envelope

修改成功响应格式,包裹 DTO

# ❌ 当前(直接返回 DTO
/api/admin/users:
  get:
    responses:
      200:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserDTO'

# ✅ 修复后(包裹 envelope
/api/admin/users:
  get:
    responses:
      200:
        content:
          application/json:
            schema:
              type: object
              properties:
                code: { type: integer, example: 0 }
                msg: { type: string, example: "success" }
                data:
                  $ref: '#/components/schemas/UserDTO'
                timestamp: { type: string, format: date-time }

4.3 补齐 handlers 清单

在文档生成器中补充缺失的 handler

// cmd/api/docs.go 和 cmd/gendocs/main.go

handlers := &bootstrap.Handlers{
    // ... 现有 handlers
    
    // 补充缺失的 handlers
    PersonalCustomer:             personal.NewPersonalCustomerHandler(nil),
    ShopPackageBatchAllocation:   admin.NewShopPackageBatchAllocationHandler(nil),
    ShopPackageBatchPricing:      admin.NewShopPackageBatchPricingHandler(nil),
}

4.4 个人客户路由纳入文档

改造 internal/routes/personal.go 使用 Register(...) 机制:

// ❌ 当前
func RegisterPersonalRoutes(app *fiber.App, handlers *bootstrap.Handlers) {
    api := app.Group("/api/c/v1")
    api.Get("/cards/:iccid", handlers.PersonalCustomer.GetCard)
    // ...
}

// ✅ 修复后
func RegisterPersonalRoutes(doc *openapi.Generator, basePath string, handlers *bootstrap.Handlers) {
    doc.Register(openapi.RouteSpec{
        Method:  "GET",
        Path:    "/api/c/v1/cards/:iccid",
        Handler: handlers.PersonalCustomer.GetCard,
        Summary: "获取个人客户卡详情",
        Tags:    []string{"个人客户"},
        Auth:    true,
        Input:   nil,  // 路径参数
        Output:  &dto.CardDetailResponse{},
    })
    // ...
}

Decisions

OpenAPI 生成策略

  1. 统一 envelope 包裹:所有成功响应使用 {code, data, msg, timestamp}
  2. 字段名一致:错误响应使用 msg 而非 message
  3. DTO 保持具体类型data 字段保留具体的 DTO schema
  4. 自动化 handlers 构造:文档生成时 handlers 可以传入 nil 依赖

文档生成复用

抽取公共函数避免重复:

// pkg/openapi/handlers.go (新建)
func BuildDocHandlers() *bootstrap.Handlers {
    // 文档生成用,所有依赖传 nil
    return &bootstrap.Handlers{
        Account:    admin.NewAccountHandler(nil, nil),
        Shop:       admin.NewShopHandler(nil, nil),
        // ... 所有 handlers
    }
}

cmd/api/docs.gocmd/gendocs/main.go 中复用:

handlers := openapi.BuildDocHandlers()

Impact

Breaking Changes

  • OpenAPI 文档结构变化(响应格式)
  • 需要通知 SDK 使用方重新生成 SDK
  • 前端可能需要调整响应解析逻辑(如果直接使用 OpenAPI 生成的类型)

Documentation Updates

  • 更新 docs/api-documentation-guide.md 补充 envelope 说明
  • 补充个人客户 API 路由注册示例
  • 在 API 文档中说明 envelope 格式

Testing Requirements

生成文档后对比验证:

# 1. 重新生成文档
go run cmd/gendocs/main.go

# 2. 对比差异
diff logs/openapi.yaml logs/openapi.yaml.old

# 3. 验证关键点
# - 检查响应字段名是否为 msg非 message
# - 检查成功响应是否包含 envelope
# - 检查 /api/c/v1 路由是否出现
# - 检查接口数量是否完整

Affected Specs

  • UPDATE: openspec/specs/openapi-generation/spec.md

    • 补充 envelope 包裹要求
    • 更新字段名规范
  • UPDATE: openspec/specs/personal-customer/spec.md

    • 个人客户 API 进入文档体系

Verification Checklist

编译检查

go build -o /tmp/test_gendocs ./cmd/gendocs

文档生成

go run cmd/gendocs/main.go

文档验证

检查生成的 logs/openapi.yaml

  • 错误响应字段名为 msg(非 message
  • 成功响应包含 envelope
    200:
      content:
        application/json:
          schema:
            type: object
            properties:
              code: { type: integer }
              msg: { type: string }
              data: { ... }
              timestamp: { type: string }
    
  • /api/c/v1 路由出现在文档中
  • 接口数量完整(与已注册路由一致)

示例响应验证

对比文档示例与真实响应:

文档示例

{
  "code": 0,
  "msg": "success",
  "data": {
    "id": 1,
    "username": "admin"
  },
  "timestamp": "2026-01-29T10:00:00Z"
}

真实响应curl 测试):

curl -X GET http://localhost:8080/api/admin/users/1 \
  -H "Authorization: Bearer $TOKEN"

确认字段名和结构一致。

Estimated Effort

任务 预估时间
4.1 响应字段名对齐 0.5h
4.2 成功响应 envelope 1h
4.3 补齐 handlers 清单 0.5h
4.4 个人客户路由纳入 1h
文档验证 0.5h
文档更新 0.5h

总计:约 4 小时