# Gateway API 参考文档

## 概述

本文档提供 Gateway 客户端所有 API 接口的完整参考，包括请求参数、响应格式和使用示例。

**API 分类**：
- 流量卡管理（7 个接口）
- 设备管理（7 个接口）

**基础信息**：
- 协议：HTTPS
- 请求方法：POST
- 内容类型：application/json
- 编码方式：UTF-8
- 加密方式：AES-128-ECB + Base64
- 签名方式：MD5

---

## 流量卡管理 API

### 1. 查询流量卡状态

查询流量卡的当前状态信息。

**方法**: `QueryCardStatus`

**请求参数**:
```go
type CardStatusReq struct {
    CardNo string `json:"cardNo" validate:"required"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |

**响应参数**:
```go
type CardStatusResp struct {
    ICCID      string `json:"iccid"`
    CardStatus string `json:"cardStatus"`
    Extend     string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| ICCID | string | 流量卡 ICCID |
| CardStatus | string | 卡状态（准备、正常、停机） |
| Extend | string | 扩展字段（广电国网特殊参数） |

**使用示例**:
```go
resp, err := client.QueryCardStatus(ctx, &gateway.CardStatusReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Printf("卡状态: %s\n", resp.CardStatus)
```

---

### 2. 查询流量使用情况

查询流量卡的流量使用详情。

**方法**: `QueryFlow`

**请求参数**:
```go
type FlowQueryReq struct {
    CardNo string `json:"cardNo" validate:"required"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |

**响应参数**:
```go
type FlowUsageResp struct {
    UsedFlow int64  `json:"usedFlow"`
    Unit     string `json:"unit"`
    Extend   string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| UsedFlow | int64 | 已用流量 |
| Unit | string | 流量单位（MB） |
| Extend | string | 扩展字段 |

**使用示例**:
```go
resp, err := client.QueryFlow(ctx, &gateway.FlowQueryReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Printf("已用流量: %d %s\n", resp.UsedFlow, resp.Unit)
```

---

### 3. 查询实名认证状态

查询流量卡的实名认证状态。

**方法**: `QueryRealnameStatus`

**请求参数**:
```go
type CardStatusReq struct {
    CardNo string `json:"cardNo" validate:"required"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |

**响应参数**:
```go
type RealnameStatusResp struct {
    Status string `json:"status"`
    Extend string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| Status | string | 实名认证状态 |
| Extend | string | 扩展字段 |

**使用示例**:
```go
resp, err := client.QueryRealnameStatus(ctx, &gateway.CardStatusReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Printf("实名状态: %s\n", resp.Status)
```

---

### 4. 流量卡停机

对流量卡执行停机操作。

**方法**: `StopCard`

**请求参数**:
```go
type CardOperationReq struct {
    CardNo string `json:"cardNo" validate:"required"`
    Extend string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |
| Extend | string | ❌ | 扩展字段（广电国网特殊参数） |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.StopCard(ctx, &gateway.CardOperationReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Println("停机成功")
```

---

### 5. 流量卡复机

对流量卡执行复机操作。

**方法**: `StartCard`

**请求参数**:
```go
type CardOperationReq struct {
    CardNo string `json:"cardNo" validate:"required"`
    Extend string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.StartCard(ctx, &gateway.CardOperationReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Println("复机成功")
```

---

### 6. 获取实名认证跳转链接

获取流量卡实名认证的跳转链接。

**方法**: `GetRealnameLink`

**请求参数**:
```go
type CardStatusReq struct {
    CardNo string `json:"cardNo" validate:"required"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | ✅ | 流量卡号（ICCID） |

**响应参数**:
```go
type RealnameLinkResp struct {
    Link   string `json:"link"`
    Extend string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| Link | string | 实名认证跳转链接（HTTPS URL） |
| Extend | string | 扩展字段 |

**使用示例**:
```go
resp, err := client.GetRealnameLink(ctx, &gateway.CardStatusReq{
    CardNo: "898608070422D0010269",
})
if err != nil {
    return err
}

fmt.Printf("实名链接: %s\n", resp.Link)
```

---

### 7. 批量查询（预留）

批量查询流量卡信息（暂未实现）。

**方法**: `BatchQuery`

**请求参数**:
```go
type BatchQueryReq struct {
    CardNos []string `json:"cardNos" validate:"required,min=1,max=100"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNos | []string | ✅ | 流量卡号列表（最多100个） |

**响应参数**:
```go
type BatchQueryResp struct {
    Results []CardStatusResp `json:"results"`
}
```

**状态**: ⚠️ 暂未实现，调用将返回错误

---

## 设备管理 API

### 1. 获取设备信息

通过卡号或设备 ID 查询设备的在线状态、信号强度、WiFi 信息等。

**方法**: `GetDeviceInfo`

**请求参数**:
```go
type DeviceInfoReq struct {
    CardNo   string `json:"cardNo,omitempty"`
    DeviceID string `json:"deviceId,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | 二选一 | 流量卡号（ICCID） |
| DeviceID | string | 二选一 | 设备 ID/IMEI |

**响应参数**:
```go
type DeviceInfoResp struct {
    IMEI          string `json:"imei"`
    OnlineStatus  int    `json:"onlineStatus"`
    SignalLevel   int    `json:"signalLevel"`
    WiFiSSID      string `json:"wifiSsid,omitempty"`
    WiFiEnabled   int    `json:"wifiEnabled"`
    UploadSpeed   int    `json:"uploadSpeed"`
    DownloadSpeed int    `json:"downloadSpeed"`
    Extend        string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| IMEI | string | 设备 IMEI |
| OnlineStatus | int | 在线状态（0:离线, 1:在线） |
| SignalLevel | int | 信号强度（0-31） |
| WiFiSSID | string | WiFi 名称 |
| WiFiEnabled | int | WiFi 启用状态（0:禁用, 1:启用） |
| UploadSpeed | int | 上行速率（KB/s） |
| DownloadSpeed | int | 下行速率（KB/s） |
| Extend | string | 扩展字段 |

**使用示例**:
```go
resp, err := client.GetDeviceInfo(ctx, &gateway.DeviceInfoReq{
    DeviceID: "123456789012345",
})
if err != nil {
    return err
}

fmt.Printf("设备状态: %s, 信号: %d\n", 
    map[int]string{0:"离线", 1:"在线"}[resp.OnlineStatus],
    resp.SignalLevel,
)
```

---

### 2. 获取设备卡槽信息

查询设备的所有卡槽及其中的卡信息。

**方法**: `GetSlotInfo`

**请求参数**:
```go
type DeviceInfoReq struct {
    CardNo   string `json:"cardNo,omitempty"`
    DeviceID string `json:"deviceId,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| CardNo | string | 二选一 | 流量卡号（ICCID） |
| DeviceID | string | 二选一 | 设备 ID/IMEI |

**响应参数**:
```go
type SlotInfoResp struct {
    IMEI   string     `json:"imei"`
    Slots  []SlotInfo `json:"slots"`
    Extend string     `json:"extend,omitempty"`
}

type SlotInfo struct {
    SlotNo     int    `json:"slotNo"`
    ICCID      string `json:"iccid"`
    CardStatus string `json:"cardStatus"`
    IsActive   int    `json:"isActive"`
    Extend     string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| IMEI | string | 设备 IMEI |
| Slots | []SlotInfo | 卡槽信息列表 |
| SlotNo | int | 卡槽编号 |
| ICCID | string | 卡槽中的 ICCID |
| CardStatus | string | 卡状态（准备、正常、停机） |
| IsActive | int | 是否为当前使用的卡槽（0:否, 1:是） |

**使用示例**:
```go
resp, err := client.GetSlotInfo(ctx, &gateway.DeviceInfoReq{
    DeviceID: "123456789012345",
})
if err != nil {
    return err
}

for _, slot := range resp.Slots {
    fmt.Printf("卡槽%d: %s (%s)%s\n",
        slot.SlotNo,
        slot.ICCID,
        slot.CardStatus,
        map[int]string{0:"", 1:" [当前使用]"}[slot.IsActive],
    )
}
```

---

### 3. 设置设备限速

设置设备的上行和下行速率限制。

**方法**: `SetSpeedLimit`

**请求参数**:
```go
type SpeedLimitReq struct {
    DeviceID      string `json:"deviceId" validate:"required"`
    UploadSpeed   int    `json:"uploadSpeed" validate:"required,min=1"`
    DownloadSpeed int    `json:"downloadSpeed" validate:"required,min=1"`
    Extend        string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| DeviceID | string | ✅ | 设备 ID/IMEI |
| UploadSpeed | int | ✅ | 上行速率（KB/s），最小1 |
| DownloadSpeed | int | ✅ | 下行速率（KB/s），最小1 |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.SetSpeedLimit(ctx, &gateway.SpeedLimitReq{
    DeviceID:      "123456789012345",
    UploadSpeed:   100,
    DownloadSpeed: 500,
})
if err != nil {
    return err
}

fmt.Println("限速设置成功")
```

---

### 4. 设置设备 WiFi

设置设备的 WiFi 名称、密码和启用状态。

**方法**: `SetWiFi`

**请求参数**:
```go
type WiFiReq struct {
    DeviceID string `json:"deviceId" validate:"required"`
    SSID     string `json:"ssid" validate:"required,min=1,max=32"`
    Password string `json:"password" validate:"required,min=8,max=63"`
    Enabled  int    `json:"enabled" validate:"required,oneof=0 1"`
    Extend   string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| DeviceID | string | ✅ | 设备 ID/IMEI |
| SSID | string | ✅ | WiFi 名称（1-32字符） |
| Password | string | ✅ | WiFi 密码（8-63字符） |
| Enabled | int | ✅ | 启用状态（0:禁用, 1:启用） |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.SetWiFi(ctx, &gateway.WiFiReq{
    DeviceID: "123456789012345",
    SSID:     "MyWiFi",
    Password: "password123",
    Enabled:  1,
})
if err != nil {
    return err
}

fmt.Println("WiFi设置成功")
```

---

### 5. 设备切换卡

切换设备当前使用的卡到指定的目标卡。

**方法**: `SwitchCard`

**请求参数**:
```go
type SwitchCardReq struct {
    DeviceID    string `json:"deviceId" validate:"required"`
    TargetICCID string `json:"targetIccid" validate:"required"`
    Extend      string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| DeviceID | string | ✅ | 设备 ID/IMEI |
| TargetICCID | string | ✅ | 目标卡 ICCID |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.SwitchCard(ctx, &gateway.SwitchCardReq{
    DeviceID:    "123456789012345",
    TargetICCID: "898608070422D0010270",
})
if err != nil {
    return err
}

fmt.Println("切换卡成功")
```

---

### 6. 设备恢复出厂设置

将设备恢复到出厂设置状态。

**方法**: `ResetDevice`

**请求参数**:
```go
type DeviceOperationReq struct {
    DeviceID string `json:"deviceId" validate:"required"`
    Extend   string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| DeviceID | string | ✅ | 设备 ID/IMEI |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.ResetDevice(ctx, &gateway.DeviceOperationReq{
    DeviceID: "123456789012345",
})
if err != nil {
    return err
}

fmt.Println("恢复出厂设置成功")
```

---

### 7. 设备重启

远程重启设备。

**方法**: `RebootDevice`

**请求参数**:
```go
type DeviceOperationReq struct {
    DeviceID string `json:"deviceId" validate:"required"`
    Extend   string `json:"extend,omitempty"`
}
```

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| DeviceID | string | ✅ | 设备 ID/IMEI |
| Extend | string | ❌ | 扩展字段 |

**响应参数**: 无（成功返回 nil，失败返回 error）

**使用示例**:
```go
err := client.RebootDevice(ctx, &gateway.DeviceOperationReq{
    DeviceID: "123456789012345",
})
if err != nil {
    return err
}

fmt.Println("重启设备成功")
```

---

## 通用响应结构

所有 API 的底层响应都遵循统一的 Gateway 响应格式：

```go
type GatewayResponse struct {
    Code    int             `json:"code"`
    Msg     string          `json:"msg"`
    Data    json.RawMessage `json:"data"`
    TraceID string          `json:"trace_id"`
}
```

| 参数 | 类型 | 说明 |
|------|------|------|
| Code | int | 业务状态码（200 = 成功） |
| Msg | string | 业务提示信息 |
| Data | json.RawMessage | 业务数据（原始 JSON） |
| TraceID | string | 链路追踪 ID |

**成功响应示例**:
```json
{
  "code": 200,
  "msg": "成功",
  "data": {
    "iccid": "898608070422D0010269",
    "cardStatus": "正常"
  },
  "trace_id": "abc123xyz"
}
```

**失败响应示例**:
```json
{
  "code": 404,
  "msg": "卡号不存在",
  "data": null,
  "trace_id": "abc123xyz"
}
```

---

## 错误码说明

### Gateway 业务错误码

| 错误码 | 说明 | 解决方案 |
|-------|------|---------|
| 200 | 成功 | - |
| 400 | 请求参数错误 | 检查请求参数格式和内容 |
| 401 | 认证失败 | 检查 AppID 和 AppSecret |
| 404 | 资源不存在 | 检查卡号或设备 ID 是否正确 |
| 500 | 服务器内部错误 | 联系 Gateway 服务提供方 |

### 客户端错误码

客户端封装的统一错误码（`pkg/errors/codes.go`）：

| 错误码 | 常量 | 说明 |
|-------|------|------|
| 1110 | CodeGatewayError | Gateway 连接失败 |
| 1111 | CodeGatewayTimeout | Gateway 请求超时 |
| 1112 | CodeGatewayBusinessError | Gateway 业务错误 |
| 1113 | CodeGatewayInvalidResp | Gateway 响应解析失败 |
| 1114 | CodeGatewaySignError | Gateway 签名验证失败 |

---

## 请求流程

### 完整请求流程

```
1. 构造业务请求参数
   ↓
2. 序列化为 JSON
   ↓
3. AES-128-ECB 加密（Base64 编码）
   ↓
4. 生成 MD5 签名（参数排序 + appSecret）
   ↓
5. 构造最终请求体
   {
     "appId": "xxx",
     "data": "encrypted_base64_string",
     "sign": "md5_signature",
     "timestamp": 1706620800000
   }
   ↓
6. POST 请求到 Gateway
   ↓
7. 解析响应 JSON
   ↓
8. 检查业务状态码
   ↓
9. 解密并解析业务数据
   ↓
10. 返回结果或错误
```

### 签名算法

```
1. 将请求参数按 key 字母序排序
2. 拼接为 key1=value1&key2=value2 格式
3. 末尾追加 &appSecret=xxx
4. 计算 MD5 哈希
5. 转为大写字符串
```

**示例**:
```
参数: {cardNo: "123", appId: "abc"}
排序: appId=abc&cardNo=123
追加: appId=abc&cardNo=123&appSecret=secret
MD5:  D41D8CD98F00B204E9800998ECF8427E
```

---

## 相关文档

- [Gateway 客户端使用指南](./gateway-client-usage.md) - 详细的使用指南和最佳实践
- [错误处理指南](./003-error-handling/使用指南.md) - 统一错误处理规范
