Files
junhong_cmp_fiber/.sisyphus/plans/gateway-integration.md
huang 4856a88d41
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 43s
docs: 新增 Gateway 集成和微信公众号支付集成的 OpenSpec 规划文档
2026-01-30 16:09:32 +08:00

56 KiB
Raw Permalink Blame History

Gateway API 集成工作计划

TL;DR

Quick Summary: 封装 Gateway API 为统一的能力模块,提供类型安全的接口、统一的错误处理、配置管理和自动重试机制

Deliverables:

  • Gateway 客户端封装(internal/gateway/ 包)
  • 14 个 API 接口(流量卡 7 个 + 设备 7 个)
  • AES-128-ECB 加密 + MD5 签名机制
  • 配置集成GatewayConfig
  • 错误码定义1110-1119
  • 依赖注入Bootstrap
  • 完整测试覆盖(单元测试 + 集成测试)

Estimated Effort: Medium预计 2-3 小时) Parallel Execution: YES - 5 waves Critical Path: Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5


Context

Original Request

用户需要实施 gateway-integration 提案,该提案包含 61 个任务,分为 5 个 Phase。核心目标是封装 Gateway API 为统一的能力模块,提供类型安全的接口、统一的错误处理和配置管理。

Interview Summary

Key Discussions:

  • Gateway 测试环境配置: 已提供 BaseURL、AppID、AppSecret、测试 ICCID
  • 加密/签名算法: 基于 Apifox 文档AES-128-ECB + MD5遗留系统
  • Service 集成范围: 仅 iot_card Service新增 SyncCardStatus 方法作为示例
  • 批量查询接口: 完全不需要(删除 Task 20
  • 错误处理策略: 需要自动重试3 次指数退避1s, 2s, 4s

Research Findings:

  • 项目现有模式: SMS 客户端pkg/sms/)可作为参考(接口 + 依赖注入)
  • 测试模式: testutils全局单例 DB/Redis + 事务回滚)
  • 错误处理: 统一错误码系统pkg/errors/
  • 配置管理: Viper 加载支持环境变量覆盖JUNHONG_ 前缀)
  • 安全警告: ⚠️ AES-128-ECB 和 MD5 存在密码学漏洞(遗留系统,需添加注释警告)

自我审查

识别的潜在风险:

  1. 加密算法实现风险: AES-128-ECB + PKCS5Padding 实现容易出错
    • 缓解措施: 使用 Apifox 文档中的 Go 示例代码,添加单元测试验证加密/解密
  2. 签名计算风险: 参数排序和拼接逻辑可能与 Gateway 不一致
    • 缓解措施: 严格按照 Apifox 文档实现,集成测试验证真实签名
  3. 重试逻辑风险: 不当重试可能导致重复操作(如停机/复机)
    • 缓解措施: 只重试网络错误和 5xx 错误业务错误4xx不重试
  4. 测试覆盖风险: 设备 API 无法真实测试
    • 缓解措施: 设备 API 只实现方法签名,集成测试跳过

明确的边界和约束:

  • 必须包含: 流量卡 7 个接口完整实现和测试
  • 必须排除: 批量查询接口BatchQuery
  • ⚠️ 可选实现: 设备 API 只需方法签名(测试跳过)
  • 🔒 不可变更: 加密/签名算法(遗留系统约束)

Work Objectives

Core Objective

封装 Gateway API 为统一的能力模块,提供类型安全的接口、统一的错误处理、配置管理和自动重试机制,支持流量卡和设备管理的 14 个 API 接口。

Concrete Deliverables

  • internal/gateway/client.go - Gateway 客户端核心实现
  • internal/gateway/crypto.go - AES-128-ECB 加密 + MD5 签名工具
  • internal/gateway/models.go - 请求/响应 DTO 定义
  • internal/gateway/flow_card.go - 流量卡 API7 个接口)
  • internal/gateway/device.go - 设备 API7 个接口)
  • internal/gateway/client_test.go - 单元测试 + 集成测试
  • pkg/config/config.go - GatewayConfig 配置结构
  • pkg/errors/codes.go - Gateway 错误码1110-1119
  • internal/bootstrap/bootstrap.go - Bootstrap 初始化 Gateway 客户端
  • internal/service/iot_card/service.go - SyncCardStatus 集成示例
  • docs/gateway-client-usage.md - 使用文档

Definition of Done

  • 所有 14 个 Gateway API 接口成功封装(流量卡 7 个 + 设备 7 个)
  • 加密/签名通过单元测试验证(与 Apifox 文档一致)
  • 集成测试验证真实 Gateway API 调用(测试 ICCID: 8986062580006141710
  • 重试逻辑测试通过3 次重试,指数退避)
  • 配置通过环境变量成功加载JUNHONG_GATEWAY_*
  • 依赖注入到 iot_card Service 成功SyncCardStatus 方法)
  • 单元测试覆盖率 ≥ 90%(核心逻辑)
  • 无 LSP 错误,编译通过
  • 符合项目代码规范中文注释、Go 命名规范)
  • 文档完整(使用示例、错误码说明)

Must Have

  • AES-128-ECB 加密PKCS5Padding + Base64
  • MD5 签名(大写输出)
  • 自动重试逻辑3 次指数退避1s, 2s, 4s
  • 流量卡 7 个 API 完整实现
  • 真实 Gateway 环境集成测试
  • 错误码定义1110-1119
  • 配置环境变量支持

