TAOCARTS 知识

跨境电商独立站国际运费计算引擎设计:策略模式处理复杂计费规则-阿里云开发者社区

2026-06-26 系统功能介绍

一、问题背景

国际运费计算是跨境电商独立站中最复杂的业务模块之一。不同物流渠道的计费方式完全不同——有的按实际重量,有的按体积重(长×宽×高÷5000),还有的首重续重阶梯不同、不同国家价格不同。如果将这些规则硬编码在业务逻辑中,每次新增渠道都需要修改代码。本文以 Taocarts 的运费计算引擎为例,讲解如何使用策略模式实现灵活可扩展的计费方案。

二、策略模式实现计费算法

java

// 计费策略接口

public interface FreightCalculator {

BigDecimal calculate(ShippingPackage pkg, FreightRule rule);

}

// 实际重量策略

@Component

public class ActualWeightCalculator implements FreightCalculator {

@Override

public BigDecimal calculate(ShippingPackage pkg, FreightRule rule) {

double weight = pkg.getActualWeightKg();

if (weight <= rule.getFirstWeight()) {

return rule.getFirstPrice();

}

double additional = weight - rule.getFirstWeight();

int units = (int) Math.ceil(additional / rule.getAdditionalUnit());

return rule.getFirstPrice().add(rule.getAdditionalPrice().multiply(BigDecimal.valueOf(units)));

}

}

// 体积重策略

@Component

public class VolumetricWeightCalculator implements FreightCalculator {

@Override

public BigDecimal calculate(ShippingPackage pkg, FreightRule rule) {

double volumetric = pkg.getLengthCm()

pkg.getWidthCm()

pkg.getHeightCm() / 5000.0;

double weight = Math.max(pkg.getActualWeightKg(), volumetric);

return actualWeightCalculator.calculate(new ShippingPackage(weight), rule);

}

}

三、运费模板配置与策略工厂

运费规则存储在数据库中,通过管理后台动态配置。新增渠道时只需插入一条配置记录,无需修改代码。

java

@Entity

@Table(name = "freight_rule")

public class FreightRule {

private String channel; // 物流渠道: yunexpress, ems, dhl

private String destinationCountry;

private Double firstWeight;

private BigDecimal firstPrice;

private Double additionalUnit;

private BigDecimal additionalPrice;

private String calculatorType; // actual / volumetric

}

@Component

public class FreightCalculatorFactory {

private final Map calculatorMap;

public FreightCalculatorFactory(List calculators) {

this.calculatorMap = calculators.stream()

.collect(Collectors.toMap(

c -> c.getClass().getSimpleName().replace("Calculator", "").toLowerCase(),

Function.identity()

));

}

}

四、合并发货与运费分摊

多个订单合并成一个包裹时,汇总总重量并计算总运费,然后按每个订单的重量比例分摊。

java

@Service

public class CombinedFreightService {

public CombinedFreightResult calculate(List orderIds, String channel, String country) {

List orders = orderMapper.selectBatchIds(orderIds);

double totalActualWeight = orders.stream().mapToDouble(Order::getTotalWeight).sum();

FreightRule rule = freightRuleMapper.selectByChannelAndCountry(channel, country);

FreightCalculator calculator = calculatorFactory.getCalculator(rule.getCalculatorType());

BigDecimal totalFreight = calculator.calculate(new ShippingPackage(totalActualWeight), rule);

List shares = new ArrayList<>();

for (Order order : orders) {

double ratio = order.getTotalWeight() / totalActualWeight;

BigDecimal share = totalFreight.multiply(BigDecimal.valueOf(ratio));

shares.add(new FreightShare(order.getId(), order.getOrderNo(), share));

}

return new CombinedFreightResult(totalFreight, shares);

}

}

五、性能优化

运费规则变更不频繁,可缓存到 Redis 避免每次查询数据库:

java

@Component

public class FreightRuleCache {

public FreightRule getRule(String channel, String country) {

String key = "freight:rule:" + channel + ":" + country;

FreightRule rule = redisTemplate.opsForValue().get(key);

if (rule == null) {

rule = ruleMapper.selectByChannelAndCountry(channel, country);

if (rule != null) {

redisTemplate.opsForValue().set(key, rule, Duration.ofHours(2));

}

}

return rule;

}

}

六、总结

Taocarts 的运费计算引擎通过策略模式将不同计费算法解耦,通过数据库配置实现规则的动态管理,已适配全球数十种物流渠道和上百个国家的差异化定价,准确率达 99.9% 以上。