order_diy_offer Handler 参考
o_order_diy_offer 订单级附加项:Handler、Redis、触发时机。主文档 §3。
与实现冲突时以 extend/orderdiyoffers/、OrderDiyOfferService 为准。
目录
- 1. 与 diy_offer 的区别
- 2. from_name 总表
- 3. 扩展路由
- 4. 各 Handler 详解
- 5. 触发时机(按结账形态)
- 5.1 offer_from_name 传参(多个)
- 6. saveDiyOffer vs updateDiyOfferInfo
- 7. Redis 写入方
- 8. 互斥与共存
- 9. 订单字段
- 10. 排障
- 11. 代码索引
1. 与 diy_offer 的区别
o_diy_offer | o_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_seel | Seel 延保 | 通常正 | 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
| 项 | deliveryprotec | seel |
|---|---|---|
| Redis | order_diy_offer:{storeId}{checkoutToken} | order_diy_offer:{storeId}seel:{checkoutToken} |
| PHP | hGetAll 只读 | 同左 |
| save | del 旧行 → 读 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 POST | offer_from_name |
| 单页 | complete / preComplete | trans_info.offer_from_name |
| 渐进式 | shipping-method POST | offer_from_name |
| COD 带 token | — | 不支持 |
| COD 单页 | — | 不支持 |
查询:POST /order/orderdiyofferinfo;列表:OrderDiyOfferHandleService::getOfferList。
5.1 offer_from_name 传参(多个)
支持一次提交多种订单级 offer。 协议:
- HTTP 字段名:
offer_from_name - 类型:字符串,内容为 JSON 对象(
from_name→ 参数) - 解析:
OrderDiyOfferHandleService::saveDiyOffer→json_decode→OrderDiyOfferService::saveDiyOffer
{
"customer_points": 1,
"app_seel": 1,
"app_deliveryprotec": 1
}| from_name | value | 行为 |
|---|---|---|
customer_points | 1 | 扣积分,写负价 o_order_diy_offer 行 |
customer_points | 0 | 退还已用积分 |
app_seel | 1(或任意非空) | 读 Redis seel hash → insert |
app_deliveryprotec | 1 | 读 Redis deliveryprotec hash → insert |
注意:
app_seel、app_deliveryprotec在saveDiyOffer内 强制 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_name 在 trans_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 写入 |
updateDiyOfferInfo | CartService::getList 有订单时 | 读 DB 已有 from_name,全量重跑 save(积分随订单变价 reconcile) |
7. Redis 写入方
| Key | 写入方 | 读取方 |
|---|---|---|
order_diy_offer:{store}{checkoutToken} | 主题 JS / 第三方 App | OrderDiyOfferDeliveryprotec |
order_diy_offer:{store}seel:{checkoutToken} | 主题 JS / Seel 插件 | OrderDiyOfferSeel |
order_diy_offers_from_name:{storeId} | 店铺启用 offer 类型 registry | 结账页展示哪些 offer |
| 积分 | 无 checkout Redis;直接 DB | OrderDiyOfferPoints |
PHP 对 deliveryprotec/seel 不负责写入 Redis;未入库先查前端是否写入 hash。
8. 互斥与共存
| 规则 | 说明 |
|---|---|
| 跨类型 | 积分 + Seel + 后台改价 可共存 |
| 同 from_name | save 前 delOrderOffer,仅保留最新 |
| 与商品优惠 | 不参与 promotion_price;只进 total 最后加 diy_offer_price |
9. 订单字段
| Cart | Order |
|---|---|
diy_offer_price | current_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 |
| 前台 save | common/services/OrderDiyOfferHandleService.php |
| getList reconcile | CartService.php 708 行 |
| Handler | extend/orderdiyoffers/OrderDiyOffer*.php |
| Model | common/models/OrderDiyOfferModel.php |