current_insurance_price 全链路(超详细版)

1. 业务定义(一句话)

⚡⚡⚡ 运费险在国家命中且商家开启时,按固定额或比例基数计算保费并写入订单;比例模式下可选以订单额/商品额/运费额为基数,并有上限截断。

2. 后台配置到前台计算的关系

2.1 商家后台可控项(配置映射表)

后台配置项典型存储/来源前台读取点对计算的直接影响
运费险开关StoreInsuranceSettingModel.statusgetInsuranceSetting未开启则始终返回0
生效国家白名单param.countriesgetInsurancePrice国家不命中则不展示/不计算
计费类型param.typegetInsurancePrice1=固定额,≠1=比例模式
固定金额param.fee_amountgetInsurancePricetype=1 时直接作为保费
比例基数类型param.ratio.fee_typegetInsurancePrice1=订单额/2=商品额/3=运费额
比例值param.ratio.fee_ratiogetInsurancePricetype≠1 时乘以基数再除100
上限截断param.ratio.fee_maxgetInsurancePrice计算结果超出此值则取该值

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 交叉计算表

typefee_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_pricefloat运费险保费(正数)2.50
cart.insurance.descriptionstring运费险说明"运费险"
cart.insurance.selectedbool买家是否选中true
cart.insurance.settingsarray完整配置快照{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. 关键代码索引

逻辑文件:行号
入口:getInsuranceSettingInsuranceService.php:335-375
核心:getInsurancePriceInsuranceService.php:382-421
国家白名单过滤InsuranceService.php:394-399
固定额分支InsuranceService.php:400-401
比例基数三分支InsuranceService.php:404-410
上限截断InsuranceService.php:412-417
落库触发OrderService::renew
运费险保存OrderService::saveShippingInsurance