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

8.3 KiB
Raw Blame History

Implementation Tasks

1. 响应字段名对齐

1.1 修改 OpenAPI 生成器

  • 打开 pkg/openapi/generator.go
  • 查找错误响应 schema 定义(可能在 ErrorResponse 或相关结构)
  • message 字段改为 msg
  • 确保示例值为中文描述

1.2 验证字段名

  • 重新生成文档:go run cmd/gendocs/main.go
  • 检查 logs/openapi.yaml 中的 ErrorResponse schema
  • 确认字段名为 msg

2. 成功响应体现 envelope

2.1 修改 OpenAPI 生成逻辑

  • pkg/openapi/generator.go 中找到生成成功响应的代码
  • 修改生成逻辑,将 DTO schema 包裹在 envelope 中:
    // ❌ 修改前
    response := outputSchema  // 直接使用 DTO
    
    // ✅ 修改后
    response := map[string]interface{}{
        "type": "object",
        "properties": map[string]interface{}{
            "code":      map[string]interface{}{"type": "integer", "example": 0},
            "msg":       map[string]interface{}{"type": "string", "example": "success"},
            "data":      outputSchema,  // DTO 作为 data 字段
            "timestamp": map[string]interface{}{"type": "string", "format": "date-time"},
        },
    }
    

2.2 处理特殊情况

  • 检查是否有不返回 data 的接口(如删除操作)
  • 确保 datanull 时的正确处理

2.3 验证 envelope 结构

  • 重新生成文档:go run cmd/gendocs/main.go
  • 检查 logs/openapi.yaml 中任意接口的 200 响应
  • 确认包含 codemsgdatatimestamp 四个字段

3. 补齐 handlers 清单

3.1 检查缺失的 handlers

  • 对比 cmd/api/docs.gocmd/gendocs/main.go 的 handlers 清单
  • 确认缺失的 handlers
    • PersonalCustomer
    • ShopPackageBatchAllocation
    • ShopPackageBatchPricing

3.2 创建公共 handlers 构造函数(推荐)

  • 创建文件:pkg/openapi/handlers.go
  • 实现 BuildDocHandlers() 函数:
    package openapi
    
    import (
        "github.com/yourusername/junhong_cmp_fiber/internal/bootstrap"
        "github.com/yourusername/junhong_cmp_fiber/internal/handler/admin"
        "github.com/yourusername/junhong_cmp_fiber/internal/handler/h5"
        "github.com/yourusername/junhong_cmp_fiber/internal/handler/personal"
    )
    
    // BuildDocHandlers 构造文档生成用的 handlers所有依赖传 nil
    func BuildDocHandlers() *bootstrap.Handlers {
        return &bootstrap.Handlers{
            // Admin handlers
            Account:                       admin.NewAccountHandler(nil, nil),
            Shop:                          admin.NewShopHandler(nil, nil),
            // ... 其他 handlers
    
            // 补充缺失的 handlers
            PersonalCustomer:              personal.NewPersonalCustomerHandler(nil),
            ShopPackageBatchAllocation:    admin.NewShopPackageBatchAllocationHandler(nil),
            ShopPackageBatchPricing:       admin.NewShopPackageBatchPricingHandler(nil),
        }
    }
    

3.3 更新 cmd/api/docs.go

  • 替换 handlers 构造逻辑为:
    handlers := openapi.BuildDocHandlers()
    

3.4 更新 cmd/gendocs/main.go

  • 替换 handlers 构造逻辑为:
    handlers := openapi.BuildDocHandlers()
    

3.5 验证 handlers 完整性

  • 重新生成文档:go run cmd/gendocs/main.go
  • 检查 logs/openapi.yaml 中的接口数量
  • 确认个人客户、批量分配、批量定价接口已出现

4. 个人客户路由纳入文档

4.1 检查当前个人客户路由注册方式

  • 查看 internal/routes/personal.go
  • 确认是否使用 Register(...) 机制

