# Delta Spec: commission-trigger

## MODIFIED Requirements

### Requirement: 佣金计算任务幂等性

系统 SHALL 确保佣金计算任务可重复执行，不重复发放佣金。**一次性佣金从系列分配获取金额**。

**关键变更**：
- 任务执行时，一次性佣金金额从 `ShopSeriesAllocation` 获取
- 不再依赖 `ShopPackageAllocation.one_time_commission_amount`

#### Scenario: 任务重复执行跳过计算
- **WHEN** 佣金计算任务执行时，订单 `commission_status` 已为 `calculated`
- **THEN** 系统跳过佣金计算和钱包入账操作
- **AND** 任务返回成功（避免 Asynq 重试）
- **AND** 日志记录"订单佣金已计算，跳过执行"

#### Scenario: 并发任务只有一个成功
- **WHEN** 同一订单的佣金计算任务被重复入队，两个 worker 并发执行
- **THEN** 第一个任务成功完成计算并更新状态为 `calculated`
- **AND** 第二个任务检查到状态已为 `calculated`，跳过计算

#### Scenario: 任务失败可安全重试
- **WHEN** 佣金计算任务执行失败（数据库异常、钱包服务不可用）
- **THEN** Asynq 自动重试任务
- **AND** 重试时幂等检查确保不重复发放佣金

---

## ADDED Requirements

### Requirement: 佣金计算时查询系列分配

系统 SHALL 在佣金计算任务中通过系列分配获取一次性佣金配置。

#### Scenario: 获取销售代理的系列分配
- **WHEN** 佣金计算任务执行，需要计算一次性佣金
- **THEN** 系统根据订单的卡/设备找到销售代理（shop_id）
- **AND** 根据套餐找到系列（series_id）
- **AND** 查询 ShopSeriesAllocation(shop_id, series_id) 获取 one_time_commission_amount

#### Scenario: 系列分配不存在时处理
- **WHEN** 佣金计算任务执行，但找不到对应的系列分配
- **THEN** 系统记录警告日志 "未找到系列分配，跳过一次性佣金"
- **AND** 继续计算成本价差收入（不因此失败）
- **AND** 订单 commission_status 正常更新为 calculated

#### Scenario: 获取代理链的系列分配
- **WHEN** 需要计算一次性佣金的链式分配
- **THEN** 系统沿代理链向上查询每级代理的系列分配
- **AND** 计算每级的差价收入

---

### Requirement: CommissionStats 关联系列分配

系统 SHALL 将 ShopSeriesCommissionStats 的 allocation_id 关联到系列分配，而非套餐分配。

#### Scenario: 创建佣金统计记录
- **WHEN** 发放一次性佣金后更新统计
- **THEN** CommissionStats.allocation_id 指向 ShopSeriesAllocation.id
- **AND** 不再指向 ShopPackageAllocation.id

#### Scenario: 查询店铺的系列佣金统计
- **WHEN** 查询某店铺在某系列的佣金统计
- **THEN** 通过 ShopSeriesAllocation.id 关联查询
- **AND** 统计包括该系列下所有套餐产生的一次性佣金

---

## REMOVED Requirements

### Requirement: 从套餐分配读取佣金金额

**Reason**: 一次性佣金配置迁移到系列分配

**Migration**:
- 原逻辑：通过 `GetByShopAndSeries` 查询套餐分配，取第一条的 `one_time_commission_amount`
- 新逻辑：直接查询 `ShopSeriesAllocation(shop_id, series_id)`
- 删除 `ShopPackageAllocationStore.GetByShopAndSeries` 方法
