# 需求10:限速规则 --- ## 背景 Gateway 已有 `SetSpeedLimit` 接口,支持卡和设备: ```go // internal/gateway/device.go func (c *Client) SetSpeedLimit(ctx context.Context, req *SpeedLimitReq) error // internal/gateway/models.go type SpeedLimitReq struct { CardNo string `json:"cardNo,omitempty"` // 卡(与 DeviceID 二选一) DeviceID string `json:"deviceId,omitempty"` // 设备(与 CardNo 二选一) SpeedLimit int `json:"speedLimit"` // 限速值(KB/s),0=不限速 Extend string `json:"extend,omitempty"` } ``` **"根据不同运营商限速规则,基于套餐流量设置不同的卡/设备的限速规则"** 设计思路:限速规则配置在**套餐**上(不同运营商、不同流量档位对应不同套餐,套餐本身就是差异载体)。套餐激活时自动调用 Gateway 设置限速。 --- ## 数据库变更 ```sql -- 套餐表增加限速字段 ALTER TABLE tb_package ADD COLUMN speed_limit_kbps INT NOT NULL DEFAULT 0 COMMENT '限速值(KB/s),0=不限速'; ``` --- ## Model 变更 ```go // internal/model/package.go type Package struct { // ...原有字段... SpeedLimitKbps int `gorm:"column:speed_limit_kbps;type:int;not null;default:0;comment:限速值(KB/s),0=不限速" json:"speed_limit_kbps"` } ``` --- ## 后端实现 ### 1. 套餐激活时设置限速 在 `internal/service/package/activation_service.go` 中,套餐激活(`ActivatedAt` 赋值)时调用限速: ```go // activatePackageUsage 套餐激活核心逻辑(已有函数,追加限速调用) func (s *ActivationService) activatePackageUsage(ctx context.Context, usage *model.PackageUsage, pkg *model.Package) error { // ...现有激活逻辑... // 套餐有限速配置时,调用 Gateway 设置 if pkg.SpeedLimitKbps > 0 { if err := s.applySpeedLimit(ctx, usage, pkg.SpeedLimitKbps); err != nil { // 限速失败不阻断套餐激活,记录错误日志 s.logger.Error("设置限速失败", zap.Error(err), zap.Uint("package_usage_id", usage.ID), zap.Int("speed_limit", pkg.SpeedLimitKbps)) } } return nil } // applySpeedLimit 调用 Gateway 设置限速 func (s *ActivationService) applySpeedLimit(ctx context.Context, usage *model.PackageUsage, speedKbps int) error { req := &gateway.SpeedLimitReq{SpeedLimit: speedKbps} switch usage.UsageType { case constants.PackageUsageTypeSingleCard: // "single_card",定义在 pkg/constants/iot.go // 查卡的 ICCID card, err := s.iotCardStore.GetByID(ctx, usage.IotCardID) if err != nil { return err } req.CardNo = card.ICCID case constants.PackageUsageTypeDevice: // "device",定义在 pkg/constants/iot.go // 查设备的 IMEI device, err := s.deviceStore.GetByID(ctx, usage.DeviceID) if err != nil { return err } req.DeviceID = device.IMEI } return s.gatewayClient.SetSpeedLimit(ctx, req) } ``` ### 2. 套餐到期/失效时取消限速 当套餐状态变为 `已过期(3)` 或 `已失效(4)` 时,重置限速为 0(不限速)。 在套餐过期处理任务中追加: ```go // 套餐过期时取消限速(SpeedLimit=0 表示不限速) if expiredPkg.SpeedLimitKbps > 0 { s.applySpeedLimit(ctx, usage, 0) // 0 = 不限速 } ``` > **注意**:如果有续费的排队套餐立即生效,应以新套餐的限速为准,而不是先取消再设置。 ### 3. 手动操作限速(后台管理) ``` POST /admin/iot-cards/{id}/speed-limit POST /admin/devices/{id}/speed-limit ``` 请求体: ```go type SetSpeedLimitRequest struct { SpeedLimitKbps int `json:"speed_limit_kbps" validate:"min=0" description:"限速值(KB/s),0=取消限速"` } ``` 直接调 Gateway,不更新套餐配置(临时操作)。 --- ## API 变更(套餐管理) ### 创建/更新套餐新增字段 ``` POST /admin/packages PUT /admin/packages/{id} ``` 新增参数: ```go SpeedLimitKbps int `json:"speed_limit_kbps" validate:"min=0" description:"限速值(KB/s),0=不限速"` ``` ### 套餐列表/详情响应新增字段 ```go type PackageResponse struct { // ...原有字段... SpeedLimitKbps int `json:"speed_limit_kbps" description:"限速值(KB/s),0=不限速"` SpeedLimitDesc string `json:"speed_limit_desc" description:"限速描述(如 '512 KB/s',0时为'不限速')"` } ``` --- ## 前端对接 ### 页面:套餐创建/编辑 在"套餐配置"区域新增限速项: ``` 限速配置: 限速值:[____] KB/s (0 或留空 = 不限速) 提示文案:留空或填 0 表示不限速;建议根据运营商规则填写 ``` 前端换算提示(可选):512 KB/s ≈ 4 Mbps ### 页面:套餐列表 新增"限速"列: - `0` → 显示"不限速" - `>0` → 显示"XXX KB/s" ### 页面:资产详情 > 操作区 新增"手动设置限速"按钮(仅当有生效套餐时显示): ``` 弹框: 当前限速:xxx KB/s(来自套餐配置) 临时设置:[____] KB/s [0=取消限速] [确认设置] → POST /admin/iot-cards/{id}/speed-limit ``` --- ## 注意事项 1. **Gateway 调用失败不阻断业务**:限速设置失败只记录日志,套餐仍然激活。 2. **限速精度**:Gateway `SpeedLimit` 单位是 KB/s,最小值为 1;填 0 表示不限速。 3. **运营商差异**:不同运营商通过创建不同套餐来体现限速差异,不需要在套餐外额外维护运营商-限速映射表。 4. **续费场景**:新套餐生效时重新设置限速,以新套餐的限速值为准。