# commission-trigger Specification

## Purpose
TBD - created by archiving change fix-commission-calculation-trigger-and-snapshot. Update Purpose after archive.
## Requirements
### Requirement: 支付成功后自动入队佣金计算任务

系统 SHALL 在订单首次支付成功时自动 enqueue 佣金计算异步任务（`commission:calculate`），确保佣金及时发放。

**触发条件**：
- 订单从"待支付"变为"已支付"（首次成功支付）
- 订单 `commission_status` 为 `pending`（未计算）

**任务参数**：
- 任务类型：`commission:calculate`
- Payload：`{"order_id": <订单ID>}`

#### Scenario: 首次支付成功触发计算

- **WHEN** 订单支付成功，订单状态从"待支付"变为"已支付"
- **THEN** 系统自动 enqueue `commission:calculate` 任务，payload 包含订单 ID
- **AND** 订单 `commission_status` 保持为 `pending`（任务执行后才更新为 `calculated`）

#### Scenario: 重复支付不重复触发

- **WHEN** 订单已经是"已支付"状态，再次收到支付成功通知（幂等场景）
- **THEN** 系统不重复 enqueue 佣金计算任务
- **AND** 日志记录"订单已支付，跳过重复入队"

#### Scenario: 已计算佣金的订单不触发

- **WHEN** 订单 `commission_status` 为 `calculated`（已计算）
- **THEN** 系统跳过入队操作
- **AND** 日志记录"订单佣金已计算，跳过入队"

---

### Requirement: 入队失败不影响支付主链路

系统 SHALL 确保佣金任务入队失败时不回滚订单支付成功状态，保障主业务链路稳定。

**失败处理策略**：
- 入队失败时记录 ERROR 级别日志（包含订单 ID、失败原因）
- 订单状态保持为"已支付"，不回滚
- 订单 `commission_status` 保持为 `pending`，允许后续补偿

**补偿机制**：
- 后台补偿任务扫描 `commission_status=pending` 且已支付的订单
- 人工触发佣金计算（后台接口）
- 定时任务重试入队（可选）

#### Scenario: 入队失败记录日志

- **WHEN** 佣金任务入队失败（队列服务不可用或网络超时）
- **THEN** 系统记录 ERROR 日志，包含订单 ID、失败原因、队列配置信息
- **AND** 订单支付状态保持为"已支付"，不回滚

#### Scenario: 失败后允许补偿

- **WHEN** 后台补偿任务扫描到 `commission_status=pending` 且 `payment_status=paid` 的订单
- **THEN** 系统可重新 enqueue 佣金计算任务或直接执行计算
- **AND** 避免佣金永久丢失

---

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

系统 SHALL 确保佣金计算任务可重复执行，不重复发放佣金。

**幂等检查**：
- 任务执行前检查订单 `commission_status`
- 如果已为 `calculated`，跳过计算并返回成功

**状态更新**：
- 计算完成后将订单 `commission_status` 更新为 `calculated`
- 状态更新与佣金记录创建在同一事务中

#### Scenario: 任务重复执行跳过计算

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

#### Scenario: 并发任务只有一个成功

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

#### Scenario: 任务失败可安全重试

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

---

### Requirement: 队列客户端依赖注入

系统 SHALL 通过依赖注入方式将队列客户端注入到订单服务，遵循现有 bootstrap 架构。

**注入位置**：
- `internal/service/order/service.go` 的 `Service` 结构体
- 添加 `queueClient *asynq.Client` 字段

**注入方式**：
- 在 `internal/bootstrap/services.go` 中初始化订单服务时传入队列客户端
- 队列客户端在 `bootstrap.Bootstrap()` 中统一创建

#### Scenario: 订单服务接收队列客户端

- **WHEN** 系统启动时执行 `bootstrap.Bootstrap()`
- **THEN** 订单服务（`order.Service`）通过构造函数接收队列客户端实例
- **AND** 队列客户端可在服务内部调用 `Enqueue()` 方法

#### Scenario: 支付成功时调用队列客户端

- **WHEN** 订单支付成功，订单服务执行入队操作
- **THEN** 系统通过注入的队列客户端调用 `Enqueue("commission:calculate", payload)`
- **AND** 不在服务内部直接创建队列客户端（遵循依赖注入原则）

---

