Files
break 395e5fb47c
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 11m13s
修复套餐接续停机竞态与轮询兜底
2026-08-29 16:27:48 +08:00

121 lines
7.8 KiB
Markdown
Raw Permalink 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.
# package-lifecycle 当前行为
## Purpose
描述套餐状态流转与批量操作追踪的当前行为。
## Requirements
### Requirement: 套餐状态流转
系统 SHALL 按当前套餐和套餐使用状态控制上架、订购、激活、失效与到期处理。主套餐到期时,系统 MUST 先确定同一载体是否存在待生效的后续主套餐:存在时,后续套餐激活与停复机重新评估 MUST 由同一条顺序流程完成;系统 MUST NOT 依据后续套餐激活前的无套餐快照发起停机。后续套餐成功生效后,系统 MUST 依据最新套餐、流量和实名事实重新判断卡网络状态,且不得遗留 `no_package` 停机。不存在后续套餐或后续套餐经业务校验不能生效时,系统 SHALL 按现有停机规则评估卡状态。后续套餐激活结果未知或任务投递失败不得被当作无后续套餐处理并据此停机,系统 SHALL 保留既有激活恢复与轮询兜底路径。
#### Scenario: 套餐状态流转
- **GIVEN** 套餐或使用记录处于允许的前置状态
- **WHEN** 执行状态操作
- **THEN** 仅发生一次允许的状态变化;不满足前置状态时返回业务错误
#### Scenario: 到期主套餐接续后续套餐
- **GIVEN** 某载体的当前主套餐到期,且存在满足激活条件的待生效后续主套餐
- **WHEN** 系统处理该主套餐到期
- **THEN** 系统先完成后续套餐激活并按最新权益事实重新评估停复机,且不得因到期前的无套餐快照对该载体发起 `no_package` 停机
#### Scenario: 到期主套餐无后续可生效套餐
- **GIVEN** 某载体的当前主套餐到期,且不存在后续主套餐或队首后续主套餐不满足激活条件
- **WHEN** 系统完成该套餐到期处理
- **THEN** 系统按当前套餐、流量和实名事实执行既有停机评估
#### Scenario: 后续套餐激活结果未知
- **GIVEN** 某载体的当前主套餐到期,存在待生效后续主套餐,但激活任务投递或执行结果暂时未知
- **WHEN** 系统处理该套餐到期
- **THEN** 系统不得将该未知结果视为不存在后续套餐而依据旧快照发起停机,并保留既有激活恢复与套餐轮询兜底
#### Scenario: 卡状态轮询发现缺失的套餐任务
- **GIVEN** 启用轮询的卡匹配套餐检查配置,且其 `polling:package` 分片队列项因异常缺失
- **WHEN** 卡状态轮询成功完成且未命中风险停机
- **THEN** 系统基于最新卡状态仅补入缺失的套餐任务,不改写已存在套餐任务的执行时间;后续套餐任务仍按既有停复机条件评估该卡
### Requirement: 批量操作可追踪
系统 SHALL 为同步批量分配和调价直接返回处理结果;对异步批量订购返回任务标识并提供状态查询。异步批量订购完成后,系统 MUST 持久化每个输入行的成功或失败结果及与其一致的总数、成功数和失败数;部分资产因余额不足、资产校验或重复输入失败不得阻止任务进入完成终态。
#### Scenario: 批量操作可追踪
- **GIVEN** 操作者提交非空且有权处理的资源集合
- **WHEN** 创建批量操作
- **THEN** 同步操作直接返回结果;异步订购返回任务标识且可查询处理状态
#### Scenario: 批量订购部分失败后查询结果
- **GIVEN** 异步批量订购中的部分资产已成功创建订单,其他资产因钱包余额不足或输入重复失败
- **WHEN** Worker 完成全部输入行的处理
- **THEN** 任务状态为已完成,逐行结果保留成功订单与失败原因,且总数等于成功数与失败数之和
### Requirement: 授权页面禁止重复选择套餐
系统 SHALL 使代理系列授权页面能够区分目标店铺已授权和未授权套餐;已授权套餐 MUST 以不可新增的状态返回,首次创建系列授权和既有系列新增套餐均适用。
#### Scenario: 首次创建前查询候选套餐
- **WHEN** 操作者选择目标店铺和套餐系列以创建系列授权
- **THEN** 系统返回可用于选择的候选套餐及其授权状态,前端可阻止选择已授权套餐
#### Scenario: 既有授权新增套餐前查询候选套餐
- **WHEN** 操作者为已有系列授权添加套餐
- **THEN** 系统返回同一店铺和系列的候选套餐及其授权状态,且不改变现有套餐管理提交接口的调价和删除语义
### Requirement: 套餐使用记录价格快照
系统 SHALL 在创建套餐使用记录时分别快照套餐成本价与零售价:`paid_amount`成本价SHALL 取订单 `seller_cost_price`(销售成本价,即卖家店铺向平台结算的成本),`retail_amount`零售价SHALL 取订单 `total_amount`(零售总价)。成本价与实付金额在个人客户场景下不相等时,`paid_amount` MUST 使用成本价而非实付金额。
#### Scenario: 个人客户购买时快照成本价与零售价
- **WHEN** 个人客户为资产购买套餐,店铺成本价 10900 分,零售价 15900 分,客户实付 15900 分
- **THEN** 创建的套餐使用记录 `paid_amount = 10900``retail_amount = 15900`
#### Scenario: 代理钱包自购时快照成本价与零售价
- **WHEN** 代理以钱包支付为自有资产购买套餐,成本价 7000 分,零售价 9900 分
- **THEN** 创建的套餐使用记录 `paid_amount = 7000``retail_amount = 9900`
#### Scenario: 赠送套餐时快照零成本价与零售价
- **WHEN** 平台赠送套餐,零售价 9900 分,成本价 0 分
- **THEN** 创建的套餐使用记录 `paid_amount = 0``retail_amount = 9900`
## 可达操作索引
本节只用于入口导航,不是行为 Requirement业务义务以上述 Requirements 为准。
### 套餐管理
`GET /api/admin/packages`(套餐列表);`POST /api/admin/packages`(创建套餐);`DELETE /api/admin/packages/{id}`(删除套餐);`GET /api/admin/packages/{id}`(获取套餐详情);`PUT /api/admin/packages/{id}`(更新套餐);`PATCH /api/admin/packages/{id}/retail-price`(修改零售价(代理));`PATCH /api/admin/packages/{id}/shelf`(更新套餐上架状态);`PATCH /api/admin/packages/{id}/status`(更新套餐状态)。
### 套餐系列管理
`GET /api/admin/package-series`(套餐系列列表);`POST /api/admin/package-series`(创建套餐系列);`DELETE /api/admin/package-series/{id}`(删除套餐系列);`GET /api/admin/package-series/{id}`(获取套餐系列详情);`PUT /api/admin/package-series/{id}`(更新套餐系列);`PATCH /api/admin/package-series/{id}/status`(更新套餐系列状态)。
### 套餐使用记录
`GET /api/admin/package-usage/{id}/daily-records`(获取套餐流量详单)。
### 代理系列授权
`GET /api/admin/shop-series-grants`(查询代理系列授权列表);`GET /api/admin/shop-series-grants/package-options`(查询代理系列授权套餐候选项);`POST /api/admin/shop-series-grants`(创建代理系列授权);`DELETE /api/admin/shop-series-grants/{id}`(删除代理系列授权);`GET /api/admin/shop-series-grants/{id}`(查询代理系列授权详情);`PUT /api/admin/shop-series-grants/{id}`(更新代理系列授权);`PUT /api/admin/shop-series-grants/{id}/packages`(管理授权套餐,支持新增、更新和删除)。
### 批量套餐分配
`PATCH /api/admin/shop-package-allocations/{id}/expiry-base`(修改套餐分配生效条件覆盖);`POST /api/admin/shop-package-batch-allocations`(批量分配套餐)。
### 批量套餐调价
`POST /api/admin/shop-package-batch-pricing`(批量调价)。
### 批量订购套餐
`GET /api/admin/asset-package-batch-orders`(查询资产套餐批量订购任务列表);`POST /api/admin/asset-package-batch-orders`(创建资产套餐批量订购任务);`GET /api/admin/asset-package-batch-orders/{id}`(查询资产套餐批量订购任务详情)。