feat: 实现套餐管理模块,包含套餐系列、双状态管理、废弃模型清理
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
- 新增套餐系列管理 (CRUD + 状态切换) - 新增套餐管理 (CRUD + 启用/禁用 + 上架/下架双状态) - 清理 8 个废弃分佣模型及对应数据库表 - Package 模型新增建议成本价、建议售价、上架状态字段 - 完整的 Store/Service/Handler 三层实现 - 包含单元测试和集成测试 - 归档 add-package-module change - 新增多个 OpenSpec changes (订单支付、店铺套餐分配、一次性分佣、卡设备系列绑定)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-01-27
|
||||
199
openspec/changes/archive/2026-01-27-add-package-module/design.md
Normal file
199
openspec/changes/archive/2026-01-27-add-package-module/design.md
Normal file
@@ -0,0 +1,199 @@
|
||||
## Context
|
||||
|
||||
当前系统中存在大量为号卡业务设计的分佣模型(冻结/解冻、组合分佣、运营商结算等),但流量卡业务只需要简单的一次性佣金机制。这些模型增加了代码复杂度且从未使用。
|
||||
|
||||
现有套餐模型 `Package` 缺少建议价格字段和上架状态管理,无法支持后续的代理套餐分配功能。
|
||||
|
||||
**当前代码结构**:
|
||||
- Handler 在 `internal/handler/admin/` 下,每个模块一个文件
|
||||
- Service 在 `internal/service/{module}/service.go`,每个模块一个包
|
||||
- Store 在 `internal/store/postgres/{module}_store.go`
|
||||
- Bootstrap 在 `internal/bootstrap/` 负责组件注册
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 清理 8 个废弃模型,减少代码复杂度
|
||||
- 扩展 Package 模型支持建议价格和上架状态
|
||||
- 提供完整的套餐系列 CRUD API
|
||||
- 提供完整的套餐 CRUD API(含双状态管理)
|
||||
- 遵循现有代码架构风格
|
||||
|
||||
**Non-Goals:**
|
||||
- 不实现代理套餐分配(Phase 2)
|
||||
- 不实现一次性佣金计算(Phase 5)
|
||||
- 不迁移现有数据(表内无数据)
|
||||
- 不修改 `CommissionRecord` 模型(后续 Phase 简化)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 模型文件处理策略
|
||||
|
||||
**决策**:直接删除废弃模型定义,不保留注释或空文件
|
||||
|
||||
**理由**:
|
||||
- 这些模型从未在生产环境使用
|
||||
- Git 历史可追溯
|
||||
- 保留空定义增加维护负担
|
||||
|
||||
**替代方案**:
|
||||
- ❌ 标记为 deprecated 保留:增加代码噪音
|
||||
- ❌ 移到 archive 目录:过度设计
|
||||
|
||||
### 2. Package 模型字段设计
|
||||
|
||||
**决策**:新增三个字段
|
||||
|
||||
```go
|
||||
type Package struct {
|
||||
// ... 现有字段 ...
|
||||
SuggestedCostPrice int64 `gorm:"column:suggested_cost_price;type:bigint;default:0;comment:建议成本价(分为单位)" json:"suggested_cost_price"`
|
||||
SuggestedRetailPrice int64 `gorm:"column:suggested_retail_price;type:bigint;default:0;comment:建议售价(分为单位)" json:"suggested_retail_price"`
|
||||
ShelfStatus int `gorm:"column:shelf_status;type:int;default:2;not null;comment:上架状态 1-上架 2-下架" json:"shelf_status"`
|
||||
}
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- `suggested_cost_price`:平台定义的建议成本价,代理分配时参考
|
||||
- `suggested_retail_price`:平台定义的建议零售价,代理设置售价时参考
|
||||
- `shelf_status`:与 `status`(启用/禁用)分离,支持独立的上架控制
|
||||
- 默认 `shelf_status=2`(下架):新套餐需要显式上架
|
||||
|
||||
**替代方案**:
|
||||
- ❌ 用 JSON 字段存储扩展属性:查询不便,类型不安全
|
||||
- ❌ 合并 status 和 shelf_status:语义不同,分开更清晰
|
||||
|
||||
### 3. 双状态业务规则
|
||||
|
||||
**决策**:启用状态(status)和上架状态(shelf_status)独立但有约束
|
||||
|
||||
| status | shelf_status | 允许操作 |
|
||||
|--------|--------------|----------|
|
||||
| 启用(1) | 上架(1) | 可购买 |
|
||||
| 启用(1) | 下架(2) | 不可购买,可上架 |
|
||||
| 禁用(2) | 上架(1) | ❌ 禁止 - 禁用时强制下架 |
|
||||
| 禁用(2) | 下架(2) | 不可购买,需先启用再上架 |
|
||||
|
||||
**理由**:
|
||||
- 禁用套餐不应该可购买,强制下架保证数据一致性
|
||||
- 启用但下架:允许平台配置套餐但暂不开放购买
|
||||
|
||||
### 4. API 路由设计
|
||||
|
||||
**决策**:使用 RESTful 风格,状态变更使用 PATCH
|
||||
|
||||
```
|
||||
# 套餐系列
|
||||
POST /api/admin/package-series 创建
|
||||
GET /api/admin/package-series 列表
|
||||
GET /api/admin/package-series/:id 详情
|
||||
PUT /api/admin/package-series/:id 更新
|
||||
DELETE /api/admin/package-series/:id 删除
|
||||
PATCH /api/admin/package-series/:id/status 启用/禁用
|
||||
|
||||
# 套餐
|
||||
POST /api/admin/packages 创建
|
||||
GET /api/admin/packages 列表
|
||||
GET /api/admin/packages/:id 详情
|
||||
PUT /api/admin/packages/:id 更新
|
||||
DELETE /api/admin/packages/:id 删除
|
||||
PATCH /api/admin/packages/:id/status 启用/禁用
|
||||
PATCH /api/admin/packages/:id/shelf 上架/下架
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 与现有 API 风格一致(参考 `/api/admin/carriers`)
|
||||
- 状态变更使用 PATCH 符合 HTTP 语义
|
||||
- 路径清晰,易于前端对接
|
||||
|
||||
### 5. Service 层设计
|
||||
|
||||
**决策**:每个模块独立 Service 包
|
||||
|
||||
```
|
||||
internal/service/package_series/service.go # 套餐系列 Service
|
||||
internal/service/package/service.go # 套餐 Service
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 与现有架构一致(carrier, iot_card 等)
|
||||
- 便于后续扩展(如套餐关联其他模块)
|
||||
|
||||
### 6. 数据库迁移策略
|
||||
|
||||
**决策**:单个迁移文件,先删后改
|
||||
|
||||
迁移顺序:
|
||||
1. DROP 8 个废弃表
|
||||
2. ALTER tb_package 添加 3 个新字段
|
||||
|
||||
**理由**:
|
||||
- 这些表无生产数据,可直接删除
|
||||
- 单文件便于回滚
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
### 风险 1:删除模型后发现有隐藏引用
|
||||
|
||||
**风险**:代码中可能有对废弃模型的隐藏引用导致编译失败
|
||||
|
||||
**缓解**:
|
||||
- 删除模型后执行 `go build ./...` 确认编译通过
|
||||
- 使用 IDE 全局搜索确认无引用
|
||||
|
||||
### 风险 2:双状态逻辑复杂度
|
||||
|
||||
**风险**:禁用时强制下架的逻辑可能被遗漏
|
||||
|
||||
**缓解**:
|
||||
- 在 Service 层统一处理状态变更逻辑
|
||||
- 添加单元测试覆盖所有状态组合
|
||||
|
||||
### 风险 3:API 命名与现有冲突
|
||||
|
||||
**风险**:`/packages` 路径可能与未来其他套餐类型冲突
|
||||
|
||||
**缓解**:
|
||||
- 当前只有流量卡套餐,命名合理
|
||||
- 未来如有号卡套餐,可使用 `/number-card-packages`
|
||||
|
||||
## Migration Plan
|
||||
|
||||
### 部署步骤
|
||||
|
||||
1. **代码部署前**:
|
||||
- 确认生产环境废弃表无数据
|
||||
- 备份数据库(预防措施)
|
||||
|
||||
2. **执行迁移**:
|
||||
```bash
|
||||
go run cmd/migrate/main.go up
|
||||
```
|
||||
|
||||
3. **验证**:
|
||||
- 确认 8 个表已删除
|
||||
- 确认 tb_package 新增 3 个字段
|
||||
- API 健康检查
|
||||
|
||||
### 回滚策略
|
||||
|
||||
```bash
|
||||
go run cmd/migrate/main.go down
|
||||
```
|
||||
|
||||
迁移 down 脚本:
|
||||
- 重建 8 个废弃表(结构保留)
|
||||
- 删除 tb_package 的 3 个新字段
|
||||
|
||||
**注意**:回滚不恢复数据,仅恢复表结构
|
||||
|
||||
## Open Questions
|
||||
|
||||
1. **套餐系列禁用是否级联影响套餐?**
|
||||
- 当前设计:不级联,套餐系列禁用只影响系列本身
|
||||
- 待确认:是否需要禁用系列时自动禁用下属套餐?
|
||||
|
||||
2. **删除套餐/套餐系列的约束?**
|
||||
- 当前设计:物理删除(soft delete via GORM)
|
||||
- 待确认:是否需要检查关联数据(如已分配给代理的套餐)?
|
||||
- 建议:Phase 2 实现代理分配后再添加约束检查
|
||||
@@ -0,0 +1,63 @@
|
||||
## Why
|
||||
|
||||
当前分佣模型过于复杂(包含冻结/解冻审批、组合分佣、号卡结算等),而流量卡业务只需要简单的一次性佣金。现有的 `AgentPackageAllocation` 模型也不支持套餐系列级别的分配和梯度佣金配置。需要清理废弃模型,调整 Package 模型支持建议价格和上架状态,并提供完整的套餐/套餐系列 CRUD API。
|
||||
|
||||
## What Changes
|
||||
|
||||
**模型清理(commission.go):**
|
||||
- **BREAKING** 删除 `AgentHierarchy` - 代理层级通过 `Shop.parent_id` 维护
|
||||
- **BREAKING** 删除 `CommissionRule` - 过于复杂,后续用新模型替代
|
||||
- **BREAKING** 删除 `CommissionLadder` - 后续用 `ShopSeriesCommissionTier` 替代
|
||||
- **BREAKING** 删除 `CommissionCombinedCondition` - 流量卡不需要组合分佣
|
||||
- **BREAKING** 删除 `CommissionApproval` - 不需要冻结/解冻审批流程
|
||||
- **BREAKING** 删除 `CommissionTemplate` - 简化后不需要模板
|
||||
- **BREAKING** 删除 `CarrierSettlement` - 号卡专用,本期不做
|
||||
|
||||
**模型清理(package.go):**
|
||||
- **BREAKING** 删除 `AgentPackageAllocation` - 用新的分配模型替代
|
||||
|
||||
**Package 模型调整:**
|
||||
- 新增 `suggested_cost_price` 字段(建议成本价,分为单位)
|
||||
- 新增 `suggested_retail_price` 字段(建议售价,分为单位)
|
||||
- 新增 `shelf_status` 字段(上架状态:1-上架 2-下架)
|
||||
|
||||
**新增 API:**
|
||||
- 套餐系列 CRUD(创建、更新、删除、列表、详情、启用/禁用)
|
||||
- 套餐 CRUD(创建、更新、删除、列表、详情、启用/禁用、上架/下架)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `package-series-management`: 套餐系列管理 - 创建/更新/删除/列表/详情,支持启用/禁用状态切换
|
||||
- `package-management`: 套餐管理 - 创建/更新/删除/列表/详情,支持启用/禁用和上架/下架双状态管理
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
<!-- 无需修改现有 capability,这是全新的套餐管理模块 -->
|
||||
|
||||
## Impact
|
||||
|
||||
**代码影响:**
|
||||
- `internal/model/commission.go` - 删除 7 个模型
|
||||
- `internal/model/package.go` - 删除 1 个模型,修改 Package 模型
|
||||
- `migrations/` - 需要创建迁移文件删除废弃表、修改 package 表
|
||||
- `internal/handler/admin/` - 新增套餐系列和套餐管理 Handler
|
||||
- `internal/service/` - 新增套餐系列和套餐管理 Service
|
||||
- `internal/store/postgres/` - 新增套餐系列和套餐 Store
|
||||
- `internal/model/dto/` - 新增请求/响应 DTO
|
||||
- `internal/bootstrap/` - 注册新的 Store/Service/Handler
|
||||
- `internal/router/` - 注册新的 API 路由
|
||||
- `cmd/api/docs.go` 和 `cmd/gendocs/main.go` - 更新文档生成器
|
||||
|
||||
**API 影响:**
|
||||
- 新增 `/api/admin/package-series/*` 路由组
|
||||
- 新增 `/api/admin/packages/*` 路由组
|
||||
|
||||
**数据库影响:**
|
||||
- 删除表:`tb_agent_hierarchy`, `tb_commission_rule`, `tb_commission_ladder`, `tb_commission_combined_condition`, `tb_commission_approval`, `tb_commission_template`, `tb_carrier_settlement`, `tb_agent_package_allocation`
|
||||
- 修改表:`tb_package` 新增 3 个字段
|
||||
|
||||
**依赖关系:**
|
||||
- 本期不涉及外部依赖变更
|
||||
- 后续 Phase 2(代理套餐分配)依赖本期完成
|
||||
@@ -0,0 +1,180 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 创建套餐
|
||||
|
||||
系统 SHALL 允许平台管理员创建套餐,包含套餐编码、套餐名称、所属系列、套餐类型、时长、流量配置、价格和建议价格。套餐编码 MUST 全局唯一(排除已删除记录)。新创建的套餐默认为启用状态(1)和下架状态(2)。
|
||||
|
||||
#### Scenario: 成功创建套餐
|
||||
- **WHEN** 管理员提交有效的套餐信息
|
||||
- **THEN** 系统创建套餐记录,状态为启用(1),上架状态为下架(2),返回创建的套餐详情
|
||||
|
||||
#### Scenario: 套餐编码重复
|
||||
- **WHEN** 管理员提交的套餐编码已存在(未删除)
|
||||
- **THEN** 系统返回错误 "套餐编码已存在"
|
||||
|
||||
#### Scenario: 关联不存在的套餐系列
|
||||
- **WHEN** 管理员指定的系列 ID 不存在
|
||||
- **THEN** 系统返回错误 "套餐系列不存在"
|
||||
|
||||
#### Scenario: 缺少必填字段
|
||||
- **WHEN** 管理员未提供必填字段(套餐编码、套餐名称、套餐类型、时长、价格)
|
||||
- **THEN** 系统返回参数验证错误
|
||||
|
||||
---
|
||||
|
||||
### 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 允许管理员查询单个套餐的详细信息。
|
||||
|
||||
#### Scenario: 查询存在的套餐
|
||||
- **WHEN** 管理员请求指定 ID 的套餐详情
|
||||
- **THEN** 系统返回该套餐的完整信息
|
||||
|
||||
#### Scenario: 查询不存在的套餐
|
||||
- **WHEN** 管理员请求不存在或已删除的套餐 ID
|
||||
- **THEN** 系统返回 "套餐不存在" 错误
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 更新套餐
|
||||
|
||||
系统 SHALL 允许管理员更新套餐的基本信息。套餐编码创建后 MUST NOT 允许修改。
|
||||
|
||||
#### Scenario: 成功更新套餐
|
||||
- **WHEN** 管理员提交有效的更新信息
|
||||
- **THEN** 系统更新套餐记录,返回更新后的详情
|
||||
|
||||
#### Scenario: 尝试修改套餐编码
|
||||
- **WHEN** 管理员尝试修改套餐编码
|
||||
- **THEN** 系统忽略套餐编码字段,不进行修改
|
||||
|
||||
#### Scenario: 更新不存在的套餐
|
||||
- **WHEN** 管理员更新不存在的套餐
|
||||
- **THEN** 系统返回 "套餐不存在" 错误
|
||||
|
||||
#### Scenario: 关联不存在的套餐系列
|
||||
- **WHEN** 管理员将套餐关联到不存在的系列
|
||||
- **THEN** 系统返回错误 "套餐系列不存在"
|
||||
|
||||
---
|
||||
|
||||
### 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 允许管理员切换套餐的上架状态。只有启用状态的套餐才能上架。
|
||||
|
||||
#### Scenario: 上架启用的套餐
|
||||
- **WHEN** 管理员将启用且下架的套餐设置为上架
|
||||
- **THEN** 系统更新上架状态为上架(1)
|
||||
|
||||
#### Scenario: 尝试上架禁用的套餐
|
||||
- **WHEN** 管理员尝试上架一个禁用的套餐
|
||||
- **THEN** 系统返回错误 "禁用的套餐不能上架,请先启用"
|
||||
|
||||
#### Scenario: 下架套餐
|
||||
- **WHEN** 管理员将上架的套餐设置为下架
|
||||
- **THEN** 系统更新上架状态为下架(2)
|
||||
|
||||
#### 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** 项目能够正常编译,无编译错误
|
||||
@@ -0,0 +1,99 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 创建套餐系列
|
||||
|
||||
系统 SHALL 允许平台管理员创建套餐系列,包含系列编码、系列名称、描述信息。系列编码 MUST 全局唯一(排除已删除记录)。新创建的套餐系列默认为启用状态。
|
||||
|
||||
#### Scenario: 成功创建套餐系列
|
||||
- **WHEN** 管理员提交有效的套餐系列信息(系列编码、系列名称)
|
||||
- **THEN** 系统创建套餐系列记录,返回创建的套餐系列详情,状态为启用(1)
|
||||
|
||||
#### Scenario: 系列编码重复
|
||||
- **WHEN** 管理员提交的系列编码已存在(未删除)
|
||||
- **THEN** 系统返回错误 "系列编码已存在"
|
||||
|
||||
#### Scenario: 缺少必填字段
|
||||
- **WHEN** 管理员未提供系列编码或系列名称
|
||||
- **THEN** 系统返回参数验证错误
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 查询套餐系列列表
|
||||
|
||||
系统 SHALL 提供套餐系列列表查询功能,支持按系列名称模糊搜索、按状态筛选。结果 MUST 分页返回,按创建时间倒序排列。
|
||||
|
||||
#### Scenario: 查询所有套餐系列
|
||||
- **WHEN** 管理员请求套餐系列列表,不带筛选条件
|
||||
- **THEN** 系统返回所有未删除的套餐系列,分页显示
|
||||
|
||||
#### Scenario: 按名称搜索
|
||||
- **WHEN** 管理员提供系列名称关键字
|
||||
- **THEN** 系统返回名称包含该关键字的套餐系列
|
||||
|
||||
#### Scenario: 按状态筛选
|
||||
- **WHEN** 管理员指定状态筛选(启用/禁用)
|
||||
- **THEN** 系统只返回匹配状态的套餐系列
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 查询套餐系列详情
|
||||
|
||||
系统 SHALL 允许管理员查询单个套餐系列的详细信息。
|
||||
|
||||
#### Scenario: 查询存在的套餐系列
|
||||
- **WHEN** 管理员请求指定 ID 的套餐系列详情
|
||||
- **THEN** 系统返回该套餐系列的完整信息
|
||||
|
||||
#### Scenario: 查询不存在的套餐系列
|
||||
- **WHEN** 管理员请求不存在或已删除的套餐系列 ID
|
||||
- **THEN** 系统返回 "套餐系列不存在" 错误
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 更新套餐系列
|
||||
|
||||
系统 SHALL 允许管理员更新套餐系列的基本信息(系列名称、描述)。系列编码创建后 MUST NOT 允许修改。
|
||||
|
||||
#### Scenario: 成功更新套餐系列
|
||||
- **WHEN** 管理员提交有效的更新信息
|
||||
- **THEN** 系统更新套餐系列记录,返回更新后的详情
|
||||
|
||||
#### Scenario: 尝试修改系列编码
|
||||
- **WHEN** 管理员尝试修改系列编码
|
||||
- **THEN** 系统忽略系列编码字段,不进行修改
|
||||
|
||||
#### Scenario: 更新不存在的套餐系列
|
||||
- **WHEN** 管理员更新不存在的套餐系列
|
||||
- **THEN** 系统返回 "套餐系列不存在" 错误
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 删除套餐系列
|
||||
|
||||
系统 SHALL 允许管理员删除套餐系列(软删除)。
|
||||
|
||||
#### Scenario: 成功删除套餐系列
|
||||
- **WHEN** 管理员删除指定的套餐系列
|
||||
- **THEN** 系统软删除该记录,后续查询不再返回
|
||||
|
||||
#### Scenario: 删除不存在的套餐系列
|
||||
- **WHEN** 管理员删除不存在的套餐系列
|
||||
- **THEN** 系统返回 "套餐系列不存在" 错误
|
||||
|
||||
---
|
||||
|
||||
### Requirement: 启用/禁用套餐系列
|
||||
|
||||
系统 SHALL 允许管理员切换套餐系列的启用状态。
|
||||
|
||||
#### Scenario: 启用套餐系列
|
||||
- **WHEN** 管理员将禁用的套餐系列设置为启用
|
||||
- **THEN** 系统更新状态为启用(1)
|
||||
|
||||
#### Scenario: 禁用套餐系列
|
||||
- **WHEN** 管理员将启用的套餐系列设置为禁用
|
||||
- **THEN** 系统更新状态为禁用(2)
|
||||
|
||||
#### Scenario: 状态未变化
|
||||
- **WHEN** 管理员设置的状态与当前状态相同
|
||||
- **THEN** 系统正常返回成功,不产生错误
|
||||
128
openspec/changes/archive/2026-01-27-add-package-module/tasks.md
Normal file
128
openspec/changes/archive/2026-01-27-add-package-module/tasks.md
Normal file
@@ -0,0 +1,128 @@
|
||||
## 1. 模型清理
|
||||
|
||||
- [x] 1.1 删除 `internal/model/commission.go` 中的废弃模型(AgentHierarchy, CommissionRule, CommissionLadder, CommissionCombinedCondition, CommissionApproval, CommissionTemplate, CarrierSettlement)
|
||||
- [x] 1.2 删除 `internal/model/package.go` 中的 `AgentPackageAllocation` 模型
|
||||
- [x] 1.3 执行 `go build ./...` 确认无编译错误,如有引用则同步清理
|
||||
|
||||
## 2. Package 模型调整
|
||||
|
||||
- [x] 2.1 在 `internal/model/package.go` 的 Package 结构体中新增 `suggested_cost_price` 字段(bigint, 默认 0, 注释:建议成本价)
|
||||
- [x] 2.2 在 Package 结构体中新增 `suggested_retail_price` 字段(bigint, 默认 0, 注释:建议售价)
|
||||
- [x] 2.3 在 Package 结构体中新增 `shelf_status` 字段(int, 默认 2, 注释:上架状态 1-上架 2-下架)
|
||||
|
||||
## 3. 数据库迁移
|
||||
|
||||
- [x] 3.1 创建迁移文件,UP 脚本删除 8 个废弃表:tb_agent_hierarchy, tb_commission_rule, tb_commission_ladder, tb_commission_combined_condition, tb_commission_approval, tb_commission_template, tb_carrier_settlement, tb_agent_package_allocation
|
||||
- [x] 3.2 在迁移 UP 脚本中添加 tb_package 表的 3 个新字段
|
||||
- [x] 3.3 编写迁移 DOWN 脚本(重建表结构、删除新字段)
|
||||
- [x] 3.4 本地执行迁移验证
|
||||
|
||||
## 4. 套餐系列 DTO
|
||||
|
||||
- [x] 4.1 创建 `internal/model/dto/package_series.go`,定义 CreatePackageSeriesRequest(series_code 必填, series_name 必填, description 可选)
|
||||
- [x] 4.2 定义 UpdatePackageSeriesRequest(series_name, description)
|
||||
- [x] 4.3 定义 PackageSeriesListRequest(page, page_size, series_name 模糊, status 筛选)
|
||||
- [x] 4.4 定义 UpdatePackageSeriesStatusRequest(status 必填)
|
||||
- [x] 4.5 定义 PackageSeriesResponse 响应结构
|
||||
|
||||
## 5. 套餐系列 Store
|
||||
|
||||
- [x] 5.1 创建 `internal/store/postgres/package_series_store.go`,实现 Create 方法
|
||||
- [x] 5.2 实现 GetByID 方法
|
||||
- [x] 5.3 实现 GetByCode 方法(用于编码唯一性检查)
|
||||
- [x] 5.4 实现 Update 方法
|
||||
- [x] 5.5 实现 Delete 方法(软删除)
|
||||
- [x] 5.6 实现 List 方法(支持分页、名称模糊搜索、状态筛选)
|
||||
- [x] 5.7 实现 UpdateStatus 方法
|
||||
|
||||
## 6. 套餐系列 Service
|
||||
|
||||
- [x] 6.1 创建 `internal/service/package_series/service.go`,实现 Create 方法(检查编码唯一性)
|
||||
- [x] 6.2 实现 Get 方法
|
||||
- [x] 6.3 实现 Update 方法(忽略编码修改)
|
||||
- [x] 6.4 实现 Delete 方法
|
||||
- [x] 6.5 实现 List 方法
|
||||
- [x] 6.6 实现 UpdateStatus 方法
|
||||
|
||||
## 7. 套餐系列 Handler
|
||||
|
||||
- [x] 7.1 创建 `internal/handler/admin/package_series.go`,实现 Create 接口
|
||||
- [x] 7.2 实现 Get 接口
|
||||
- [x] 7.3 实现 Update 接口
|
||||
- [x] 7.4 实现 Delete 接口
|
||||
- [x] 7.5 实现 List 接口
|
||||
- [x] 7.6 实现 UpdateStatus 接口
|
||||
|
||||
## 8. 套餐 DTO
|
||||
|
||||
- [x] 8.1 创建 `internal/model/dto/package.go`,定义 CreatePackageRequest(package_code 必填, package_name 必填, series_id, package_type 必填, duration_months 必填, data_type, real_data_mb, virtual_data_mb, data_amount_mb, price 必填, suggested_cost_price, suggested_retail_price)
|
||||
- [x] 8.2 定义 UpdatePackageRequest(除 package_code 外的字段)
|
||||
- [x] 8.3 定义 PackageListRequest(page, page_size, package_name 模糊, series_id, status, shelf_status, package_type)
|
||||
- [x] 8.4 定义 UpdatePackageStatusRequest(status 必填)
|
||||
- [x] 8.5 定义 UpdatePackageShelfStatusRequest(shelf_status 必填)
|
||||
- [x] 8.6 定义 PackageResponse 响应结构(包含新增的 3 个字段)
|
||||
|
||||
## 9. 套餐 Store
|
||||
|
||||
- [x] 9.1 创建 `internal/store/postgres/package_store.go`,实现 Create 方法
|
||||
- [x] 9.2 实现 GetByID 方法
|
||||
- [x] 9.3 实现 GetByCode 方法
|
||||
- [x] 9.4 实现 Update 方法
|
||||
- [x] 9.5 实现 Delete 方法
|
||||
- [x] 9.6 实现 List 方法(支持分页、名称模糊、系列筛选、状态筛选、上架状态筛选、类型筛选)
|
||||
- [x] 9.7 实现 UpdateStatus 方法
|
||||
- [x] 9.8 实现 UpdateShelfStatus 方法
|
||||
|
||||
## 10. 套餐 Service
|
||||
|
||||
- [x] 10.1 创建 `internal/service/package/service.go`,实现 Create 方法(检查编码唯一性、验证系列存在)
|
||||
- [x] 10.2 实现 Get 方法
|
||||
- [x] 10.3 实现 Update 方法(忽略编码修改、验证系列存在)
|
||||
- [x] 10.4 实现 Delete 方法
|
||||
- [x] 10.5 实现 List 方法
|
||||
- [x] 10.6 实现 UpdateStatus 方法(禁用时强制下架)
|
||||
- [x] 10.7 实现 UpdateShelfStatus 方法(检查启用状态才能上架)
|
||||
|
||||
## 11. 套餐 Handler
|
||||
|
||||
- [x] 11.1 创建 `internal/handler/admin/package.go`,实现 Create 接口
|
||||
- [x] 11.2 实现 Get 接口
|
||||
- [x] 11.3 实现 Update 接口
|
||||
- [x] 11.4 实现 Delete 接口
|
||||
- [x] 11.5 实现 List 接口
|
||||
- [x] 11.6 实现 UpdateStatus 接口
|
||||
- [x] 11.7 实现 UpdateShelfStatus 接口
|
||||
|
||||
## 12. Bootstrap 注册
|
||||
|
||||
- [x] 12.1 在 `internal/bootstrap/stores.go` 中注册 PackageSeriesStore 和 PackageStore
|
||||
- [x] 12.2 在 `internal/bootstrap/services.go` 中注册 PackageSeriesService 和 PackageService
|
||||
- [x] 12.3 在 `internal/bootstrap/handlers.go` 中注册 PackageSeriesHandler 和 PackageHandler
|
||||
|
||||
## 13. 路由注册
|
||||
|
||||
- [x] 13.1 在 `internal/router/` 中注册套餐系列路由组 `/api/admin/package-series`(POST, GET, GET/:id, PUT/:id, DELETE/:id, PATCH/:id/status)
|
||||
- [x] 13.2 注册套餐路由组 `/api/admin/packages`(POST, GET, GET/:id, PUT/:id, DELETE/:id, PATCH/:id/status, PATCH/:id/shelf)
|
||||
|
||||
## 14. 文档生成器更新
|
||||
|
||||
- [x] 14.1 在 `cmd/api/docs.go` 中添加 PackageSeriesHandler 和 PackageHandler
|
||||
- [x] 14.2 在 `cmd/gendocs/main.go` 中添加 PackageSeriesHandler 和 PackageHandler
|
||||
- [x] 14.3 执行 `go run cmd/gendocs/main.go` 生成 OpenAPI 文档
|
||||
|
||||
## 15. 测试
|
||||
|
||||
- [x] 15.1 为 PackageSeriesStore 编写单元测试
|
||||
- [x] 15.2 为 PackageStore 编写单元测试
|
||||
- [x] 15.3 为 PackageSeriesService 编写单元测试(覆盖编码唯一性检查)
|
||||
- [x] 15.4 为 PackageService 编写单元测试(覆盖双状态逻辑)
|
||||
- [x] 15.5 编写套餐系列 API 集成测试
|
||||
- [x] 15.6 编写套餐 API 集成测试(覆盖禁用强制下架、禁用不能上架场景)
|
||||
- [x] 15.7 执行 `go test ./...` 确认所有测试通过
|
||||
|
||||
## 16. 最终验证
|
||||
|
||||
- [x] 16.1 执行 `go build ./...` 确认编译通过
|
||||
- [x] 16.2 执行 `go vet ./...` 检查代码质量
|
||||
- [x] 16.3 启动服务,手动测试 API 接口
|
||||
- [x] 16.4 确认 OpenAPI 文档正确生成
|
||||
Reference in New Issue
Block a user