current_insurance_price 全链路(超详细版)
1. 业务定义(一句话)
⚡⚡⚡ 运费险在国家命中且商家开启时,按固定额或比例基数计算保费并写入订单;比例模式下可选以订单额/商品额/运费额为基数,并有上限截断。
2. 后台配置到前台计算的关系
2.1 商家后台可控项(配置映射表)
| 后台配置项 | 典型存储/来源 | 前台读取点 | 对计算的直接影响 |
|---|---|---|---|
| 运费险开关 | StoreInsuranceSettingModel.status | getInsuranceSetting | 未开启则始终返回0 |
| 生效国家白名单 | param.countries | getInsurancePrice | 国家不命中则不展示/不计算 |
| 计费类型 | param.type | getInsurancePrice | 1=固定额,≠1=比例模式 |
| 固定金额 | param.fee_amount | getInsurancePrice | type=1 时直接作为保费 |
| 比例基数类型 | param.ratio.fee_type | getInsurancePrice | 1=订单额/2=商品额/3=运费额 |
| 比例值 | param.ratio.fee_ratio | getInsurancePrice | type≠1 时乘以基数再除100 |
| 上限截断 | param.ratio.fee_max | getInsurancePrice | 计算结果超出此值则取该值 |
2.2 全链路时序图(配置 → 计算 → 落库)
flowchart TD A[商家配置运费险规则] --> B[getInsuranceSetting 读取店铺配置] C[买家国家 country_id] --> D[getInsurancePrice] D --> E{status == enabled?} E -->|否| F[返回 0] E -->|是| G{country in whitelist?} G -->|否| F G -->|是| H{type == 1?} H -->|是 固定额| I[fee = fee_amount] H -->|否 比例模式| J{fee_type?} J -->|1 订单额| K[base = order_price] J -->|2 商品额| L[base = product_price] J -->|3 运费额| M[base = shipping_price] K --> N[fee = base × fee_ratio ÷ 100] L --> N M --> N N --> O{"fee > fee_max?"} O -->|是| P[fee = fee_max 截断] O -->|否| Q[fee = 计算值] P --> R["price_format(fee, 2)"] Q --> R R --> S[写入订单 current_insurance_price] S --> T[renew 重算 total_price]
3. 核心入口与数据结构
3.1 核心方法链路
CheckoutOnePageService / OrderService
└─ getInsuranceSetting() 读取店铺运费险配置
└─ getInsurancePrice() 计算保费(固定额 or 比例 + 上限)
3.2 type × fee_type 交叉计算表
| type | fee_type | 含义 | 计算公式 |
|---|---|---|---|
| 1 | - | 固定额 | fee = fee_amount |
| ≠1(比例) | 1 | 订单额比例 | fee = order_price × fee_ratio / 100 |
| ≠1(比例) | 2 | 商品额比例 | fee = product_price × fee_ratio / 100 |
| ≠1(比例) | 3 | 运费额比例 | fee = shipping_price × fee_ratio / 100 |
4. 完整计算逻辑
4.1 国家白名单过滤
if (isset($param['countries'])) {
// 国家不符合展示要求 → 不展示,返回 0
if (!in_array($country_id, $param['countries']) && $param['countries'] != []) {
return $insurance_price; // 初始值 0
}
}countries为空数组[]时:不限制,所有国家均可展示countries有值但买家国家不在内:返回 0(不展示)
4.2 固定额模式(type == 1)
$insurance_price = $param['fee_amount'];直接取配置中的固定金额,无需比例计算。
4.3 比例模式(type ≠ 1)
第一步:确定比例基数
if ($param['ratio']['fee_type'] == 1) {
// 订单金额比例(最常用)
$price = bcmul(bcdiv($order_price, 100, 4), $param['ratio']['fee_ratio'], 4);
} elseif ($param['ratio']['fee_type'] == 2) {
// 商品金额比例
$price = bcmul(bcdiv($product_price, 100, 4), $param['ratio']['fee_ratio'], 4);
} elseif ($param['ratio']['fee_type'] == 3) {
// 运费金额比例
$price = bcmul(bcdiv($shipping_price, 100, 4), $param['ratio']['fee_ratio'], 4);
}⚠️ 使用 bcmul/bcdiv 精度计算(4位小数),避免浮点精度问题。
第二步:上限截断
if (!empty($param['ratio']['fee_max']) && $param['ratio']['fee_max'] > 0 && $price > $param['ratio']['fee_max']) {
$insurance_price = $param['ratio']['fee_max']; // 截断到上限
} else {
$insurance_price = $price;
}4.4 输出与落库
return price_format($insurance_price, 2); // 2位小数5. 比例基数三兄弟详解
5.1 fee_type = 1(订单金额比例)
以 current_total_price(商品小计 + 运费)为基数。适合保费随订单总额联动的场景。
5.2 fee_type = 2(商品金额比例)
以商品小计 subtotal_price(不含运费)为基数。适合保费仅与商品价值挂钩的场景。
5.3 fee_type = 3(运费金额比例)
以运费 current_shipping_price 为基数。适合保费随运费金额联动的场景。
⚠️ 注意:比例基数中的各子项(product_price/order_price/shipping_price)是经过优惠扣减后的实付金额还是原价,需确认上游传入口径——通常使用 current_subtotal_price 等已优惠的金额。
6. 字段输出结构
| 字段 | 类型 | 含义 | 示例 |
|---|---|---|---|
cart.insurance_price | float | 运费险保费(正数) | 2.50 |
cart.insurance.description | string | 运费险说明 | "运费险" |
cart.insurance.selected | bool | 买家是否选中 | true |
cart.insurance.settings | array | 完整配置快照 | {type, ratio, countries} |
7. 边界场景说明
7.1 运费险未开启
status 不为启用值 → getInsuranceSetting 直接返回空配置 → insurance_price = 0
7.2 国家白名单为空
countries = [] 时视为不限制,所有国家均可展示和计算。
7.3 比例计算后恰好等于上限
price == fee_max 时不会触发截断(> 才截断),保持原值。
7.4 上限为 0 或负数
fee_max <= 0 时跳过截断判断,保持计算值。
7.5 比例值为 0
fee_ratio = 0 时保费为 0(即使在白名单内)。
8. 与总价 total_price 的关系
total_price 的组成(9字段):
subtotal_price = Σ(final_line_price) 商品行小计
discount_price = 整单折扣(负数)
refund_price = 退款口径独立
current_total_price = subtotal + shipping 仅展示
total_price = subtotal - promotion - coupon + shipping + tax + tip + payment + insurance + offer
└─ insurance_price 在此处被加(正数)
⚡⚡⚡ insurance_price 是正数,在 total_price 公式中做加法(买家额外支付的保费)。
9. 关键代码索引
| 逻辑 | 文件:行号 |
|---|---|
| 入口:getInsuranceSetting | InsuranceService.php:335-375 |
| 核心:getInsurancePrice | InsuranceService.php:382-421 |
| 国家白名单过滤 | InsuranceService.php:394-399 |
| 固定额分支 | InsuranceService.php:400-401 |
| 比例基数三分支 | InsuranceService.php:404-410 |
| 上限截断 | InsuranceService.php:412-417 |
| 落库触发 | OrderService::renew |
| 运费险保存 | OrderService::saveShippingInsurance |