# 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调用审计日志