订单相关以及佣金相关修复
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m57s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 7m57s
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
## 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 扩展处理。
|
||||
Reference in New Issue
Block a user