42 KiB
Gateway Integration 并行执行计划
TL;DR
快速摘要: 实现Gateway API统一封装,提供14个物联网卡和设备管理接口的类型安全封装,支持AES-128-ECB加密+MD5签名认证机制。
交付物:
- internal/gateway/ 包(完整的Gateway客户端)
- 14个API接口封装(流量卡7个+设备7个)
- 配置集成(GatewayConfig+环境变量)
- 错误码定义(1110-1119)
- 单元测试(覆盖率≥90%)
- 集成测试(验证真实Gateway API)
预计工作量: 120分钟(2小时) 并行执行: YES - 6个波次,最多4个任务并行 关键路径: 加密工具 → Client结构 → API封装 → 单元测试 → Service集成 → 集成测试
上下文
原始需求
实现Gateway API统一封装,解决当前调用逻辑分散、加密签名重复实现的问题,提供类型安全的接口和统一的错误处理。
背景调研总结
1. 项目架构模式(已验证)
- Bootstrap模式: 6步初始化(Stores→Callbacks→Services→Admin→Middlewares→Handlers)
- 依赖注入: Dependencies结构 → main.go初始化 → Service构造器
- Config管理: 层级结构 + JUNHONG_前缀环境变量 + 双层验证
- Error处理: 错误码范围(1000-1999/2000-2999) + errors.New/Wrap
2. 测试模式(已验证)
- 核心工具: NewTestTransaction(自动回滚) + GetTestRedis(全局连接)
- 集成测试: NewIntegrationTestEnv(完整环境) + AsSuperAdmin/AsUser
- 执行方式:
source .env.local && go test - 覆盖率要求: Service层≥90%
3. AES-ECB加密(已调研)
- 实现方式: 手动实现cipher.BlockMode接口(~50行)
- 参考代码: TiDB的aes.go实现
- PKCS5Padding: 标准填充算法(验证所有字节)
- MD5密钥: crypto/md5.Sum()返回[16]byte
- Base64编码: encoding/base64.StdEncoding
Metis预审(gap分析)
无 - 本计划由Prometheus生成,Metis审查在plan生成后进行。
工作目标
核心目标
封装Gateway API为统一的能力模块,提供类型安全、配置化、可测试的接口调用能力。
具体交付物
internal/gateway/client.go- Gateway客户端主体internal/gateway/crypto.go- 加密/签名工具internal/gateway/flow_card.go- 流量卡7个APIinternal/gateway/device.go- 设备7个APIinternal/gateway/models.go- 请求/响应DTOinternal/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流程:
加密/签名测试:
-
RED: 编写测试用例
- 测试文件:
internal/gateway/client_test.go - 测试函数:
TestAESEncrypt,TestGenerateSign - 预期: FAIL(函数不存在)
- 测试文件:
-
GREEN: 实现最小代码
- 实现:
crypto.go中的aesEncrypt, generateSign - 运行:
go test -v ./internal/gateway -run TestAES - 预期: PASS
- 实现:
-
REFACTOR: 优化代码
- 清理重复代码
- 添加注释
- 预期: PASS(测试仍然通过)
API接口测试:
-
RED: 测试QueryCardStatus
- 测试: 构造mock响应,验证解析逻辑
- 预期: FAIL
-
GREEN: 实现QueryCardStatus
- 实现: flow_card.go
- 预期: PASS
-
REFACTOR: 优化
- 预期: PASS
集成测试(client_test.go)
验证真实Gateway API调用:
func TestIntegration_QueryCardStatus(t *testing.T) {
if testing.Short() {
t.Skip("跳过集成测试")
}
// 使用真实配置
cfg := config.Get()
client := gateway.NewClient(
cfg.Gateway.BaseURL,
cfg.Gateway.AppID,
cfg.Gateway.AppSecret,
).WithTimeout(30 * time.Second)
// 调用真实API
resp, err := client.QueryCardStatus(ctx, &gateway.CardStatusReq{
CardNo: "898608070422D0010269",
})
require.NoError(t, err)
require.NotNil(t, resp)
require.NotEmpty(t, resp.ICCID)
}
验证命令:
# 单元测试
source .env.local && go test -v ./internal/gateway -run TestAES
source .env.local && go test -v ./internal/gateway -run TestGenerate
# 集成测试
source .env.local && go test -v ./internal/gateway -run TestIntegration
# 覆盖率
source .env.local && go test -cover ./internal/gateway
执行策略
并行执行波次
系统使用严格的依赖管理,最大化并行执行效率:
Wave 1 (立即启动,无依赖):
├── Task 1.1: 创建目录结构
├── Task 1.4: DTO定义
├── Task 3.1: Gateway配置
└── Task 3.2: Gateway错误码
Wave 2 (依赖: Wave 1):
└── Task 1.2: 加密/签名工具
Wave 3 (依赖: Task 1.2):
└── Task 1.3: Client基础结构
Wave 4 (依赖: Task 1.3):
├── Task 2.1: 流量卡API
├── Task 2.2: 设备API
└── Task 4.1: Bootstrap初始化
Wave 5 (依赖: Wave 4):
├── Task 2.3: 单元测试
└── Task 4.2: Service集成
Wave 6 (依赖: Wave 5):
├── Task 5.1: 集成测试
└── Task 5.2: 文档更新
关键路径: 1.2 → 1.3 → 2.1/2.2 → 2.3 → 4.2 → 5.1
并行加速: ~40%性能提升(相比顺序执行)
依赖矩阵
| Task | 依赖任务 | 阻塞任务 | 可并行任务 |
|---|---|---|---|
| 1.1 | 无 | 无 | 1.4, 3.1, 3.2 |
| 1.2 | 1.1 | 1.3 | 1.4, 3.1, 3.2 |
| 1.3 | 1.2 | 2.1, 2.2, 4.1 | 无 |
| 1.4 | 无 | 2.1, 2.2 | 1.1, 1.2, 3.1, 3.2 |
| 2.1 | 1.3, 1.4 | 2.3 | 2.2, 4.1 |
| 2.2 | 1.3, 1.4 | 2.3 | 2.1, 4.1 |
| 2.3 | 2.1, 2.2 | 5.1 | 4.2 |
| 3.1 | 无 | 4.1 | 1.1, 1.2, 1.4, 3.2 |
| 3.2 | 无 | 无 | 1.1, 1.2, 1.4, 3.1 |
| 4.1 | 1.3, 3.1 | 4.2 | 2.1, 2.2 |
| 4.2 | 4.1 | 5.1 | 2.3 |
| 5.1 | 2.3, 4.2 | 无 | 5.2 |
| 5.2 | 无 | 无 | 5.1 |
Agent调度建议
| Wave | 任务组 | 推荐Category | 推荐Skills |
|---|---|---|---|
| 1 | 1.1, 1.4, 3.1, 3.2 | quick | 无 |
| 2 | 1.2 | ultrabrain | 无 |
| 3 | 1.3 | unspecified-high | 无 |
| 4 | 2.1, 2.2, 4.1 | unspecified-high | 无 |
| 5 | 2.3, 4.2 | unspecified-high | 无 |
| 6 | 5.1, 5.2 | quick | 无 |
TODOs
实现+测试=一个任务。每个任务必须包含:推荐Agent Profile + 并行化信息。
Phase 1: 基础结构搭建
-
1.1. 创建Gateway包目录结构
要做什么:
- 创建
internal/gateway/目录 - 创建占位文件:
client.go,crypto.go,models.go,flow_card.go,device.go - 每个文件添加package声明和基础注释
禁止做什么:
- 不要实现任何逻辑(只创建文件骨架)
- 不要添加import语句(后续任务添加)
推荐Agent Profile:
- Category:
quick- 理由: 简单的文件创建操作,无复杂逻辑
- Skills: 无
- 理由: 目录创建是通用操作,不需要专项技能
- Skills评估但省略:
api-routing: 域不重叠(这是客户端代码,非API路由)model-standards: 域不重叠(DTO定义在Task 1.4)
并行化:
- 可并行运行: YES
- 并行组: Wave 1(与1.4, 3.1, 3.2并行)
- 阻塞任务: 无
- 被阻塞: 无(可立即启动)
参考:
- 项目结构模式:
/internal/service/account/(类似的service包结构) - Go包命名规范: 小写、单数、无下划线
验收标准:
- 目录
internal/gateway/创建成功 - 5个.go文件创建成功(client, crypto, models, flow_card, device)
- 每个文件包含
package gateway声明 - 每个文件包含中文注释说明用途
go build ./internal/gateway编译通过(即使是空包)
提交: NO(与后续任务合并提交)
- 创建
-
1.2. 实现加密/签名工具函数
要做什么:
- 在
crypto.go中实现aesEncrypt函数(AES-128-ECB + PKCS5Padding + Base64) - 实现
generateSign函数(MD5签名,大写输出) - 添加单元测试验证加密/签名正确性
禁止做什么:
- 不要使用第三方加密库(必须用标准库)
- 不要跳过PKCS5填充验证(必须验证所有字节)
- 不要使用小写MD5输出(必须大写)
推荐Agent Profile:
- Category:
ultrabrain- 理由: 加密算法实现需要高精度,涉及字节操作和安全性
- Skills: 无
- 理由: 加密是通用算法,已有TiDB参考代码
- Skills评估但省略:
api-routing: 域不重叠(这是工具函数,非API)dto-standards: 域不重叠(这是加密,非DTO)
并行化:
- 可并行运行: NO(Wave 1必须先完成)
- 并行组: Wave 2(独立任务)
- 阻塞任务: 1.3(Client需要加密函数)
- 被阻塞: 1.1(需要crypto.go文件存在)
参考:
- 加密实现: TiDB的aes.go(ECB模式实现)
- PKCS5Padding: Lancet库的padding.go
- MD5密钥:
crypto/md5.Sum([]byte(appSecret))返回[16]byte - Base64:
encoding/base64.StdEncoding.EncodeToString() - 签名格式:
appId=xxx&data=xxx×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
自动化验证:
# 编译检查 go build ./internal/gateway # 预期: 成功编译,无错误 # 单元测试 source .env.local && go test -v ./internal/gateway -run TestAES # 预期: PASS, 测试覆盖率 ≥ 90% source .env.local && go test -v ./internal/gateway -run TestGenerate # 预期: PASS, 签名输出32位大写 # 覆盖率检查 source .env.local && go test -cover ./internal/gateway # 预期: 覆盖率 ≥ 90%提交: NO(与Phase 1合并提交)
- 在
-
1.3. 实现Gateway客户端基础结构
要做什么:
- 在
client.go中定义Client结构体(baseURL, appID, appSecret, httpClient, timeout) - 实现
NewClient构造函数(配置HTTP Keep-Alive) - 实现
WithTimeout方法(支持链式调用) - 实现
doRequest统一请求方法(加密→签名→HTTP→解密)
禁止做什么:
- 不要在doRequest中处理业务逻辑(只处理加密和HTTP)
- 不要硬编码超时时间(通过WithTimeout配置)
- 不要忽略Context超时(必须判断ctx.Err())
推荐Agent Profile:
- Category:
unspecified-high- 理由: 核心架构代码,需要仔细设计并发安全和错误处理
- Skills: 无
- 理由: HTTP客户端是通用模式,项目中有类似实现
- Skills评估但省略:
api-routing: 域不重叠(这是HTTP客户端,非路由)
并行化:
- 可并行运行: NO(依赖1.2)
- 并行组: Wave 3(独立任务)
- 阻塞任务: 2.1, 2.2, 4.1(所有API都依赖Client)
- 被阻塞: 1.2(需要加密函数)
参考:
- HTTP客户端:
net/http.Client配置Keep-Alive - Context超时:
http.NewRequestWithContext(ctx, ...) - JSON序列化:
sonic.Marshal/Unmarshal - 错误处理:
pkg/errors/errors.go(New/Wrap模式) - 类似实现:
/pkg/auth/token.go(TokenManager的HTTP调用)
验收标准:
TDD流程:
-
RED: 编写
TestNewClient测试- 验证Client初始化正确
- 验证httpClient配置
- 预期: FAIL
-
GREEN: 实现NewClient和WithTimeout
- 配置http.Client(Transport + Timeout)
- 预期: PASS
-
REFACTOR: 优化结构
- 预期: PASS
-
RED: 编写
TestDoRequest_Success测试- Mock HTTP响应
- 验证加密→签名→请求流程
- 预期: FAIL
-
GREEN: 实现doRequest方法
- 步骤1: 序列化业务数据(sonic.Marshal)
- 步骤2: AES加密(调用aesEncrypt)
- 步骤3: 生成签名(调用generateSign)
- 步骤4: 构建请求体(appId, data, sign, timestamp)
- 步骤5: 发送HTTP POST
- 步骤6: 解析响应(GatewayResponse)
- 步骤7: 检查业务状态码
- 预期: PASS
-
REFACTOR: 错误处理优化
- 网络错误→CodeGatewayError
- 超时错误→CodeGatewayTimeout
- 响应格式错误→CodeGatewayInvalidResp
- 预期: PASS
自动化验证:
# 编译检查 go build ./internal/gateway # 预期: 成功编译 # 单元测试 source .env.local && go test -v ./internal/gateway -run TestNewClient # 预期: PASS, Client初始化正确 source .env.local && go test -v ./internal/gateway -run TestDoRequest # 预期: PASS, 请求流程完整 # 错误处理测试 source .env.local && go test -v ./internal/gateway -run TestDoRequest_Error # 预期: PASS, 所有错误场景覆盖提交: NO(与Phase 1合并提交)
- 在
-
1.4. 定义请求/响应DTO
要做什么:
- 在
models.go中定义GatewayResponse通用响应结构 - 定义流量卡DTO:
CardStatusReq/Resp,FlowQueryReq,FlowUsageResp,CardOperationReq - 定义设备DTO:
DeviceInfoReq/Resp,SpeedLimitReq,WiFiReq,SwitchCardReq,DeviceOperationReq - 添加JSON标签和验证标签
禁止做什么:
- 不要省略description注释(每个字段必须有中文说明)
- 不要使用驼峰JSON字段(必须与Gateway文档一致)
- 不要省略validate标签(必填字段必须标记)
推荐Agent Profile:
- Category:
quick- 理由: 结构体定义为机械性工作,遵循DTO规范即可
- Skills:
dto-standards- 理由: 需要遵循项目DTO规范(description标签、验证标签)
- Skills评估但省略:
model-standards: 域不重叠(这是DTO,非数据库Model)api-routing: 域不重叠(这是请求响应结构,非路由)
并行化:
- 可并行运行: YES
- 并行组: Wave 1(与1.1, 3.1, 3.2并行)
- 阻塞任务: 2.1, 2.2(API方法需要DTO)
- 被阻塞: 无(可立即启动)
参考:
- DTO规范:
docs/dto-standards.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方法:QueryCardStatus- 流量卡状态查询QueryFlow- 流量使用查询QueryRealnameStatus- 实名认证状态查询StopCard- 流量卡停机StartCard- 流量卡复机GetRealnameLink- 获取实名认证跳转链接BatchQuery- 批量查询(预留,返回NotImplemented错误)
禁止做什么:
- 不要在方法内实现加密逻辑(复用doRequest)
- 不要硬编码API路径(使用常量)
- 不要省略错误处理(所有错误必须包装)
推荐Agent Profile:
- Category:
unspecified-high- 理由: API封装需要准确映射Gateway接口,错误处理要完整
- Skills: 无
- 理由: API封装是通用模式,复用doRequest即可
- Skills评估但省略:
api-routing: 域不重叠(这是HTTP客户端,非路由注册)
并行化:
- 可并行运行: YES
- 并行组: Wave 4(与2.2, 4.1并行)
- 阻塞任务: 2.3(单元测试需要API实现)
- 被阻塞: 1.3, 1.4(需要Client和DTO)
参考:
- API路径: design.md中的接口列表(/flow-card/status等)
- 请求构造:
map[string]interface{}{"params": {...}} - 响应解析:
sonic.Unmarshal(resp, &result) - 错误包装:
errors.Wrap(errors.CodeGatewayInvalidResp, err, "解析响应失败") - 类似实现:
/internal/service/auth/service.go(调用外部API模式)
验收标准:
TDD流程(每个API重复):
-
RED: 编写
TestQueryCardStatus- Mock doRequest返回
- 验证请求参数构造
- 验证响应解析
- 预期: FAIL
-
GREEN: 实现QueryCardStatus
- 构建businessData
- 调用doRequest("/flow-card/status", businessData)
- 解析响应为CardStatusResp
- 预期: PASS
-
REFACTOR: 优化代码
- 预期: PASS
-
重复上述流程实现其他6个API
自动化验证:
# 编译检查 go build ./internal/gateway # 预期: 成功编译,7个方法定义正确 # 单元测试(每个API) source .env.local && go test -v ./internal/gateway -run TestQueryCardStatus source .env.local && go test -v ./internal/gateway -run TestQueryFlow source .env.local && go test -v ./internal/gateway -run TestStopCard # 预期: PASS # 覆盖率 source .env.local && go test -cover ./internal/gateway # 预期: 覆盖率 ≥ 90%提交: NO(与Phase 2合并提交)
- 在
-
2.2. 实现设备API(7个接口)
要做什么:
- 在
device.go中实现7个设备API方法:GetDeviceInfo- 获取设备信息GetSlotInfo- 获取设备卡槽信息SetSpeedLimit- 设置设备限速SetWiFi- 设置设备WiFiSwitchCard- 设备切换卡ResetDevice- 设备恢复出厂设置RebootDevice- 设备重启
禁止做什么:
- 不要在方法内实现加密逻辑(复用doRequest)
- 不要硬编码API路径(使用常量)
- 不要省略参数验证(CardNo和DeviceID二选一)
推荐Agent Profile:
- Category:
unspecified-high- 理由: 同2.1,API封装需要准确映射
- Skills: 无
- Skills评估但省略:
api-routing: 域不重叠
并行化:
- 可并行运行: YES
- 并行组: Wave 4(与2.1, 4.1并行)
- 阻塞任务: 2.3
- 被阻塞: 1.3, 1.4
参考:
- API路径: design.md(/device/info等)
- 请求构造: 同2.1
- 响应解析: DeviceInfoResp包含多个字段
- 参数验证: CardNo和DeviceID至少一个非空
验收标准:
TDD流程(每个API重复):
- RED: 编写TestGetDeviceInfo等测试
- GREEN: 实现7个设备API
- REFACTOR: 优化代码
自动化验证:
go build ./internal/gateway source .env.local && go test -v ./internal/gateway -run TestGetDeviceInfo source .env.local && go test -v ./internal/gateway -run TestSetWiFi source .env.local && go test -cover ./internal/gateway提交: NO(与Phase 2合并提交)
- 在
-
2.3. 添加单元测试
要做什么:
- 在
client_test.go中添加加密/签名单元测试 - 添加doRequest的mock测试
- 验证错误处理逻辑(超时、网络错误、响应格式错误)
- 验证覆盖率≥90%
禁止做什么:
- 不要跳过错误场景测试(必须覆盖所有错误码)
- 不要使用真实Gateway API(单元测试用mock)
- 不要传递nil绕过依赖(必须mock完整响应)
推荐Agent Profile:
- Category:
unspecified-high- 理由: 测试覆盖率要求高,需要设计完整的测试用例
- Skills: 无
- Skills评估但省略:
db-validation: 域不重叠(这是HTTP客户端测试,非数据库)
并行化:
- 可并行运行: YES
- 并行组: Wave 5(与4.2并行)
- 阻塞任务: 5.1(集成测试依赖单元测试通过)
- 被阻塞: 2.1, 2.2(需要API实现完成)
参考:
- 测试模式:
/internal/service/package/service_test.go - Table-driven tests:
/tests/unit/shop_store_test.go - Mock模式: 不传递nil,构造完整mock响应
- 覆盖率工具:
go test -cover
验收标准:
测试用例清单:
- TestAESEncrypt_Success - 正常加密
- TestAESEncrypt_EmptyData - 空数据加密
- TestGenerateSign_Correctness - 签名正确性
- TestGenerateSign_Uppercase - 签名大写验证
- TestDoRequest_Success - 成功请求
- TestDoRequest_NetworkError - 网络错误
- TestDoRequest_Timeout - 超时错误
- TestDoRequest_InvalidResponse - 响应格式错误
- TestDoRequest_BusinessError - Gateway业务错误
- TestQueryCardStatus_Success - 卡状态查询成功
- TestQueryCardStatus_InvalidCard - 无效卡号
- (每个API至少2个测试用例)
自动化验证:
# 运行所有单元测试 source .env.local && go test -v ./internal/gateway # 预期: PASS, 所有测试通过 # 检查覆盖率 source .env.local && go test -cover ./internal/gateway # 预期: 覆盖率 ≥ 90% # 生成覆盖率报告 source .env.local && go test -coverprofile=coverage.out ./internal/gateway go tool cover -html=coverage.out # 预期: 可视化报告显示 ≥ 90% 覆盖提交: YES - "feat(gateway): 实现Gateway客户端基础功能和API封装"
- 提交内容: Phase 1 + Phase 2所有代码
- 验证:
source .env.local && go test -cover ./internal/gateway - 预期: 覆盖率≥90%
- 在
Phase 3: 配置和错误码集成
-
3.1. 添加Gateway配置
要做什么:
- 在
pkg/config/config.go中添加GatewayConfig结构体 - 在
Config中添加Gateway GatewayConfig字段 - 在
pkg/config/defaults/config.yaml中添加gateway配置项 - 添加配置验证逻辑(必填项检查)
禁止做什么:
- 不要省略mapstructure标签(Viper解析需要)
- 不要硬编码默认值(必须在config.yaml中定义)
- 不要省略环境变量绑定(loader.go)
- 不要跳过验证逻辑(Validate方法)
推荐Agent Profile:
- Category:
quick- 理由: 配置结构定义遵循现有模式即可
- Skills: 无
- Skills评估但省略:
dto-standards: 域不重叠(这是配置,非DTO)
并行化:
- 可并行运行: YES
- 并行组: Wave 1(与1.1, 1.4, 3.2并行)
- 阻塞任务: 4.1(Bootstrap需要配置)
- 被阻塞: 无
参考:
- 配置结构:
/pkg/config/config.golines 14-26(现有Config结构) - 配置文件:
/pkg/config/defaults/config.yaml(YAML格式) - 环境变量:
/pkg/config/loader.golines 49-121(bindEnvVariables) - 验证逻辑:
/pkg/config/config.golines 180-274(Validate方法) - 类似配置: StorageConfig(可选配置的验证模式)
验收标准:
-
GatewayConfig结构体定义完整
- BaseURL string
mapstructure:"base_url" - AppID string
mapstructure:"app_id" - AppSecret string
mapstructure:"app_secret" - Timeout int
mapstructure:"timeout"
- BaseURL string
-
Config结构体添加Gateway字段
- Gateway GatewayConfig
mapstructure:"gateway"
- Gateway GatewayConfig
-
defaults/config.yaml添加gateway节
gateway: base_url: "https://lplan.whjhft.com/openapi" app_id: "60bgt1X8i7AvXqkd" app_secret: "BZeQttaZQt0i73moF" timeout: 30 -
loader.go添加环境变量绑定
- "gateway.base_url"
- "gateway.app_id"
- "gateway.app_secret"
- "gateway.timeout"
-
Validate方法添加Gateway验证
- BaseURL非空
- BaseURL格式验证(http/https开头)
- AppID非空
- AppSecret非空
- Timeout范围验证(5-300秒)
自动化验证:
# 设置环境变量测试 export JUNHONG_GATEWAY_BASE_URL=https://test.example.com export JUNHONG_GATEWAY_APP_ID=test_app_id export JUNHONG_GATEWAY_APP_SECRET=test_secret export JUNHONG_GATEWAY_TIMEOUT=60 # 启动应用验证配置加载 go run cmd/api/main.go # 预期: 启动成功,日志显示Gateway配置加载(AppSecret脱敏为***) # 验证配置验证逻辑 unset JUNHONG_GATEWAY_APP_SECRET go run cmd/api/main.go # 预期: 启动失败,错误提示"gateway.app_secret不能为空"提交: NO(与3.2合并提交)
- 在
-
3.2. 添加Gateway错误码
要做什么:
- 在
pkg/errors/codes.go中添加Gateway错误码常量(1110-1119) - 在
allErrorCodes数组中注册新错误码 - 在
errorMessages映射表中添加中文错误消息 - 运行错误码验证测试
禁止做什么:
- 不要使用已占用的错误码(检查codes.go避免冲突)
- 不要省略错误消息(必须在errorMessages中定义)
- 不要省略错误码注册(allErrorCodes数组)
推荐Agent Profile:
- Category:
quick- 理由: 错误码定义遵循现有模式即可
- Skills: 无
- Skills评估但省略:
dto-standards: 域不重叠(这是错误码,非DTO)
并行化:
- 可并行运行: YES
- 并行组: Wave 1(与1.1, 1.4, 3.1并行)
- 阻塞任务: 无(错误码可随时使用)
- 被阻塞: 无
参考:
- 错误码范围:
/pkg/errors/codes.golines 6-102(现有错误码) - 错误消息:
/pkg/errors/codes.golines 193-271(errorMessages) - 注册模式: allErrorCodes数组
- 测试:
/pkg/errors/codes_test.go(错误码验证测试)
验收标准:
-
错误码常量定义(codes.go)
// Gateway 相关错误(1110-1119) const ( CodeGatewayError = 1110 // Gateway 通用错误 CodeGatewayEncryptError = 1111 // 数据加密失败 CodeGatewaySignError = 1112 // 签名生成失败 CodeGatewayTimeout = 1113 // 请求超时 CodeGatewayInvalidResp = 1114 // 响应格式错误 ) -
allErrorCodes数组注册
var allErrorCodes = []int{ // ... 现有错误码 CodeGatewayError, CodeGatewayEncryptError, CodeGatewaySignError, CodeGatewayTimeout, CodeGatewayInvalidResp, } -
errorMessages映射表添加
var errorMessages = map[int]string{ // ... 现有消息 CodeGatewayError: "Gateway 请求失败", CodeGatewayEncryptError: "数据加密失败", CodeGatewaySignError: "签名生成失败", CodeGatewayTimeout: "Gateway 请求超时", CodeGatewayInvalidResp: "Gateway 响应格式错误", }
自动化验证:
# 运行错误码测试 go test -v ./pkg/errors -run TestErrorCodes # 预期: PASS, 所有错误码已注册且有消息 # 验证错误码使用 go run -e 'package main; import "pkg/errors"; func main() { println(errors.GetMessage(1110, "zh-CN")) }' # 预期: 输出"Gateway 请求失败"提交: YES - "feat(errors): 添加Gateway错误码(1110-1119)"
- 提交内容: Phase 3所有代码(3.1 + 3.2)
- 验证:
go test -v ./pkg/errors -run TestErrorCodes - 预期: PASS
- 在
Phase 4: 依赖注入和集成
-
4.1. Bootstrap初始化Gateway客户端
要做什么:
- 在
internal/bootstrap/dependencies.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.golines 13-24 - 初始化模式:
/cmd/api/main.golines 299-329(initStorage模式) - Bootstrap调用:
/cmd/api/main.gomain函数 - 错误处理: 配置缺失返回nil(可选服务模式)
验收标准:
-
Dependencies添加GatewayClient字段
type Dependencies struct { // ... 现有字段 GatewayClient *gateway.Client } -
main.go添加initGateway函数
func initGateway(cfg *config.Config, logger *zap.Logger) *gateway.Client { if cfg.Gateway.BaseURL == "" { logger.Info("Gateway未配置,跳过初始化") return nil } client := gateway.NewClient( cfg.Gateway.BaseURL, cfg.Gateway.AppID, cfg.Gateway.AppSecret, ).WithTimeout(time.Duration(cfg.Gateway.Timeout) * time.Second) logger.Info("Gateway客户端初始化成功") return client } -
main函数调用initGateway
gatewayClient := initGateway(cfg, appLogger) result, err := bootstrap.Bootstrap(&bootstrap.Dependencies{ // ... 现有依赖 GatewayClient: gatewayClient, })
自动化验证:
# 设置环境变量 export JUNHONG_GATEWAY_BASE_URL=https://lplan.whjhft.com/openapi export JUNHONG_GATEWAY_APP_ID=60bgt1X8i7AvXqkd export JUNHONG_GATEWAY_APP_SECRET=BZeQttaZQt0i73moF # 启动应用 source .env.local && go run cmd/api/main.go # 预期: 日志显示"Gateway客户端初始化成功" # 测试配置缺失场景 unset JUNHONG_GATEWAY_BASE_URL source .env.local && go run cmd/api/main.go # 预期: 日志显示"Gateway未配置,跳过初始化"提交: NO(与4.2合并提交)
- 在
-
4.2. Service层集成示例
要做什么:
- 选择一个Service(如
iot_card)集成Gateway客户端 - 添加
SyncCardStatus方法示例 - 添加错误处理和日志记录
禁止做什么:
- 不要在Service中实现加密逻辑(使用Gateway客户端)
- 不要使用fmt.Errorf(必须用errors.New/Wrap)
- 不要省略日志记录(调用外部API必须记录)
推荐Agent Profile:
- Category:
unspecified-high- 理由: Service层集成需要准确的错误处理和日志记录
- Skills: 无
- Skills评估但省略:
api-routing: 域不重叠(这是Service业务逻辑,非路由)
并行化:
- 可并行运行: YES
- 并行组: Wave 5(与2.3并行)
- 阻塞任务: 5.1(集成测试需要Service集成)
- 被阻塞: 4.1(需要Gateway客户端注入)
参考:
- Service模式:
/internal/service/auth/service.golines 27-43(外部客户端注入) - 错误处理:
/internal/service/account/service.golines 36-91(errors.New/Wrap) - 日志记录: zap.Logger使用模式
- Store更新: 调用store.UpdateStatus更新数据库
验收标准:
-
Service构造器添加gatewayClient参数
type Service struct { store *postgres.IotCardStore gatewayClient *gateway.Client logger *zap.Logger } func New( store *postgres.IotCardStore, gatewayClient *gateway.Client, logger *zap.Logger, ) *Service { return &Service{ store: store, gatewayClient: gatewayClient, logger: logger, } } -
实现SyncCardStatus方法
func (s *Service) SyncCardStatus(ctx context.Context, cardNo string) error { // 调用Gateway API resp, err := s.gatewayClient.QueryCardStatus(ctx, &gateway.CardStatusReq{ CardNo: cardNo, }) if err != nil { s.logger.Error("查询卡状态失败", zap.String("cardNo", cardNo), zap.Error(err)) return errors.Wrap(errors.CodeGatewayError, err, "查询卡状态失败") } // 更新数据库 if err := s.store.UpdateStatus(ctx, cardNo, resp.CardStatus); err != nil { return errors.Wrap(errors.CodeInternalError, err, "更新卡状态失败") } s.logger.Info("同步卡状态成功", zap.String("cardNo", cardNo), zap.String("status", resp.CardStatus)) return nil } -
Bootstrap注入Gateway客户端
// services.go IotCard: iotCardSvc.New(s.IotCard, deps.GatewayClient, deps.Logger),
自动化验证:
# 编译检查 go build ./internal/service/iot_card # 预期: 成功编译 # 单元测试(如果已有) source .env.local && go test -v ./internal/service/iot_card -run TestSyncCardStatus # 预期: PASS(需要mock Gateway客户端)提交: YES - "feat(service): 集成Gateway客户端到Service层"
- 提交内容: Phase 4所有代码(4.1 + 4.2)
- 验证:
go build ./... - 预期: 全部编译通过
- 选择一个Service(如
Phase 5: 集成测试和文档
-
5.1. 编写集成测试
要做什么:
- 在
client_test.go中添加集成测试(需要真实Gateway环境) - 测试至少2个接口(如
QueryCardStatus,StopCard) - 验证加密/签名与Gateway文档一致
禁止做什么:
- 不要跳过集成测试(必须验证真实API)
- 不要使用硬编码测试数据(使用.env.local配置)
- 不要忽略测试失败(超时=生产超时)
推荐Agent Profile:
- Category:
quick- 理由: 集成测试调用真实API,验证端到端流程
- Skills: 无
- Skills评估但省略:
db-validation: 域不重叠(这是HTTP API测试,非数据库)
并行化:
- 可并行运行: YES
- 并行组: Wave 6(与5.2并行)
- 阻塞任务: 无(最后一波)
- 被阻塞: 2.3, 4.2(需要单元测试通过和Service集成)
参考:
- 集成测试模式:
/tests/integration/enterprise_device_h5_test.go - 测试跳过:
if testing.Short() { t.Skip() } - 配置加载:
config.Get() - 真实API调用: 不使用mock,调用真实Gateway
验收标准:
集成测试用例:
-
TestIntegration_QueryCardStatus
- 使用真实配置(config.Get())
- 调用真实Gateway API
- 验证响应完整性
- 预期: PASS
-
TestIntegration_StopCard
- 调用停机API
- 验证Gateway返回成功
- 预期: PASS
-
TestIntegration_EncryptionCompatibility
- 验证加密输出与Gateway文档一致
- 验证签名可被Gateway验证
- 预期: PASS
自动化验证:
# 设置测试环境变量 source .env.local # 运行集成测试 go test -v ./internal/gateway -run TestIntegration # 预期: PASS, 真实API调用成功 # 跳过集成测试(仅单元测试) go test -short -v ./internal/gateway # 预期: 集成测试被跳过提交: NO(与5.2合并提交)
- 在
-
5.2. 更新文档
要做什么:
- 在
docs/目录下创建gateway-client-usage.md - 添加Gateway客户端使用示例
- 添加错误码说明
- 更新
README.md添加Gateway模块说明
禁止做什么:
- 不要省略代码示例(必须包含完整调用示例)
- 不要省略错误码说明(所有1110-1119错误码)
- 不要使用英文(文档必须中文)
推荐Agent Profile:
- Category:
quick- 理由: 文档编写遵循现有模板即可
- Skills: 无
- Skills评估但省略:
doc-management: 这是新功能文档,非规范文档
并行化:
- 可并行运行: YES
- 并行组: Wave 6(与5.1并行)
- 阻塞任务: 无
- 被阻塞: 无(文档可随时编写)
参考:
- 文档模板:
/docs/auth-usage-guide.md(使用指南模板) - API文档:
/docs/api/auth.md(API文档模板) - README更新:
/README.md(核心功能章节)
验收标准:
-
创建
docs/gateway-client-usage.md- Gateway客户端介绍
- 配置说明(环境变量)
- 使用示例(完整代码)
- 错误处理说明
- 最佳实践
-
创建
docs/gateway-api-reference.md- 14个API接口列表
- 每个接口的请求/响应示例
- 参数说明
-
更新
README.md- 核心功能章节添加Gateway模块
- 技术栈章节说明
- 配置示例(环境变量)
自动化验证:
# 检查文档文件存在 ls -la docs/gateway-client-usage.md ls -la docs/gateway-api-reference.md # 预期: 文件存在 # 检查README更新 grep -i "gateway" README.md # 预期: 找到Gateway模块说明提交: YES - "docs: 添加Gateway客户端使用文档"
- 提交内容: Phase 5所有代码(5.1 + 5.2)
- 验证: 文档完整性检查
- 预期: 所有文档文件存在且内容完整
- 在
提交策略
| 完成阶段 | 提交信息 | 包含文件 | 验证命令 |
|---|---|---|---|
| Phase 2完成 | feat(gateway): 实现Gateway客户端基础功能和API封装 |
Phase 1 + Phase 2 | source .env.local && go test -cover ./internal/gateway |
| Phase 3完成 | feat(errors): 添加Gateway错误码(1110-1119) |
Phase 3 (3.1 + 3.2) | go test -v ./pkg/errors -run TestErrorCodes |
| Phase 4完成 | feat(service): 集成Gateway客户端到Service层 |
Phase 4 (4.1 + 4.2) | go build ./... |
| Phase 5完成 | docs: 添加Gateway客户端使用文档 |
Phase 5 (5.1 + 5.2) | 文档完整性检查 |
成功标准
验证命令
# 1. 编译检查
go build ./internal/gateway
go build ./...
# 2. 单元测试
source .env.local && go test -v ./internal/gateway
source .env.local && go test -cover ./internal/gateway
# 3. 集成测试
source .env.local && go test -v ./internal/gateway -run TestIntegration
# 4. 错误码验证
go test -v ./pkg/errors -run TestErrorCodes
# 5. 配置验证
export JUNHONG_GATEWAY_BASE_URL=https://lplan.whjhft.com/openapi
export JUNHONG_GATEWAY_APP_ID=60bgt1X8i7AvXqkd
export JUNHONG_GATEWAY_APP_SECRET=BZeQttaZQt0i73moF
source .env.local && go run cmd/api/main.go
最终检查清单
- 所有14个Gateway API接口成功封装
- 加密/签名验证通过(与Gateway文档一致)
- 错误处理覆盖所有异常场景(网络错误、响应格式错误等)
- 单元测试覆盖率≥90%
- 集成测试验证真实Gateway API调用
- 配置通过环境变量成功加载
- 依赖注入到Service层成功
- 文档完整(使用示例、错误码说明、API参考)
- 无LSP错误,编译通过
- 符合项目代码规范(中文注释、Go命名规范、errors.New/Wrap)
风险与缓解
| 风险 | 影响 | 概率 | 缓解措施 |
|---|---|---|---|
| AES-ECB实现错误 | 高(加密失败导致无法调用API) | 中 | 1. 使用TiDB参考代码 2. 端到端集成测试验证 3. 与Gateway文档示例对比 |
| 签名算法兼容性 | 高(签名不匹配导致认证失败) | 中 | 1. 严格按字母序拼接 2. 大写MD5输出 3. 集成测试验证 |
| Gateway响应格式变更 | 中(解析失败) | 低 | 1. 使用json.RawMessage 2. 统一错误处理 3. 日志记录原始响应 |
| 配置验证不完整 | 中(运行时错误) | 低 | 1. 双层验证(Required+Format) 2. 启动时验证 3. 明确错误提示 |
| 测试覆盖率不足 | 低(隐藏bug) | 低 | 1. TDD流程 2. 覆盖率工具检查 3. 集成测试补充 |
后续计划
本次变更完成后的优化方向:
-
阶段2(未来优化):
- 实现异步模式回调接口
- 添加批量查询接口
- 实现请求重试和超时控制
-
阶段3(性能优化):
- 添加响应缓存(Redis)
- 实现请求限流(防止Gateway过载)
- 监控和告警集成
-
阶段4(安全增强):
- 替换AES-ECB为更安全的模式(如需Gateway支持)
- 实现请求签名双向验证
- 添加API调用审计日志