current_subtotal_price 全链路(超详细版)

1. 业务定义(一句话)

⚡⚡⚡ 逐行取商品最终单价 final_price × 数量求和,得到商品小计,写入 o_order.current_subtotal_price;是 total_price 9字段中的第一项,也是 current_total_price 的组成项。

2. 后台配置到前台计算的关系

2.1 商家后台可控项(配置映射表)

后台配置项典型存储/来源前台读取点对计算的直接影响
SKU/变体价格product_variant.priceCartService::cartDataComposition行价基础
商品属性加价自定义属性配置CartService::cartDataComposition叠加到 final_price
数量购物车行 quantityCartService::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_pricecartDataComposition 时等于 variant.price + propertyPrice,与 price/unit_price/original_price 初始值相同。行级改价插件(DiyOfferService)的修改落在 priceunit_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_pricefloat最终单价105.00
cart.items[].final_line_pricefloat行小计(final_price × qty)210.00
cart.items_subtotal_pricefloat全部行小计之和525.00
o_order.current_subtotal_pricefloat同上,落库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_priceDiyOfferService::cartDiyOffers 计算后存入 diy_offers 数组,最后在 CartService.php:552promotion.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_priceCartService.php:804-918
final_line_price 计算CartService.php cartDataComposition 内
会员价修改 unit_priceMembershipDiscountService::applyMemberPriceToCart
行级改价修改 price/unit_priceDiyOfferService::cartDiyOffers
promotion_price 合并(含 diy_offers)CartService.php:552
renew 读 current_subtotal_priceOrderService.php:2271
renew 写回 current_subtotal_priceOrderService.php:2294