临时备份一次

This commit is contained in:
2026-07-13 12:01:18 +09:00
parent 5cdcdad534
commit 2e130b98f5
17 changed files with 3938 additions and 0 deletions

View File

@@ -0,0 +1,194 @@
# 需求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. **续费场景**:新套餐生效时重新设置限速,以新套餐的限速值为准。