# 需求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` 契约。