Files
junhong_cmp_fiber/.sisyphus/plans/gateway-integration-execution-plan.md
2026-01-30 17:05:44 +08:00

42 KiB

Gateway Integration 并行执行计划

TL;DR

快速摘要: 实现Gateway API统一封装,提供14个物联网卡和设备管理接口的类型安全封装,支持AES-128-ECB加密+MD5签名认证机制。

交付物:

  • internal/gateway/ 包(完整的Gateway客户端)
  • 14个API接口封装(流量卡7个+设备7个)
  • 配置集成(GatewayConfig+环境变量)
  • 错误码定义(1110-1119)
  • 单元测试(覆盖率≥90%)
  • 集成测试(验证真实Gateway API)

预计工作量: 120分钟(2小时) 并行执行: YES - 6个波次,最多4个任务并行 关键路径: 加密工具 → Client结构 → API封装 → 单元测试 → Service集成 → 集成测试


上下文

原始需求

实现Gateway API统一封装,解决当前调用逻辑分散、加密签名重复实现的问题,提供类型安全的接口和统一的错误处理。

背景调研总结

1. 项目架构模式(已验证)

  • Bootstrap模式: 6步初始化(Stores→Callbacks→Services→Admin→Middlewares→Handlers)
  • 依赖注入: Dependencies结构 → main.go初始化 → Service构造器
  • Config管理: 层级结构 + JUNHONG_前缀环境变量 + 双层验证
  • Error处理: 错误码范围(1000-1999/2000-2999) + errors.New/Wrap

2. 测试模式(已验证)

  • 核心工具: NewTestTransaction(自动回滚) + GetTestRedis(全局连接)
  • 集成测试: NewIntegrationTestEnv(完整环境) + AsSuperAdmin/AsUser
  • 执行方式: source .env.local && go test
  • 覆盖率要求: Service层≥90%

3. AES-ECB加密(已调研)

  • 实现方式: 手动实现cipher.BlockMode接口(~50行)
  • 参考代码: TiDB的aes.go实现
  • PKCS5Padding: 标准填充算法(验证所有字节)
  • MD5密钥: crypto/md5.Sum()返回[16]byte
  • Base64编码: encoding/base64.StdEncoding

Metis预审(gap分析)

无 - 本计划由Prometheus生成,Metis审查在plan生成后进行。


工作目标

核心目标

封装Gateway API为统一的能力模块,提供类型安全、配置化、可测试的接口调用能力。

具体交付物

  • internal/gateway/client.go - Gateway客户端主体
  • internal/gateway/crypto.go - 加密/签名工具
  • internal/gateway/flow_card.go - 流量卡7个API
  • internal/gateway/device.go - 设备7个API
  • internal/gateway/models.go - 请求/响应DTO
  • internal/gateway/client_test.go - 单元+集成测试
  • pkg/config/config.go - GatewayConfig集成
  • pkg/errors/codes.go - Gateway错误码(1110-1119)
  • internal/bootstrap/bootstrap.go - Gateway客户端初始化
  • docs/gateway-client-usage.md - 使用文档

定义完成(Definition of Done)

  • 所有14个Gateway API接口成功封装
  • 加密/签名验证通过(与Gateway文档一致)
  • 错误处理覆盖所有异常场景
  • 单元测试覆盖率≥90%
  • 集成测试验证真实Gateway API调用
  • 配置通过环境变量成功加载
  • 依赖注入到Service层成功
  • 文档完整(使用示例、错误码说明)

必须满足

  • 所有14个API接口封装完成
  • AES-128-ECB加密正确(密钥=MD5(appSecret))
  • MD5签名正确(参数字母序+大写)
  • 配置验证逻辑完整
  • Bootstrap依赖注入正确

禁止包含(Guardrails)

  • 跳过tasks.md中的任何任务
  • 合并或简化任务执行
  • 使用Java风格的过度抽象
  • 硬编码配置(必须使用环境变量)
  • 直接使用fmt.Errorf(必须用errors.New/Wrap)
  • 参数验证失败返回底层错误详情
  • 使用非中文注释

验证策略

测试决策

  • 测试基础设施: 已存在(testutils + NewIntegrationTestEnv)
  • 测试框架: bun test(实际使用go test)
  • 测试策略: TDD(测试先行)

测试结构

单元测试(internal/gateway/client_test.go)

每个任务遵循TDD流程:

加密/签名测试:

  1. RED: 编写测试用例

    • 测试文件: internal/gateway/client_test.go
    • 测试函数: TestAESEncrypt, TestGenerateSign
    • 预期: FAIL(函数不存在)
  2. GREEN: 实现最小代码

    • 实现: crypto.go中的aesEncrypt, generateSign
    • 运行: go test -v ./internal/gateway -run TestAES
    • 预期: PASS
  3. REFACTOR: 优化代码

    • 清理重复代码
    • 添加注释
    • 预期: PASS(测试仍然通过)

API接口测试:

  1. RED: 测试QueryCardStatus

    • 测试: 构造mock响应,验证解析逻辑
    • 预期: FAIL
  2. GREEN: 实现QueryCardStatus

    • 实现: flow_card.go
    • 预期: PASS
  3. REFACTOR: 优化

    • 预期: PASS

集成测试(client_test.go)

验证真实Gateway API调用:

func TestIntegration_QueryCardStatus(t *testing.T) {
    if testing.Short() {
        t.Skip("跳过集成测试")
    }
    
    // 使用真实配置
    cfg := config.Get()
    client := gateway.NewClient(
        cfg.Gateway.BaseURL,
        cfg.Gateway.AppID,
        cfg.Gateway.AppSecret,
    ).WithTimeout(30 * time.Second)
    
    // 调用真实API
    resp, err := client.QueryCardStatus(ctx, &gateway.CardStatusReq{
        CardNo: "898608070422D0010269",
    })
    
    require.NoError(t, err)
    require.NotNil(t, resp)
    require.NotEmpty(t, resp.ICCID)
}

