Files
huang 61155952a7
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m56s
feat: 新增代理分配套餐上架状态(shelf_status)功能
- 新增数据库迁移:为 shop_package_allocation 表添加 shelf_status 字段
- 更新模型/DTO:ShopPackageAllocation 增加 ShelfStatus 字段及相关枚举
- 更新套餐分配 Service:支持上架/下架状态管理逻辑
- 更新套餐 Store/Service:根据 shelf_status 过滤可售套餐
- 更新购买验证 Service:引入上架状态校验逻辑
- 归档 OpenSpec 变更:2026-03-02-agent-allocation-shelf-status
- 同步更新主规范文档:allocation-shelf-status、package-management、purchase-validation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 15:38:54 +08:00

276 lines
11 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.
## Requirements
### Requirement: 创建套餐
系统 SHALL 允许平台管理员创建套餐,包含套餐编码、套餐名称、所属系列、套餐类型、时长、**周期类型calendar_type、流量重置周期data_reset_cycle、是否需要实名激活enable_realname_activation**、流量配置、价格和建议价格。套餐编码 MUST 全局唯一(排除已删除记录)。新创建的套餐默认为启用状态(1)和下架状态(2)。
#### Scenario: 成功创建自然月套餐
- **GIVEN** 管理员提供套餐信息calendar_type=natural_monthduration_months=1
- **WHEN** 提交创建请求
- **THEN** 系统创建套餐,状态=1上架状态=2calendar_type=natural_month
#### Scenario: 成功创建按天套餐
- **GIVEN** 管理员提供套餐信息calendar_type=by_dayduration_days=30
- **WHEN** 提交创建请求
- **THEN** 系统创建套餐calendar_type=by_dayduration_days=30
#### Scenario: 套餐编码重复
- **GIVEN** 数据库中存在套餐编码为 "PKG001" 的套餐(未删除)
- **WHEN** 管理员创建套餐,编码为 "PKG001"
- **THEN** 系统返回错误 "套餐编码已存在"
#### Scenario: 关联不存在的套餐系列
- **GIVEN** 管理员指定 series_id=999但系列不存在
- **WHEN** 提交创建请求
- **THEN** 系统返回错误 "套餐系列不存在"
#### Scenario: 缺少必填字段
- **GIVEN** 管理员未提供套餐编码
- **WHEN** 提交创建请求
- **THEN** 系统返回参数验证错误 "套餐编码为必填项"
#### Scenario: 创建自然月套餐时必须提供 duration_months
- **GIVEN** 管理员创建套餐calendar_type=natural_month但未提供 duration_months
- **WHEN** 提交创建请求
- **THEN** 系统返回错误 "自然月套餐必须指定 duration_months"
#### Scenario: 创建按天套餐时必须提供 duration_days
- **GIVEN** 管理员创建套餐calendar_type=by_day但未提供 duration_days
- **WHEN** 提交创建请求
- **THEN** 系统返回错误 "按天套餐必须指定 duration_days"
#### Scenario: 默认 data_reset_cycle 为 monthly
- **GIVEN** 管理员创建主套餐,未指定 data_reset_cycle
- **WHEN** 提交创建请求
- **THEN** 系统自动设置 data_reset_cycle=monthly
#### Scenario: 默认 enable_realname_activation 为 true
- **GIVEN** 管理员创建主套餐,未指定 enable_realname_activation
- **WHEN** 提交创建请求
- **THEN** 系统自动设置 enable_realname_activation=true
---
### Requirement: 查询套餐列表
系统 SHALL 提供套餐列表查询功能,支持按套餐名称模糊搜索、按系列 ID 筛选、按状态筛选、按上架状态筛选、按套餐类型筛选。结果 MUST 分页返回,按创建时间倒序排列。
#### Scenario: 查询所有套餐
- **WHEN** 管理员请求套餐列表,不带筛选条件
- **THEN** 系统返回所有未删除的套餐,分页显示
#### Scenario: 按系列筛选
- **WHEN** 管理员指定套餐系列 ID
- **THEN** 系统只返回属于该系列的套餐
#### Scenario: 按名称搜索
- **WHEN** 管理员提供套餐名称关键字
- **THEN** 系统返回名称包含该关键字的套餐
#### Scenario: 按状态筛选
- **WHEN** 管理员指定启用状态
- **THEN** 系统只返回匹配启用状态的套餐
#### Scenario: 按上架状态筛选
- **WHEN** 管理员指定上架状态
- **THEN** 系统只返回匹配上架状态的套餐
#### Scenario: 按套餐类型筛选
- **WHEN** 管理员指定套餐类型formal/addon
- **THEN** 系统只返回匹配类型的套餐
---
### Requirement: 查询套餐详情
系统 SHALL 允许管理员查询单个套餐的详细信息,**响应包含新增字段calendar_type, data_reset_cycle, enable_realname_activation**。
#### Scenario: 查询存在的套餐
- **GIVEN** 数据库中存在套餐 ID=1
- **WHEN** 管理员请求套餐详情
- **THEN** 系统返回该套餐的完整信息,包含所有新增字段
#### Scenario: 查询不存在的套餐
- **GIVEN** 管理员请求套餐 ID=999但套餐不存在
- **WHEN** 提交查询请求
- **THEN** 系统返回 "套餐不存在" 错误
#### Scenario: 响应包含周期类型信息
- **GIVEN** 套餐 calendar_type=natural_monthduration_months=1
- **WHEN** 管理员查询套餐详情
- **THEN** 响应包含 calendar_type=natural_monthduration_months=1
#### Scenario: 响应包含流量重置周期信息
- **GIVEN** 套餐 data_reset_cycle=monthly
- **WHEN** 管理员查询套餐详情
- **THEN** 响应包含 data_reset_cycle=monthly
#### Scenario: 响应包含实名激活配置
- **GIVEN** 套餐 enable_realname_activation=true
- **WHEN** 管理员查询套餐详情
- **THEN** 响应包含 enable_realname_activation=true
---
### Requirement: 更新套餐
系统 SHALL 允许管理员更新套餐的基本信息,**包括周期类型、流量重置周期、实名激活配置等新增字段**。套餐编码创建后 MUST NOT 允许修改。
#### Scenario: 成功更新套餐基本信息
- **GIVEN** 管理员更新套餐名称和价格
- **WHEN** 提交更新请求
- **THEN** 系统更新套餐记录,返回更新后的详情
#### Scenario: 尝试修改套餐编码
- **GIVEN** 管理员尝试修改套餐编码
- **WHEN** 提交更新请求
- **THEN** 系统忽略套餐编码字段,不进行修改
#### Scenario: 更新不存在的套餐
- **GIVEN** 管理员更新套餐 ID=999但套餐不存在
- **WHEN** 提交更新请求
- **THEN** 系统返回 "套餐不存在" 错误
#### Scenario: 关联不存在的套餐系列
- **GIVEN** 管理员将套餐的 series_id 改为 999但系列不存在
- **WHEN** 提交更新请求
- **THEN** 系统返回错误 "套餐系列不存在"
#### Scenario: 更新套餐周期类型(从自然月改为按天)
- **GIVEN** 套餐当前 calendar_type=natural_monthduration_months=1
- **WHEN** 管理员更新 calendar_type=by_dayduration_days=30
- **THEN** 系统更新成功calendar_type=by_dayduration_days=30
#### Scenario: 更新套餐周期类型(从按天改为自然月)
- **GIVEN** 套餐当前 calendar_type=by_dayduration_days=30
- **WHEN** 管理员更新 calendar_type=natural_monthduration_months=1
- **THEN** 系统更新成功calendar_type=natural_monthduration_months=1
#### Scenario: 更新周期类型但未提供对应时长字段
- **GIVEN** 套餐当前 calendar_type=by_day
- **WHEN** 管理员更新 calendar_type=natural_month但未提供 duration_months
- **THEN** 系统返回错误 "自然月套餐必须指定 duration_months"
#### Scenario: 更新 data_reset_cycle
- **GIVEN** 套餐当前 data_reset_cycle=monthly
- **WHEN** 管理员更新 data_reset_cycle=daily
- **THEN** 系统更新成功data_reset_cycle=daily
#### Scenario: 更新 enable_realname_activation
- **GIVEN** 套餐当前 enable_realname_activation=true
- **WHEN** 管理员更新 enable_realname_activation=false
- **THEN** 系统更新成功enable_realname_activation=false
---
### Requirement: 删除套餐
系统 SHALL 允许管理员删除套餐(软删除)。
#### Scenario: 成功删除套餐
- **WHEN** 管理员删除指定的套餐
- **THEN** 系统软删除该记录,后续查询不再返回
#### Scenario: 删除不存在的套餐
- **WHEN** 管理员删除不存在的套餐
- **THEN** 系统返回 "套餐不存在" 错误
---
### Requirement: 启用/禁用套餐
系统 SHALL 允许管理员切换套餐的启用状态。禁用套餐时 MUST 同时将上架状态设置为下架。
#### Scenario: 启用套餐
- **WHEN** 管理员将禁用的套餐设置为启用
- **THEN** 系统更新状态为启用(1),上架状态保持不变
#### Scenario: 禁用套餐
- **WHEN** 管理员将启用的套餐设置为禁用
- **THEN** 系统更新状态为禁用(2),同时将上架状态设置为下架(2)
#### Scenario: 禁用已上架的套餐
- **WHEN** 管理员禁用一个当前已上架的套餐
- **THEN** 系统更新状态为禁用(2),上架状态强制设置为下架(2)
---
### Requirement: 上架/下架套餐
系统 SHALL 通过 `PATCH /api/admin/packages/:id/shelf` 接口允许不同角色切换套餐上下架状态。**操作目标因调用者角色而不同**:平台/超管修改 `tb_package.shelf_status`(全局状态),代理修改自己的 `tb_shop_package_allocation.shelf_status`(代理独立状态)。只有启用状态的套餐才能上架。
#### Scenario: 平台管理员上架启用的套餐
- **WHEN** 平台/超管将启用且下架的套餐设置为上架
- **THEN** 系统更新 `tb_package.shelf_status=1`
#### Scenario: 平台管理员尝试上架禁用的套餐
- **WHEN** 平台/超管尝试上架一个 status=2禁用的套餐
- **THEN** 系统返回错误 "禁用的套餐不能上架,请先启用"
#### Scenario: 平台管理员下架套餐
- **WHEN** 平台/超管将上架的套餐设置为下架
- **THEN** 系统更新 `tb_package.shelf_status=2`,只影响平台自营渠道
#### Scenario: 代理上架自己分配的套餐
- **GIVEN** 代理拥有该套餐的分配记录,且 `tb_package.status=1`(启用)
- **WHEN** 代理调用接口设置 shelf_status=1
- **THEN** 系统更新该代理的 `allocation.shelf_status=1`,不修改 `tb_package.shelf_status`
#### Scenario: 代理下架自己分配的套餐
- **GIVEN** 代理拥有该套餐的分配记录allocation.shelf_status=1
- **WHEN** 代理调用接口设置 shelf_status=2
- **THEN** 系统更新该代理的 `allocation.shelf_status=2`,不影响其他代理
#### Scenario: 代理尝试上架全局禁用的套餐
- **GIVEN** `tb_package.status=2`(禁用)
- **WHEN** 代理尝试将 shelf_status 设置为1
- **THEN** 系统返回错误 "套餐已禁用,无法上架"
#### Scenario: 代理操作未分配的套餐
- **GIVEN** 代理没有该套餐的分配记录
- **WHEN** 代理调用接口操作该套餐的上下架
- **THEN** 系统返回错误 "该套餐未分配给您,无法操作上下架"
#### Scenario: 状态未变化
- **WHEN** 管理员设置的上架状态与当前状态相同
- **THEN** 系统正常返回成功,不产生错误
---
### Requirement: Package 模型新增字段
系统 MUST 在 Package 模型中新增以下字段:
- `suggested_cost_price`:建议成本价(分为单位),默认 0
- `suggested_retail_price`:建议售价(分为单位),默认 0
- `shelf_status`上架状态1-上架 2-下架,默认 2
#### Scenario: 创建套餐时设置建议价格
- **WHEN** 管理员创建套餐并设置建议成本价和建议售价
- **THEN** 系统保存这些价格信息
#### Scenario: 查询套餐时返回建议价格
- **WHEN** 管理员查询套餐详情或列表
- **THEN** 响应中包含 suggested_cost_price、suggested_retail_price、shelf_status 字段
---
### Requirement: 清理废弃模型
系统 MUST 删除以下废弃的分佣相关模型和对应的数据库表:
- `AgentHierarchy` (tb_agent_hierarchy)
- `CommissionRule` (tb_commission_rule)
- `CommissionLadder` (tb_commission_ladder)
- `CommissionCombinedCondition` (tb_commission_combined_condition)
- `CommissionApproval` (tb_commission_approval)
- `CommissionTemplate` (tb_commission_template)
- `CarrierSettlement` (tb_carrier_settlement)
- `AgentPackageAllocation` (tb_agent_package_allocation)
#### Scenario: 迁移后废弃表不存在
- **WHEN** 执行数据库迁移后
- **THEN** 上述 8 个表在数据库中不再存在
#### Scenario: 代码中无废弃模型引用
- **WHEN** 删除模型定义后
- **THEN** 项目能够正常编译,无编译错误