## 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 实现代理分配后再添加约束检查