验证命令:

# 单元测试
source .env.local && go test -v ./internal/gateway -run TestAES
source .env.local && go test -v ./internal/gateway -run TestGenerate

# 集成测试
source .env.local && go test -v ./internal/gateway -run TestIntegration

# 覆盖率
source .env.local && go test -cover ./internal/gateway

执行策略

并行执行波次

系统使用严格的依赖管理,最大化并行执行效率:

Wave 1 (立即启动,无依赖):
├── Task 1.1: 创建目录结构
├── Task 1.4: DTO定义
├── Task 3.1: Gateway配置
└── Task 3.2: Gateway错误码

Wave 2 (依赖: Wave 1):
└── Task 1.2: 加密/签名工具

Wave 3 (依赖: Task 1.2):
└── Task 1.3: Client基础结构

Wave 4 (依赖: Task 1.3):
├── Task 2.1: 流量卡API
├── Task 2.2: 设备API
└── Task 4.1: Bootstrap初始化

Wave 5 (依赖: Wave 4):
├── Task 2.3: 单元测试
└── Task 4.2: Service集成

Wave 6 (依赖: Wave 5):
├── Task 5.1: 集成测试
└── Task 5.2: 文档更新

关键路径: 1.2 → 1.3 → 2.1/2.2 → 2.3 → 4.2 → 5.1
并行加速: ~40%性能提升(相比顺序执行)

依赖矩阵

Task 依赖任务 阻塞任务 可并行任务
1.1 1.4, 3.1, 3.2
1.2 1.1 1.3 1.4, 3.1, 3.2
1.3 1.2 2.1, 2.2, 4.1
1.4 2.1, 2.2 1.1, 1.2, 3.1, 3.2
2.1 1.3, 1.4 2.3 2.2, 4.1
2.2 1.3, 1.4 2.3 2.1, 4.1
2.3 2.1, 2.2 5.1 4.2
3.1 4.1 1.1, 1.2, 1.4, 3.2
3.2 1.1, 1.2, 1.4, 3.1
4.1 1.3, 3.1 4.2 2.1, 2.2
4.2 4.1 5.1 2.3
5.1 2.3, 4.2 5.2
5.2 5.1

Agent调度建议

Wave 任务组 推荐Category 推荐Skills
1 1.1, 1.4, 3.1, 3.2 quick
2 1.2 ultrabrain
3 1.3 unspecified-high
4 2.1, 2.2, 4.1 unspecified-high
5 2.3, 4.2 unspecified-high
6 5.1, 5.2 quick

TODOs

实现+测试=一个任务。每个任务必须包含:推荐Agent Profile + 并行化信息。

