各结账形态可用优惠对照
本文说明 五种结账形态 下四套优惠体系(o_diy_offer / o_promotion / o_coupon / o_order_diy_offer)是否可用、计算先后顺序、如何写入订单。体系细节见 promotion-discount-checkout.md。
与实现冲突时以代码为准。
目录
- 1. 四套体系速记
- 2. 总对照表(五种形态)
- 3. 在线三种:统一计算顺序
- 4. COD 两种:计算顺序与差异
- 5. 各形态:用券入口与落库时机
- 6. 各形态:order_diy_offer
- 7. 订单 / 行字段影响一览
- 8. 行级 discount_price 分摊顺序
- 9. 已知差异与排障
- 10. 分形态详细链路
1. 四套体系速记
| 体系 | 表 | Cart 字段 | Order 字段 | 作用阶段 |
|---|---|---|---|---|
| diy_offer(购物车插件) | o_diy_offer | diy_offers[]、行 diy_offer_id | current_promotion_price(与满减合计)、order_product.diy_offer_id | 加购~结账计价 |
| promotion(标准满减) | o_promotion | promotion[]、promotion_price 一部分 | promotion_id/name、current_promotion_price | 购物车~结账计价 |
| coupon(优惠券) | o_coupon | coupon_price、coupon_code | coupon_id/code、current_coupon_price | 购物车预存~创单绑定 |
| order_diy_offer(订单级) | o_order_diy_offer | diy_offer_price(易混名) | current_offer_price | 仅在线三种,物流/complete 步 |
易混:cart.diy_offers[] ≠ cart.diy_offer_price(后者是 order_diy_offer 汇总)。
2. 总对照表(五种形态)
| 形态 | diy_offer | promotion | coupon | order_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
| 顺序 | 步骤 | 影响字段 |
|---|---|---|
| 1 | cartDataComposition | 行 price/final_price;过期限时促销清 diy_offer_id |
| 2 | minmaxoffer | 可能 has_minmaxoffer=true,跳过后续 diy_offer 类型 |
| 3 | cartDiyOffers(非 minmax) | diy_offers[]、可能直接改行价 |
| 4 | getCartPromotion | promotion[];minmax 时仍执行 |
| 5 | 汇总 | promotion_price(负值) |
| 6 | calPreCouponPrice | coupon_price(读 Cache 预券) |
| 7 | checkCouponUseWithPromotionStatus | 替换型券可能 清零 promotion_price |
| 8 | 有订单时 reconcile | 写回 o_order.current_*;saveProducts 刷新行 |
| 9 | updateDiyOfferInfo | diy_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 行):
| 顺序 | 在线 CartService | COD |
|---|---|---|
| 行价组装 | cartDataComposition | codCartDataComposition |
| minmaxoffer | ✅ | ✅ |
| diy_offer 批次 | promotion/bundlesale/skubundlesale 然后 gift(两轮) | 同一轮 promotion/bundlesale/skubundales/gift |
| 满减 | getCartPromotion ✅ | ✅ |
| promotion_price | ✅ | ✅ |
| 预券 | CouponHandlerService::calPreCouponPrice | CodCouponService::calPreCouponPrice |
| 替换型券清满减 | checkCouponUseWithPromotionStatus ✅ | ❌ 未调用 |
| order_diy_offer | updateDiyOfferInfo | 无 |
| 写 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};创单 savePreCoupon | o_order.coupon_* | saveProducts(getList reconcile) |
| 单页 | complete/presave 的 trans_info.coupon_code;calCartCouponPrice | savePreCoupon | saveOrderProducts |
| 渐进式 | POST .../use-coupon;savePreCoupon | 同标准 | 同标准 |
| COD token | POST .../use-coupon → Cache;saveOrder 读 DTO | o_cod_order.coupon_* | CodOrderProductService 创单时 |
| COD 单页 | 请求 trans_info.coupon_code / 预券 Cache | o_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 + promotion | current_promotion_price、promotion_id(满减时) | 创单初值;getList reconcile;complete/saveProducts |
| coupon | coupon_id、coupon_code、current_coupon_price | savePreCoupon / useCoupon / reconcile |
| order_diy_offer | current_offer_price | shipping/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(变体+属性) |
| 2 | diy_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 与步骤;完整流程见:
| 形态 | 文档 |
|---|---|
| 标准 | standard-checkout-flow.md |
| 单页 | one-page-checkout-flow.md |
| 渐进式 | single-page-checkout-flow.md |
| COD token | cod-checkout-flow.md |
| COD 单页 | cod-one-page-checkout.md |
购物车 Redis / 加购逻辑见 shopping-cart.md。
体系细则:promotion-discount-checkout.md · FAQ:faq.md