## 1. 数据库迁移文件准备

- [x] 1.1 使用 db-migration 规范创建迁移文件：
  - **删除** `tb_shop_series_allocation` 的 3 列：`enable_one_time_commission`、`one_time_commission_trigger`、`one_time_commission_threshold`
  - **新增** `commission_tiers_json JSONB NOT NULL DEFAULT '[]'`（梯度模式专属阶梯金额）
  - DOWN 脚本添加说明注释（不恢复数据）

## 2. 删除旧接口（Handler / routes / DTO / Service）

- [x] 2.1 删除文件：
  - `internal/handler/admin/shop_series_allocation.go`
  - `internal/handler/admin/shop_package_allocation.go`
  - `internal/routes/shop_series_allocation.go`
  - `internal/routes/shop_package_allocation.go`
  - `internal/model/dto/shop_series_allocation.go`
  - `internal/model/dto/shop_package_allocation.go`
  - `internal/service/shop_series_allocation/`（整个目录）
  - `internal/service/shop_package_allocation/`（整个目录）
- [x] 2.2 `internal/bootstrap/types.go`：删除 `ShopSeriesAllocation`、`ShopPackageAllocation` 两个 Handler 字段
- [x] 2.3 `internal/bootstrap/handlers.go`：删除对应 Handler 初始化行；删除对应 service 引用
- [x] 2.4 `internal/bootstrap/services.go`：删除 `ShopSeriesAllocation`、`ShopPackageAllocation` 两个 Service 字段及初始化；移除对应 import
- [x] 2.5 `pkg/openapi/handlers.go`：删除 `ShopSeriesAllocation`、`ShopPackageAllocation` 两行
- [x] 2.6 `internal/routes/admin.go`：删除 `registerShopSeriesAllocationRoutes`、`registerShopPackageAllocationRoutes` 两处调用
- [x] 2.7 运行 `go build ./...` 确认无编译错误

## 3. Model 更新

- [x] 3.1 `internal/model/shop_series_allocation.go`：
  - 删除 `EnableOneTimeCommission`、`OneTimeCommissionTrigger`、`OneTimeCommissionThreshold` 三个字段
  - 新增 `CommissionTiersJSON string` 字段（JSONB，默认 `'[]'`）
  - 新增辅助类型 `AllocationCommissionTier struct { Threshold int64; Amount int64 }`
  - 新增 `GetCommissionTiers() ([]AllocationCommissionTier, error)` 方法
  - 新增 `SetCommissionTiers(tiers []AllocationCommissionTier) error` 方法
- [x] 3.2 `internal/model/package.go`：
  - `OneTimeCommissionTier` 新增 `Operator string` 字段（json:"operator"）
  - 新增运算符常量：`TierOperatorGT = ">"` / `TierOperatorGTE = ">="` / `TierOperatorLT = "<"` / `TierOperatorLTE = "<="`
- [x] 3.3 运行 `go build ./...` 确认无编译错误

## 4. 修复梯度模式计算引擎

- [x] 4.1 `internal/service/commission_calculation/service.go`：
  修改 `calculateChainOneTimeCommission` 中梯度模式分支：
  - 原来：直接把 `config.Tiers`（全局）传给 `matchOneTimeCommissionTier`
  - 新的：从 `currentSeriesAllocation.GetCommissionTiers()` 取专属金额列表，结合 `config.Tiers`（取 operator/dimension/stat_scope/threshold）做匹配
  - 匹配逻辑：根据代理销售统计和 tier.Operator 判断是否命中 threshold → 查专属列表同 threshold 的 amount → 即 myAmount；未命中任何阶梯时 myAmount = 0
  - commission_tiers_json 为空时（历史数据）fallback 到 `currentSeriesAllocation.OneTimeCommissionAmount`
- [x] 4.2 修改 `matchOneTimeCommissionTier`：接受 agentTiers `[]AllocationCommissionTier` 参数作为金额来源；根据 `tier.Operator` 选择对应比较逻辑（>、>=、<、<=），Operator 为空时默认 `>=`
- [x] 4.3 运行 `go build ./...` 确认无编译错误

## 5. 修复强充层级