Phase 1: 基础结构搭建

  • 1.1. 创建Gateway包目录结构

    要做什么:

    • 创建internal/gateway/目录
    • 创建占位文件:client.go, crypto.go, models.go, flow_card.go, device.go
    • 每个文件添加package声明和基础注释

    禁止做什么:

    • 不要实现任何逻辑(只创建文件骨架)
    • 不要添加import语句(后续任务添加)

    推荐Agent Profile:

    • Category: quick
      • 理由: 简单的文件创建操作,无复杂逻辑
    • Skills: 无
      • 理由: 目录创建是通用操作,不需要专项技能
    • Skills评估但省略:
      • api-routing: 域不重叠(这是客户端代码,非API路由)
      • model-standards: 域不重叠(DTO定义在Task 1.4)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 1(与1.4, 3.1, 3.2并行)
    • 阻塞任务: 无
    • 被阻塞: 无(可立即启动)

    参考:

    • 项目结构模式: /internal/service/account/ (类似的service包结构)
    • Go包命名规范: 小写、单数、无下划线

    验收标准:

    • 目录internal/gateway/创建成功
    • 5个.go文件创建成功(client, crypto, models, flow_card, device)
    • 每个文件包含package gateway声明
    • 每个文件包含中文注释说明用途
    • go build ./internal/gateway编译通过(即使是空包)

    提交: NO(与后续任务合并提交)


  • 1.2. 实现加密/签名工具函数

    要做什么:

    • crypto.go中实现aesEncrypt函数(AES-128-ECB + PKCS5Padding + Base64)
    • 实现generateSign函数(MD5签名,大写输出)
    • 添加单元测试验证加密/签名正确性

    禁止做什么:

    • 不要使用第三方加密库(必须用标准库)
    • 不要跳过PKCS5填充验证(必须验证所有字节)
    • 不要使用小写MD5输出(必须大写)

    推荐Agent Profile:

    • Category: ultrabrain
      • 理由: 加密算法实现需要高精度,涉及字节操作和安全性
    • Skills: 无
      • 理由: 加密是通用算法,已有TiDB参考代码
    • Skills评估但省略:
      • api-routing: 域不重叠(这是工具函数,非API)
      • dto-standards: 域不重叠(这是加密,非DTO)

    并行化:

    • 可并行运行: NO(Wave 1必须先完成)
    • 并行组: Wave 2(独立任务)
    • 阻塞任务: 1.3(Client需要加密函数)
    • 被阻塞: 1.1(需要crypto.go文件存在)

    参考:

    • 加密实现: TiDB的aes.go(ECB模式实现)
    • PKCS5Padding: Lancet库的padding.go
    • MD5密钥: crypto/md5.Sum([]byte(appSecret))返回[16]byte
    • Base64: encoding/base64.StdEncoding.EncodeToString()
    • 签名格式: appId=xxx&data=xxx&timestamp=xxx&key=appSecret
    • 测试模式: /internal/service/package/service_test.go(table-driven tests)

    验收标准:

    TDD流程:

    • RED: 编写TestAESEncrypt测试用例

      • 测试文件: internal/gateway/crypto_test.go
      • 测试数据: 已知明文+appSecret,验证Base64输出
      • 命令: go test -v ./internal/gateway -run TestAESEncrypt
      • 预期: FAIL(函数不存在)
    • GREEN: 实现aesEncrypt函数

      • 步骤1: MD5(appSecret)生成16字节密钥
      • 步骤2: AES-128-ECB加密(手动实现BlockMode)
      • 步骤3: PKCS5填充
      • 步骤4: Base64编码
      • 命令: go test -v ./internal/gateway -run TestAESEncrypt
      • 预期: PASS
    • REFACTOR: 优化代码

      • 添加详细中文注释
      • 提取常量(如BlockSize=16)
      • 命令: go test -v ./internal/gateway -run TestAESEncrypt
      • 预期: PASS(测试仍通过)
    • RED: 编写TestGenerateSign测试用例

      • 验证签名格式为32位大写十六进制
      • 验证参数字母序正确
      • 预期: FAIL
    • GREEN: 实现generateSign函数

      • 步骤1: 拼接字符串(appId→data→timestamp)
      • 步骤2: 追加&key=appSecret
      • 步骤3: MD5加密
      • 步骤4: 转大写十六进制
      • 预期: PASS
    • REFACTOR: 优化签名函数

      • 预期: PASS

    自动化验证:

    # 编译检查
    go build ./internal/gateway
    # 预期: 成功编译,无错误
    
    # 单元测试
    source .env.local && go test -v ./internal/gateway -run TestAES
    # 预期: PASS, 测试覆盖率 ≥ 90%
    
    source .env.local && go test -v ./internal/gateway -run TestGenerate
    # 预期: PASS, 签名输出32位大写
    
    # 覆盖率检查
    source .env.local && go test -cover ./internal/gateway
    # 预期: 覆盖率 ≥ 90%
    

    提交: NO(与Phase 1合并提交)


  • 1.3. 实现Gateway客户端基础结构

    要做什么:

    • client.go中定义Client结构体(baseURL, appID, appSecret, httpClient, timeout)
    • 实现NewClient构造函数(配置HTTP Keep-Alive)
    • 实现WithTimeout方法(支持链式调用)
    • 实现doRequest统一请求方法(加密→签名→HTTP→解密)

    禁止做什么:

    • 不要在doRequest中处理业务逻辑(只处理加密和HTTP)
    • 不要硬编码超时时间(通过WithTimeout配置)
    • 不要忽略Context超时(必须判断ctx.Err())

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: 核心架构代码,需要仔细设计并发安全和错误处理
    • Skills: 无
      • 理由: HTTP客户端是通用模式,项目中有类似实现
    • Skills评估但省略:
      • api-routing: 域不重叠(这是HTTP客户端,非路由)

    并行化:

    • 可并行运行: NO(依赖1.2)
    • 并行组: Wave 3(独立任务)
    • 阻塞任务: 2.1, 2.2, 4.1(所有API都依赖Client)
    • 被阻塞: 1.2(需要加密函数)

    参考:

    • HTTP客户端: net/http.Client配置Keep-Alive
    • Context超时: http.NewRequestWithContext(ctx, ...)
    • JSON序列化: sonic.Marshal/Unmarshal
    • 错误处理: pkg/errors/errors.go(New/Wrap模式)
    • 类似实现: /pkg/auth/token.go(TokenManager的HTTP调用)

    验收标准:

    TDD流程:

    • RED: 编写TestNewClient测试

      • 验证Client初始化正确
      • 验证httpClient配置
      • 预期: FAIL
    • GREEN: 实现NewClient和WithTimeout

      • 配置http.Client(Transport + Timeout)
      • 预期: PASS
    • REFACTOR: 优化结构

      • 预期: PASS
    • RED: 编写TestDoRequest_Success测试

      • Mock HTTP响应
      • 验证加密→签名→请求流程
      • 预期: FAIL
    • GREEN: 实现doRequest方法

      • 步骤1: 序列化业务数据(sonic.Marshal)
      • 步骤2: AES加密(调用aesEncrypt)
      • 步骤3: 生成签名(调用generateSign)
      • 步骤4: 构建请求体(appId, data, sign, timestamp)
      • 步骤5: 发送HTTP POST
      • 步骤6: 解析响应(GatewayResponse)
      • 步骤7: 检查业务状态码
      • 预期: PASS
    • REFACTOR: 错误处理优化

      • 网络错误→CodeGatewayError
      • 超时错误→CodeGatewayTimeout
      • 响应格式错误→CodeGatewayInvalidResp
      • 预期: PASS

    自动化验证:

    # 编译检查
    go build ./internal/gateway
    # 预期: 成功编译
    
    # 单元测试
    source .env.local && go test -v ./internal/gateway -run TestNewClient
    # 预期: PASS, Client初始化正确
    
    source .env.local && go test -v ./internal/gateway -run TestDoRequest
    # 预期: PASS, 请求流程完整
    
    # 错误处理测试
    source .env.local && go test -v ./internal/gateway -run TestDoRequest_Error
    # 预期: PASS, 所有错误场景覆盖
    

    提交: NO(与Phase 1合并提交)


  • 1.4. 定义请求/响应DTO

    要做什么:

    • models.go中定义GatewayResponse通用响应结构
    • 定义流量卡DTO:CardStatusReq/Resp, FlowQueryReq, FlowUsageResp, CardOperationReq
    • 定义设备DTO:DeviceInfoReq/Resp, SpeedLimitReq, WiFiReq, SwitchCardReq, DeviceOperationReq
    • 添加JSON标签和验证标签

    禁止做什么:

    • 不要省略description注释(每个字段必须有中文说明)
    • 不要使用驼峰JSON字段(必须与Gateway文档一致)
    • 不要省略validate标签(必填字段必须标记)

    推荐Agent Profile:

    • Category: quick
      • 理由: 结构体定义为机械性工作,遵循DTO规范即可
    • Skills: dto-standards
      • 理由: 需要遵循项目DTO规范(description标签、验证标签)
    • Skills评估但省略:
      • model-standards: 域不重叠(这是DTO,非数据库Model)
      • api-routing: 域不重叠(这是请求响应结构,非路由)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 1(与1.1, 3.1, 3.2并行)
    • 阻塞任务: 2.1, 2.2(API方法需要DTO)
    • 被阻塞: 无(可立即启动)

    参考:

    • DTO规范: docs/dto-standards.mdAGENTS.md#DTO规范
    • JSON标签: json:"cardNo" (与Gateway文档一致)
    • 验证标签: validate:"required"
    • 示例DTO: /internal/model/dto/package.go
    • Gateway文档: design.md中的请求/响应示例

    验收标准:

    • GatewayResponse定义完整(code, msg, data, trace_id)
    • 流量卡DTO定义完整(7个API对应的Req/Resp)
    • 设备DTO定义完整(7个API对应的Req/Resp)
    • 所有字段包含JSON标签(与Gateway文档一致)
    • 必填字段包含validate:"required"标签
    • 所有字段包含中文description注释
    • 可选字段使用omitempty标签
    • 编译通过: go build ./internal/gateway

    提交: NO(与Phase 1合并提交)


