摘要:国际运费计算复杂,涉及首重续重、体积重(长×宽×高÷5000)、多渠道比价。本文给出规则引擎设计,支持动态配置运费模板。Taoify跨境电商的集运引擎每天计算数十万次运费,准确率达99.9%,并集成阿里云函数计算实现弹性伸缩。
一、运费模板数据模型
首先设计运费规则表结构。
sql
CREATE TABLE freight_rule ( id BIGINT PRIMARY KEY, channel_code VARCHAR(20) COMMENT '物流渠道代码: EMS,YUNTU,YANWEN', country_code VARCHAR(10) COMMENT '目的地国家代码', first_weight DECIMAL(10,2) COMMENT '首重重量(kg)', first_price DECIMAL(10,2) COMMENT '首重价格(元)', additional_weight DECIMAL(10,2) COMMENT '续重单位(kg)', additional_price DECIMAL(10,2) COMMENT '续重价格(元)', free_threshold DECIMAL(10,2) COMMENT '包邮门槛(元)', is_volumetric TINYINT COMMENT '是否按体积重计费', priority INT COMMENT '优先级');
二、计费引擎核心实现
采用策略模式处理不同计费方式。
java
// 计费策略接口public interface FreightCalculationStrategy { BigDecimal calculate(PackageInfo pkg, FreightRule rule);}// 实际重量策略@Componentpublic class ActualWeightStrategy implements FreightCalculationStrategy { @Override public BigDecimal calculate(PackageInfo pkg, FreightRule rule) { double weight = pkg.getActualWeight(); if (weight <= rule.getFirstWeight()) { return rule.getFirstPrice(); } double additionalUnits = Math.ceil((weight - rule.getFirstWeight()) / rule.getAdditionalWeight()); return rule.getFirstPrice().add(rule.getAdditionalPrice().multiply(BigDecimal.valueOf(additionalUnits))); }}// 体积重策略@Componentpublic class VolumetricWeightStrategy implements FreightCalculationStrategy { @Override public BigDecimal calculate(PackageInfo pkg, FreightRule rule) { double volumetricWeight = pkg.getLength() pkg.getWidth() pkg.getHeight() / 5000.0; double weight = Math.max(pkg.getActualWeight(), volumetricWeight); // 复用实际重量计算逻辑 return actualWeightStrategy.calculate(new PackageInfo(weight), rule); }}
三、多包裹合并计费
集运场景下,多个订单合并成一个包裹,需要计算总运费并分摊。
java
public class MergeFreightCalculator { public MergeResult calculate(List packages, String countryCode) { // 获取该国家适用的运费规则列表 List rules = freightRuleMapper.selectByCountry(countryCode); // 合并包裹总重量和总体积 double totalWeight = packages.stream().mapToDouble(PackageInfo::getActualWeight).sum(); double totalVolumetric = packages.stream().mapToDouble(p -> p.getLength() p.getWidth() p.getHeight() / 5000.0).sum(); double totalEffectiveWeight = Math.max(totalWeight, totalVolumetric); PackageInfo merged = new PackageInfo(totalEffectiveWeight); // 选择最优惠的渠道 FreightRule bestRule = rules.stream() .min(Comparator.comparing(r -> calculateSingle(merged, r))) .orElseThrow(); BigDecimal totalFreight = calculateSingle(merged, bestRule); // 按重量比例分摊 Map shareMap = new HashMap<>(); for (PackageInfo pkg : packages) { BigDecimal share = totalFreight.multiply(BigDecimal.valueOf(pkg.getActualWeight())) .divide(BigDecimal.valueOf(totalWeight), 2, RoundingMode.HALF_UP); shareMap.put(pkg.getOrderId(), share); } return new MergeResult(totalFreight, bestRule.getChannelCode(), shareMap); }}
四、阿里云函数计算弹性伸缩
运费计算在双十一等大促期间请求量激增。Taoify跨境电商将计算任务部署到阿里云函数计算(FC),利用其按量付费、自动弹性的特性,无需预留机器。
yaml
serverless.yamlfunctions: freight-calculator: handler: com.taoify.freight.CalculatorHandler runtime: java11 timeout: 10 memorySize: 512 triggers: - name: httpTrigger type: http config: authType: ANONYMOUS methods: - POST
函数计算自动扩容,从0到1000并发仅需数秒,大幅降低运维成本。