# Proposal: refactor-series-binding-to-series-id

## Why

当前系统中，IoT卡和设备通过 `series_allocation_id`（套餐系列分配ID）字段绑定到 `ShopSeriesAllocation` 表，而不是直接绑定到 `PackageSeries`（套餐系列）。这导致了严重的语义混乱和架构问题：

1. **语义错误**：卡/设备应该表达"只能购买某个系列的套餐"，而不是"绑定到某个权限分配"
2. **职责耦合**：资源属性（可购买的套餐范围）与权限配置（返佣规则）混在一起
3. **查询冗余**：每次需要 `series_id` 时都要通过 `allocation` 表查询，增加数据库负担
4. **配置僵化**：修改店铺的返佣配置时，需要重新绑定所有卡/设备

这是一个设计失误，需要在开发阶段彻底重构。正确的设计应该是：卡/设备直接绑定 `series_id`，权限验证和返佣查询时按需通过 `(shop_id, series_id)` 查询 `ShopSeriesAllocation`。

## What Changes

- **数据库结构**：将 `tb_iot_card` 和 `tb_device` 的 `series_allocation_id` 字段重命名为 `series_id`，直接关联到 `tb_package_series`
- **Model 层**：修改 `IotCard` 和 `Device` 模型，将 `SeriesAllocationID` 字段改为 `SeriesID`
- **DTO 层**：更新所有相关 DTO，包括查询请求、响应对象、批量设置请求
- **Store 层**：
  - 更新查询过滤条件（`series_allocation_id` → `series_id`）
  - 重命名批量更新方法（`BatchUpdateSeriesAllocation` → `BatchUpdateSeriesID`）
  - 重命名列表查询方法（`ListBySeriesAllocationID` → `ListBySeriesID`）
  - **新增** `ShopSeriesAllocationStore.GetByShopAndSeries(shopID, seriesID)` 方法，用于按需查询返佣配置
  - **新增** `PackageSeriesStore`（如不存在）及其 `GetByID()` 方法
- **Service 层**：重构核心业务逻辑
  - `BatchSetSeriesBinding`：验证 `series_id` 是否存在，检查操作者权限（通过 `GetByShopAndSeries` 查询）
  - `ValidateCardPurchase` / `ValidateDevicePurchase`：直接使用 `card.series_id` 验证套餐，按需查询返佣配置
  - `CalculateOrderCommission`：根据 `(shop_id, series_id)` 查询返佣配置，而不是通过 `allocation_id`
  - `CreateRecharge`：同样改为按需查询返佣配置
- **API 文档**：更新路由描述，说明参数从 `series_allocation_id` 改为 `series_id`
- **测试**：更新约 100+ 个测试用例，包括单元测试、集成测试、Store 测试

**关键行为变更**：
- API `/api/admin/iot-cards/series-binding` 和 `/api/admin/devices/series-binding` 的请求参数从 `series_allocation_id` 改为 `series_id`
- 卡/设备列表的查询参数和响应字段同步改为 `series_id`
- 内部逻辑从"通过 allocation 获取 series_id"改为"直接使用 series_id，按需查询 allocation"

## Capabilities

### New Capabilities
<!-- 无新增能力 -->

### Modified Capabilities
- `card-series-bindng`: 修改 IoT 卡系列绑定的数据模型和验证逻辑，从绑定"分配ID"改为绑定"系列ID"
- `device-series-bindng`: 修改设备系列绑定的数据模型和验证逻辑，从绑定"分配ID"改为绑定"系列ID"

## Impact

### 影响范围统计
- **数据库迁移**：2 个迁移文件（新增重命名迁移，修改旧迁移注释）
- **Model 层**：2 个文件（`internal/model/iot_card.go`, `internal/model/device.go`）
- **DTO 层**：2 个文件，6 个结构体（`internal/model/dto/iot_card_dto.go`, `internal/model/dto/device_dto.go`）
- **Store 层**：3 个文件，新增 2 个查询方法（`iot_card_store.go`, `device_store.go`, `shop_series_allocation_store.go`）
- **Service 层**：6 个文件的核心逻辑重构
  - `internal/service/iot_card/service.go`
  - `internal/service/device/service.go`
  - `internal/service/purchase_validation/service.go`（关键）
  - `internal/service/commission_calculation/service.go`（关键）
  - `internal/service/recharge/service.go`
  - `internal/service/order/service.go`
- **Handler/Routes 层**：2 个文件的路由描述更新
- **测试层**：约 10 个文件，100+ 测试用例更新

### 受影响的 API 端点
- `PATCH /api/admin/iot-cards/series-binding`：请求参数 `series_allocation_id` → `series_id`
- `PATCH /api/admin/devices/series-binding`：请求参数 `series_allocation_id` → `series_id`
- `GET /api/admin/iot-cards/standalone`：查询参数和响应字段 `series_allocation_id` → `series_id`
- `GET /api/admin/devices`：查询参数和响应字段 `series_allocation_id` → `series_id`

### 向后兼容性
- **BREAKING CHANGE**：API 请求/响应字段名变更，前端需要同步修改
- **数据迁移**：字段重命名，不需要数据转换（字段含义保持一致，只是重新指向 `PackageSeries.ID`）
- **业务影响**：开发阶段，无生产数据，可直接重构

### 依赖和约束
- 依赖现有的 `tb_package_series` 表和 `tb_shop_series_allocation` 表
- 依赖现有的 `PackageSeries` 模型
- 需要创建或完善 `PackageSeriesStore`（如不存在）
- 测试需要创建完整的测试数据链路：`PackageSeries` → `ShopSeriesAllocation` → `IotCard/Device`

### 性能影响
- **查询优化**：购买验证时减少一次数据库查询（不再需要先查 `ShopSeriesAllocation` 获取 `series_id`）
- **返佣查询**：增加一次按条件查询 `ShopSeriesAllocation`（`WHERE shop_id = ? AND series_id = ?`），但有索引支持，性能影响可忽略
- **整体影响**：轻微性能提升（减少了冗余查询）