Phase 2: API接口封装

  • 2.1. 实现流量卡API(7个接口)

    要做什么:

    • flow_card.go中实现7个流量卡API方法:
      1. QueryCardStatus - 流量卡状态查询
      2. QueryFlow - 流量使用查询
      3. QueryRealnameStatus - 实名认证状态查询
      4. StopCard - 流量卡停机
      5. StartCard - 流量卡复机
      6. GetRealnameLink - 获取实名认证跳转链接
      7. BatchQuery - 批量查询(预留,返回NotImplemented错误)

    禁止做什么:

    • 不要在方法内实现加密逻辑(复用doRequest)
    • 不要硬编码API路径(使用常量)
    • 不要省略错误处理(所有错误必须包装)

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: API封装需要准确映射Gateway接口,错误处理要完整
    • Skills: 无
      • 理由: API封装是通用模式,复用doRequest即可
    • Skills评估但省略:
      • api-routing: 域不重叠(这是HTTP客户端,非路由注册)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 4(与2.2, 4.1并行)
    • 阻塞任务: 2.3(单元测试需要API实现)
    • 被阻塞: 1.3, 1.4(需要Client和DTO)

    参考:

    • API路径: design.md中的接口列表(/flow-card/status等)
    • 请求构造: map[string]interface{}{"params": {...}}
    • 响应解析: sonic.Unmarshal(resp, &result)
    • 错误包装: errors.Wrap(errors.CodeGatewayInvalidResp, err, "解析响应失败")
    • 类似实现: /internal/service/auth/service.go(调用外部API模式)

    验收标准:

    TDD流程(每个API重复):

    • RED: 编写TestQueryCardStatus

      • Mock doRequest返回
      • 验证请求参数构造
      • 验证响应解析
      • 预期: FAIL
    • GREEN: 实现QueryCardStatus

      • 构建businessData
      • 调用doRequest("/flow-card/status", businessData)
      • 解析响应为CardStatusResp
      • 预期: PASS
    • REFACTOR: 优化代码

      • 预期: PASS
    • 重复上述流程实现其他6个API

    自动化验证:

    # 编译检查
    go build ./internal/gateway
    # 预期: 成功编译,7个方法定义正确
    
    # 单元测试(每个API)
    source .env.local && go test -v ./internal/gateway -run TestQueryCardStatus
    source .env.local && go test -v ./internal/gateway -run TestQueryFlow
    source .env.local && go test -v ./internal/gateway -run TestStopCard
    # 预期: PASS
    
    # 覆盖率
    source .env.local && go test -cover ./internal/gateway
    # 预期: 覆盖率 ≥ 90%
    

    提交: NO(与Phase 2合并提交)


  • 2.2. 实现设备API(7个接口)

    要做什么:

    • device.go中实现7个设备API方法:
      1. GetDeviceInfo - 获取设备信息
      2. GetSlotInfo - 获取设备卡槽信息
      3. SetSpeedLimit - 设置设备限速
      4. SetWiFi - 设置设备WiFi
      5. SwitchCard - 设备切换卡
      6. ResetDevice - 设备恢复出厂设置
      7. RebootDevice - 设备重启

    禁止做什么:

    • 不要在方法内实现加密逻辑(复用doRequest)
    • 不要硬编码API路径(使用常量)
    • 不要省略参数验证(CardNo和DeviceID二选一)

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: 同2.1,API封装需要准确映射
    • Skills: 无
    • Skills评估但省略:
      • api-routing: 域不重叠

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 4(与2.1, 4.1并行)
    • 阻塞任务: 2.3
    • 被阻塞: 1.3, 1.4

    参考:

    • API路径: design.md(/device/info等)
    • 请求构造: 同2.1
    • 响应解析: DeviceInfoResp包含多个字段
    • 参数验证: CardNo和DeviceID至少一个非空

    验收标准:

    TDD流程(每个API重复):

    • RED: 编写TestGetDeviceInfo等测试
    • GREEN: 实现7个设备API
    • REFACTOR: 优化代码

    自动化验证:

    go build ./internal/gateway
    source .env.local && go test -v ./internal/gateway -run TestGetDeviceInfo
    source .env.local && go test -v ./internal/gateway -run TestSetWiFi
    source .env.local && go test -cover ./internal/gateway
    

    提交: NO(与Phase 2合并提交)


  • 2.3. 添加单元测试

    要做什么:

    • client_test.go中添加加密/签名单元测试
    • 添加doRequest的mock测试
    • 验证错误处理逻辑(超时、网络错误、响应格式错误)
    • 验证覆盖率≥90%

    禁止做什么:

    • 不要跳过错误场景测试(必须覆盖所有错误码)
    • 不要使用真实Gateway API(单元测试用mock)
    • 不要传递nil绕过依赖(必须mock完整响应)

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: 测试覆盖率要求高,需要设计完整的测试用例
    • Skills: 无
    • Skills评估但省略:
      • db-validation: 域不重叠(这是HTTP客户端测试,非数据库)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 5(与4.2并行)
    • 阻塞任务: 5.1(集成测试依赖单元测试通过)
    • 被阻塞: 2.1, 2.2(需要API实现完成)

    参考:

    • 测试模式: /internal/service/package/service_test.go
    • Table-driven tests: /tests/unit/shop_store_test.go
    • Mock模式: 不传递nil,构造完整mock响应
    • 覆盖率工具: go test -cover

    验收标准:

    测试用例清单:

    • TestAESEncrypt_Success - 正常加密
    • TestAESEncrypt_EmptyData - 空数据加密
    • TestGenerateSign_Correctness - 签名正确性
    • TestGenerateSign_Uppercase - 签名大写验证
    • TestDoRequest_Success - 成功请求
    • TestDoRequest_NetworkError - 网络错误
    • TestDoRequest_Timeout - 超时错误
    • TestDoRequest_InvalidResponse - 响应格式错误
    • TestDoRequest_BusinessError - Gateway业务错误
    • TestQueryCardStatus_Success - 卡状态查询成功
    • TestQueryCardStatus_InvalidCard - 无效卡号
    • (每个API至少2个测试用例)

    自动化验证:

    # 运行所有单元测试
    source .env.local && go test -v ./internal/gateway
    # 预期: PASS, 所有测试通过
    
    # 检查覆盖率
    source .env.local && go test -cover ./internal/gateway
    # 预期: 覆盖率 ≥ 90%
    
    # 生成覆盖率报告
    source .env.local && go test -coverprofile=coverage.out ./internal/gateway
    go tool cover -html=coverage.out
    # 预期: 可视化报告显示 ≥ 90% 覆盖
    

    提交: YES - "feat(gateway): 实现Gateway客户端基础功能和API封装"

    • 提交内容: Phase 1 + Phase 2所有代码
    • 验证: source .env.local && go test -cover ./internal/gateway
    • 预期: 覆盖率≥90%

