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

195 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 需求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/s0=不限速
Extend string `json:"extend,omitempty"`
}
```
**"根据不同运营商限速规则,基于套餐流量设置不同的卡/设备的限速规则"**
设计思路:限速规则配置在**套餐**上(不同运营商、不同流量档位对应不同套餐,套餐本身就是差异载体)。套餐激活时自动调用 Gateway 设置限速。
---
## 数据库变更
```sql
-- 套餐表增加限速字段
ALTER TABLE tb_package
ADD COLUMN speed_limit_kbps INT NOT NULL DEFAULT 0
COMMENT '限速值KB/s0=不限速';
```
---
## Model 变更
```go
// 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` 赋值)时调用限速:
```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/s0=取消限速"`
}
```
直接调 Gateway不更新套餐配置临时操作
---
## API 变更(套餐管理)
### 创建/更新套餐新增字段
```
POST /admin/packages
PUT /admin/packages/{id}
```
新增参数:
```go
SpeedLimitKbps int `json:"speed_limit_kbps" validate:"min=0" description:"限速值KB/s0=不限速"`
```
### 套餐列表/详情响应新增字段
```go
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. **续费场景**:新套餐生效时重新设置限速,以新套餐的限速值为准。