137 lines
5.3 KiB
Markdown
137 lines
5.3 KiB
Markdown
# 需求10:Gateway 手动卡限速
|
||
|
||
> 状态:待评审
|
||
> 已确认边界:本期只提供手动设置/取消限速;Gateway 最终对象始终是卡,统一使用 `cardNo`。
|
||
|
||
---
|
||
|
||
## 一、范围
|
||
|
||
本期提供一个统一的后台能力:对一张实际联网卡设置或取消限速。
|
||
|
||
- 单卡资产:使用 `IotCard.ICCID` 作为 `cardNo`。
|
||
- 设备资产:查询 `tb_device_sim_binding.is_current=true` 的当前绑定卡,再使用该卡 ICCID。
|
||
- `speed_kbps > 0` 表示设置限速;`speed_kbps = 0` 表示取消限速。
|
||
- 内部业务单位固定为 `kbps`,前端也按 `kbps` 输入和展示。
|
||
|
||
本期不做:
|
||
|
||
- 套餐限速字段或套餐编辑页限速配置。
|
||
- 套餐激活、到期、续费、停机、切卡后的自动限速或自动取消。
|
||
- 设备 IMEI 限速。
|
||
- “Gateway 当前实际限速”查询展示。上游未提供查询接口时,页面只能展示最近一次本系统操作记录。
|
||
|
||
取消限速仍调用同一个 Gateway 方法。`speed_kbps=0` 是本系统语义,Gateway 所需的取消报文仅在 Infrastructure 适配器中转换,不散落在 Handler、Service 或前端。
|
||
|
||
---
|
||
|
||
## 二、流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
actor User as 平台员工
|
||
participant Web as 资产详情页
|
||
participant API as SpeedLimit Application
|
||
participant DB as PostgreSQL
|
||
participant Resolver as 当前卡解析器
|
||
participant Gateway as Gateway Adapter
|
||
|
||
User->>Web: 输入限速值或点击取消
|
||
Web->>API: asset identifier + speed_kbps + request_id
|
||
API->>Resolver: 解析实际 cardNo
|
||
Resolver-->>API: ICCID 或无当前卡错误
|
||
API->>DB: 创建或复用限速操作记录
|
||
API->>Gateway: SetSpeedLimit(cardNo, speedKbps)
|
||
Gateway-->>API: 成功或失败
|
||
API->>DB: 写结果、错误摘要和操作审计
|
||
API-->>Web: 返回本次操作结果
|
||
```
|
||
|
||
设备不存在当前绑定卡时,接口返回业务错误并记录失败原因;绝不把设备 IMEI、设备 ID 或空字符串发送给 Gateway。
|
||
|
||
---
|
||
|
||
## 三、应用端口与数据
|
||
|
||
```go
|
||
// GatewaySpeedLimitPort 设置或取消卡限速。
|
||
// speedKbps=0 表示取消,具体上游参数由适配器转换。
|
||
type GatewaySpeedLimitPort interface {
|
||
SetSpeedLimit(ctx context.Context, cardNo string, speedKbps int) error
|
||
}
|
||
|
||
// SpeedLimitCardResolver 将卡或设备资产解析为实际联网卡 ICCID。
|
||
type SpeedLimitCardResolver interface {
|
||
ResolveCardNo(ctx context.Context, assetType string, assetID uint) (string, error)
|
||
}
|
||
```
|
||
|
||
新建操作记录表,既用于审计,也用于同一 `request_id` 的幂等重试:
|
||
|
||
```sql
|
||
CREATE TABLE tb_gateway_speed_limit_operation (
|
||
id BIGSERIAL PRIMARY KEY,
|
||
request_id VARCHAR(64) NOT NULL,
|
||
asset_type VARCHAR(20) NOT NULL,
|
||
asset_id BIGINT NOT NULL,
|
||
card_no VARCHAR(100) NOT NULL,
|
||
desired_speed_kbps INT NOT NULL,
|
||
status INT NOT NULL DEFAULT 1,
|
||
error_summary TEXT NOT NULL DEFAULT '',
|
||
operator_id BIGINT NOT NULL,
|
||
completed_at TIMESTAMPTZ,
|
||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE UNIQUE INDEX uq_gateway_speed_limit_operation_request
|
||
ON tb_gateway_speed_limit_operation(request_id);
|
||
CREATE INDEX idx_gateway_speed_limit_operation_asset
|
||
ON tb_gateway_speed_limit_operation(asset_type, asset_id, created_at DESC);
|
||
```
|
||
|
||
状态:`1=处理中, 2=成功, 3=失败`。同一 `request_id` 重试时,任务、操作人、资产和目标限速一致才返回或继续原记录;不一致返回冲突。Gateway 网络超时后允许使用相同目标值重试,因为 `SetSpeedLimit` 是“设为目标状态”的幂等操作。
|
||
|
||
---
|
||
|
||
## 四、接口与前端
|
||
|
||
```text
|
||
POST /api/admin/assets/{identifier}/speed-limit
|
||
```
|
||
|
||
```json
|
||
{
|
||
"request_id": "01JZ...",
|
||
"speed_kbps": 1024
|
||
}
|
||
```
|
||
|
||
- `speed_kbps` 必须为整数且大于等于 0。
|
||
- 取消操作提交 `speed_kbps=0`,不新增第二个取消接口。
|
||
- 后端根据 `identifier` 解析资产类型和数据范围,前端不得传 `cardNo`、设备 ID 或资产类型作为权威依据。
|
||
|
||
资产详情页提供两个独立命令:
|
||
|
||
- 数字输入框加“保存”用于设置限速,单位固定显示 `kbps`。
|
||
- 取消按钮用于提交 `speed_kbps=0`,并使用确认弹窗避免误操作。
|
||
|
||
设备详情页显示“当前使用卡 ICCID”。没有当前卡时禁用两个命令并展示后端返回的原因。页面可展示最近一次操作的目标限速、结果、操作人和时间,但不能把该记录描述为 Gateway 的实时状态。
|
||
|
||
---
|
||
|
||
## 五、审计与人工验收
|
||
|
||
每次请求记录:资产类型/ID、最终 `cardNo`、目标 `speed_kbps`、设置或取消、操作人、`request_id`、Gateway 结果和脱敏错误摘要。
|
||
|
||
人工验收覆盖:
|
||
|
||
1. 单卡按 ICCID 设置限速。
|
||
2. 设备按 `is_current=true` 的绑定卡设置限速,请求中不出现设备 IMEI。
|
||
3. 设备无当前卡时拒绝调用 Gateway。
|
||
4. `speed_kbps=0` 经同一 Gateway 方法取消限速。
|
||
5. 同一 `request_id` 重试不重复生成操作记录;不同请求复用 ID 返回冲突。
|
||
6. Gateway 失败记录错误并允许相同目标值重试。
|
||
|
||
上线前置条件:Gateway 提供设置和取消所需的最终报文字段约定。业务层只保持 `cardNo + speedKbps` 契约。
|