Phase 3: 配置和错误码集成

  • 3.1. 添加Gateway配置

    要做什么:

    • pkg/config/config.go中添加GatewayConfig结构体
    • Config中添加Gateway GatewayConfig字段
    • pkg/config/defaults/config.yaml中添加gateway配置项
    • 添加配置验证逻辑(必填项检查)

    禁止做什么:

    • 不要省略mapstructure标签(Viper解析需要)
    • 不要硬编码默认值(必须在config.yaml中定义)
    • 不要省略环境变量绑定(loader.go)
    • 不要跳过验证逻辑(Validate方法)

    推荐Agent Profile:

    • Category: quick
      • 理由: 配置结构定义遵循现有模式即可
    • Skills: 无
    • Skills评估但省略:
      • dto-standards: 域不重叠(这是配置,非DTO)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 1(与1.1, 1.4, 3.2并行)
    • 阻塞任务: 4.1(Bootstrap需要配置)
    • 被阻塞: 无

    参考:

    • 配置结构: /pkg/config/config.go lines 14-26(现有Config结构)
    • 配置文件: /pkg/config/defaults/config.yaml(YAML格式)
    • 环境变量: /pkg/config/loader.go lines 49-121(bindEnvVariables)
    • 验证逻辑: /pkg/config/config.go lines 180-274(Validate方法)
    • 类似配置: StorageConfig(可选配置的验证模式)

    验收标准:

    • GatewayConfig结构体定义完整

      • BaseURL string mapstructure:"base_url"
      • AppID string mapstructure:"app_id"
      • AppSecret string mapstructure:"app_secret"
      • Timeout int mapstructure:"timeout"
    • Config结构体添加Gateway字段

      • Gateway GatewayConfig mapstructure:"gateway"
    • defaults/config.yaml添加gateway节

      gateway:
        base_url: "https://lplan.whjhft.com/openapi"
        app_id: "60bgt1X8i7AvXqkd"
        app_secret: "BZeQttaZQt0i73moF"
        timeout: 30
      
    • loader.go添加环境变量绑定

      • "gateway.base_url"
      • "gateway.app_id"
      • "gateway.app_secret"
      • "gateway.timeout"
    • Validate方法添加Gateway验证

      • BaseURL非空
      • BaseURL格式验证(http/https开头)
      • AppID非空
      • AppSecret非空
      • Timeout范围验证(5-300秒)

    自动化验证:

    # 设置环境变量测试
    export JUNHONG_GATEWAY_BASE_URL=https://test.example.com
    export JUNHONG_GATEWAY_APP_ID=test_app_id
    export JUNHONG_GATEWAY_APP_SECRET=test_secret
    export JUNHONG_GATEWAY_TIMEOUT=60
    
    # 启动应用验证配置加载
    go run cmd/api/main.go
    # 预期: 启动成功,日志显示Gateway配置加载(AppSecret脱敏为***)
    
    # 验证配置验证逻辑
    unset JUNHONG_GATEWAY_APP_SECRET
    go run cmd/api/main.go
    # 预期: 启动失败,错误提示"gateway.app_secret不能为空"
    

    提交: NO(与3.2合并提交)


  • 3.2. 添加Gateway错误码

    要做什么:

    • pkg/errors/codes.go中添加Gateway错误码常量(1110-1119)
    • allErrorCodes数组中注册新错误码
    • errorMessages映射表中添加中文错误消息
    • 运行错误码验证测试

    禁止做什么:

    • 不要使用已占用的错误码(检查codes.go避免冲突)
    • 不要省略错误消息(必须在errorMessages中定义)
    • 不要省略错误码注册(allErrorCodes数组)

    推荐Agent Profile:

    • Category: quick
      • 理由: 错误码定义遵循现有模式即可
    • Skills: 无
    • Skills评估但省略:
      • dto-standards: 域不重叠(这是错误码,非DTO)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 1(与1.1, 1.4, 3.1并行)
    • 阻塞任务: 无(错误码可随时使用)
    • 被阻塞: 无

    参考:

    • 错误码范围: /pkg/errors/codes.go lines 6-102(现有错误码)
    • 错误消息: /pkg/errors/codes.go lines 193-271(errorMessages)
    • 注册模式: allErrorCodes数组
    • 测试: /pkg/errors/codes_test.go(错误码验证测试)

    验收标准:

    • 错误码常量定义(codes.go)

      // Gateway 相关错误(1110-1119)
      const (
          CodeGatewayError        = 1110 // Gateway 通用错误
          CodeGatewayEncryptError = 1111 // 数据加密失败
          CodeGatewaySignError    = 1112 // 签名生成失败
          CodeGatewayTimeout      = 1113 // 请求超时
          CodeGatewayInvalidResp  = 1114 // 响应格式错误
      )
      
    • allErrorCodes数组注册

      var allErrorCodes = []int{
          // ... 现有错误码
          CodeGatewayError,
          CodeGatewayEncryptError,
          CodeGatewaySignError,
          CodeGatewayTimeout,
          CodeGatewayInvalidResp,
      }
      
    • errorMessages映射表添加

      var errorMessages = map[int]string{
          // ... 现有消息
          CodeGatewayError:        "Gateway 请求失败",
          CodeGatewayEncryptError: "数据加密失败",
          CodeGatewaySignError:    "签名生成失败",
          CodeGatewayTimeout:      "Gateway 请求超时",
          CodeGatewayInvalidResp:  "Gateway 响应格式错误",
      }
      

    自动化验证:

    # 运行错误码测试
    go test -v ./pkg/errors -run TestErrorCodes
    # 预期: PASS, 所有错误码已注册且有消息
    
    # 验证错误码使用
    go run -e 'package main; import "pkg/errors"; func main() { println(errors.GetMessage(1110, "zh-CN")) }'
    # 预期: 输出"Gateway 请求失败"
    

    提交: YES - "feat(errors): 添加Gateway错误码(1110-1119)"

    • 提交内容: Phase 3所有代码(3.1 + 3.2)
    • 验证: go test -v ./pkg/errors -run TestErrorCodes
    • 预期: PASS

