## Context

当前订单领域同时承载了三类不同语义：

1. **谁操作了订单**：应该回答“哪个账号发起了这次下单动作”。
2. **订单属于谁/卖给谁**：由 `buyer_type/buyer_id/seller_shop_id/purchase_role` 等业务字段表达。
3. **这笔订单的差价佣金该给谁**：应从 `seller_shop_id` 出发，沿完整父级链逐级计算。

现状问题在于：`operator_id/operator_type` 被混成了“真实账号 + 店铺 ID + 平台空值”三种含义，导致平台订单无法稳定展示操作者；`commission_status` 又被混成“流程状态 + 业务结果”，导致“未触发计算”“已算完但没有佣金”“链路异常待处理”无法区分。历史提案已经明确：支付成功后要自动入队佣金计算，代购订单只跳过一次性佣金，不跳过差价佣金。因此本次设计不是新增分佣能力，而是把已有能力的语义纠偏为可长期维护的模型。

## Goals / Non-Goals

**Goals:**
- 将订单操作者统一收敛为“真实账号语义”，平台与代理都能准确落库、展示和追溯。
- 将订单佣金模型拆成“流程状态 + 业务结果”两层，明确区分无佣金与异常待人工处理。
- 将差价佣金统一定义为基于 `seller_shop_id` 的完整上级链分配，不再受 `operator_id/operator_type` 影响。
- 修正平台代扣、代理自购、代理为下级代购、代购自动完单等触发链路，保证所有适用订单都能进入佣金计算。
- 设计兼容迁移方案，避免直接改变旧字段含义造成历史数据误读。

**Non-Goals:**
- 不重构一次性佣金规则，也不改变“代购订单不触发一次性佣金、不更新累计充值”的既有结论。
- 不重写整套钱包模型，不额外引入外键、关联关系或新的异步基础设施。
- 不在本次设计中解决所有历史脏数据，只定义兼容读取、渐进回填和后续修复路径。

## Decisions

### Decision 1：新增“权威操作者快照”，不直接复用被污染的旧 `operator_*` 字段

**选择**：订单模型新增一组权威操作者字段（账号 ID、账号类型、名称快照），作为“谁操作了这笔订单”的唯一来源；现有 `operator_id/operator_type` 视为历史兼容字段，在迁移期通过回填/映射兼容读取。

**原因**：
- 直接把旧 `operator_id` 改成账号 ID 会让历史上保存店铺 ID 的订单立即失真。
- 单独依赖 `creator` 虽然能覆盖部分后台订单，但缺少名称快照，且无法优雅覆盖未来其他入口。
- 新增权威字段可以让写路径和读路径先稳定下来，再逐步清理历史值。

**备选方案**：
- **直接复用 `operator_id/operator_type`**：实现快，但会让历史数据语义混乱，风险最高。
- **只依赖 `creator/updater`**：不需要新字段，但响应层仍需额外查询/推导，且无法表达多入口场景下的操作者快照。

### Decision 2：佣金采用“双轴模型”——流程状态与业务结果分离

**选择**：保留 `commission_status` 作为流程状态字段，但重新定义为“待计算 / 已完成 / 待人工处理”；新增 `commission_result` 作为业务结果字段，至少表达“有佣金 / 无佣金 / 链路异常”。

**原因**：
- “无佣金”是计算结果，不是待处理状态。
- “链路断裂”需要触发人工排障，不应与普通完成混淆。
- 双轴模型最适合补偿扫描：补偿只看流程状态，运营展示同时看流程状态和业务结果。

**备选方案**：
- **仅扩展 `commission_status` 单字段**：字段值会迅速膨胀，前后端更容易再次把流程与结果混用。
- **仅依赖佣金记录条数推导结果**：0 条记录既可能是无佣金，也可能是任务未执行或链路异常，无法可靠区分。

### Decision 3：差价佣金只从 `seller_shop_id` 出发，沿完整父级链逐级计算

**选择**：佣金归属只依赖 `seller_shop_id` 的完整上级链，以及每一层对应的成本价/分配配置；`operator_id/operator_type/purchase_role` 不参与佣金归属判断，只参与审计和展示。

