195 lines
5.6 KiB
Markdown
195 lines
5.6 KiB
Markdown
# 需求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. **续费场景**:新套餐生效时重新设置限速,以新套餐的限速值为准。
|