# 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调用:
```go
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)
}
```
**验证命令**:
```bash
# 单元测试
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×tamp=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
**自动化验证**:
```bash
# 编译检查
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
**自动化验证**:
```bash
# 编译检查
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.md`或`AGENTS.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
**自动化验证**:
```bash
# 编译检查
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**: 优化代码
**自动化验证**:
```bash
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个测试用例)
**自动化验证**:
```bash
# 运行所有单元测试
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节
```yaml
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秒)
**自动化验证**:
```bash
# 设置环境变量测试
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)
```go
// Gateway 相关错误(1110-1119)
const (
CodeGatewayError = 1110 // Gateway 通用错误
CodeGatewayEncryptError = 1111 // 数据加密失败
CodeGatewaySignError = 1112 // 签名生成失败
CodeGatewayTimeout = 1113 // 请求超时
CodeGatewayInvalidResp = 1114 // 响应格式错误
)
```
- [ ] allErrorCodes数组注册
```go
var allErrorCodes = []int{
// ... 现有错误码
CodeGatewayError,
CodeGatewayEncryptError,
CodeGatewaySignError,
CodeGatewayTimeout,
CodeGatewayInvalidResp,
}
```
- [ ] errorMessages映射表添加
```go
var errorMessages = map[int]string{
// ... 现有消息
CodeGatewayError: "Gateway 请求失败",
CodeGatewayEncryptError: "数据加密失败",
CodeGatewaySignError: "签名生成失败",
CodeGatewayTimeout: "Gateway 请求超时",
CodeGatewayInvalidResp: "Gateway 响应格式错误",
}
```
**自动化验证**:
```bash
# 运行错误码测试
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.go`的`Dependencies`中添加`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字段
```go
type Dependencies struct {
// ... 现有字段
GatewayClient *gateway.Client
}
```
- [ ] main.go添加initGateway函数
```go
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
```go
gatewayClient := initGateway(cfg, appLogger)
result, err := bootstrap.Bootstrap(&bootstrap.Dependencies{
// ... 现有依赖
GatewayClient: gatewayClient,
})
```
**自动化验证**:
```bash
# 设置环境变量
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参数
```go
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方法
```go
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客户端
```go
// services.go
IotCard: iotCardSvc.New(s.IotCard, deps.GatewayClient, deps.Logger),
```
**自动化验证**:
```bash
# 编译检查
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
**自动化验证**:
```bash
# 设置测试环境变量
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模块
- 技术栈章节说明
- 配置示例(环境变量)
**自动化验证**:
```bash
# 检查文档文件存在
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) | 文档完整性检查 |
---
## 成功标准
### 验证命令
```bash
# 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调用审计日志