Must NOT Have (Guardrails)

  • 批量查询接口BatchQuery- 用户明确不需要
  • 设备 API 的真实测试 - 无测试环境,只需方法签名
  • 使用第三方重试库 - 项目无依赖,使用简单循环
  • 降级策略 - 用户不需要,失败直接返回错误
  • 使用 AES-GCM 或 HMAC-SHA256 - 遗留系统约束,必须使用 ECB + MD5
  • AI-Slop 模式:
    • 过度抽象(不需要工厂模式、策略模式)
    • 过度验证(不需要 15 个参数校验)
    • 过度文档(不需要 JSDoc everywhere

Verification Strategy

Test Decision

  • Infrastructure exists: YES项目已有 testutils
  • User wants tests: TDD测试驱动开发
  • Framework: Go 标准库testing + httptest

TDD Workflow

每个 TODO 遵循 RED-GREEN-REFACTOR

Task Structure:

  1. RED: 编写失败测试
    • 测试文件: internal/gateway/*_test.go
    • 测试命令: go test -v ./internal/gateway/...
    • 预期: FAIL测试存在实现不存在
  2. GREEN: 最小实现使测试通过
    • 实现代码: internal/gateway/*.go
    • 测试命令: go test -v ./internal/gateway/...
    • 预期: PASS
  3. REFACTOR: 优化代码保持绿色
    • 重构: 提取常量、优化逻辑、添加注释
    • 测试命令: go test -v ./internal/gateway/...
    • 预期: PASS仍然通过

集成测试环境

真实 Gateway 配置:

export JUNHONG_GATEWAY_BASEURL="https://lplan.whjhft.com/openapi"
export JUNHONG_GATEWAY_APPID="60bgt1X8i7AvXqkd"
export JUNHONG_GATEWAY_APPSECRET="BZeQttaZQt0i73moF"

测试数据:

  • 测试 ICCID: 8986062580006141710
  • 设备测试: 跳过(无测试环境)

集成测试验证:

# 运行集成测试(需先加载环境变量)
source .env.local && go test -v ./internal/gateway/... -run TestIntegration

Execution Strategy

Parallel Execution Waves

Wave 1 (Start Immediately - 基础结构):
├── Task 1.1: 创建 Gateway 包目录结构
└── Task 3.1: 添加 Gateway 配置结构

Wave 2 (After Wave 1 - 加密和模型):
├── Task 1.2: 实现加密/签名工具函数crypto.go
├── Task 1.3: 定义 DTO 结构models.go
└── Task 3.2: 添加 Gateway 错误码

Wave 3 (After Wave 2 - 客户端核心):
├── Task 1.4: 实现 Gateway 客户端基础结构client.go
└── Task 1.5: 实现 HTTP 重试逻辑doRequest 方法)

Wave 4 (After Wave 3 - API 接口实现):
├── Task 2.1: 实现流量卡 APIflow_card.go7 个接口)
└── Task 2.2: 实现设备 APIdevice.go7 个接口签名)

Wave 5 (After Wave 4 - 集成和测试):
├── Task 2.3: 添加单元测试client_test.go
├── Task 4.1: Bootstrap 初始化 Gateway 客户端
├── Task 4.2: Service 层集成示例SyncCardStatus
├── Task 5.1: 编写集成测试
└── Task 5.2: 更新文档

Critical Path: Wave 1 → Wave 2 → Wave 3 → Wave 4 → Wave 5
Parallel Speedup: ~30% faster than sequential

Dependency Matrix

Task Depends On Blocks Can Parallelize With
1.1 None All 3.1
1.2 1.1 1.4, 2.1, 2.2 1.3, 3.2
1.3 1.1 2.1, 2.2 1.2, 3.2
1.4 1.1, 1.2, 1.3 1.5, 2.1, 2.2 None
1.5 1.4 2.1, 2.2 None
2.1 1.4, 1.5, 1.2, 1.3 2.3, 5.1 2.2
2.2 1.4, 1.5, 1.2, 1.3 2.3, 5.1 2.1
2.3 2.1, 2.2 None 4.1, 4.2
3.1 None 4.1 1.1
3.2 1.1 4.1 1.2, 1.3
4.1 1.4, 3.1, 3.2 4.2 2.3
4.2 4.1 None 2.3, 5.1
5.1 2.1, 2.2, 4.1 None 2.3, 4.2, 5.2
5.2 All None 5.1

TODOs

Phase 1: 基础结构搭建

  • 1.1 创建 Gateway 包目录结构

    What to do:

    • 创建目录 internal/gateway/
    • 创建占位文件:
      • client.go - Gateway 客户端核心实现
      • crypto.go - 加密/签名工具
      • models.go - 请求/响应 DTO
      • flow_card.go - 流量卡 API
      • device.go - 设备 API
      • client_test.go - 测试文件
    • 每个文件添加 package 声明和中文注释说明用途

    Must NOT do:

    • 不添加任何实现代码(只创建文件结构)
    • 不创建 batch_query.go批量查询不需要

    Recommended Agent Profile:

    • Category: quick
      • Reason: 简单的目录和文件创建任务,无复杂逻辑
    • Skills: 无
      • Reason: 不涉及特定规范,只是文件结构创建

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1与 Task 3.1 并行)
    • Blocks: 所有后续任务(目录结构是基础)
    • Blocked By: None可立即开始

    References:

    • pkg/sms/client.go - 参考 SMS 客户端的包结构模式
    • pkg/sms/http_client.go - 参考 HTTP 客户端文件组织方式
    • internal/service/iot_card/service.go - 参考 Service 包的文件组织

    Acceptance Criteria:

    • 目录存在: internal/gateway/
    • 文件存在: client.go, crypto.go, models.go, flow_card.go, device.go, client_test.go
    • 每个文件包含 package gateway 声明
    • 每个文件顶部有中文注释说明用途
    • 验证命令: ls -la internal/gateway/ → 显示所有文件
    • 编译通过: go build ./internal/gateway/... → 无错误

    Commit: YES

    • Message: feat(gateway): 创建 Gateway 包目录结构
    • Files: internal/gateway/*.go
    • Pre-commit: go build ./internal/gateway/...

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

    What to do:

    • internal/gateway/crypto.go 中实现加密函数:
      • aesEncrypt(plaintext, appSecret string) (string, error) - AES-128-ECB + PKCS5Padding + Base64
      • 步骤: MD5(appSecret) → 16字节密钥 → PKCS5填充 → ECB加密 → Base64编码
    • 实现签名函数:
      • generateSign(params map[string]interface{}, appSecret string) string - MD5签名大写
      • 步骤: 排序参数键 → 拼接 key=value&... → 追加 &key=appSecret → MD5 → 转大写
    • 实现解密函数(用于测试验证):
      • aesDecrypt(ciphertext, appSecret string) (string, error) - 解密验证
    • 添加详细中文注释和安全警告:
      // ⚠️ 安全警告: AES-128-ECB 模式已被证明存在密码学漏洞(相同明文产生相同密文,泄漏模式)
      // 仅用于对接遗留系统的 Gateway API不应用于新系统设计
      // 推荐替代方案: AES-256-GCM提供认证加密
      

    Must NOT do:

    • 不使用第三方加密库(使用标准库 crypto/aes
    • 不实现 AES-GCM 或其他模式(遗留系统约束)
    • 不跳过安全警告注释

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: 加密算法实现需要精确逻辑,容易出错
    • Skills: 无
      • Reason: 加密实现不涉及项目特定规范

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2与 Task 1.3, 3.2 并行)
    • Blocks: Task 1.4, 2.1, 2.2(客户端和 API 依赖加密)
    • Blocked By: Task 1.1(需要文件结构存在)

    References:

    • Apifox 文档: https://omp5mq28pq.apifox.cn/7819761m0 - 官方 Go 语言加密/签名示例
    • Librarian 研究: AES-128-ECB 实现模式(手动 ECB 循环,因为标准库无 ECB
    • crypto/aes - AES 加密标准库
    • crypto/md5 - MD5 哈希标准库
    • encoding/base64 - Base64 编码标准库

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件创建: internal/gateway/crypto_test.go
    • 编写失败测试:
      func TestAESEncrypt(t *testing.T) {
          plaintext := "test"
          appSecret := "BZeQttaZQt0i73moF"
          encrypted, err := aesEncrypt(plaintext, appSecret)
          require.NoError(t, err)
          assert.NotEmpty(t, encrypted)
      
          // 验证可解密
          decrypted, err := aesDecrypt(encrypted, appSecret)
          require.NoError(t, err)
          assert.Equal(t, plaintext, decrypted)
      }
      
      func TestGenerateSign(t *testing.T) {
          params := map[string]interface{}{
              "appId": "60bgt1X8i7AvXqkd",
              "timestamp": 1704067200,
              "data": "encrypted_data",
          }
          appSecret := "BZeQttaZQt0i73moF"
          sign := generateSign(params, appSecret)
          assert.NotEmpty(t, sign)
          assert.Regexp(t, "^[A-F0-9]{32}$", sign) // 32位大写MD5
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestAES → FAIL未实现

    TDD - GREEN Phase:

    • 实现 aesEncrypt 函数(参考 Apifox 文档示例)
    • 实现 aesDecrypt 函数(用于测试验证)
    • 实现 generateSign 函数(参考 Apifox 文档示例)
    • 运行测试: go test -v ./internal/gateway/ -run TestAES → PASS

    TDD - REFACTOR Phase:

    • 添加详细中文注释和安全警告
    • 提取 PKCS5 填充逻辑为独立函数
    • 运行测试: go test -v ./internal/gateway/ -run TestAES → PASS仍然通过

    Commit: YES

    • Message: feat(gateway): 实现 AES-128-ECB 加密和 MD5 签名工具
    • Files: internal/gateway/crypto.go, internal/gateway/crypto_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestAES

  • 1.3 定义请求/响应 DTO 结构

    What to do:

    • internal/gateway/models.go 中定义:
    • 通用请求/响应结构:
      // GatewayRequest Gateway API 统一请求结构
      type GatewayRequest struct {
          AppID     string `json:"appId"`     // 应用ID
          Data      string `json:"data"`      // AES加密后的Base64字符串
          Sign      string `json:"sign"`      // MD5签名大写
          Timestamp int64  `json:"timestamp"` // 时间戳(秒)
      }
      
      // GatewayResponse Gateway API 统一响应结构
      type GatewayResponse struct {
          Code int         `json:"code"` // 响应码200=成功)
          Msg  string      `json:"msg"`  // 响应消息
          Data interface{} `json:"data"` // 业务数据(需解密)
      }
      
    • 流量卡相关 DTO7 个接口):
      • CardStatusReq - 卡状态查询请求
      • CardStatusResp - 卡状态查询响应
      • FlowQueryReq - 流量查询请求
      • FlowUsageResp - 流量使用响应
      • RealnameStatusReq - 实名认证状态请求
      • RealnameStatusResp - 实名认证状态响应
      • CardOperationReq - 停机/复机请求(通用)
      • RealmnameLinkResp - 实名认证链接响应
    • 设备相关 DTO7 个接口):
      • DeviceInfoReq - 设备信息请求
      • DeviceInfoResp - 设备信息响应
      • SlotInfoReq - 卡槽信息请求
      • SlotInfoResp - 卡槽信息响应
      • SpeedLimitReq - 限速设置请求
      • WiFiConfigReq - WiFi 配置请求
      • CardSwitchReq - 切换卡请求
      • DeviceOperationReq - 设备操作请求(重启/恢复出厂)
    • 每个结构体添加:
      • JSON 标签(使用 sonic 序列化)
      • 中文注释description
      • 验证标签validaterequired

    Must NOT do:

    • 不定义 BatchQuery 相关 DTO不需要
    • 不过度抽象(不需要接口或泛型)
    • 不添加 Getter/SetterGo 惯用法直接访问字段)

    Recommended Agent Profile:

    • Category: quick
      • Reason: DTO 定义是结构化任务,无复杂逻辑
    • Skills: [dto-standards]
      • dto-standards: DTO 规范description 标签、枚举字段、验证标签)
      • Reason: 定义 DTO 结构需要遵循项目规范

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2与 Task 1.2, 3.2 并行)
    • Blocks: Task 2.1, 2.2API 实现依赖 DTO
    • Blocked By: Task 1.1(需要文件结构存在)

    References:

    • Apifox 文档: https://omp5mq28pq.apifox.cn/7819761m0 - 完整的请求/响应结构定义
    • internal/model/dto/ - 参考现有 DTO 定义模式
    • docs/dto-standards.md - DTO 规范description 标签、验证标签)
    • Validator 库: github.com/go-playground/validator/v10 - 验证标签语法

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件创建: internal/gateway/models_test.go
    • 编写失败测试:
      func TestGatewayRequestJSON(t *testing.T) {
          req := GatewayRequest{
              AppID: "test",
              Data: "encrypted",
              Sign: "ABC123",
              Timestamp: 1704067200,
          }
          data, err := json.Marshal(req)
          require.NoError(t, err)
          assert.Contains(t, string(data), "appId")
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestGatewayRequest → FAIL

    TDD - GREEN Phase:

    • 定义所有 DTO 结构体(参考 Apifox 文档)
    • 运行测试: go test -v ./internal/gateway/ -run TestGatewayRequest → PASS

    TDD - REFACTOR Phase:

    • 添加中文注释(每个字段说明用途)
    • 添加验证标签required, min, max 等)
    • 运行测试: go test -v ./internal/gateway/ -run TestGatewayRequest → PASS

    Commit: YES

    • Message: feat(gateway): 定义 Gateway API 请求/响应 DTO 结构
    • Files: internal/gateway/models.go, internal/gateway/models_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestGatewayRequest

  • 1.4 实现 Gateway 客户端基础结构

    What to do:

    • internal/gateway/client.go 中定义客户端接口和结构:
      // Client Gateway API 客户端接口(用于 mock 测试)
      type Client interface {
          // 流量卡相关接口
          QueryCardStatus(ctx context.Context, iccid string) (*CardStatusResp, error)
          QueryFlow(ctx context.Context, iccid string) (*FlowUsageResp, error)
          // ... 其他接口声明
      }
      
      // GatewayClient Gateway API 客户端实现
      type GatewayClient struct {
          baseURL    string       // Gateway API 地址
          appID      string       // 应用ID
          appSecret  string       // 应用密钥
          httpClient *http.Client // HTTP 客户端
          logger     *zap.Logger  // 日志记录器
      }
      
    • 实现构造函数:
      func NewClient(baseURL, appID, appSecret string, logger *zap.Logger) *GatewayClient
      
    • 实现配置方法:
      func (c *GatewayClient) WithTimeout(timeout time.Duration) *GatewayClient
      
    • 不实现 doRequest 方法(留给 Task 1.5

    Must NOT do:

    • 不实现具体的 API 方法(留给 Task 2.1, 2.2
    • 不实现重试逻辑(留给 Task 1.5
    • 不使用 Getter/Setter 模式Go 惯用法)

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: 客户端架构设计需要考虑接口设计、依赖注入
    • Skills: 无
      • Reason: 不涉及项目特定规范,是通用的客户端设计

    Parallelization:

    • Can Run In Parallel: NO
    • Parallel Group: Wave 3顺序执行
    • Blocks: Task 1.5, 2.1, 2.2API 实现依赖客户端结构)
    • Blocked By: Task 1.1, 1.2, 1.3需要目录、加密、DTO

    References:

    • pkg/sms/client.go - 参考 SMS 客户端的接口 + 结构体模式
    • pkg/sms/http_client.go - 参考 HTTP 客户端的依赖注入模式
    • Librarian 研究: API 客户端设计模式(接口 + 依赖注入)

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件更新: internal/gateway/client_test.go
    • 编写失败测试:
      func TestNewClient(t *testing.T) {
          client := NewClient("https://api.example.com", "appid", "secret", zap.NewNop())
          assert.NotNil(t, client)
          assert.Equal(t, "https://api.example.com", client.baseURL)
      }
      
      func TestWithTimeout(t *testing.T) {
          client := NewClient("https://api.example.com", "appid", "secret", zap.NewNop())
          client = client.WithTimeout(5 * time.Second)
          assert.Equal(t, 5*time.Second, client.httpClient.Timeout)
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestNewClient → FAIL

    TDD - GREEN Phase:

    • 定义 Client 接口(所有方法签名)
    • 定义 GatewayClient 结构体
    • 实现 NewClient 构造函数
    • 实现 WithTimeout 方法
    • 运行测试: go test -v ./internal/gateway/ -run TestNewClient → PASS

    TDD - REFACTOR Phase:

    • 添加详细中文注释
    • 配置默认 HTTP 客户端(参考 librarian 研究的超时配置)
    • 运行测试: go test -v ./internal/gateway/ -run TestNewClient → PASS

    Commit: YES

    • Message: feat(gateway): 实现 Gateway 客户端基础结构
    • Files: internal/gateway/client.go, internal/gateway/client_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestNewClient

  • 1.5 实现 HTTP 重试逻辑doRequest 方法)

    What to do:

    • internal/gateway/client.go 中实现统一请求方法:
      func (c *GatewayClient) doRequest(ctx context.Context, endpoint string, reqData interface{}) (*GatewayResponse, error)
      
    • 请求流程:
      1. 序列化请求数据sonic
      2. AES 加密请求数据
      3. 构建 GatewayRequestappId, data, timestamp, sign
      4. 重试循环(最多 3 次):
        • 发送 HTTP POST 请求
        • 解析响应
        • 网络错误或 5xx → 重试指数退避1s, 2s, 4s
        • 4xx 业务错误 → 不重试(直接返回错误)
        • 超时错误 → 不重试context.DeadlineExceeded
      5. AES 解密响应数据
      6. 返回 GatewayResponse
    • 错误处理:
      • 网络错误: 返回 errors.Wrap(errors.CodeGatewayNetworkError, err)
      • 超时错误: 返回 errors.New(errors.CodeGatewayTimeout)
      • 业务错误: 返回 errors.New(errors.CodeGatewayBusinessError, resp.Msg)

    Must NOT do:

    • 不使用第三方重试库(项目无依赖)
    • 不重试业务错误4xx
    • 不重试超时错误(避免雪崩)

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: 重试逻辑复杂,需要精确的错误分类和退避算法
    • Skills: 无
      • Reason: HTTP 重试是通用逻辑,不涉及项目特定规范

    Parallelization:

    • Can Run In Parallel: NO
    • Parallel Group: Wave 3顺序执行依赖 Task 1.4
    • Blocks: Task 2.1, 2.2API 实现依赖 doRequest
    • Blocked By: Task 1.4(需要客户端结构存在)

    References:

    • Librarian 研究: HTTP 重试最佳实践(指数退避、错误分类)
    • pkg/sms/http_client.go - 参考 HTTP 请求模式(无重试,需自行实现)
    • pkg/constants/constants.go - 参考 DefaultRetryMaxAsynq 任务重试次数 5
    • Sonic 库: github.com/bytedance/sonic - JSON 序列化

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件更新: internal/gateway/client_test.go
    • 编写失败测试:
      func TestDoRequest_Success(t *testing.T) {
          // 使用 httptest.Server 模拟成功响应
          server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              w.WriteHeader(http.StatusOK)
              json.NewEncoder(w).Encode(GatewayResponse{Code: 200, Msg: "success"})
          }))
          defer server.Close()
      
          client := NewClient(server.URL, "appid", "secret", zap.NewNop())
          resp, err := client.doRequest(context.Background(), "/test", map[string]string{"iccid": "test"})
          require.NoError(t, err)
          assert.Equal(t, 200, resp.Code)
      }
      
      func TestDoRequest_Retry(t *testing.T) {
          // 模拟前 2 次失败,第 3 次成功
          attempts := 0
          server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              attempts++
              if attempts < 3 {
                  w.WriteHeader(http.StatusInternalServerError)
                  return
              }
              w.WriteHeader(http.StatusOK)
              json.NewEncoder(w).Encode(GatewayResponse{Code: 200, Msg: "success"})
          }))
          defer server.Close()
      
          client := NewClient(server.URL, "appid", "secret", zap.NewNop())
          resp, err := client.doRequest(context.Background(), "/test", map[string]string{"iccid": "test"})
          require.NoError(t, err)
          assert.Equal(t, 200, resp.Code)
          assert.Equal(t, 3, attempts) // 验证重试了 2 次
      }
      
      func TestDoRequest_NoRetryOn4xx(t *testing.T) {
          // 4xx 错误不重试
          attempts := 0
          server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              attempts++
              w.WriteHeader(http.StatusBadRequest)
          }))
          defer server.Close()
      
          client := NewClient(server.URL, "appid", "secret", zap.NewNop())
          _, err := client.doRequest(context.Background(), "/test", map[string]string{"iccid": "test"})
          require.Error(t, err)
          assert.Equal(t, 1, attempts) // 验证没有重试
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestDoRequest → FAIL

    TDD - GREEN Phase:

    • 实现 doRequest 方法(加密、重试、解密)
    • 实现重试逻辑指数退避1s, 2s, 4s
    • 实现错误分类(网络/超时/业务)
    • 运行测试: go test -v ./internal/gateway/ -run TestDoRequest → PASS

    TDD - REFACTOR Phase:

    • 提取重试配置为常量maxRetries, baseDelay
    • 添加详细日志(每次重试记录日志)
    • 运行测试: go test -v ./internal/gateway/ -run TestDoRequest → PASS

    Commit: YES

    • Message: feat(gateway): 实现 HTTP 重试逻辑3 次重试,指数退避)
    • Files: internal/gateway/client.go, internal/gateway/client_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestDoRequest

Phase 2: API 接口封装

  • 2.1 实现流量卡 API7 个接口)

    What to do:

    • internal/gateway/flow_card.go 中实现 7 个流量卡 API:
      1. QueryCardStatus - 查询流量卡状态(在线/离线)
        • 参数: iccid
        • 返回: 卡状态、运营商、信号强度等
      2. QueryFlow - 查询流量使用情况
        • 参数: iccid
        • 返回: 已用流量、剩余流量、套餐流量等
      3. QueryRealnameStatus - 查询实名认证状态
        • 参数: iccid
        • 返回: 是否已实名、实名信息等
      4. StopCard - 停机(暂停流量卡)
        • 参数: iccid
        • 返回: 操作结果
      5. StartCard - 复机(恢复流量卡)
        • 参数: iccid
        • 返回: 操作结果
      6. GetRealnameLink - 获取实名认证跳转链接
        • 参数: iccid
        • 返回: H5 认证链接
      7. BatchQuery - 删除(用户明确不需要)
    • 每个方法:
      • 接收 context.Context 参数
      • 调用 doRequest 统一请求方法
      • 解析响应数据到对应的 Resp 结构体
      • 返回 (*XxxResp, error)
    • 添加详细中文注释

    Must NOT do:

    • 不实现 BatchQuery删除
    • 不在方法内重复加密/签名逻辑(使用 doRequest
    • 不忽略错误处理

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: API 实现需要精确的请求/响应映射和错误处理
    • Skills: 无
      • Reason: API 实现是标准流程,不涉及项目特定规范

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 4与 Task 2.2 并行)
    • Blocks: Task 2.3, 5.1(测试依赖 API 实现)
    • Blocked By: Task 1.4, 1.5, 1.2, 1.3需要客户端、重试、加密、DTO

    References:

    • Apifox 文档: https://omp5mq28pq.apifox.cn/7819761m0 - 流量卡接口文档endpoint、请求/响应格式)
    • internal/gateway/client.go - doRequest 方法(统一请求入口)
    • internal/gateway/models.go - DTO 定义CardStatusReq, CardStatusResp 等)
    • internal/gateway/crypto.go - 加密/签名工具(已在 doRequest 中使用)

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件更新: internal/gateway/flow_card_test.go
    • 编写失败测试(每个接口一个测试):
      func TestQueryCardStatus(t *testing.T) {
          server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
              // 模拟 Gateway 响应
              w.WriteHeader(http.StatusOK)
              json.NewEncoder(w).Encode(GatewayResponse{
                  Code: 200,
                  Msg: "success",
                  Data: CardStatusResp{Status: "online", Operator: "移动"},
              })
          }))
          defer server.Close()
      
          client := NewClient(server.URL, "appid", "secret", zap.NewNop())
          resp, err := client.QueryCardStatus(context.Background(), "8986062580006141710")
          require.NoError(t, err)
          assert.Equal(t, "online", resp.Status)
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestQuery → FAIL

    TDD - GREEN Phase:

    • 实现 QueryCardStatus 方法
    • 实现 QueryFlow 方法
    • 实现 QueryRealnameStatus 方法
    • 实现 StopCard 方法
    • 实现 StartCard 方法
    • 实现 GetRealnameLink 方法
    • 运行测试: go test -v ./internal/gateway/ -run TestQuery → PASS

    TDD - REFACTOR Phase:

    • 添加详细中文注释(每个方法说明用途和参数)
    • 提取公共逻辑(如错误码映射)
    • 运行测试: go test -v ./internal/gateway/ -run TestQuery → PASS

    Commit: YES

    • Message: feat(gateway): 实现流量卡 API6 个接口)
    • Files: internal/gateway/flow_card.go, internal/gateway/flow_card_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestQuery

  • 2.2 实现设备 API7 个接口签名)

    What to do:

    • internal/gateway/device.go 中实现 7 个设备 API 方法签名:
      1. GetDeviceInfo - 获取设备信息
      2. GetSlotInfo - 获取设备卡槽信息
      3. SetSpeedLimit - 设置设备限速
      4. SetWiFi - 设置设备 WiFi
      5. SwitchCard - 设备切换卡
      6. ResetDevice - 设备恢复出厂设置
      7. RebootDevice - 设备重启
    • 每个方法:
      • 接收 context.Context 和相应参数
      • 暂时返回 "未实现" 错误:
        return nil, errors.New(errors.CodeGatewayNotImplemented, "设备 API 暂未实现(无测试环境)")
        
      • 添加 TODO 注释说明未来需实现
    • 保留完整的方法签名和 DTO 定义

    Must NOT do:

    • 不实现真实的 HTTP 请求(无测试环境)
    • 不删除这些方法(保留接口定义)
    • 不在集成测试中测试设备 API

    Recommended Agent Profile:

    • Category: quick
      • Reason: 只实现方法签名,无复杂逻辑
    • Skills: 无
      • Reason: 简单的方法签名定义

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 4与 Task 2.1 并行)
    • Blocks: Task 2.3, 5.1(测试需要所有方法存在)
    • Blocked By: Task 1.4, 1.5, 1.2, 1.3需要客户端、重试、加密、DTO

    References:

    • Apifox 文档: https://omp5mq28pq.apifox.cn/7819761m0 - 设备接口文档(了解参数和返回值)
    • internal/gateway/flow_card.go - 参考流量卡 API 的方法签名模式
    • internal/gateway/models.go - 设备相关 DTO

    Acceptance Criteria:

    TDD - RED Phase:

    • 测试文件创建: internal/gateway/device_test.go
    • 编写失败测试(验证方法存在且返回未实现错误):
      func TestGetDeviceInfo_NotImplemented(t *testing.T) {
          client := NewClient("https://api.example.com", "appid", "secret", zap.NewNop())
          _, err := client.GetDeviceInfo(context.Background(), "device-123")
          require.Error(t, err)
          assert.Contains(t, err.Error(), "未实现")
      }
      
    • 运行测试: go test -v ./internal/gateway/ -run TestGetDevice → FAIL

    TDD - GREEN Phase:

    • 定义所有 7 个设备 API 方法签名
    • 每个方法返回 "未实现" 错误
    • 运行测试: go test -v ./internal/gateway/ -run TestGetDevice → PASS

    TDD - REFACTOR Phase:

    • 添加 TODO 注释(说明未来需实现)
    • 添加详细中文注释(每个方法说明用途)
    • 运行测试: go test -v ./internal/gateway/ -run TestGetDevice → PASS

    Commit: YES

    • Message: feat(gateway): 实现设备 API 方法签名7 个接口,暂未实现)
    • Files: internal/gateway/device.go, internal/gateway/device_test.go
    • Pre-commit: go test -v ./internal/gateway/ -run TestGetDevice

  • 2.3 添加单元测试(覆盖率 ≥ 90%

    What to do:

    • internal/gateway/client_test.go 中补充完整单元测试:
      • 加密/签名测试(已在 Task 1.2 完成)
      • doRequest 测试(已在 Task 1.5 完成)
      • 流量卡 API 测试(已在 Task 2.1 完成)
      • 设备 API 测试(已在 Task 2.2 完成)
    • 新增测试覆盖:
      • 边界条件测试: 空字符串、nil 参数、超长字符串
      • 错误处理测试: 网络错误、超时、业务错误
      • 并发安全测试: 多 goroutine 同时调用客户端
    • 使用 table-driven tests 模式
    • 运行覆盖率检查:
      go test -v ./internal/gateway/... -cover -coverprofile=coverage.out
      go tool cover -func=coverage.out
      
    • 确保核心逻辑覆盖率 ≥ 90%

    Must NOT do:

    • 不测试设备 API 的真实调用(无环境)
    • 不过度测试(不需要 100% 覆盖率)
    • 不跳过错误场景测试

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: 测试覆盖率需要全面考虑边界条件和错误场景
    • Skills: 无
      • Reason: 单元测试是通用技能

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 5与 Task 4.1, 4.2 并行)
    • Blocks: None测试不阻塞其他任务
    • Blocked By: Task 2.1, 2.2(需要 API 实现存在)

    References:

    • tests/unit/shop_store_test.go - 参考项目的 table-driven tests 模式
    • Go 测试文档: testing 标准库t.Run, subtests
    • httptest 文档: net/http/httptest - 模拟 HTTP 服务器

    Acceptance Criteria:

    测试覆盖验证:

    • 运行覆盖率检查: go test -v ./internal/gateway/... -cover -coverprofile=coverage.out
    • 查看覆盖率报告: go tool cover -func=coverage.out | grep total
    • 核心逻辑覆盖率 ≥ 90%:
      • crypto.go ≥ 90%
      • client.go ≥ 90%
      • flow_card.go ≥ 90%
      • device.go = 100%(只返回错误,简单)
    • 所有测试通过: go test -v ./internal/gateway/... → PASS

    测试场景覆盖:

    • 加密/签名正确性测试(加密 → 解密 → 验证)
    • 重试逻辑测试5xx 重试、4xx 不重试、超时不重试)
    • 错误分类测试(网络/超时/业务错误)
    • 边界条件测试空参数、nil、超长字符串
    • 并发安全测试(多 goroutine 调用)

    Commit: YES

    • Message: test(gateway): 补充单元测试覆盖率(≥ 90%
    • Files: internal/gateway/*_test.go
    • Pre-commit: go test -v ./internal/gateway/... -cover

Phase 3: 配置和错误码集成

  • 3.1 添加 Gateway 配置结构

    What to do:

    • pkg/config/config.go 中添加 GatewayConfig 结构体:
      // GatewayConfig Gateway API 配置
      type GatewayConfig struct {
          BaseURL   string        `mapstructure:"base_url" validate:"required,url"` // Gateway API 地址
          AppID     string        `mapstructure:"app_id" validate:"required"`       // 应用ID
          AppSecret string        `mapstructure:"app_secret" validate:"required"`   // 应用密钥
          Timeout   time.Duration `mapstructure:"timeout" validate:"min=1s"`        // 请求超时时间(默认 30s
      }
      
    • Config 结构体中添加字段:
      type Config struct {
          // ... 现有字段
          Gateway GatewayConfig `mapstructure:"gateway"`
      }
      
    • pkg/config/defaults/config.yaml 中添加默认配置:
      gateway:
        base_url: "https://lplan.whjhft.com/openapi"
        app_id: ""
        app_secret: ""
        timeout: 30s
      
    • pkg/config/loader.go 中绑定环境变量:
      viper.BindEnv("gateway.base_url", "JUNHONG_GATEWAY_BASEURL")
      viper.BindEnv("gateway.app_id", "JUNHONG_GATEWAY_APPID")
      viper.BindEnv("gateway.app_secret", "JUNHONG_GATEWAY_APPSECRET")
      viper.BindEnv("gateway.timeout", "JUNHONG_GATEWAY_TIMEOUT")
      
    • 添加配置验证(必填项检查)

    Must NOT do:

    • 不在配置文件中硬编码 AppID/AppSecret使用环境变量
    • 不跳过验证逻辑

    Recommended Agent Profile:

    • Category: quick
      • Reason: 配置结构定义是标准化任务
    • Skills: 无
      • Reason: 配置管理遵循现有模式,无需特殊规范

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 1与 Task 1.1 并行)
    • Blocks: Task 4.1Bootstrap 初始化依赖配置)
    • Blocked By: None可立即开始

    References:

    • pkg/config/config.go - 参考现有配置结构SMSConfig, JWTConfig
    • pkg/config/defaults/config.yaml - 参考默认配置格式
    • pkg/config/loader.go - 参考环境变量绑定模式viper.BindEnv

    Acceptance Criteria:

    配置加载验证:

    • 配置结构定义完成: GatewayConfigpkg/config/config.go
    • 默认配置存在: pkg/config/defaults/config.yaml 包含 gateway 配置
    • 环境变量绑定: pkg/config/loader.go 绑定 JUNHONG_GATEWAY_* 变量
    • 配置验证通过:
      export JUNHONG_GATEWAY_BASEURL="https://lplan.whjhft.com/openapi"
      export JUNHONG_GATEWAY_APPID="60bgt1X8i7AvXqkd"
      export JUNHONG_GATEWAY_APPSECRET="BZeQttaZQt0i73moF"
      go run cmd/api/main.go --help  # 验证配置加载无错误
      
    • 编译通过: go build ./pkg/config/... → 无错误

    Commit: YES

    • Message: feat(config): 添加 Gateway API 配置结构
    • Files: pkg/config/config.go, pkg/config/defaults/config.yaml, pkg/config/loader.go
    • Pre-commit: go build ./pkg/config/...

  • 3.2 添加 Gateway 错误码1110-1119

    What to do:

    • pkg/errors/codes.go 中添加 Gateway 错误码常量:
      // Gateway 相关错误 (1110-1119)
      CodeGatewayNetworkError       = 1110 // Gateway 网络错误
      CodeGatewayTimeout            = 1111 // Gateway 请求超时
      CodeGatewayBusinessError      = 1112 // Gateway 业务错误
      CodeGatewayEncryptionError    = 1113 // Gateway 加密错误
      CodeGatewayDecryptionError    = 1114 // Gateway 解密错误
      CodeGatewaySignatureError     = 1115 // Gateway 签名错误
      CodeGatewayInvalidResponse    = 1116 // Gateway 响应格式错误
      CodeGatewayNotImplemented     = 1117 // Gateway 接口未实现
      // 预留 1118-1119 供未来扩展
      
    • allErrorCodes 数组中注册新错误码
    • errorMessages 映射表中添加中文错误消息:
      1110: "Gateway 网络错误",
      1111: "Gateway 请求超时",
      1112: "Gateway 业务错误",
      1113: "Gateway 加密失败",
      1114: "Gateway 解密失败",
      1115: "Gateway 签名错误",
      1116: "Gateway 响应格式错误",
      1117: "Gateway 接口未实现",
      
    • 运行错误码验证测试: go test -v ./pkg/errors/...

    Must NOT do:

    • 不使用其他范围的错误码(严格使用 1110-1119
    • 不跳过错误码验证测试

    Recommended Agent Profile:

    • Category: quick
      • Reason: 错误码定义是标准化任务
    • Skills: 无
      • Reason: 错误码遵循现有模式

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 2与 Task 1.2, 1.3 并行)
    • Blocks: Task 4.1Bootstrap 初始化可能需要错误码)
    • Blocked By: Task 1.1(目录结构需先存在)

    References:

    • pkg/errors/codes.go - 参考现有错误码定义模式
    • pkg/errors/errors_test.go - 参考错误码验证测试

    Acceptance Criteria:

    错误码验证:

    • 错误码常量定义完成: CodeGatewayNetworkError 等在 pkg/errors/codes.go
    • 错误码注册完成: allErrorCodes 包含 1110-1119
    • 错误消息定义完成: errorMessages 包含中文消息
    • 错误码验证测试通过: go test -v ./pkg/errors/... → PASS
    • 编译通过: go build ./pkg/errors/... → 无错误

    Commit: YES

    • Message: feat(errors): 添加 Gateway 错误码1110-1119
    • Files: pkg/errors/codes.go
    • Pre-commit: go test -v ./pkg/errors/...

Phase 4: 依赖注入和集成

  • 4.1 Bootstrap 初始化 Gateway 客户端

    What to do:

    • internal/bootstrap/dependencies.goDependencies 结构体中添加字段:
      type Dependencies struct {
          // ... 现有字段
          GatewayClient *gateway.Client // Gateway API 客户端
      }
      
    • internal/bootstrap/bootstrap.goBootstrap 函数中初始化 Gateway 客户端:
      // 初始化 Gateway 客户端
      gatewayClient := gateway.NewClient(
          cfg.Gateway.BaseURL,
          cfg.Gateway.AppID,
          cfg.Gateway.AppSecret,
          deps.Logger,
      ).WithTimeout(cfg.Gateway.Timeout)
      deps.GatewayClient = gatewayClient
      
      deps.Logger.Info("Gateway 客户端初始化成功",
          zap.String("base_url", cfg.Gateway.BaseURL),
          zap.Duration("timeout", cfg.Gateway.Timeout),
      )
      
    • 确保初始化顺序正确(在 Service 初始化之前)

    Must NOT do:

    • 不在 Bootstrap 中调用 Gateway API只初始化客户端
    • 不跳过日志记录

    Recommended Agent Profile:

    • Category: quick
      • Reason: 依赖注入是标准流程
    • Skills: 无
      • Reason: Bootstrap 模式遵循现有模式

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 5与 Task 2.3 并行)
    • Blocks: Task 4.2Service 集成依赖 Bootstrap
    • Blocked By: Task 1.4, 3.1, 3.2(需要客户端、配置、错误码)

    References:

    • internal/bootstrap/bootstrap.go - 参考现有依赖初始化模式SMS、Storage
    • internal/bootstrap/dependencies.go - 参考 Dependencies 结构体定义
    • internal/gateway/client.go - Gateway 客户端构造函数

    Acceptance Criteria:

    Bootstrap 验证:

    • Dependencies 字段添加: GatewayClient *gateway.Client
    • Bootstrap 初始化代码完成(在 Service 初始化之前)
    • 编译通过: go build ./cmd/api/... → 无错误
    • 启动验证:
      source .env.local && go run cmd/api/main.go
      # 查看日志: "Gateway 客户端初始化成功"
      

    Commit: YES

    • Message: feat(bootstrap): 初始化 Gateway 客户端
    • Files: internal/bootstrap/dependencies.go, internal/bootstrap/bootstrap.go
    • Pre-commit: go build ./cmd/api/...

  • 4.2 Service 层集成示例SyncCardStatus

    What to do:

    • internal/service/iot_card/service.goService 结构体中添加字段:
      type Service struct {
          // ... 现有字段
          gatewayClient *gateway.Client // Gateway API 客户端
      }
      
    • 更新构造函数 New 添加参数:
      func New(
          // ... 现有参数
          gatewayClient *gateway.Client,
      ) *Service {
          return &Service{
              // ... 现有字段
              gatewayClient: gatewayClient,
          }
      }
      
    • 新增方法 SyncCardStatus(从 Gateway 同步卡状态到数据库):
      // SyncCardStatus 从 Gateway 同步流量卡状态到数据库
      func (s *Service) SyncCardStatus(ctx context.Context, iccid string) error {
          // 1. 调用 Gateway API 查询卡状态
          resp, err := s.gatewayClient.QueryCardStatus(ctx, iccid)
          if err != nil {
              s.logger.Error("查询 Gateway 卡状态失败",
                  zap.String("iccid", iccid),
                  zap.Error(err),
              )
              return errors.Wrap(errors.CodeGatewayBusinessError, err, "同步卡状态失败")
          }
      
          // 2. 更新数据库中的卡状态
          err = s.iotCardStore.UpdateStatus(ctx, iccid, resp.Status)
          if err != nil {
              return errors.Wrap(errors.CodeDatabaseError, err, "更新卡状态失败")
          }
      
          s.logger.Info("同步卡状态成功",
              zap.String("iccid", iccid),
              zap.String("status", resp.Status),
          )
          return nil
      }
      
    • internal/bootstrap/services.go 中更新 iot_card Service 初始化:
      IotCard: iot_card.New(
          // ... 现有参数
          deps.GatewayClient,
      ),
      

    Must NOT do:

    • 不暴露 Gateway 响应结构给 Handler 层(封装在 Service 内)
    • 不跳过错误处理和日志记录

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: Service 集成需要考虑错误处理、日志记录、数据库更新
    • Skills: 无
      • Reason: Service 集成是标准流程

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 5与 Task 2.3, 5.1, 5.2 并行)
    • Blocks: None集成示例不阻塞其他任务
    • Blocked By: Task 4.1(需要 Bootstrap 初始化完成)

    References:

    • internal/service/iot_card/service.go - 现有 Service 结构和方法
    • internal/bootstrap/services.go - Service 初始化模式
    • internal/gateway/flow_card.go - QueryCardStatus 方法

    Acceptance Criteria:

    集成验证:

    • Service 字段添加: gatewayClient *gateway.Client
    • 构造函数更新: New 函数添加 gatewayClient 参数
    • SyncCardStatus 方法实现完成
    • Bootstrap 更新: services.go 传递 GatewayClient
    • 编译通过: go build ./internal/service/iot_card/... → 无错误
    • 启动验证: source .env.local && go run cmd/api/main.go → 无错误

    Commit: YES

    • Message: feat(service): 集成 Gateway 客户端到 iot_card ServiceSyncCardStatus 示例)
    • Files: internal/service/iot_card/service.go, internal/bootstrap/services.go
    • Pre-commit: go build ./internal/service/iot_card/...

Phase 5: 集成测试和文档

  • 5.1 编写集成测试(真实 Gateway 环境)

    What to do:

    • internal/gateway/integration_test.go 中编写集成测试:
      • 使用真实 Gateway 配置BaseURL, AppID, AppSecret
      • 使用测试 ICCID: 8986062580006141710
      • 测试至少 2 个流量卡接口:
        1. QueryCardStatus - 查询卡状态
        2. QueryFlow - 查询流量使用
      • 不测试设备 API(无测试环境)
    • 集成测试结构:
      // +build integration
      
      package gateway_test
      
      import (
          "context"
          "os"
          "testing"
          "time"
      
          "github.com/stretchr/testify/assert"
          "github.com/stretchr/testify/require"
          "go.uber.org/zap"
      
          "junhong_cmp_fiber/internal/gateway"
      )
      
      func TestIntegration_QueryCardStatus(t *testing.T) {
          if os.Getenv("JUNHONG_GATEWAY_APPID") == "" {
              t.Skip("跳过集成测试:未配置 Gateway 环境变量")
          }
      
          client := gateway.NewClient(
              os.Getenv("JUNHONG_GATEWAY_BASEURL"),
              os.Getenv("JUNHONG_GATEWAY_APPID"),
              os.Getenv("JUNHONG_GATEWAY_APPSECRET"),
              zap.NewNop(),
          ).WithTimeout(30 * time.Second)
      
          ctx := context.Background()
          resp, err := client.QueryCardStatus(ctx, "8986062580006141710")
      
          require.NoError(t, err)
          assert.NotNil(t, resp)
          assert.NotEmpty(t, resp.Status)
      
          t.Logf("卡状态查询成功: %+v", resp)
      }
      
    • 运行集成测试:
      source .env.local && go test -v ./internal/gateway/... -tags=integration -run TestIntegration
      

    Must NOT do:

    • 不在 CI/CD 中自动运行集成测试(需手动触发)
    • 不测试设备 API无环境
    • 不硬编码配置(使用环境变量)

    Recommended Agent Profile:

    • Category: ultrabrain
      • Reason: 集成测试需要处理真实 API 响应和错误场景
    • Skills: 无
      • Reason: 集成测试是通用技能

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 5与 Task 2.3, 4.2, 5.2 并行)
    • Blocks: None测试不阻塞其他任务
    • Blocked By: Task 2.1, 2.2, 4.1(需要 API 实现和 Bootstrap

    References:

    • 测试配置: BaseURL, AppID, AppSecret, 测试 ICCID用户提供
    • tests/integration/ - 参考项目的集成测试模式
    • Go build tags: // +build integration - 条件编译标记

    Acceptance Criteria:

    集成测试验证:

    • 集成测试文件创建: internal/gateway/integration_test.go
    • 测试使用 build tag: // +build integration
    • 测试使用环境变量(不硬编码配置)
    • 测试至少 2 个接口: QueryCardStatus, QueryFlow
    • 运行集成测试:
      source .env.local && go test -v ./internal/gateway/... -tags=integration -run TestIntegration
      
    • 集成测试通过(验证真实 Gateway API
    • 日志输出完整(包含请求/响应详情)

    Commit: YES

    • Message: test(gateway): 添加集成测试(真实 Gateway 环境)
    • Files: internal/gateway/integration_test.go
    • Pre-commit: source .env.local && go test -v ./internal/gateway/... -tags=integration -run TestIntegration

  • 5.2 更新文档

    What to do:

    • 创建 docs/gateway-client-usage.md 使用文档:
      • 概述: Gateway 客户端功能和用途
      • 配置说明: 环境变量配置示例
      • 使用示例: 代码示例QueryCardStatus, QueryFlow 等)
      • 错误码说明: Gateway 错误码1110-1119和处理方式
      • 重试机制: 重试策略说明3 次,指数退避)
      • 安全警告: AES-128-ECB 和 MD5 的安全风险说明
      • 最佳实践: 超时设置、错误处理、日志记录
    • 更新 README.md:
      • 在 "功能模块" 部分添加 Gateway 集成模块
      • 在 "配置说明" 部分添加 Gateway 环境变量说明
    • 确保所有文档使用中文

    Must NOT do:

    • 不过度详细(不需要 API 文档级别的详情)
    • 不添加英文文档(项目要求中文)

    Recommended Agent Profile:

    • Category: writing
      • Reason: 文档编写任务
    • Skills: [doc-management]
      • doc-management: 规范文档管理流程
      • Reason: 文档更新需要遵循项目文档规范

    Parallelization:

    • Can Run In Parallel: YES
    • Parallel Group: Wave 5与 Task 2.3, 4.2, 5.1 并行)
    • Blocks: None文档不阻塞其他任务
    • Blocked By: All文档需要完整功能才能编写

    References:

    • docs/ - 参考现有文档结构和风格
    • README.md - 参考现有 README 格式
    • Apifox 文档: https://omp5mq28pq.apifox.cn/7819761m0 - Gateway API 文档(参考)
    • internal/gateway/client.go - Gateway 客户端实现(提取使用示例)

    Acceptance Criteria:

    文档验证:

    • 文档创建: docs/gateway-client-usage.md
    • 文档包含以下部分:
      • 概述
      • 配置说明(环境变量)
      • 使用示例(至少 2 个接口)
      • 错误码说明1110-1119
      • 重试机制说明
      • 安全警告ECB + MD5
      • 最佳实践
    • README.md 更新:
      • "功能模块" 部分添加 Gateway 集成
      • "配置说明" 部分添加 Gateway 环境变量
    • 所有文档使用中文
    • 代码示例可编译(复制到项目中能运行)

    Commit: YES

    • Message: docs(gateway): 添加 Gateway 客户端使用文档
    • Files: docs/gateway-client-usage.md, README.md
    • Pre-commit: 无(文档无需测试)

Commit Strategy

After Task Message Files Verification
1.1 feat(gateway): 创建 Gateway 包目录结构 internal/gateway/*.go go build ./internal/gateway/...
1.2 feat(gateway): 实现 AES-128-ECB 加密和 MD5 签名工具 internal/gateway/crypto.go, crypto_test.go go test -v ./internal/gateway/ -run TestAES
1.3 feat(gateway): 定义 Gateway API 请求/响应 DTO 结构 internal/gateway/models.go, models_test.go go test -v ./internal/gateway/ -run TestGatewayRequest
1.4 feat(gateway): 实现 Gateway 客户端基础结构 internal/gateway/client.go, client_test.go go test -v ./internal/gateway/ -run TestNewClient
1.5 feat(gateway): 实现 HTTP 重试逻辑3 次重试,指数退避) internal/gateway/client.go, client_test.go go test -v ./internal/gateway/ -run TestDoRequest
2.1 feat(gateway): 实现流量卡 API6 个接口) internal/gateway/flow_card.go, flow_card_test.go go test -v ./internal/gateway/ -run TestQuery
2.2 feat(gateway): 实现设备 API 方法签名7 个接口,暂未实现) internal/gateway/device.go, device_test.go go test -v ./internal/gateway/ -run TestGetDevice
2.3 test(gateway): 补充单元测试覆盖率(≥ 90% internal/gateway/*_test.go go test -v ./internal/gateway/... -cover
3.1 feat(config): 添加 Gateway API 配置结构 pkg/config/config.go, defaults/config.yaml, loader.go go build ./pkg/config/...
3.2 feat(errors): 添加 Gateway 错误码1110-1119 pkg/errors/codes.go go test -v ./pkg/errors/...
4.1 feat(bootstrap): 初始化 Gateway 客户端 internal/bootstrap/dependencies.go, bootstrap.go go build ./cmd/api/...
4.2 feat(service): 集成 Gateway 客户端到 iot_card ServiceSyncCardStatus 示例) internal/service/iot_card/service.go, bootstrap/services.go go build ./internal/service/iot_card/...
5.1 test(gateway): 添加集成测试(真实 Gateway 环境) internal/gateway/integration_test.go source .env.local && go test -v ./internal/gateway/... -tags=integration
5.2 docs(gateway): 添加 Gateway 客户端使用文档 docs/gateway-client-usage.md, README.md

Success Criteria

Verification Commands

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

# 2. 单元测试(覆盖率 ≥ 90%
go test -v ./internal/gateway/... -cover -coverprofile=coverage.out
go tool cover -func=coverage.out | grep total

# 3. 集成测试(需先配置环境变量)
source .env.local
go test -v ./internal/gateway/... -tags=integration -run TestIntegration

# 4. LSP 错误检查
gopls check ./internal/gateway/...

# 5. 启动服务验证
source .env.local && go run cmd/api/main.go
# 查看日志: "Gateway 客户端初始化成功"

Final Checklist

  • 所有 14 个 Gateway API 接口成功封装(流量卡 6 个 + 设备 7 个签名)
  • 加密/签名通过单元测试验证(与 Apifox 文档一致)
  • 集成测试验证真实 Gateway API 调用(测试 ICCID: 8986062580006141710
  • 重试逻辑测试通过3 次重试,指数退避)
  • 配置通过环境变量成功加载JUNHONG_GATEWAY_*
  • 依赖注入到 iot_card Service 成功SyncCardStatus 方法)
  • 单元测试覆盖率 ≥ 90%(核心逻辑)
  • 无 LSP 错误,编译通过
  • 符合项目代码规范中文注释、Go 命名规范)
  • 文档完整(使用示例、错误码说明)
  • 所有 "Must NOT Have" 约束遵守(无批量查询、无设备真实测试、无第三方重试库)