order_diy_offer Handler 参考

o_order_diy_offer 订单级附加项:Handler、Redis、触发时机。主文档 §3

与实现冲突时以 extend/orderdiyoffers/OrderDiyOfferService 为准。

目录


1. 与 diy_offer 的区别

o_diy_offero_order_diy_offer
阶段加购~购物车计价创单后结账步
Cart 字段diy_offers[]diy_offer_price
Order 字段计入 current_promotion_price(部分)current_offer_price
典型bundlesale、gift积分、Seel

2. from_name 总表

from_name含义价格方向PHP 类
customer_points积分抵扣OrderDiyOfferPoints
admin_custom_price后台改价可正可负OrderDiyOfferCustomPrice(checkout 空实现)
app_deliveryprotec运输保障通常正OrderDiyOfferDeliveryprotec
app_seelSeel 延保通常正OrderDiyOfferSeel

3. 扩展路由

OrderDiyOfferService::getExtendObj($type)

  • 输入:from_name 去掉第一个 _ 前缀后的段 → camelCase
  • 类名:orderdiyoffers\OrderDiyOffer{Type}

新增类型:实现 Handler + 在 order_diy_offers_from_name:{storeId} 注册(积分等)。


4. 各 Handler 详解

customer_points

说明
前台参数offer_from_name JSON:{"customer_points": 1} 使用 / 0 退还
落库price 负、values=积分数、key_name/from_name=customer_points
基数subtotal + coupon + promotion + shipping + tax(不含 insurance/tip/seel/deliveryprotec)
限制已有积分行不可重复用;已付订单禁退

admin_custom_price

说明
前台 checkout不写入
入口Admin POST /api/order/updateOrderPriceDiyOffer
二次改价重算基数时排除自身行

app_deliveryprotec / app_seel

deliveryprotecseel
Redisorder_diy_offer:{storeId}{checkoutToken}order_diy_offer:{storeId}seel:{checkoutToken}
PHPhGetAll 只读同左
savedel 旧行 → 读 Redis → insert → order tag同左
saveDiyOffer强制 merge 这两种(即使 POST 未带)同左

表字段store_id, order_id, params, title, price, key_name, values, descript, from_id, from_name, created_at, updated_at


5. 触发时机(按结账形态)

形态写入入口参数
标准shipping_method POSToffer_from_name
单页complete / preCompletetrans_info.offer_from_name
渐进式shipping-method POSToffer_from_name
COD 带 token不支持
COD 单页不支持

查询:POST /order/orderdiyofferinfo;列表:OrderDiyOfferHandleService::getOfferList

5.1 offer_from_name 传参(多个)

支持一次提交多种订单级 offer。 协议:

  1. HTTP 字段名:offer_from_name
  2. 类型:字符串,内容为 JSON 对象from_name → 参数)
  3. 解析:OrderDiyOfferHandleService::saveDiyOfferjson_decodeOrderDiyOfferService::saveDiyOffer
{
  "customer_points": 1,
  "app_seel": 1,
  "app_deliveryprotec": 1
}
from_namevalue行为
customer_points1扣积分,写负价 o_order_diy_offer
customer_points0退还已用积分
app_seel1(或任意非空)读 Redis seel hash → insert
app_deliveryprotec1读 Redis deliveryprotec hash → insert

注意

  • app_seelapp_deliveryprotecsaveDiyOffer强制 merge,POST 省略也会尝试入库(依赖 Redis 是否有前端写入的数据)。
  • POST / 不传 → 仅处理上述两种插件类;不会自动用积分。
  • from_name 多次 save:先 delOrderOffer 再 insert,只保留最新一行
  • 不同 from_name可共存(如积分 + Seel 同时存在)。

标准结账Order.php shipping_method POST):

offer_from_name={"customer_points":1}

单页CheckoutOnePageService::handlerOrderDiyOffer):

{
  "trans_info": {
    "offer_from_name": "{\"customer_points\":1,\"app_seel\":1}"
  }
}

offer_from_nametrans_info 里仍是 字符串,需再包一层 JSON 转义。)

reconcile:已有订单时 updateDiyOfferInfo 从 DB 读出全部 from_name,拼成 {customer_points:1, app_seel:1, ...}全量重跑 save,使积分金额随订单变价更新。


6. saveDiyOffer vs updateDiyOfferInfo

方法触发行为
saveDiyOffer前台 POST offer_from_name按 JSON 调各 Handler 写入
updateDiyOfferInfoCartService::getList 有订单时读 DB 已有 from_name,全量重跑 save(积分随订单变价 reconcile)

7. Redis 写入方

Key写入方读取方
order_diy_offer:{store}{checkoutToken}主题 JS / 第三方 AppOrderDiyOfferDeliveryprotec
order_diy_offer:{store}seel:{checkoutToken}主题 JS / Seel 插件OrderDiyOfferSeel
order_diy_offers_from_name:{storeId}店铺启用 offer 类型 registry结账页展示哪些 offer
积分无 checkout Redis;直接 DBOrderDiyOfferPoints

PHP 对 deliveryprotec/seel 不负责写入 Redis;未入库先查前端是否写入 hash。


8. 互斥与共存

规则说明
跨类型积分 + Seel + 后台改价 可共存
同 from_namesave 前 delOrderOffer,仅保留最新
与商品优惠不参与 promotion_price;只进 total 最后加 diy_offer_price

9. 订单字段

CartOrder
diy_offer_pricecurrent_offer_price

汇总:OrderDiyOfferService::getOrderDiyOfferPrice()o_order_diy_offer 行求和。


10. 排障

现象排查
积分扣了 total 不对updateDiyOfferInfo、主从延迟
Seel 未入库Redis seel key、complete 是否传 offer_from_name
deliveryprotec 未入库Redis hash、主题 JS
单页积分无效complete/preComplete + offer_from_name
COD 无积分设计不支持 order_diy_offer

11. 代码索引

模块路径
调度common/services/OrderDiyOfferService.php
前台 savecommon/services/OrderDiyOfferHandleService.php
getList reconcileCartService.php 708 行
Handlerextend/orderdiyoffers/OrderDiyOffer*.php
Modelcommon/models/OrderDiyOfferModel.php