current_subtotal_price 全链路(超详细版)
1. 业务定义(一句话)
⚡⚡⚡ 逐行取商品最终单价 final_price × 数量求和,得到商品小计,写入 o_order.current_subtotal_price;是 total_price 9字段中的第一项,也是 current_total_price 的组成项。
2. 后台配置到前台计算的关系
2.1 商家后台可控项(配置映射表)
| 后台配置项 | 典型存储/来源 | 前台读取点 | 对计算的直接影响 |
|---|---|---|---|
| SKU/变体价格 | product_variant.price | CartService::cartDataComposition | 行价基础 |
| 商品属性加价 | 自定义属性配置 | CartService::cartDataComposition | 叠加到 final_price |
| 数量 | 购物车行 quantity | CartService::cartDataComposition | 参与乘算 |
| 会员价 | 会员等级配置 | MembershipDiscountService::applyMemberPriceToCart | 修改 unit_price/final_price |
| 行级改价插件 | DIY Offer 插件 | DiyOfferService::cartDiyOffers | 修改 items[].price / unit_price |
| 划线价对比 | original_compare_at_price | 仅展示,不参与求和 | 仅对账展示 |
2.2 全链路时序图(配置 → 计算 → 落库)
flowchart TD A[商品/SKU/属性价格] --> B[CartService.cartDataComposition 组装 final_price] C[MembershipDiscountService 会员价] --> B B --> D[DiyOfferService.cartDiyOffers 行级改价] D --> E["Σ(final_price × quantity)"] E --> F[renew 中写 current_subtotal_price] F --> G[参与 total_price 汇总] F --> H[参与 current_total_price]
3. 核心入口与数据结构
3.1 final_price 的生成链路
CartService.php:804-918 (cartDataComposition)
└─ final_price = variant.price + propertyPrice
├─ 基础单价(variant.price)
├─ 属性加价(propertyPrice)
└─ 注意:注释说支持行级改价插件,但当前代码中未接入
⚠️ 现状:final_price 在 cartDataComposition 时等于 variant.price + propertyPrice,与 price/unit_price/original_price 初始值相同。行级改价插件(DiyOfferService)的修改落在 price 和 unit_price 上,不在 final_price 上,这是当前代码的实现事实。
3.2 subtotal_price 的计算
// CartService.php:cartDataComposition 中
$final_line_price = $final_price * $quantity; // 行小计
$items_subtotal_price = Σ($final_line_price); // 商品小计汇总4. 完整计算逻辑
4.1 cart.items 每行的价格字段(对比)
| 字段 | 含义 | 参与 subtotal? | 当前值来源 |
|---|---|---|---|
price | 原始单价 | ❌ 否 | variant.price |
unit_price | 当前单价 | ❌ 否 | variant.price + propertyPrice |
final_price | 最终单价 | ✅ 是 | variant.price + propertyPrice(行级改价未接入) |
final_line_price | 行小计 | ✅ 汇总用 | final_price × quantity |
original_price | 划线价 | ❌ 否 | 对账展示 |
original_line_price | 划线小计 | ❌ 否 | 对账展示 |
original_compare_at_price | 划线单价 | ❌ 否 | 展示用对比 |
promotion_price | 活动优惠(负数) | ❌ 否 | 汇总在 promotion_price 字段 |
⚡⚡⚡ subtotal_price 只汇总 final_line_price,与 promotion_price(活动优惠)完全独立。
4.2 落库中的 subtotal
// OrderService::renew 中
$current_subtotal_price = $order['current_subtotal_price']; // 从 DB 读
// 用于 total_price 求和
// renew 写回时同样写入 current_subtotal_price(保持不变)
$data['current_subtotal_price'] = $current_subtotal_price;5. 字段输出结构
| 字段 | 类型 | 含义 | 示例 |
|---|---|---|---|
cart.items[].final_price | float | 最终单价 | 105.00 |
cart.items[].final_line_price | float | 行小计(final_price × qty) | 210.00 |
cart.items_subtotal_price | float | 全部行小计之和 | 525.00 |
o_order.current_subtotal_price | float | 同上,落库 | 525.00 |
6. 边界场景说明
6.1 数量为 0 的商品行
final_line_price = final_price × 0 = 0,不参与求和,但行数据仍保留在 cart 中。
6.2 行级改价对 subtotal 的影响(现状)
DiyOfferService 的行级改价(diy_offers)目前修改 items[].price/unit_price,不修改 final_price,因此:
- subtotal_price 不会因为 diy_offers 而变化
promotion_price由DiyOfferService::cartDiyOffers计算后存入diy_offers数组,最后在CartService.php:552与promotion.discount合并计入promotion_price
6.3 会员价对 subtotal 的影响
MembershipDiscountService::applyMemberPriceToCart 修改 unit_price(会员价),而 final_price 初始值等于 variant.price + propertyPrice,会员价可能覆盖了 unit_price 但同样不影响当前 final_price。若需会员价参与 subtotal,需确认 DiyOfferService 是否会将会员价同步到 final_price。
7. 与总价的关系
current_subtotal_price = Σ(final_price × quantity)
= subtotal_price 的正式名称
total_price = current_subtotal_price
+ current_shipping_price
+ current_insurance_price
+ current_tip_price
+ current_tax_price
+ current_coupon_price ← 负数,coupon_price
+ current_payment_price
+ current_promotion_price ← 负数,promotion_price
+ current_offer_price
current_total_price = current_subtotal_price + current_shipping_price
8. 关键代码索引
| 逻辑 | 文件:行号 |
|---|---|
| cartDataComposition 生成 final_price | CartService.php:804-918 |
| final_line_price 计算 | CartService.php cartDataComposition 内 |
| 会员价修改 unit_price | MembershipDiscountService::applyMemberPriceToCart |
| 行级改价修改 price/unit_price | DiyOfferService::cartDiyOffers |
| promotion_price 合并(含 diy_offers) | CartService.php:552 |
| renew 读 current_subtotal_price | OrderService.php:2271 |
| renew 写回 current_subtotal_price | OrderService.php:2294 |