feat: 新增数据库迁移,重命名 device_no 为 virtual_no,新增 iot_card.virtual_no 和 package.virtual_ratio 字段
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m3s

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-03-14 18:27:28 +08:00
parent b5147d1acb
commit b9c3875c08
77 changed files with 5832 additions and 2393 deletions

View File

@@ -16,7 +16,7 @@ This capability supports:
### Requirement: 套餐实体定义
系统 SHALL 定义套餐(Package)实体,包含套餐的基本属性、定价、流量配置。
系统 SHALL 定义套餐(Package)实体,包含套餐的基本属性、定价、流量配置,以及用于客户端展示流量换算的 `virtual_ratio` 字段
**核心概念**: 套餐只适用于 IoT 卡(ICCID),用户可以为单张 IoT 卡购买套餐,也可以为设备购买套餐(套餐分配到设备绑定的所有 IoT 卡,流量设备级共享)。
@@ -27,32 +27,54 @@ This capability supports:
- `series_id`: 套餐系列 ID(BIGINT,关联 package_series 表,用于组织套餐分组和配置一次性分佣)
- `package_type`: 套餐类型(VARCHAR(20),"formal"-正式套餐 | "addon"-加油包)
- `duration_months`: 套餐时长(INT,月数,1-月套餐 12-年套餐,加油包为 0)
- `real_data_mb`: 真流量额度(BIGINT,MB 为单位,可选)
- `virtual_data_mb`: 虚流量额度(BIGINT,MB 为单位,用于停机判断,可选)
- `real_data_mb`: 真流量额度(BIGINT,MB 为单位,套餐标称总流量)
- `virtual_data_mb`: 虚流量额度(BIGINT,MB 为单位,停机阈值,始终小于或等于真流量)
- `data_amount_mb`: 总流量额度(BIGINT,MB 为单位,real_data_mb + virtual_data_mb)
- `virtual_ratio`: 虚流量换算比例(DECIMAL(10,6),套餐创建时计算并存储,用于客户端展示)
- `enable_virtual_data`: 是否启用虚流量(BOOLEAN,false 时 virtual_ratio=1.0)
- `price`: 套餐价格(DECIMAL(10,2),元)
- `status`: 套餐状态(INT,1-上架 2-下架)
- `created_at`: 创建时间(TIMESTAMP,自动填充)
- `updated_at`: 更新时间(TIMESTAMP,自动填充)
**virtual_ratio 计算规则**:
- `enable_virtual_data = true``virtual_data_mb > 0``virtual_ratio = real_data_mb / virtual_data_mb`
- 其他情况(未启用虚流量):`virtual_ratio = 1.0`
- 套餐创建或更新时由 Service 层自动计算并存储,不由调用方传入
**virtual_ratio 使用场景**(展示换算):
- `展示已使用 = 真已使用 × virtual_ratio`
- `展示剩余 = real_data_mb - 展示已使用`
- 目的当真用量达到停机阈值virtual_data_mb客户看到的展示用量恰好等于 real_data_mb100% 已使用)
**套餐类型说明**:
- **正式套餐(formal)**: 每张 IoT 卡只能有一个有效的正式套餐,购买新的正式套餐会替换旧的
- **加油包(addon)**: 每张 IoT 卡可以购买多个加油包,与正式套餐共存
#### Scenario: 创建月套餐
#### Scenario: 创建月套餐(未启用虚流量)
- **WHEN** 平台创建月套餐,套餐编码为 "PKG-M-001",套餐名称为 "月套餐 10GB",套餐系列 ID 为 1,类型为正式套餐,时长为 1 个月,真流量为 10240 MB,虚流量为 0,价格为 30.00 元
- **THEN** 系统创建套餐记录,`package_code` 为 "PKG-M-001",`series_id` 为 1,`package_type` 为 "formal",`duration_months` 为 1,`real_data_mb` 为 10240,`virtual_data_mb` 为 0,`data_amount_mb` 为 10240,`price` 为 30.00
- **WHEN** 平台创建月套餐,套餐编码为 "PKG-M-001"`enable_virtual_data = false``real_data_mb = 10240`
- **THEN** 系统创建套餐记录`virtual_ratio = 1.0`(未启用虚流量时无换算)
#### Scenario: 创建启用虚流量的套餐
- **WHEN** 平台创建套餐,`enable_virtual_data = true``real_data_mb = 10240`10G`virtual_data_mb = 9216`9G
- **THEN** 系统自动计算并存储 `virtual_ratio = 10240 / 9216 ≈ 1.111111`
#### Scenario: 展示流量换算正确
- **WHEN** 客户的卡真已使用 = 9216 MB已达停机阈值`real_data_mb = 10240``virtual_ratio = 1.111111`
- **THEN** 展示已使用 = 9216 × 1.111111 ≈ 10240 MB展示剩余 = 0 MB客户看到"已用 10G / 共 10G"
#### Scenario: 创建年套餐
- **WHEN** 平台创建年套餐,套餐编码为 "PKG-Y-001",套餐名称为 "年套餐 120GB",套餐系列 ID 为 1,类型为正式套餐,时长为 12 个月,真流量为 122880 MB,虚流量为 0,价格为 300.00 元
- **THEN** 系统创建套餐记录,`package_code` 为 "PKG-Y-001",`series_id` 为 1,`package_type` 为 "formal",`duration_months` 为 12,`real_data_mb` 为 122880,`virtual_data_mb` 为 0,`data_amount_mb` 为 122880,`price` 为 300.00
- **THEN** 系统创建套餐记录,`package_code` 为 "PKG-Y-001",`series_id` 为 1,`package_type` 为 "formal",`duration_months` 为 12,`real_data_mb` 为 122880,`virtual_data_mb` 为 0,`data_amount_mb` 为 122880,`price` 为 300.00`virtual_ratio` 为 1.0
#### Scenario: 创建流量加油包
- **WHEN** 平台创建加油包,套餐编码为 "PKG-ADD-001",套餐名称为 "流量包 5GB",套餐系列 ID 为 2,类型为加油包,时长为 0,真流量为 5120 MB,虚流量为 0,价格为 10.00 元
- **THEN** 系统创建套餐记录,`package_code` 为 "PKG-ADD-001",`series_id` 为 2,`package_type` 为 "addon",`duration_months` 为 0,`real_data_mb` 为 5120,`virtual_data_mb` 为 0,`data_amount_mb` 为 5120,`price` 为 10.00
- **THEN** 系统创建套餐记录,`package_code` 为 "PKG-ADD-001",`series_id` 为 2,`package_type` 为 "addon",`duration_months` 为 0,`real_data_mb` 为 5120,`virtual_data_mb` 为 0,`data_amount_mb` 为 5120,`price` 为 10.00`virtual_ratio` 为 1.0
---