各结账形态可用优惠对照

本文说明 五种结账形态 下四套优惠体系(o_diy_offer / o_promotion / o_coupon / o_order_diy_offer)是否可用、计算先后顺序、如何写入订单。体系细节见 promotion-discount-checkout.md。

与实现冲突时以代码为准。

目录


1. 四套体系速记

体系表Cart 字段Order 字段作用阶段
diy_offer(购物车插件)o_diy_offerdiy_offers[]、行 diy_offer_idcurrent_promotion_price(与满减合计)、order_product.diy_offer_id加购~结账计价
promotion(标准满减)o_promotionpromotion[]、promotion_price 一部分promotion_id/name、current_promotion_price购物车~结账计价
coupon(优惠券)o_couponcoupon_price、coupon_codecoupon_id/code、current_coupon_price购物车预存~创单绑定
order_diy_offer(订单级)o_order_diy_offerdiy_offer_price(易混名)current_offer_price仅在线三种,物流/complete 步

易混:cart.diy_offers[] ≠ cart.diy_offer_price(后者是 order_diy_offer 汇总)。


2. 总对照表(五种形态)

形态diy_offerpromotioncouponorder_diy_offer计价 Service读 o_order 计价
标准 standard✅✅✅✅CartService::getList创单后 ✅
单页 one_page✅✅✅✅getList + CheckoutOnePageService::getCart 补算创单后 ✅
渐进式 single_page✅✅✅✅CartService::getList创单后 ✅
COD 带 token✅✅✅(有坑)❌CodCartService::getCartList❌ 仅内存
COD 单页✅✅✅(有坑)❌CodCartService(临时 DTO)❌

图例:

  • ✅ = 支持且与在线主链一致(或文档化差异见 §9)
  • ❌ = 不支持写入 o_order_diy_offer / current_offer_price(COD 表也无等价表)
  • coupon(有坑) = COD 未走 checkCouponUseWithPromotionStatus,替换型券可能与满减双计

3. 在线三种:统一计算顺序

主链:CartService::getList()(523–758 行)。

flowchart TD
    A[cartDataComposition 行价] --> B[defaultDiyOffer minmaxoffer]
    B --> C{has_minmaxoffer?}
    C -->|否| D[cartDiyOffers: promotion/bundlesale/skubundlesale]
    C -->|否| E[cartDiyOffers: gift]
    C -->|是| F[getCartPromotion o_promotion]
    D --> E --> F
    F --> G["promotion_price = Σ满减 + Σdiy_offers.discount"]
    G --> H[calPreCouponPrice 预券]
    H --> I[checkCouponUseWithPromotionStatus]
    I --> J{已有 order?}
    J -->|是| K[验券/税/运费 reconcile]
    K --> L[saveProducts 行 discount]
    K --> M[OrderDiyOfferService.updateDiyOfferInfo]
    J -->|否| N[仅内存 total]
    L --> O[total_price 汇总]
    M --> O
    N --> O
顺序步骤影响字段
1cartDataComposition行 price/final_price;过期限时促销清 diy_offer_id
2minmaxoffer可能 has_minmaxoffer=true,跳过后续 diy_offer 类型
3cartDiyOffers(非 minmax)diy_offers[]、可能直接改行价
4getCartPromotionpromotion[];minmax 时仍执行
5汇总promotion_price(负值)
6calPreCouponPricecoupon_price(读 Cache 预券)
7checkCouponUseWithPromotionStatus替换型券可能 清零 promotion_price
8有订单时 reconcile写回 o_order.current_*;saveProducts 刷新行
9updateDiyOfferInfodiy_offer_price → current_offer_price + o_order_diy_offer 表
10汇总 total_price见 promotion-discount-checkout §2.2 公式

单页额外步骤(CheckoutOnePageService::getCart):在 getList 之后按请求再算 券/税/运费/险/小费/支付费(744–767 行),不改变 1–7 的商品优惠顺序。


4. COD 两种:计算顺序与差异

CodCartService::getCartList()(20–61 行):