Phase 4: 依赖注入和集成

  • 4.1. Bootstrap初始化Gateway客户端

    要做什么:

    • internal/bootstrap/dependencies.goDependencies中添加GatewayClient *gateway.Client字段
    • cmd/api/main.go中添加initGateway函数
    • Bootstrap调用中传递deps.GatewayClient

    禁止做什么:

    • 不要在Bootstrap函数内初始化Gateway(必须在main.go)
    • 不要忽略配置验证(必须检查必填项)
    • 不要硬编码超时时间(使用config.Gateway.Timeout)

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: 依赖注入是核心架构,需要准确遵循现有模式
    • Skills: 无
    • Skills评估但省略:
      • api-routing: 域不重叠(这是依赖注入,非路由)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 4(与2.1, 2.2并行)
    • 阻塞任务: 4.2(Service需要Gateway客户端)
    • 被阻塞: 1.3, 3.1(需要Client和Config)

    参考:

    • Dependencies: /internal/bootstrap/dependencies.go lines 13-24
    • 初始化模式: /cmd/api/main.go lines 299-329(initStorage模式)
    • Bootstrap调用: /cmd/api/main.go main函数
    • 错误处理: 配置缺失返回nil(可选服务模式)

    验收标准:

    • Dependencies添加GatewayClient字段

      type Dependencies struct {
          // ... 现有字段
          GatewayClient *gateway.Client
      }
      
    • main.go添加initGateway函数

      func initGateway(cfg *config.Config, logger *zap.Logger) *gateway.Client {
          if cfg.Gateway.BaseURL == "" {
              logger.Info("Gateway未配置,跳过初始化")
              return nil
          }
      
          client := gateway.NewClient(
              cfg.Gateway.BaseURL,
              cfg.Gateway.AppID,
              cfg.Gateway.AppSecret,
          ).WithTimeout(time.Duration(cfg.Gateway.Timeout) * time.Second)
      
          logger.Info("Gateway客户端初始化成功")
          return client
      }
      
    • main函数调用initGateway

      gatewayClient := initGateway(cfg, appLogger)
      
      result, err := bootstrap.Bootstrap(&bootstrap.Dependencies{
          // ... 现有依赖
          GatewayClient: gatewayClient,
      })
      

    自动化验证:

    # 设置环境变量
    export JUNHONG_GATEWAY_BASE_URL=https://lplan.whjhft.com/openapi
    export JUNHONG_GATEWAY_APP_ID=60bgt1X8i7AvXqkd
    export JUNHONG_GATEWAY_APP_SECRET=BZeQttaZQt0i73moF
    
    # 启动应用
    source .env.local && go run cmd/api/main.go
    # 预期: 日志显示"Gateway客户端初始化成功"
    
    # 测试配置缺失场景
    unset JUNHONG_GATEWAY_BASE_URL
    source .env.local && go run cmd/api/main.go
    # 预期: 日志显示"Gateway未配置,跳过初始化"
    

    提交: NO(与4.2合并提交)


  • 4.2. Service层集成示例

    要做什么:

    • 选择一个Service(如iot_card)集成Gateway客户端
    • 添加SyncCardStatus方法示例
    • 添加错误处理和日志记录

    禁止做什么:

    • 不要在Service中实现加密逻辑(使用Gateway客户端)
    • 不要使用fmt.Errorf(必须用errors.New/Wrap)
    • 不要省略日志记录(调用外部API必须记录)

    推荐Agent Profile:

    • Category: unspecified-high
      • 理由: Service层集成需要准确的错误处理和日志记录
    • Skills: 无
    • Skills评估但省略:
      • api-routing: 域不重叠(这是Service业务逻辑,非路由)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 5(与2.3并行)
    • 阻塞任务: 5.1(集成测试需要Service集成)
    • 被阻塞: 4.1(需要Gateway客户端注入)

    参考:

    • Service模式: /internal/service/auth/service.go lines 27-43(外部客户端注入)
    • 错误处理: /internal/service/account/service.go lines 36-91(errors.New/Wrap)
    • 日志记录: zap.Logger使用模式
    • Store更新: 调用store.UpdateStatus更新数据库

    验收标准:

    • Service构造器添加gatewayClient参数

      type Service struct {
          store         *postgres.IotCardStore
          gatewayClient *gateway.Client
          logger        *zap.Logger
      }
      
      func New(
          store *postgres.IotCardStore,
          gatewayClient *gateway.Client,
          logger *zap.Logger,
      ) *Service {
          return &Service{
              store:         store,
              gatewayClient: gatewayClient,
              logger:        logger,
          }
      }
      
    • 实现SyncCardStatus方法

      func (s *Service) SyncCardStatus(ctx context.Context, cardNo string) error {
          // 调用Gateway API
          resp, err := s.gatewayClient.QueryCardStatus(ctx, &gateway.CardStatusReq{
              CardNo: cardNo,
          })
          if err != nil {
              s.logger.Error("查询卡状态失败", 
                  zap.String("cardNo", cardNo),
                  zap.Error(err))
              return errors.Wrap(errors.CodeGatewayError, err, "查询卡状态失败")
          }
      
          // 更新数据库
          if err := s.store.UpdateStatus(ctx, cardNo, resp.CardStatus); err != nil {
              return errors.Wrap(errors.CodeInternalError, err, "更新卡状态失败")
          }
      
          s.logger.Info("同步卡状态成功",
              zap.String("cardNo", cardNo),
              zap.String("status", resp.CardStatus))
      
          return nil
      }
      
    • Bootstrap注入Gateway客户端

      // services.go
      IotCard: iotCardSvc.New(s.IotCard, deps.GatewayClient, deps.Logger),
      

    自动化验证:

    # 编译检查
    go build ./internal/service/iot_card
    # 预期: 成功编译
    
    # 单元测试(如果已有)
    source .env.local && go test -v ./internal/service/iot_card -run TestSyncCardStatus
    # 预期: PASS(需要mock Gateway客户端)
    

    提交: YES - "feat(service): 集成Gateway客户端到Service层"

    • 提交内容: Phase 4所有代码(4.1 + 4.2)
    • 验证: go build ./...
    • 预期: 全部编译通过

