Files
junhong_cmp_fiber/openspec/specs/gateway-client/spec.md
huang 62708892ec
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m2s
文档
2026-01-31 13:06:30 +08:00

221 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gateway Client Specification
Gateway API 统一客户端,提供 14 个接口的类型安全封装。
## ADDED Requirements
### Requirement: Gateway 客户端结构
系统 SHALL 提供 `gateway.Client` 结构体,封装所有 Gateway API 调用。
客户端字段:
- `baseURL string` - Gateway API 基础 URL
- `appID string` - 应用 ID
- `appSecret string` - 应用密钥
- `httpClient *http.Client` - HTTP 客户端(支持连接复用)
- `timeout time.Duration` - 请求超时时间
#### Scenario: 创建 Gateway 客户端
- **WHEN** 调用 `gateway.NewClient(baseURL, appID, appSecret)`
- **THEN** 返回已初始化的 `Client` 实例
- **AND** HTTP 客户端配置正确(支持 Keep-Alive
#### Scenario: 配置超时时间
- **WHEN** 调用 `client.WithTimeout(30 * time.Second)`
- **THEN** 客户端的 `timeout` 字段更新为 30 秒
- **AND** 返回客户端自身(支持链式调用)
### Requirement: 统一请求方法
系统 SHALL 提供 `doRequest` 方法统一处理加密、签名、HTTP 请求和响应解析。
#### Scenario: 成功的 API 调用
- **WHEN** 调用 `doRequest(ctx, "/flow-card/status", businessData)`
- **THEN** 业务数据使用 AES-128-ECB 加密
- **AND** 请求使用 MD5 签名
- **AND** HTTP POST 发送到 `{baseURL}/flow-card/status`
- **AND** 响应中的 `data` 字段解密并返回
#### Scenario: 网络错误
- **WHEN** HTTP 请求失败网络中断、DNS 解析失败)
- **THEN** 返回 `CodeGatewayError` 错误
- **AND** 错误信息包含原始网络错误
#### Scenario: 请求超时
- **WHEN** HTTP 请求超过配置的超时时间
- **THEN** 返回 `CodeGatewayTimeout` 错误
- **AND** Context 超时错误被正确识别
#### Scenario: 响应格式错误
- **WHEN** Gateway 响应无法解析为 JSON
- **THEN** 返回 `CodeGatewayInvalidResp` 错误
- **AND** 错误信息包含原始响应内容(限制 200 字符)
#### Scenario: Gateway 业务错误
- **WHEN** Gateway 响应中 `code != 200`
- **THEN** 返回 `CodeGatewayError` 错误
- **AND** 错误信息包含 Gateway 的 code 和 msg
### Requirement: 流量卡 API 封装
系统 SHALL 提供 7 个流量卡相关的 API 方法。
#### Scenario: 查询流量卡状态
- **WHEN** 调用 `client.QueryCardStatus(ctx, &CardStatusReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回 `CardStatusResp` 包含 ICCID 和卡状态
- **AND** 卡状态为:"准备"、"正常" 或 "停机" 之一
#### Scenario: 查询流量使用
- **WHEN** 调用 `client.QueryFlow(ctx, &FlowQueryReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回 `FlowUsageResp` 包含已用流量和单位
- **AND** 流量单位为 "MB"
#### Scenario: 查询实名认证状态
- **WHEN** 调用 `client.QueryRealnameStatus(ctx, &CardStatusReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回实名认证状态信息
#### Scenario: 流量卡停机
- **WHEN** 调用 `client.StopCard(ctx, &CardOperationReq{CardNo: "898608070422D0010269"})`
- **THEN** Gateway 执行停机操作
- **AND** 方法返回 nil成功或错误
#### Scenario: 流量卡复机
- **WHEN** 调用 `client.StartCard(ctx, &CardOperationReq{CardNo: "898608070422D0010269"})`
- **THEN** Gateway 执行复机操作
- **AND** 方法返回 nil成功或错误
#### Scenario: 获取实名认证链接
- **WHEN** 调用 `client.GetRealnameLink(ctx, &CardStatusReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回实名认证跳转链接
- **AND** 链接格式为有效的 HTTPS URL
#### Scenario: 广电国网扩展参数
- **WHEN** 停机/复机请求中 `Extend` 字段不为空
- **THEN** 请求包含 `extend` 参数
- **AND** Gateway 正确处理广电国网特殊逻辑
### Requirement: 设备 API 封装
系统 SHALL 提供 7 个设备相关的 API 方法。
#### Scenario: 查询设备信息
- **WHEN** 调用 `client.GetDeviceInfo(ctx, &DeviceInfoReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回 `DeviceInfoResp` 包含设备详细信息
- **AND** 信息包括IMEI、在线状态、信号强度、WiFi 配置、速率等
#### Scenario: 通过设备 ID 查询
- **WHEN** 调用 `client.GetDeviceInfo(ctx, &DeviceInfoReq{DeviceID: "868123456789012"})`
- **THEN** 通过设备 IMEI 查询设备信息
- **AND** 返回结果与通过卡号查询一致
#### Scenario: 查询设备卡槽信息
- **WHEN** 调用 `client.GetSlotInfo(ctx, &DeviceInfoReq{CardNo: "898608070422D0010269"})`
- **THEN** 返回设备中已安装的物联网卡信息
#### Scenario: 设置设备限速
- **WHEN** 调用 `client.SetSpeedLimit(ctx, &SpeedLimitReq{DeviceID: "868123456789012", UploadSpeed: 1024, DownloadSpeed: 2048})`
- **THEN** 设备上下行速率设置为指定值KB/s
#### Scenario: 设置设备 WiFi
- **WHEN** 调用 `client.SetWiFi(ctx, &WiFiReq{DeviceID: "868123456789012", SSID: "MyWiFi", Password: "12345678", Enabled: true})`
- **THEN** 设备 WiFi 配置更新
- **AND** WiFi 名称、密码和启用状态正确设置
#### Scenario: 设备切换卡
- **WHEN** 调用 `client.SwitchCard(ctx, &SwitchCardReq{DeviceID: "868123456789012", TargetICCID: "898608070422D0010270"})`
- **THEN** 多卡设备切换到目标 ICCID
#### Scenario: 设备恢复出厂设置
- **WHEN** 调用 `client.ResetDevice(ctx, &DeviceOperationReq{DeviceID: "868123456789012"})`
- **THEN** 设备恢复为出厂状态
#### Scenario: 设备重启
- **WHEN** 调用 `client.RebootDevice(ctx, &DeviceOperationReq{DeviceID: "868123456789012"})`
- **THEN** 设备执行重启操作
### Requirement: 类型安全的 DTO
系统 SHALL 为所有请求和响应定义类型安全的结构体。
#### Scenario: 请求 DTO 包含验证标签
- **WHEN** 定义 `CardStatusReq` 结构体
- **THEN** `CardNo` 字段包含 `validate:"required"` 标签
- **AND** 可以使用 Validator 库进行验证
#### Scenario: 响应 DTO 正确解析
- **WHEN** Gateway 返回 JSON 响应
- **THEN** `CardStatusResp` 结构体正确解析 `iccid``cardStatus``extend` 字段
- **AND** 字段类型与 Gateway 文档一致
### Requirement: 并发安全
系统 SHALL 确保 `Client` 结构体可以安全地并发调用。
#### Scenario: 多个 Goroutine 并发调用
- **WHEN** 10 个 Goroutine 同时调用 `client.QueryCardStatus`
- **THEN** 所有请求都正确执行
- **AND** 不发生 race condition
#### Scenario: HTTP 连接复用
- **WHEN** 多次调用相同的 Gateway API
- **THEN** HTTP 客户端复用 TCP 连接
- **AND** 减少连接建立开销
### Requirement: 错误处理一致性
系统 SHALL 使用项目统一的错误码系统。
#### Scenario: Gateway 错误返回统一错误码
- **WHEN** Gateway API 调用失败
- **THEN** 返回 `errors.AppError` 类型
- **AND** 错误码为 `CodeGatewayError``CodeGatewayTimeout` 等之一
#### Scenario: 错误包含上下文信息
- **WHEN** 加密失败
- **THEN** 错误信息为 "数据加密失败"
- **AND** 包含底层错误的详细信息
### Requirement: Context 支持
系统 SHALL 支持通过 Context 控制请求超时和取消。
#### Scenario: 使用 Context 控制超时
- **WHEN** 调用 `client.QueryCardStatus(ctx, req)` 且 ctx 设置了 30 秒超时
- **THEN** 请求在 30 秒后自动超时
- **AND** 返回 `CodeGatewayTimeout` 错误
#### Scenario: 取消请求
- **WHEN** 调用 `client.QueryCardStatus(ctx, req)` 且 ctx 被取消
- **THEN** 请求立即停止
- **AND** 返回 context canceled 错误