- [x] 5.1 `internal/service/order/service.go` `checkForceRechargeRequirement()`：
  - `config.TriggerType == "first_recharge"` 时：直接返回需要强充（不变），不查代理配置
  - `config.TriggerType == "accumulated_recharge"` 且 `config.EnableForceRecharge == false` 时：
    从 `result.Card.ShopID`（或 `result.Device.ShopID`）查询该代理的 `ShopSeriesAllocation`，
    若该分配 `EnableForceRecharge=true` 则返回代理强充配置，查询不到时降级返回 `need_force_recharge=false`
- [x] 5.2 验证 `GetPurchaseCheck` 调用路径已覆盖新逻辑（复用同一函数，无需额外修改）
- [x] 5.3 运行 `go build ./...` 确认无编译错误

## 6. 新系列授权 DTO

- [x] 6.1 创建 `internal/model/dto/shop_series_grant_dto.go`，定义：
  - `GrantPackageItem`（package_id、cost_price、remove *bool）
  - `ShopSeriesGrantPackageItem`（package_id、package_name、package_code、cost_price、shelf_status、status）
  - `GrantCommissionTierItem`（operator string、threshold int64、amount int64）
    —— operator 仅出现在响应中（从 PackageSeries 合并），请求中不传 operator
- [x] 6.2 定义 `ShopSeriesGrantResponse`：
  - id、shop_id/name、series_id/name/code、commission_type
  - one_time_commission_amount（固定模式有效，梯度模式返回 0）
  - commission_tiers []GrantCommissionTierItem（梯度模式有值，固定模式为空）
  - force_recharge_locked、force_recharge_enabled、force_recharge_amount
  - allocator_shop_id/name、status、packages、created_at、updated_at
- [x] 6.3 定义 `CreateShopSeriesGrantRequest`：
  - shop_id、series_id
  - one_time_commission_amount *int64（固定模式必填）
  - commission_tiers []GrantCommissionTierItem（梯度模式必填）
  - enable_force_recharge *bool、force_recharge_amount *int64
  - packages []GrantPackageItem
- [x] 6.4 定义 `UpdateShopSeriesGrantRequest`：
  - one_time_commission_amount *int64
  - commission_tiers []GrantCommissionTierItem
  - enable_force_recharge *bool、force_recharge_amount *int64
- [x] 6.5 定义 `ManageGrantPackagesRequest`（packages []GrantPackageItem）
- [x] 6.6 定义 `ShopSeriesGrantListRequest`（page、page_size、shop_id *uint、series_id *uint、allocator_shop_id *uint、status *int）及列表 DTO（`ShopSeriesGrantListItem` 含 package_count、`ShopSeriesGrantPageResult`）

## 7. 新系列授权 Service

- [x] 7.1 创建 `internal/service/shop_series_grant/service.go`，定义 Service 结构及 New() 构造函数
  （依赖：db、shopSeriesAllocationStore、shopPackageAllocationStore、shopPackageAllocationPriceHistoryStore、shopStore、packageStore、packageSeriesStore）

- [x] 7.2 实现私有方法 `getParentCeilingFixed()`：固定模式天花板查询
  - allocatorShopID=0 → 读 PackageSeries.commission_amount
  - allocatorShopID>0 → 读分配者自身的 ShopSeriesAllocation.one_time_commission_amount

- [x] 7.3 实现私有方法 `getParentCeilingTiered()`：梯度模式天花板查询
  - allocatorShopID=0 → 读 PackageSeries.config.Tiers 中各 threshold 的 amount
  - allocatorShopID>0 → 读分配者自身 ShopSeriesAllocation.commission_tiers_json

- [x] 7.4 实现 `Create()`：
  - 查询 PackageSeries 确认 commission_type
  - 检查重复授权：shop_id + series_id 已有 active 记录 → 错误"该代理已存在此系列授权"
  - allocator 是代理时：查分配者自身的 ShopSeriesAllocation，无记录 → 错误"当前账号无此系列授权，无法向下分配"
  - 固定模式：one_time_commission_amount 必填 + 天花板校验
  - 梯度模式：commission_tiers 必填 + 阶梯数量和 threshold 必须与 PackageSeries 完全一致（不多不少，amount 可为 0）+ 每档位天花板校验
  - 强充层级判断（TriggerType=first_recharge 或平台已设强充 → locked=true 忽略代理传入；仅 accumulated_recharge 且平台未设时接受代理强充配置）
  - 事务中创建 ShopSeriesAllocation + N 条 ShopPackageAllocation
  - 返回聚合响应

