Files
junhong_cmp_fiber/openspec/changes/fix-package-usage-paid-amount-cost-snapshot/design.md
break cbadf77517
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 8m28s
修复价格不一致的问题
2026-08-13 17:43:14 +08:00

4.7 KiB
Raw Blame History

Context

套餐使用记录 tb_package_usage 已存在两个价格快照字段:paid_amount(迁移 000129 引入,语义演进为成本价,见迁移 000150「paid_amount 继续存储成本价」)与 retail_amount(迁移 000150 引入,存储零售价)。retail_amount 的写入来源已经是 order.TotalAmount,语义正确;但 paid_amount 的 4 个写入点仍从 order.ActualPaidAmount 复制,与成本价语义不符。参见 proposal.md - Why。

order.SellerCostPriceint64,非指针)已在订单各购买路径被正确填充为「销售成本价」:代理自购/代购为操作方成本价,平台代扣为目标店铺成本价,平台代购为买家成本价,个人客户下单为卖家店铺成本价,赠送/平台自营为 0。该字段即「成本价」的正确来源。

语义演进证据git 历史)

paid_amount 当初回填 actual_paid_amount(实付金额)不是笔误,而是语义中途漂移留下的不一致:

  1. 迁移 0001292026-04-18commit 2b3a9cbchange add-paid-amount-snapshot-to-package-usage当时需求即「快照购买时的实付金额」proposal 原文「用于快照购买时的实付金额」「无法在不 JOIN 订单表的情况下展示历史购买价格」。因此回填 actual_paid_amount 在当时语义下是正确实现。
  2. 迁移 0001502026-06-01commit 944526dchange fix-order-price-semantics:该 change 将 Order.ActualPaidAmount 重新定义为「实际支付成本价」,并声明 PaidAmount 继续快照成本价、仍取 order.ActualPaidAmount。但其 spec 仅覆盖三个场景(代理钱包支付、平台代购、赠送),三者恰好都满足「实付金额 = 成本价」,唯独遗漏「个人客户购买」场景,导致该假设未被验证。
  3. 本次修复的代码事实:个人客户下单(client_order)时 SellerCostPrice 取店铺成本价;而第三方支付回调(HandlePaymentCallback / 钱包支付 payOrderByWallet)把 ActualPaidAmount 写成实付金额=零售价。因此个人客户与平台自营 offline 场景下 ActualPaidAmount ≠ SellerCostPricepaid_amount 取前者即错误地快照了零售价。

Goals / Non-Goals

Goals:

  • 统一 4 个 PackageUsage 创建点的 PaidAmount 快照来源为 order.SellerCostPrice
  • 修正 model.PackageUsage.PaidAmount 的字段注释以反映成本价语义。

Non-Goals:

  • 不回填历史已写入错误的存量 paid_amount(用户明确要求历史数据保持原样)。
  • 不新增迁移、不改 retail_amount 语义、不改资产套餐接口的按角色过滤逻辑。
  • 不拆分订单级金额到逐套餐明细(多套餐订单的逐条成本拆分是既有话题,不在本变更范围)。

Decisions

order.SellerCostPrice 作为 PaidAmount 的唯一来源

paid_amount 语义为「成本价」,而 seller_cost_price 在所有购买路径中都被填充为卖家向平台结算的成本价。选择直接取 order.SellerCostPrice,而非按 buyer_type/purchase_role 分支计算,因为分支计算需要重述订单价格逻辑,且 seller_cost_price 已是这些路径各自算好的结论值。

备选方案(已排除):

  • 继续用 actual_paid_amount 并按 buyer_type = personal 特判改用 seller_cost_price:引入与订单侧重复的分支判断,且「平台自营 offline」场景actual_paid_amount 回退为零售价、seller_cost_price = 0)仍会错,覆盖不全。
  • PackageUsage 新增独立 seller_cost_price 快照字段并保留 paid_amount 原义:需要新迁移,且 paid_amount 字段语义已被迁移 000150 与 DTO 定义为成本价,重复字段造成语义分裂。

4 个写入点统一取址赋值

SellerCostPriceint64PaidAmount*int64,统一写 PaidAmount: &order.SellerCostPrice。4 个写入点必须同步修改,避免后台购买与自动购包两条链路继续产生不一致快照。

不写数据回填迁移

用户明确接受历史错误数据。保留迁移目录现状,不新增成对迁移;后续新产生的记录由修正后的代码正确写入。

Risks / Trade-offs

  • [历史错误 paid_amount 仍显示错误成本价] → 接受现状,仅修正新写入记录;如后续需要可另行发起数据修复。
  • [多套餐订单的 seller_cost_price 是订单级合计] → 与现有 paid_amount/retail_amount 同为订单级快照,保持一致;逐套餐拆分不在本变更范围。
  • [SellerCostPrice 在个别历史订单可能为 0 或不准确] → 本变更只改新写入逻辑,不触碰历史订单;新订单各路径均已填充该字段。