
## FixMissedCommission 脚本完整流程拆解

### 脚本目的

遍历所有已完成的订单，检查分销佣金是否有遗漏，自动补全缺失的分销数据。可重复执行，已有记录不会重复操作。

**执行命令**: `php think shopro:fix-missed-commission`

---

### 整体流程分 4 层

```
外层循环: 分页遍历已完成的订单
 └─ 每笔订单: 两步处理
     ├─ 第一步: 创建分销订单 + 佣金计划（模拟支付成功）
     └─ 第二步: 执行佣金拨款结算（模拟订单完成）
```

---

### 第 1 层：分页查询已完成订单（第 96-113 行）

- 每页 100 条（`PAGE_SIZE = 100`）
- 查询条件：`status = completed` + `type != score`（排除纯积分商品订单）
- 查询字段：`id, user_id, order_sn, deduct_score`
- 按 `id` 升序排列
- 当某页数据不足 100 条时，说明已是最后一页，退出循环

---

### 第 2 层：每笔订单的前置处理（第 115-133 行）

进入订单循环后，先做 2 件事：

| 步骤 | 内容 |
|------|------|
| ① 查询订单商品 | 查询 `shopro_order_item` 表，条件：`order_id = $orderId` + `refund_status = 0`（未退款） |
| ② 判断商品是否为空 | 没有未退款商品 → 跳过整笔订单 |

---

### 第 3 层 A：积分抵扣修正（第 135-143 行）

这是之前修复的 Bug：`order_item.pay_fee` 不含积分抵扣，需要手动扣减。

| 条件 | 操作 |
|------|------|
| `deduct_score = 0` | 无需修正，跳过 |
| `deduct_score > 0` | `积分抵扣金额 = deduct_score ÷ 100`（100 积分 = 1 元），汇总所有 item 的 `pay_fee` 总额 |

---

### 第 3 层 B：第一步 — 创建分销订单（第 145-216 行）

**目的**：为没有 `commission_order` 记录的商品补建分销订单和佣金计划（模拟"订单支付成功"事件）。

遍历每个 `order_item`，执行以下检查链：

```
商品 item → 检查1: ext.is_commission 标记
    → 是 → 跳过（标记不参与分销）
    → 否 → 检查2: shopro_commission_order 表是否已有记录
        → 有 → 跳过（已存在分佣记录）
        → 无 → 检查3: 积分抵扣修正 pay_fee
            → 按比例分摊：itemScoreDeduction = 积分抵扣金额 × (item.pay_fee / 各item.pay_fee总和)
            → 调整后 pay_fee = 原始 pay_fee - itemScoreDeduction
            → 检查4: OrderService.checkAndSetCommission()
                → 不满足 → 跳过（无分销商/无佣金规则等）
                → 满足 → 步骤4a: createCommissionOrder()
                    → 失败 → 跳过
                    → 成功 → 步骤4b: runCommissionPlan()
                        → 递归向上创建所有上级分销商的佣金奖励（commission_reward）
```

**关键代码调用链**：
- `new OrderService($item)` — 传入商品 item，内部会调用 `getGoodsCommissionAmount()` 拿到调整后的 `pay_fee`
- `checkAndSetCommission()` — 检查购买者是否有上级分销商、商品是否有佣金规则等
- `createCommissionOrder()` — 写入 `shopro_commission_order` 表
- `runCommissionPlan()` — 递归计算各级分销佣金，写入 `shopro_commission_reward` 表

---

### 第 3 层 C：第二步 — 执行拨款结算（第 221-266 行）

**目的**：对已有 `commission_order` 但佣金状态为"待结算"的记录，执行拨款（模拟"订单完成"事件）。

```
查所有 commission_order（order_id = $orderId）
    → 无 → 跳过第二步
    → 有 → 遍历每条分佣单：
        → 状态 != 待结算 → 跳过（已结算/已取消/已退款）
        → 状态 = 待结算 → RewardService('admin').runCommissionRewardByOrder()
            → 成功 → 写入钱包余额（total_income + 对应金额）
            → 失败 → 记录 warning
```

**关键点**：使用 `RewardService('admin')` 而非正常事件（如 `confirm`/`finish`），因为 `admin` 事件不受 `reward_event` 配置限制，确保一定能拨款。

---

### 第 4 层：统计汇总（第 278-290 行）

循环全部结束后输出汇总：

| 统计项 | 含义 |
|--------|------|
| `totalOrders` | 遍历的已完成订单总数 |
| `totalCreated` | 新创建的分销订单 `commission_order` 数 |
| `totalSettled` | 新执行的拨款结算数 |
| `totalSkipped` | 已结算/已取消跳过的分佣单数 |

---

### 可重复执行的安全性

| 保护机制 | 说明 |
|----------|------|
| `order_item_id` 唯一检查 | 已有 `commission_order` 记录的不重复创建 |
| 状态过滤 | 已结算/已取消/已退款的 `commission_order` 跳过拨款 |
| `is_commission` 标记 | 商品标记不参与分销的不处理 |
| 退款商品过滤 | `refund_status != 0` 的不处理 |

---

### 流程图总结

```
开始
  ↓
分页查询 shopro_order (status=completed, type!=score)
  ↓
遍历每笔订单 ──→ 无未退款商品 → 跳过
  ↓ 有
积分抵扣修正: 按比例从 item.pay_fee 扣减 deduct_score/100
  ↓
【第一步】遍历 item:
  ├─ ext.is_commission=false → 跳过
  ├─ commission_order 已存在 → 跳过
  ├─ checkAndSetCommission 不满足 → 跳过
  └─ ✓ createCommissionOrder → runCommissionPlan → 创建成功
  ↓
【第二步】遍历 commission_order:
  ├─ 状态 != 待结算 → 跳过
  └─ 待结算 → runCommissionRewardByOrder → 拨款到钱包
  ↓
下一页 / 结束汇总
```