顺序在线 CartServiceCOD
行价组装cartDataCompositioncodCartDataComposition
minmaxoffer✅✅
diy_offer 批次promotion/bundlesale/skubundlesale 然后 gift(两轮)同一轮 promotion/bundlesale/skubundales/gift
满减getCartPromotion ✅✅
promotion_price✅✅
预券CouponHandlerService::calPreCouponPriceCodCouponService::calPreCouponPrice
替换型券清满减checkCouponUseWithPromotionStatus ✅❌ 未调用
order_diy_offerupdateDiyOfferInfo无
写 MySQL 订单getList reconcile仅 saveOrder 一次 写 o_cod_order

COD 订单字段:current_promotion_price、current_coupon_price、current_offer_price(offer 来自 cart 级 diy 折扣,非 o_order_diy_offer 表)。


5. 各形态:用券入口与落库时机

形态用券 API / 时机绑定订单刷新行价
标准GET /coupon/use/{token};创单 savePreCoupono_order.coupon_*saveProducts(getList reconcile)
单页complete/presave 的 trans_info.coupon_code;calCartCouponPricesavePreCouponsaveOrderProducts
渐进式POST .../use-coupon;savePreCoupon同标准同标准
COD tokenPOST .../use-coupon → Cache;saveOrder 读 DTOo_cod_order.coupon_*CodOrderProductService 创单时
COD 单页请求 trans_info.coupon_code / 预券 Cacheo_cod_order创单时

购物车阶段(未创单):POST /coupons 或 COD use-coupon 仅写 预券 Cache,不落订单。


6. 各形态:order_diy_offer

形态是否支持写入 API / 参数典型 type
标准✅shipping_method POST → offer_from_name积分、Seel、deliveryprotec
单页✅complete / preComplete → trans_info.offer_from_name同上
渐进式✅shipping-method POST → offer_from_name同上
COD❌—积分/Seel 不可用
COD 单页❌—同上

写入 Service:OrderDiyOfferHandleService::saveDiyOffer → 表 o_order_diy_offer;汇总进 o_order.current_offer_price。


7. 订单 / 行字段影响一览

7.1 订单头(在线 o_order / COD o_cod_order)

优惠来源主要写入字段何时持久化
diy_offer + promotioncurrent_promotion_price、promotion_id(满减时)创单初值;getList reconcile;complete/saveProducts
couponcoupon_id、coupon_code、current_coupon_pricesavePreCoupon / useCoupon / reconcile
order_diy_offercurrent_offer_priceshipping/complete 步 saveDiyOffer;getList updateDiyOfferInfo
汇总current_subtotal_price、total_price、current_* 全套各 POST 步 + renew

7.2 订单行 o_order_product / o_cod_order_product

来源字段
行活动diy_offer_id
分摊后单价discount_price(OrderPriceService::calOrderProductDiscountPrice)
属性o_order_product_property

分摊在 OrderService::saveProducts(在线)或 COD 创单 时执行。


8. 行级 discount_price 分摊顺序

OrderPriceService::calOrderProductDiscountPrice()(在线,saveProducts 内):

顺序扣减来源
1基线 = final_price(变体+属性)
2diy_offer 行 discount
3优惠券(按适用商品比例)
4满减 promotion(按 product_list)

时机:OrderService::saveProducts(创单)时执行 calOrderProductDiscountPrice,逐层递减计算每行 discount_price。cart 阶段不出现在 discount_price 字段。

注意:cart 阶段 promotion_price 先汇总满减+diy_offers;行分摊顺序为 diy → 券 → 满减(与汇总顺序不同层)。


9. 已知差异与排障

现象原因
COD 替换型券仍带满减未调 checkCouponUseWithPromotionStatus
单页 price 与 complete 不一致complete 前未传齐 shipping/coupon/offer_from_name
积分未扣COD 不支持;在线查对应步是否传 offer_from_name
diy_offer_price 为 0 但有积分混淆 diy_offers[] 与 order_diy_offer
改 cart 后 order 价不变未触发 getList reconcile(需 has_contact_information 或 order 存在)

10. 分形态详细链路

各形态文档内 § 可用优惠 为本表在该形态下的具体 API 与步骤;完整流程见:

购物车 Redis / 加购逻辑见 shopping-cart.md。


体系细则:promotion-discount-checkout.md · FAQ:faq.md