Files
junhong_cmp_fiber/openspec/changes/archive/2026-01-27-add-package-module/design.md
huang 79c061b6fa
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 5m24s
feat: 实现套餐管理模块,包含套餐系列、双状态管理、废弃模型清理
- 新增套餐系列管理 (CRUD + 状态切换)
- 新增套餐管理 (CRUD + 启用/禁用 + 上架/下架双状态)
- 清理 8 个废弃分佣模型及对应数据库表
- Package 模型新增建议成本价、建议售价、上架状态字段
- 完整的 Store/Service/Handler 三层实现
- 包含单元测试和集成测试
- 归档 add-package-module change
- 新增多个 OpenSpec changes (订单支付、店铺套餐分配、一次性分佣、卡设备系列绑定)
2026-01-27 19:55:47 +08:00

200 lines
6.3 KiB
Markdown
Raw 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.
## 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 层统一处理状态变更逻辑
- 添加单元测试覆盖所有状态组合
### 风险 3API 命名与现有冲突
**风险**`/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 实现代理分配后再添加约束检查