**原因**：
- 这样可以统一覆盖平台代扣、代理自购、代理为下级代购、多级代理链等所有场景。
- 这与现有 `commission_calculation` 服务的主体算法一致，只是把现有“例子式理解”提升为正式规则。
- 顶级代理“无佣金”只是父链终止后的自然结果，不需要单独特判。

**备选方案**：
- **按操作者类型分支**：会错误跳过平台代扣二级/三级代理的上级差价佣金。
- **按买家类型硬编码一级/二级场景**：无法覆盖灵活多级代理链，且后续每加场景都要加特判。

### Decision 4：所有适用订单都要先进入佣金计算，再由计算结果决定“有/无佣金”

**选择**：只要订单属于差价佣金适用范围，并在支付成功或自动完单后仍处于“待计算”，就必须入队 `commission:calculate`；worker 负责最终把订单落成“已完成+有佣金”“已完成+无佣金”或“待人工处理+链路异常”。

**原因**：
- 这样可以修复“平台代扣代理钱包但未入队”的现有缺口。
- “无佣金”订单也需要被 worker 明确盖章为完成，否则后台永远分不清是漏算还是确实没有佣金。
- Asynq 的幂等和补偿扫描可以继续复用，只需调整判定口径。

**备选方案**：
- **创建订单时先同步判断是否有佣金再决定是否入队**：会把链路计算逻辑分散到订单服务，增加重复实现和口径漂移风险。

### Decision 5：链路断裂统一落为待人工处理，并记录断点信息

**选择**：当父级链缺失、分配配置不存在、成本价无法继续求解等情况发生时，订单流程状态落为“待人工处理”，业务结果落为“链路异常”；同时保留待审佣金记录和断点备注，作为人工补偿入口。

**原因**：
- 这与现有 `CommissionStatusPendingReview` 及待审记录机制兼容。
- 运营和财务可以明确知道这不是“无佣金”，而是“规则/数据有问题”。

**备选方案**：
- **仍然写成已完成但 0 佣金**：会掩盖真实异常，导致漏补偿。

## Risks / Trade-offs

- **[历史订单语义混杂]** → 先新增权威字段、兼容读取，再做分批回填；旧 `operator_*` 不立即删除。
- **[前端依赖旧 `operator_name` 为店铺名]** → 在提案中明确 BREAKING，订单响应补充账号名与业务店铺名分离的展示规则。
- **[补偿扫描口径变化导致重复入队]** → 继续以流程状态做幂等守卫，worker 内部使用条件更新和重复执行跳过。
- **[多级链路查询带来性能波动]** → 沿用现有逐级查询逻辑，但只在支付后异步执行；列表查询不做链路展开，避免影响接口时延。
- **[历史挂起订单无法自动判断应为无佣金还是异常]** → 回填脚本只处理可确定样本，其余保持待人工处理，由运营复核。

## Migration Plan

1. **Schema 先行**：为订单新增权威操作者字段与佣金结果字段，保留旧字段不删。
2. **读路径兼容**：订单详情/列表优先读新字段；若新字段为空，则按 `creator + 旧 operator_*` 做兼容回显，并在响应中明确异常/历史场景。
3. **写路径切换**：后台订单创建、支付回调、自动完单全部改为写入新操作者字段，并统一使用新佣金状态/结果模型。
4. **触发链修复**：所有适用支付成功路径统一调用佣金入队逻辑；worker 在完成时写回流程状态和业务结果。
5. **历史数据回填**：优先回填可由 `creator` 明确推断的后台订单；旧 `operator_id` 为店铺 ID 的记录不强改旧列，只填新列。
6. **补偿与验证**：扫描“已支付且待计算”的订单重新入队；手工验证顶级代理无佣金、多级链路有佣金、链路断裂待人工处理三类样本。
7. **回滚策略**：如新字段展示异常，可回退到兼容读逻辑并暂停新状态写入；旧字段仍保留，数据可读不丢失。

## Open Questions

- 本次不保留开放性业务问题，默认采用“状态看流程、结果看业务”的双轴模型推进；如后续需要增加“计算中”状态，可在实现阶段作为非 breaking 扩展处理。