- [x] 7.5 实现 `Get()`：查询 ShopSeriesAllocation → 查 PackageSeries 取全局 tiers（含 operator）→ 关联套餐分配 → 拼装 ShopSeriesGrantResponse
  （梯度模式下，commission_tiers 响应需将 agent 的 amount 与 PackageSeries tiers 的 operator 按 threshold 合并）

- [x] 7.6 实现 `List()`：分页查询 → 统计 package_count → 返回 ShopSeriesGrantPageResult

- [x] 7.7 实现 `Update()`：
  - 固定模式：含 one_time_commission_amount 时做天花板校验
  - 梯度模式：含 commission_tiers 时做每档位天花板校验
  - 平台已设强充时忽略强充变更
  - 保存更新

- [x] 7.8 实现 `ManagePackages()`：事务中处理 packages 列表：
  - remove=true：查找 active 的 ShopPackageAllocation，找到则软删除，找不到则静默忽略
  - 无 remove 标志：校验套餐归属和分配权限，查现有 active 记录（有则更新 cost_price+写历史，无则新建）

- [x] 7.9 实现 `Delete()`：检查子级依赖 → 事务软删除 ShopSeriesAllocation + 所有关联 ShopPackageAllocation

- [x] 7.10 运行 `go build ./...` 确认无编译错误

## 8. Handler、路由及文档生成器

- [x] 8.1 创建 `internal/handler/admin/shop_series_grant.go`，实现 Create、Get、List、Update、ManagePackages、Delete 六个 Handler 方法
- [x] 8.2 创建 `internal/routes/shop_series_grant.go`，注册路由（Tag: "代理系列授权"）：
  - `GET    /shop-series-grants`
  - `POST   /shop-series-grants`
  - `GET    /shop-series-grants/:id`
  - `PUT    /shop-series-grants/:id`
  - `DELETE /shop-series-grants/:id`
  - `PUT    /shop-series-grants/:id/packages`
- [x] 8.3 运行 `go build ./...` 确认无编译错误

## 9. 依赖注入 & Bootstrap

- [x] 9.1 `internal/bootstrap/types.go`：添加 `ShopSeriesGrant *admin.ShopSeriesGrantHandler` 字段
- [x] 9.2 `internal/bootstrap/services.go`：import shop_series_grant service 包，添加字段并在 `initServices()` 中初始化
- [x] 9.3 `internal/bootstrap/handlers.go`：添加 ShopSeriesGrant Handler 初始化
- [x] 9.4 `pkg/openapi/handlers.go`：添加 `ShopSeriesGrant: admin.NewShopSeriesGrantHandler(nil)`
- [x] 9.5 `internal/routes/admin.go`：添加 `registerShopSeriesGrantRoutes()` 调用
- [x] 9.6 运行 `go build ./...` 确认完整构建通过

## 10. 执行迁移 & 数据验证

- [x] 10.1 执行迁移（`make migrate-up`），用 db-migration 规范验证：3 列已删除，commission_tiers_json 列已添加
- [x] 10.2 db-validation：创建固定模式授权（正常路径），确认 ShopSeriesAllocation 和 ShopPackageAllocation 均创建成功
- [x] 10.3 db-validation：固定模式金额超过父级天花板时接口返回错误
- [x] 10.4 db-validation：梯度模式创建授权，commission_tiers_json 正确写入
- [x] 10.5 db-validation：梯度模式某档位金额超过父级同档位时接口返回错误
- [x] 10.6 db-validation：代理自设强充后，购买预检接口返回 need_force_recharge=true，金额与代理设置一致
- [x] 10.7 db-validation：平台系列已设强充时，代理自设强充被锁定，购买预检使用平台强充金额
- [x] 10.8 db-validation：梯度模式阶梯含 `operator="<"` 时，销售统计低于阈值的代理命中该档位，高于阈值的代理不命中