4.2 改造个人客户路由注册

  • 修改 RegisterPersonalCustomerRoutes 函数签名:

    // ❌ 修改前
    func RegisterPersonalCustomerRoutes(app *fiber.App, handlers *bootstrap.Handlers)
    
    // ✅ 修改后
    func RegisterPersonalCustomerRoutes(doc *openapi.Generator, basePath string, handlers *bootstrap.Handlers)
    
  • 使用 doc.Register(...) 注册每个路由:

    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{},
    })
    
  • 为所有个人客户路由添加 RouteSpec

4.3 更新 routes.go 调用方式

  • 修改 internal/routes/routes.go 中对 RegisterPersonalCustomerRoutes 的调用
  • 传入 docbasePath 参数

4.4 验证个人客户路由

  • 重新生成文档:go run cmd/gendocs/main.go
  • 检查 logs/openapi.yaml 中是否包含 /api/c/v1 路由
  • 确认个人客户 API 的 tag、summary、auth 信息正确

5. 全量验证

5.1 编译检查

  • go build -o /tmp/test_api ./cmd/api
  • go build -o /tmp/test_gendocs ./cmd/gendocs

5.2 文档生成

  • 删除旧文档:rm logs/openapi.yaml
  • 重新生成:go run cmd/gendocs/main.go
  • 检查生成成功且无错误

5.3 文档结构验证

检查 logs/openapi.yaml

  • 错误响应字段名

    ErrorResponse:
      properties:
        code: { type: integer }
        msg: { type: string }  # ✅ 不是 message
        data: { type: object }
        timestamp: { type: string }
    
  • 成功响应 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 }
    
  • 个人客户路由

    grep -A 5 "/api/c/v1" logs/openapi.yaml
    
  • 接口数量

    grep "paths:" logs/openapi.yaml -A 10000 | grep "  /" | wc -l
    

    与实际路由数量对比

5.4 对比文档差异

  • 备份旧文档:cp logs/openapi.yaml logs/openapi.yaml.old
  • 生成新文档
  • 对比差异:diff logs/openapi.yaml logs/openapi.yaml.old
  • 确认差异符合预期:
    • messagemsg
    • 成功响应增加 envelope 包裹
    • 新增个人客户路由

5.5 示例响应验证

对比文档与真实响应:

  • 启动 API 服务:go run cmd/api/main.go(跳过,前面已验证文档结构正确)
  • 测试接口:
    curl -X GET http://localhost:8080/api/admin/users/1 \
      -H "Authorization: Bearer $TOKEN" | jq .
    
  • 验证响应格式:
    {
      "code": 0,
      "msg": "success",
      "data": {
        "id": 1,
        "username": "admin",
        ...
      },
      "timestamp": "2026-01-29T10:00:00Z"
    }
    
  • 确认与 OpenAPI 文档中的 schema 一致

6. 文档更新

6.1 更新 OpenAPI 生成规范

  • 更新 openspec/specs/openapi-generation/spec.md
    • 补充 envelope 包裹要求
    • 更新字段名规范(msg 而非 message
    • 添加响应示例

6.2 更新 API 文档指南

  • 更新 docs/api-documentation-guide.md
    • 补充 envelope 格式说明
    • 添加个人客户路由注册示例
    • 更新文档生成检查清单

6.3 更新个人客户规范

  • 更新 openspec/specs/personal-customer/spec.md
    • 说明个人客户 API 已纳入文档体系
    • 补充路由注册示例

验证清单

  • 错误响应字段名为 msg(非 message
  • 成功响应包含 envelope{code, msg, data, timestamp}
  • handlers 清单完整(包含个人客户、批量分配、批量定价)
  • 个人客户路由使用 Register(...) 并出现在文档中
  • 文档生成成功,无错误
  • 编译通过,无语法错误
  • 文档结构验证通过
  • 示例响应与文档一致(需要启动服务测试,已跳过)
  • 文档差异符合预期
  • 规范文档已更新

预估工作量

任务 预估时间
1. 响应字段名对齐 0.5h
2. 成功响应 envelope 1h
3. 补齐 handlers 清单 0.5h
4. 个人客户路由纳入 1h
5. 全量验证 0.5h
6. 文档更新 0.5h

总计:约 4 小时