This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 套餐历史列表查询
|
||||
系统 SHALL 提供资产的全量套餐记录查询接口,包含历史和当前生效套餐。
|
||||
|
||||
**API 端点**: `GET /api/admin/assets/:asset_type/:id/packages`
|
||||
|
||||
**每条记录响应字段**:
|
||||
- `package_usage_id`
|
||||
- `package_name`
|
||||
- `package_type`
|
||||
- `master_usage_id`
|
||||
- `real_total_mb`
|
||||
- `real_used_mb`
|
||||
- `virtual_total_mb`
|
||||
- `virtual_used_mb`
|
||||
- `reduction_pct`
|
||||
- `activated_at`
|
||||
- `expires_at`
|
||||
- `status`
|
||||
- `paid_amount`
|
||||
|
||||
**兼容规则**:
|
||||
- 不再返回 `real_data_mb`、`virtual_data_mb`、`package_used_mb`、`package_remain_mb` 等旧展示字段
|
||||
|
||||
#### Scenario: 历史列表返回统一五字段
|
||||
- **WHEN** 管理员调用 `GET /api/admin/assets/card/123/packages`
|
||||
- **THEN** 每条套餐记录都返回统一五字段和 `reduction_pct`
|
||||
|
||||
#### Scenario: 无套餐历史时返回空数组
|
||||
- **WHEN** 管理员查询一张从未购买套餐的卡
|
||||
- **THEN** 系统返回空数组,不报错
|
||||
|
||||
### Requirement: 当前主套餐详情查询
|
||||
系统 SHALL 提供查询资产当前生效主套餐的接口,用于展示主套餐详细信息。
|
||||
|
||||
**API 端点**: `GET /api/admin/assets/:asset_type/:id/current-package`
|
||||
|
||||
**查询条件**: `status = 1 AND master_usage_id IS NULL`
|
||||
|
||||
**响应字段**:
|
||||
- 与套餐历史列表中的单条记录字段一致
|
||||
- 不包含加油包汇总
|
||||
- 无生效主套餐时返回 HTTP 200,且 `data = null`
|
||||
|
||||
#### Scenario: 当前套餐只返回主套餐
|
||||
- **WHEN** 管理员调用 `GET /api/admin/assets/card/123/current-package`,该卡存在 1 个生效主套餐和多个加油包
|
||||
- **THEN** 系统只返回主套餐的统一五字段与 `reduction_pct`
|
||||
|
||||
#### Scenario: 无当前主套餐返回 200 + null
|
||||
- **WHEN** 管理员查询没有生效中主套餐的资产
|
||||
- **THEN** 系统返回 HTTP 200
|
||||
- **AND** 响应体中的 `data = null`
|
||||
@@ -0,0 +1,29 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 统一资产解析入口
|
||||
系统 SHALL 提供统一的资产查找接口,通过任意标识符定位卡或设备,并返回该资产的中等聚合信息。
|
||||
|
||||
**API 端点**: `GET /api/admin/assets/resolve/:identifier`
|
||||
|
||||
**流量相关响应结构(AssetResolveResponse)**:
|
||||
- `current_package`: 当前主套餐名称(无主套餐时返回空字符串)
|
||||
- `current_package_usage_id`: 当前主套餐的套餐使用记录 ID(无主套餐时为 null)
|
||||
- `real_total_mb`: 当前主套餐真实总量
|
||||
- `real_used_mb`: 当前主套餐真实已用量
|
||||
- `virtual_total_mb`: 当前主套餐业务停机阈值
|
||||
- `virtual_used_mb`: 当前主套餐展示已用量
|
||||
- `reduction_pct`: 按 `(real_total_mb / virtual_total_mb) - 1` 计算
|
||||
|
||||
**流量展示规则**:
|
||||
- 流量摘要只代表当前生效主套餐,不聚合加油包
|
||||
- 无当前生效主套餐时,上述流量字段全部返回 0,`current_package_usage_id` 返回 null
|
||||
|
||||
#### Scenario: 返回当前主套餐五字段摘要
|
||||
- **WHEN** 管理员解析一张存在生效主套餐的卡
|
||||
- **THEN** 响应中包含 `current_package_usage_id`、`real_total_mb`、`real_used_mb`、`virtual_total_mb`、`virtual_used_mb` 和 `reduction_pct`
|
||||
|
||||
#### Scenario: 无主套餐时返回空摘要
|
||||
- **WHEN** 管理员解析一台没有当前生效主套餐的设备
|
||||
- **THEN** 响应中 `current_package = ""`
|
||||
- **AND** `current_package_usage_id = null`
|
||||
- **AND** 流量字段均返回 `0`
|
||||
@@ -0,0 +1,27 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 流量耗尽自动停机
|
||||
系统 SHALL 在主套餐和所有加油包都达到各自业务耗尽阈值后,调用运营商接口停机。
|
||||
|
||||
**业务耗尽阈值**:
|
||||
- 启用虚流量:`real_used_mb >= virtual_total_mb`
|
||||
- 未启用虚流量:`real_used_mb >= real_total_mb`
|
||||
|
||||
#### Scenario: 主套餐达到虚阈值触发停机
|
||||
- **WHEN** 卡的当前主套餐 `real_total_mb=100`、`virtual_total_mb=70`,并且 `real_used_mb=70`
|
||||
- **THEN** 系统将该主套餐视为耗尽并进入停机检查
|
||||
|
||||
#### Scenario: 加油包仍有业务额度时不停机
|
||||
- **WHEN** 主套餐已达到业务阈值,但仍存在加油包 `real_used_mb < effective_threshold_mb`
|
||||
- **THEN** 系统不触发停机
|
||||
|
||||
### Requirement: 购买套餐自动复机
|
||||
系统 SHALL 在存在 active 套餐且该套餐仍有业务剩余额度时,自动触发复机。
|
||||
|
||||
#### Scenario: 购买加油包后恢复可用额度则自动复机
|
||||
- **WHEN** 卡已因 `traffic_exhausted` 停机,随后新增一条 active 加油包,且该加油包 `real_used_mb < effective_threshold_mb`
|
||||
- **THEN** 系统自动调用运营商复机接口
|
||||
|
||||
#### Scenario: 仅存在已用完套餐时不复机
|
||||
- **WHEN** 卡处于停机状态,所有 active 或 depleted 套餐都满足 `real_used_mb >= effective_threshold_mb`
|
||||
- **THEN** 系统不触发复机
|
||||
@@ -0,0 +1,46 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: B1 资产基本信息查询接口
|
||||
系统 SHALL 提供 `GET /api/c/v1/asset/info?identifier=xxx`,并且 MUST 要求个人客户认证(C 端 Token)。
|
||||
|
||||
**流量相关响应字段**:
|
||||
- `current_package`
|
||||
- `current_package_usage_id`
|
||||
- `real_total_mb`
|
||||
- `real_used_mb`
|
||||
- `virtual_total_mb`
|
||||
- `virtual_used_mb`
|
||||
- `reduction_pct`
|
||||
|
||||
**约束**:
|
||||
- 流量摘要仅表示当前主套餐,不聚合加油包
|
||||
- 不再返回 `package_total_mb`、`package_used_mb`、`package_remain_mb` 等旧字段
|
||||
|
||||
#### Scenario: C 端资产信息返回主套餐五字段
|
||||
- **WHEN** 客户调用 `GET /api/c/v1/asset/info?identifier=8986xxxx` 且资产存在当前主套餐
|
||||
- **THEN** 响应包含当前主套餐的统一五字段与 `reduction_pct`
|
||||
|
||||
#### Scenario: 无主套餐时返回空摘要
|
||||
- **WHEN** 客户查询的资产没有当前主套餐
|
||||
- **THEN** 响应中流量字段返回 `0`,`current_package_usage_id` 返回 `null`
|
||||
|
||||
### Requirement: B3 历史套餐列表接口
|
||||
系统 SHALL 提供 `GET /api/c/v1/asset/package-history?identifier=xxx&page=1&page_size=20`,并且 MUST 要求个人客户认证。
|
||||
|
||||
**列表项字段**:
|
||||
- `package_usage_id`
|
||||
- `package_name`
|
||||
- `package_type`
|
||||
- `master_usage_id`
|
||||
- `real_total_mb`
|
||||
- `real_used_mb`
|
||||
- `virtual_total_mb`
|
||||
- `virtual_used_mb`
|
||||
- `reduction_pct`
|
||||
- `activated_at`
|
||||
- `expires_at`
|
||||
- `status`
|
||||
|
||||
#### Scenario: C 端历史列表复用统一字段
|
||||
- **WHEN** 客户调用 `GET /api/c/v1/asset/package-history`
|
||||
- **THEN** 列表项使用统一五字段与 `reduction_pct`,不再混用旧展示字段
|
||||
@@ -0,0 +1,39 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 套餐实体定义
|
||||
系统 SHALL 定义套餐(Package)实体,包含套餐的基本属性、定价、流量配置,以及用于展示映射的内部比例字段。
|
||||
|
||||
**实体字段**:
|
||||
- `real_data_mb`: 真流量额度(MB,套餐真实总量)
|
||||
- `virtual_data_mb`: 虚流量额度(MB,业务停机阈值,必须大于 0 且小于等于 `real_data_mb`)
|
||||
- `virtual_ratio`: 内部展示映射比例,定义为 `real_data_mb / virtual_data_mb`
|
||||
- `enable_virtual_data`: 是否启用虚流量(未启用时 `virtual_ratio = 1.0`)
|
||||
|
||||
**约束**:
|
||||
- `enable_virtual_data = true` 时,`virtual_data_mb` MUST 大于 0
|
||||
- `enable_virtual_data = true` 时,`virtual_data_mb` MUST 小于等于 `real_data_mb`
|
||||
- `virtual_ratio` 由 Service 层自动计算并存储,不由调用方传入
|
||||
|
||||
#### Scenario: 创建启用虚流量的套餐
|
||||
- **WHEN** 平台创建套餐,`real_data_mb=100`、`virtual_data_mb=70`、`enable_virtual_data=true`
|
||||
- **THEN** 系统自动计算并存储 `virtual_ratio = 100 / 70 ≈ 1.428571`
|
||||
|
||||
#### Scenario: 创建虚流量大于真流量的套餐失败
|
||||
- **WHEN** 平台创建套餐,`real_data_mb=100`、`virtual_data_mb=120`、`enable_virtual_data=true`
|
||||
- **THEN** 系统拒绝保存并返回参数错误
|
||||
|
||||
### Requirement: 套餐流量类型和真虚流量共存
|
||||
系统 SHALL 支持同一套餐同时定义真实总量和业务阈值,但 `virtual_data_mb` 不再表示与真流量相加的“另一份总量”,而是展示映射与停机判断所依赖的阈值。
|
||||
|
||||
**流量类型定义**:
|
||||
- **真流量(real_data_mb)**: 套餐真实总量
|
||||
- **虚流量(virtual_data_mb)**: 业务停机阈值和展示映射基准
|
||||
- **未启用虚流量**: `virtual_data_mb` 视为等于 `real_data_mb`
|
||||
|
||||
#### Scenario: 真总量与虚阈值并存
|
||||
- **WHEN** 平台创建套餐,`real_data_mb=100`、`virtual_data_mb=70`
|
||||
- **THEN** 系统将其解释为“真实总量 100、业务阈值 70”,而不是“总量 170”
|
||||
|
||||
#### Scenario: 不允许纯虚流量套餐
|
||||
- **WHEN** 平台尝试创建 `real_data_mb=0` 且 `virtual_data_mb>0` 的套餐
|
||||
- **THEN** 系统拒绝保存并返回参数错误
|
||||
@@ -0,0 +1,22 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 流量优先扣减加油包
|
||||
系统 SHALL 在扣减流量时优先扣减加油包,再扣减主套餐;每条套餐的剩余额度 SHALL 基于业务耗尽阈值而不再直接等同于 `real_total_mb`。
|
||||
|
||||
**业务阈值定义**:
|
||||
- 启用虚流量:`effective_threshold_mb = virtual_total_mb_snapshot`
|
||||
- 未启用虚流量:`effective_threshold_mb = real_total_mb`
|
||||
|
||||
**扣减规则**:
|
||||
- `real_used_mb` 继续累计真实用量
|
||||
- `remaining_quota_mb = effective_threshold_mb - real_used_mb`
|
||||
- 当 `real_used_mb >= effective_threshold_mb` 时,套餐状态更新为已用完
|
||||
|
||||
#### Scenario: 启用虚流量的加油包按虚阈值耗尽
|
||||
- **WHEN** 某加油包 `real_total_mb=100`、`virtual_total_mb=70`、当前 `real_used_mb=68`,本次需要继续扣减 2MB
|
||||
- **THEN** 系统将该加油包扣减到 `real_used_mb=70`
|
||||
- **AND** 将其状态更新为已用完
|
||||
|
||||
#### Scenario: 所有 active 套餐均达到业务阈值后触发停机检查
|
||||
- **WHEN** 主套餐和所有加油包都满足 `real_used_mb >= effective_threshold_mb`
|
||||
- **THEN** 系统进入停机判断流程
|
||||
@@ -0,0 +1,56 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 套餐流量对外语义统一
|
||||
系统 SHALL 以套餐使用记录为事实源,对外统一返回 `real_total_mb`、`real_used_mb`、`virtual_total_mb`、`virtual_used_mb` 和 `reduction_pct`,不再要求调用方从旧字段或 live `tb_package` 值自行拼接流量语义。
|
||||
|
||||
**字段语义**:
|
||||
- `real_total_mb`: 套餐真实总量
|
||||
- `real_used_mb`: 套餐真实已用量
|
||||
- `virtual_total_mb`: 套餐业务停机阈值
|
||||
- `virtual_used_mb`: 将 `real_used_mb` 映射到 `real_total_mb` 刻度后的展示值
|
||||
- `reduction_pct`: 默认按 `(real_total_mb / virtual_total_mb) - 1` 计算,字段名继续沿用业务口语“缩减比例”,接口返回值使用 `0.428571` 这种比例小数格式
|
||||
|
||||
#### Scenario: 启用虚流量时返回统一五字段
|
||||
- **WHEN** 某条套餐使用记录的 `real_total_mb=100`、`real_used_mb=50`、`virtual_total_mb=70`
|
||||
- **THEN** 系统返回 `virtual_used_mb=71.43`
|
||||
- **AND** 同时返回 `reduction_pct=0.428571...`
|
||||
|
||||
#### Scenario: 未启用虚流量时退化为真流量视图
|
||||
- **WHEN** 某条套餐使用记录未启用虚流量
|
||||
- **THEN** 系统返回 `virtual_total_mb = real_total_mb`
|
||||
- **AND** `virtual_used_mb = real_used_mb`
|
||||
- **AND** `reduction_pct=0`
|
||||
|
||||
### Requirement: 虚已用展示公式统一
|
||||
系统 SHALL 使用统一公式计算 `virtual_used_mb`:`min(real_used_mb * (real_total_mb / virtual_total_mb), real_total_mb)`。
|
||||
|
||||
#### Scenario: 真已用接近虚阈值时展示接近满量程
|
||||
- **WHEN** `real_total_mb=100`、`virtual_total_mb=70`、`real_used_mb=69`
|
||||
- **THEN** 系统返回 `virtual_used_mb=98.57`
|
||||
|
||||
#### Scenario: 真已用达到虚阈值时展示封顶
|
||||
- **WHEN** `real_total_mb=100`、`virtual_total_mb=70`、`real_used_mb=70`
|
||||
- **THEN** 系统返回 `virtual_used_mb=100`
|
||||
|
||||
### Requirement: 套餐耗尽阈值统一
|
||||
系统 SHALL 将套餐业务耗尽阈值统一定义为:启用虚流量时使用 `virtual_total_mb`,未启用虚流量时使用 `real_total_mb`。
|
||||
|
||||
#### Scenario: 启用虚流量时按虚阈值耗尽
|
||||
- **WHEN** 某条套餐使用记录 `real_total_mb=100`、`virtual_total_mb=70`、`real_used_mb=70`
|
||||
- **THEN** 系统将该套餐判定为已用完
|
||||
|
||||
#### Scenario: 未启用虚流量时按真总量耗尽
|
||||
- **WHEN** 某条套餐使用记录未启用虚流量且 `real_used_mb=real_total_mb`
|
||||
- **THEN** 系统将该套餐判定为已用完
|
||||
|
||||
### Requirement: 历史套餐流量快照可回放
|
||||
系统 SHALL 在 `tb_package_usage` 上持久化虚总量与展示映射所需快照,以保证历史流量展示不继续依赖 live `tb_package` 值。
|
||||
|
||||
#### Scenario: 创建新套餐使用记录时写入快照
|
||||
- **WHEN** 系统创建新的 `PackageUsage`
|
||||
- **THEN** 同步写入 `virtual_total_mb_snapshot`、展示倍率快照和虚流量开关快照
|
||||
|
||||
#### Scenario: 历史套餐记录执行一次性回填
|
||||
- **WHEN** 本次变更上线执行历史数据迁移
|
||||
- **THEN** 系统为已有 `PackageUsage` 回填虚总量与展示倍率快照
|
||||
- **AND** 后续读链路优先读取快照值而不是 `tb_package` 当前值
|
||||
Reference in New Issue
Block a user