Phase 5: 集成测试和文档

  • 5.1. 编写集成测试

    要做什么:

    • client_test.go中添加集成测试(需要真实Gateway环境)
    • 测试至少2个接口(如QueryCardStatus, StopCard)
    • 验证加密/签名与Gateway文档一致

    禁止做什么:

    • 不要跳过集成测试(必须验证真实API)
    • 不要使用硬编码测试数据(使用.env.local配置)
    • 不要忽略测试失败(超时=生产超时)

    推荐Agent Profile:

    • Category: quick
      • 理由: 集成测试调用真实API,验证端到端流程
    • Skills: 无
    • Skills评估但省略:
      • db-validation: 域不重叠(这是HTTP API测试,非数据库)

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 6(与5.2并行)
    • 阻塞任务: 无(最后一波)
    • 被阻塞: 2.3, 4.2(需要单元测试通过和Service集成)

    参考:

    • 集成测试模式: /tests/integration/enterprise_device_h5_test.go
    • 测试跳过: if testing.Short() { t.Skip() }
    • 配置加载: config.Get()
    • 真实API调用: 不使用mock,调用真实Gateway

    验收标准:

    集成测试用例:

    • TestIntegration_QueryCardStatus

      • 使用真实配置(config.Get())
      • 调用真实Gateway API
      • 验证响应完整性
      • 预期: PASS
    • TestIntegration_StopCard

      • 调用停机API
      • 验证Gateway返回成功
      • 预期: PASS
    • TestIntegration_EncryptionCompatibility

      • 验证加密输出与Gateway文档一致
      • 验证签名可被Gateway验证
      • 预期: PASS

    自动化验证:

    # 设置测试环境变量
    source .env.local
    
    # 运行集成测试
    go test -v ./internal/gateway -run TestIntegration
    # 预期: PASS, 真实API调用成功
    
    # 跳过集成测试(仅单元测试)
    go test -short -v ./internal/gateway
    # 预期: 集成测试被跳过
    

    提交: NO(与5.2合并提交)


  • 5.2. 更新文档

    要做什么:

    • docs/目录下创建gateway-client-usage.md
    • 添加Gateway客户端使用示例
    • 添加错误码说明
    • 更新README.md添加Gateway模块说明

    禁止做什么:

    • 不要省略代码示例(必须包含完整调用示例)
    • 不要省略错误码说明(所有1110-1119错误码)
    • 不要使用英文(文档必须中文)

    推荐Agent Profile:

    • Category: quick
      • 理由: 文档编写遵循现有模板即可
    • Skills: 无
    • Skills评估但省略:
      • doc-management: 这是新功能文档,非规范文档

    并行化:

    • 可并行运行: YES
    • 并行组: Wave 6(与5.1并行)
    • 阻塞任务: 无
    • 被阻塞: 无(文档可随时编写)

    参考:

    • 文档模板: /docs/auth-usage-guide.md(使用指南模板)
    • API文档: /docs/api/auth.md(API文档模板)
    • README更新: /README.md(核心功能章节)

    验收标准:

    • 创建docs/gateway-client-usage.md

      • Gateway客户端介绍
      • 配置说明(环境变量)
      • 使用示例(完整代码)
      • 错误处理说明
      • 最佳实践
    • 创建docs/gateway-api-reference.md

      • 14个API接口列表
      • 每个接口的请求/响应示例
      • 参数说明
    • 更新README.md

      • 核心功能章节添加Gateway模块
      • 技术栈章节说明
      • 配置示例(环境变量)

    自动化验证:

    # 检查文档文件存在
    ls -la docs/gateway-client-usage.md
    ls -la docs/gateway-api-reference.md
    # 预期: 文件存在
    
    # 检查README更新
    grep -i "gateway" README.md
    # 预期: 找到Gateway模块说明
    

    提交: YES - "docs: 添加Gateway客户端使用文档"

    • 提交内容: Phase 5所有代码(5.1 + 5.2)
    • 验证: 文档完整性检查
    • 预期: 所有文档文件存在且内容完整

