Files
junhong_cmp_fiber/docs/7月迭代/需求10-限速规则.md
2026-07-13 12:01:18 +09:00

5.6 KiB
Raw Blame History

需求10限速规则


背景

Gateway 已有 SetSpeedLimit 接口,支持卡和设备:

// 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/s0=不限速
    Extend     string `json:"extend,omitempty"`
}

"根据不同运营商限速规则,基于套餐流量设置不同的卡/设备的限速规则"

设计思路:限速规则配置在套餐上(不同运营商、不同流量档位对应不同套餐,套餐本身就是差异载体)。套餐激活时自动调用 Gateway 设置限速。


数据库变更

-- 套餐表增加限速字段
ALTER TABLE tb_package
    ADD COLUMN speed_limit_kbps INT NOT NULL DEFAULT 0
        COMMENT '限速值KB/s0=不限速';

Model 变更

// internal/model/package.go
type Package struct {
    // ...原有字段...
    SpeedLimitKbps int `gorm:"column:speed_limit_kbps;type:int;not null;default:0;comment:限速值KB/s0=不限速" json:"speed_limit_kbps"`
}

后端实现

1. 套餐激活时设置限速

internal/service/package/activation_service.go 中,套餐激活(ActivatedAt 赋值)时调用限速:

// 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不限速

在套餐过期处理任务中追加:

// 套餐过期时取消限速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

请求体:

type SetSpeedLimitRequest struct {
    SpeedLimitKbps int `json:"speed_limit_kbps" validate:"min=0" description:"限速值KB/s0=取消限速"`
}

直接调 Gateway不更新套餐配置临时操作


API 变更(套餐管理)

创建/更新套餐新增字段

POST /admin/packages
PUT  /admin/packages/{id}

新增参数:

SpeedLimitKbps int `json:"speed_limit_kbps" validate:"min=0" description:"限速值KB/s0=不限速"`

套餐列表/详情响应新增字段

type PackageResponse struct {
    // ...原有字段...
    SpeedLimitKbps int    `json:"speed_limit_kbps" description:"限速值KB/s0=不限速"`
    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. 续费场景:新套餐生效时重新设置限速,以新套餐的限速值为准。