All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 6m56s
- 新增数据库迁移:为 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>
276 lines
11 KiB
Markdown
276 lines
11 KiB
Markdown
## Requirements
|
||
|
||
### Requirement: 创建套餐
|
||
|
||
系统 SHALL 允许平台管理员创建套餐,包含套餐编码、套餐名称、所属系列、套餐类型、时长、**周期类型(calendar_type)、流量重置周期(data_reset_cycle)、是否需要实名激活(enable_realname_activation)**、流量配置、价格和建议价格。套餐编码 MUST 全局唯一(排除已删除记录)。新创建的套餐默认为启用状态(1)和下架状态(2)。
|
||
|
||
#### Scenario: 成功创建自然月套餐
|
||
- **GIVEN** 管理员提供套餐信息,calendar_type=natural_month,duration_months=1
|
||
- **WHEN** 提交创建请求
|
||
- **THEN** 系统创建套餐,状态=1,上架状态=2,calendar_type=natural_month
|
||
|
||
#### Scenario: 成功创建按天套餐
|
||
- **GIVEN** 管理员提供套餐信息,calendar_type=by_day,duration_days=30
|
||
- **WHEN** 提交创建请求
|
||
- **THEN** 系统创建套餐,calendar_type=by_day,duration_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_month,duration_months=1
|
||
- **WHEN** 管理员查询套餐详情
|
||
- **THEN** 响应包含 calendar_type=natural_month,duration_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_month,duration_months=1
|
||
- **WHEN** 管理员更新 calendar_type=by_day,duration_days=30
|
||
- **THEN** 系统更新成功,calendar_type=by_day,duration_days=30
|
||
|
||
#### Scenario: 更新套餐周期类型(从按天改为自然月)
|
||
- **GIVEN** 套餐当前 calendar_type=by_day,duration_days=30
|
||
- **WHEN** 管理员更新 calendar_type=natural_month,duration_months=1
|
||
- **THEN** 系统更新成功,calendar_type=natural_month,duration_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** 项目能够正常编译,无编译错误
|