提交策略

完成阶段 提交信息 包含文件 验证命令
Phase 2完成 feat(gateway): 实现Gateway客户端基础功能和API封装 Phase 1 + Phase 2 source .env.local && go test -cover ./internal/gateway
Phase 3完成 feat(errors): 添加Gateway错误码(1110-1119) Phase 3 (3.1 + 3.2) go test -v ./pkg/errors -run TestErrorCodes
Phase 4完成 feat(service): 集成Gateway客户端到Service层 Phase 4 (4.1 + 4.2) go build ./...
Phase 5完成 docs: 添加Gateway客户端使用文档 Phase 5 (5.1 + 5.2) 文档完整性检查

成功标准

验证命令

# 1. 编译检查
go build ./internal/gateway
go build ./...

# 2. 单元测试
source .env.local && go test -v ./internal/gateway
source .env.local && go test -cover ./internal/gateway

# 3. 集成测试
source .env.local && go test -v ./internal/gateway -run TestIntegration

# 4. 错误码验证
go test -v ./pkg/errors -run TestErrorCodes

# 5. 配置验证
export JUNHONG_GATEWAY_BASE_URL=https://lplan.whjhft.com/openapi
export JUNHONG_GATEWAY_APP_ID=60bgt1X8i7AvXqkd
export JUNHONG_GATEWAY_APP_SECRET=BZeQttaZQt0i73moF
source .env.local && go run cmd/api/main.go

最终检查清单

  • 所有14个Gateway API接口成功封装
  • 加密/签名验证通过(与Gateway文档一致)
  • 错误处理覆盖所有异常场景(网络错误、响应格式错误等)
  • 单元测试覆盖率≥90%
  • 集成测试验证真实Gateway API调用
  • 配置通过环境变量成功加载
  • 依赖注入到Service层成功
  • 文档完整(使用示例、错误码说明、API参考)
  • 无LSP错误,编译通过
  • 符合项目代码规范(中文注释、Go命名规范、errors.New/Wrap)

风险与缓解

风险 影响 概率 缓解措施
AES-ECB实现错误 高(加密失败导致无法调用API) 1. 使用TiDB参考代码
2. 端到端集成测试验证
3. 与Gateway文档示例对比
签名算法兼容性 高(签名不匹配导致认证失败) 1. 严格按字母序拼接
2. 大写MD5输出
3. 集成测试验证
Gateway响应格式变更 中(解析失败) 1. 使用json.RawMessage
2. 统一错误处理
3. 日志记录原始响应
配置验证不完整 中(运行时错误) 1. 双层验证(Required+Format)
2. 启动时验证
3. 明确错误提示
测试覆盖率不足 低(隐藏bug) 1. TDD流程
2. 覆盖率工具检查
3. 集成测试补充

后续计划

本次变更完成后的优化方向:

  1. 阶段2(未来优化):

    • 实现异步模式回调接口
    • 添加批量查询接口
    • 实现请求重试和超时控制
  2. 阶段3(性能优化):

    • 添加响应缓存(Redis)
    • 实现请求限流(防止Gateway过载)
    • 监控和告警集成
  3. 阶段4(安全增强):

    • 替换AES-ECB为更安全的模式(如需Gateway支持)
    • 实现请求签名双向验证
    • 添加API调用审计日志