56 KiB
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 存在密码学漏洞(遗留系统,需添加注释警告)
自我审查
识别的潜在风险:
- 加密算法实现风险: AES-128-ECB + PKCS5Padding 实现容易出错
- 缓解措施: 使用 Apifox 文档中的 Go 示例代码,添加单元测试验证加密/解密
- 签名计算风险: 参数排序和拼接逻辑可能与 Gateway 不一致
- 缓解措施: 严格按照 Apifox 文档实现,集成测试验证真实签名
- 重试逻辑风险: 不当重试可能导致重复操作(如停机/复机)
- 缓解措施: 只重试网络错误和 5xx 错误,业务错误(4xx)不重试
- 测试覆盖风险: 设备 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- 流量卡 API(7 个接口)internal/gateway/device.go- 设备 API(7 个接口)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:
- RED: 编写失败测试
- 测试文件:
internal/gateway/*_test.go - 测试命令:
go test -v ./internal/gateway/... - 预期: FAIL(测试存在,实现不存在)
- 测试文件:
- GREEN: 最小实现使测试通过
- 实现代码:
internal/gateway/*.go - 测试命令:
go test -v ./internal/gateway/... - 预期: PASS
- 实现代码:
- 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: 实现流量卡 API(flow_card.go,7 个接口)
└── Task 2.2: 实现设备 API(device.go,7 个接口签名)
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- 请求/响应 DTOflow_card.go- 流量卡 APIdevice.go- 设备 APIclient_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"` // 业务数据(需解密) } - 流量卡相关 DTO(7 个接口):
CardStatusReq- 卡状态查询请求CardStatusResp- 卡状态查询响应FlowQueryReq- 流量查询请求FlowUsageResp- 流量使用响应RealnameStatusReq- 实名认证状态请求RealnameStatusResp- 实名认证状态响应CardOperationReq- 停机/复机请求(通用)RealmnameLinkResp- 实名认证链接响应
- 设备相关 DTO(7 个接口):
DeviceInfoReq- 设备信息请求DeviceInfoResp- 设备信息响应SlotInfoReq- 卡槽信息请求SlotInfoResp- 卡槽信息响应SpeedLimitReq- 限速设置请求WiFiConfigReq- WiFi 配置请求CardSwitchReq- 切换卡请求DeviceOperationReq- 设备操作请求(重启/恢复出厂)
- 每个结构体添加:
- JSON 标签(使用 sonic 序列化)
- 中文注释(description)
- 验证标签(validate,如
required)
Must NOT do:
- 不定义 BatchQuery 相关 DTO(不需要)
- 不过度抽象(不需要接口或泛型)
- 不添加 Getter/Setter(Go 惯用法直接访问字段)
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.2(API 实现依赖 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.2(API 实现依赖客户端结构)
- 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) - 请求流程:
- 序列化请求数据(sonic)
- AES 加密请求数据
- 构建 GatewayRequest(appId, data, timestamp, sign)
- 重试循环(最多 3 次):
- 发送 HTTP POST 请求
- 解析响应
- 网络错误或 5xx → 重试(指数退避:1s, 2s, 4s)
- 4xx 业务错误 → 不重试(直接返回错误)
- 超时错误 → 不重试(context.DeadlineExceeded)
- AES 解密响应数据
- 返回 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.2(API 实现依赖 doRequest)
- Blocked By: Task 1.4(需要客户端结构存在)
References:
- Librarian 研究: HTTP 重试最佳实践(指数退避、错误分类)
pkg/sms/http_client.go- 参考 HTTP 请求模式(无重试,需自行实现)pkg/constants/constants.go- 参考 DefaultRetryMax(Asynq 任务重试次数 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 实现流量卡 API(7 个接口)
What to do:
- 在
internal/gateway/flow_card.go中实现 7 个流量卡 API:- QueryCardStatus - 查询流量卡状态(在线/离线)
- 参数: iccid
- 返回: 卡状态、运营商、信号强度等
- QueryFlow - 查询流量使用情况
- 参数: iccid
- 返回: 已用流量、剩余流量、套餐流量等
- QueryRealnameStatus - 查询实名认证状态
- 参数: iccid
- 返回: 是否已实名、实名信息等
- StopCard - 停机(暂停流量卡)
- 参数: iccid
- 返回: 操作结果
- StartCard - 复机(恢复流量卡)
- 参数: iccid
- 返回: 操作结果
- GetRealnameLink - 获取实名认证跳转链接
- 参数: iccid
- 返回: H5 认证链接
BatchQuery- ❌ 删除(用户明确不需要)
- QueryCardStatus - 查询流量卡状态(在线/离线)
- 每个方法:
- 接收
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): 实现流量卡 API(6 个接口) - Files:
internal/gateway/flow_card.go,internal/gateway/flow_card_test.go - Pre-commit:
go test -v ./internal/gateway/ -run TestQuery
- 在
-
2.2 实现设备 API(7 个接口签名)
What to do:
- 在
internal/gateway/device.go中实现 7 个设备 API 方法签名:- GetDeviceInfo - 获取设备信息
- GetSlotInfo - 获取设备卡槽信息
- SetSpeedLimit - 设置设备限速
- SetWiFi - 设置设备 WiFi
- SwitchCard - 设备切换卡
- ResetDevice - 设备恢复出厂设置
- 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.1(Bootstrap 初始化依赖配置)
- Blocked By: None(可立即开始)
References:
pkg/config/config.go- 参考现有配置结构(SMSConfig, JWTConfig)pkg/config/defaults/config.yaml- 参考默认配置格式pkg/config/loader.go- 参考环境变量绑定模式(viper.BindEnv)
Acceptance Criteria:
配置加载验证:
- 配置结构定义完成:
GatewayConfig在pkg/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.1(Bootstrap 初始化可能需要错误码)
- 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.go的Dependencies结构体中添加字段:type Dependencies struct { // ... 现有字段 GatewayClient *gateway.Client // Gateway API 客户端 } - 在
internal/bootstrap/bootstrap.go的Bootstrap函数中初始化 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.2(Service 集成依赖 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.go的Service结构体中添加字段: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 Service(SyncCardStatus 示例) - 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 个流量卡接口:
QueryCardStatus- 查询卡状态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): 实现流量卡 API(6 个接口) |
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 Service(SyncCardStatus 示例) |
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" 约束遵守(无批量查询、无设备真实测试、无第三方重试库)