反向海淘运费计算引擎:支持多渠道、体积重、补差逻辑的实现

简介: 面向技术开发者:Taocarts反向海淘运费引擎,支持多渠道(EMS/DHL/云途等)、体积重动态计算(可配系数)、首续重计费、自动补差与多渠道比价。含模板化配置、Caffeine缓存及完整Java实现,开箱即用,助力跨境代购独立站高效落地。(239字)

视角:技术开发者

一、运费的复杂性

反向海淘业务,最让技术头疼的就是运费计算。不同的物流渠道(EMS、云途、DHL、USPS)有不同的计费规则:首重续重、体积重系数、不同国家不同价格、甚至不同季节价格还会变。而且客户提交代购集运时预付的运费,和仓库实际打包后的运费往往有差异,需要支持补差。

Taocarts系统中,我们设计了一个灵活的运费计算引擎,支持多渠道配置、实时计算、自动补差。这套引擎也被很多跨境代购独立站采用。今天分享核心代码。

二、运费模板数据结构

CREATE TABLE `freight_template` (
  `id` bigint PRIMARY KEY AUTO_INCREMENT,
  `channel_code` varchar(32) NOT NULL COMMENT '物流渠道代码: EMS, YUNTO, DHL',
  `channel_name` varchar(64) NOT NULL,
  `country_code` varchar(8) NOT NULL COMMENT '目标国家',
  `country_name` varchar(64),
  `first_weight` decimal(6,2) NOT NULL COMMENT '首重(kg)',
  `first_price` decimal(10,2) NOT NULL COMMENT '首重价格(元)',
  `additional_weight` decimal(6,2) NOT NULL COMMENT '续重单位(kg)',
  `additional_price` decimal(10,2) NOT NULL COMMENT '续重价格(元)',
  `volume_factor` int DEFAULT 5000 COMMENT '体积重系数(cm³/5000)',
  `use_volume_weight` tinyint DEFAULT 0 COMMENT '是否取体积重大值',
  `min_weight` decimal(6,2) DEFAULT 0 COMMENT '最小计费重量',
  `max_weight` decimal(6,2) DEFAULT NULL COMMENT '最大限重',
  `status` tinyint DEFAULT 1
);

三、核心计算逻辑

@Service
public class FreightCalculator {
   

    // 计算预估运费(提交集运时)
    public BigDecimal estimateFreight(Long templateId, List<OrderItem> items) {
   
        FreightTemplate template = templateMapper.selectById(templateId);
        double totalWeight = items.stream().mapToDouble(OrderItem::getWeight).sum();
        double totalVolume = items.stream()
            .mapToDouble(item -> item.getLength() * item.getWidth() * item.getHeight())
            .sum();
        double volumeWeight = totalVolume / template.getVolumeFactor();
        double finalWeight = template.isUseVolumeWeight() ? Math.max(totalWeight, volumeWeight) : totalWeight;
        finalWeight = Math.max(finalWeight, template.getMinWeight());
        if (template.getMaxWeight() != null && finalWeight > template.getMaxWeight()) {
   
            throw new BusinessException("总重量超过渠道限重,请拆分包裹或选择其他渠道");
        }
        return calculateByWeight(finalWeight, template);
    }

    private BigDecimal calculateByWeight(double weight, FreightTemplate template) {
   
        if (weight <= template.getFirstWeight()) {
   
            return template.getFirstPrice();
        }
        double additional = weight - template.getFirstWeight();
        int units = (int) Math.ceil(additional / template.getAdditionalWeight());
        return template.getFirstPrice().add(template.getAdditionalPrice().multiply(BigDecimal.valueOf(units)));
    }
}

四、多渠道比价逻辑

客户在提交代购集运时,我们希望展示多个渠道的运费供选择。

@RestController
public class FreightController {
   
    @PostMapping("/api/freight/compare")
    public List<ChannelQuote> compareChannels(@RequestBody List<OrderItem> items, @RequestParam String countryCode) {
   
        List<FreightTemplate> templates = templateMapper.selectByCountry(countryCode);
        return templates.stream()
            .map(t -> {
   
                try {
   
                    BigDecimal price = freightCalculator.estimateFreight(t.getId(), items);
                    return new ChannelQuote(t.getChannelName(), price, t.getEstimatedDays());
                } catch (Exception e) {
   
                    return null;
                }
            })
            .filter(Objects::nonNull)
            .sorted(Comparator.comparing(ChannelQuote::getPrice))
            .collect(Collectors.toList());
    }
}

五、实际打包后的运费补差

仓库打包完成后,实际重量可能与预估不同。我们设计了补差逻辑。

@Service
public class PackageService {
   

    @Transactional
    public void completePacking(Long packageId, Double actualWeight, Double actualLength, Double actualWidth, Double actualHeight) {
   
        Package pkg = packageMapper.selectById(packageId);
        // 重新计算实际运费
        BigDecimal actualFreight = freightCalculator.calculateByWeight(actualWeight, pkg.getFreightTemplate());
        BigDecimal prepaidFreight = pkg.getPrepaidFreight();

        pkg.setActualWeight(actualWeight);
        pkg.setActualFreight(actualFreight);

        if (actualFreight.compareTo(prepaidFreight) > 0) {
   
            // 需要补款
            BigDecimal diff = actualFreight.subtract(prepaidFreight);
            pkg.setStatus(PackageStatus.WAITING_DIFF);
            pkg.setDiffAmount(diff);
            // 生成补款订单
            createDiffOrder(pkg.getUserId(), pkg.getId(), diff);
        } else if (actualFreight.compareTo(prepaidFreight) < 0) {
   
            // 需要退款(可选)
            BigDecimal diff = prepaidFreight.subtract(actualFreight);
            pkg.setStatus(PackageStatus.WAITING_REFUND);
            pkg.setRefundAmount(diff);
            // 自动退款到余额
            userService.refundBalance(pkg.getUserId(), diff);
            pkg.setStatus(PackageStatus.PACKED);
        } else {
   
            pkg.setStatus(PackageStatus.PACKED);
        }
        packageMapper.updateById(pkg);
    }
}

六、体积重计算的坑

实际业务中,体积重的计算容易出错。因为商品入库时录入的长宽高可能不准。我们增加了“复核”流程,打包员可以修正尺寸。

另外,有些渠道的体积重系数是6000而不是5000(比如DHL)。我们把系数放到模板里,每个渠道单独配置。

// 体积重计算
public double calcVolumeWeight(double length, double width, double height, int factor) {
   
    // 单位:cm,返回kg
    return length * width * height / factor;
}

七、缓存优化

运费模板不常变,但每次计算都要查询数据库。我们用Caffeine做本地缓存,5分钟过期。

@Cacheable(value = "freightTemplate", key = "#templateId")
public FreightTemplate getTemplate(Long templateId) {
   
    return templateMapper.selectById(templateId);
}

八、与Taocarts系统的集成

这套运费计算引擎是Taocarts系统的核心模块之一。Taocarts系统作为成熟的代购源码,已经对接了多家国际物流API,支持实时获取渠道价格、电子面单打印。如果你正在开发反向海淘独立站,可以直接复用这套逻辑,省去对接多个物流渠道的麻烦。

目录
相关文章
|
23天前
|
缓存 人工智能 自然语言处理
阿里云百炼通义千问Qwen3.6-Flash完整实操指南:轻量化旗舰功能特性、落地优势与分层优惠订阅方案详解
当前AI应用落地场景分化愈发明显,除复杂智能体、百万字长文档、全栈大型工程开发等高门槛业务外,大量企业存在高频轻量问答、实时客服对话、短文本批量生成、简单数据提取、前端实时交互等标准化轻量化需求。这类场景单日调用频次可达数万乃至数十万次,对接口响应延迟、单轮调用成本、并发承载能力有极高要求,若选用高规格旗舰模型会造成算力预算严重浪费,而普通基础轻量化模型又存在逻辑推理弱、工具调用不稳定、短文本输出质量差等短板。
322 4
|
23天前
|
人工智能 缓存 运维
阿里云百炼通义千问Qwen3.7-Plus完整指南:全维度功能特性、落地优势与优惠订阅方案实操手册
AI应用规模化落地进程中,绝大多数企业与开发者面临性能与成本难以平衡的核心难题:轻量化模型推理、图文解析、长文档处理能力不足,无法支撑中等复杂度智能体任务;旗舰级模型长期高频调用成本偏高,中小团队难以持续投入算力预算。依托自研通义千问技术体系打造的Qwen3.7-Plus,是阿里云百炼平台推出的中端全能型多模态大模型,精准填补轻量化模型与旗舰模型之间的市场空白,在保留百万级上下文、原生图文多模态、全链路工具调用、通用代码生成全套核心能力的基础上,大幅下调调用单价,适配个人开发者、小微创业团队、中小企业全层级使用需求。
530 1
|
应用服务中间件 nginx
wireshark抓包入门使用教程
wireshark抓包入门使用教程
2385 0
wireshark抓包入门使用教程
|
14天前
|
人工智能 缓存 监控
协议兼容新方案:CC Switch本地路由实现Codex CLI接入DeepSeek全流程
在命令行AI编程场景中,Codex CLI凭借高效的代码生成、脚本编写与工程辅助能力,成为开发者轻量化开发的核心工具。但Codex CLI原生仅兼容OpenAI Responses API协议,无法直接对接DeepSeek等采用Chat Completions API的第三方模型,直接修改配置会引发404报错、参数解析失败、流式输出中断等问题。CC Switch本地路由工具通过轻量化本地代理与双向协议转换,无需修改Codex CLI源码,即可实现无感接入DeepSeek等第三方模型,彻底解决协议不兼容痛点,大幅拓展Codex CLI的模型生态与使用场景。
359 4
|
23天前
|
存储 SQL API
1688物流跟踪API:实时查询快递轨迹对接方案(附python源码) 🚚 1688物流跟踪API:实时查询快递轨迹对接方案(附Python源码)
本文详解1688物流跟踪API对接方案:通过`alibaba.logistics.trade.ship`获取运单号,调用`alibaba.logistics.trace.get`实时查询轨迹,并附完整Python封装代码与ERP同步策略,含签名逻辑、异常处理及高频避坑指南。(239字)
|
23天前
|
人工智能 BI
为什么说“超级个体”是能力下放第三次循环?意图共鸣科技《AI记忆链商业化白皮书3.0》这样解释
移动互联网让个人拥有公司级能力,AI时代则催生“超级个体”:专属AI赋能分析、创作与执行,成为职场人的“能力对等器”。它不取代人,而是弥合AI鸿沟——未来竞争力,取决于你与AI协同创造的深度。
116 3
|
23天前
|
运维 安全 数据可视化
上马 AR 巡检项目前,先想清楚这 8 个核心问题
AR巡检虽具虚实融合、远程协作等优势,但盲目上马易致“买而不用”。本文直击落地痛点,提出八大关键问题:是否真需AR?投入产出能否量化?环境与设备是否适配?一线是否愿用会用?系统能否打通?硬件选型重实用而非参数?内容运维如何持续?安全合规有无保障?唯有想透再行动,技术方能真正赋能业务。(239字)
|
23天前
|
缓存 Devops Java
软件开发进阶技能之 DevOps 工程体系(二)
本文系统讲解CI/CD全流程:涵盖CI流水线核心阶段(检出、缓存、测试、构建、镜像、集成测试等),GitHub Actions与Jenkins实战示例;CD/CD部署策略(环境链、门禁、蓝绿/金丝雀)、ArgoCD GitOps实践及数据库迁移(Flyway)方案,助力高效可靠交付。
|
23天前
|
人工智能 监控 算法
如何甄选优质 AI 搜索优化服务商?从技术、口碑、定制能力三大维度拆解选型逻辑
本文解析生成式引擎优化(GEO)选型关键,提出“技术实力、行业口碑、定制化能力”三大评估标尺,揭示服务商三类分层,并结合真实案例与数据,指导企业科学筛选靠谱合作伙伴,抢占AI流量红利。
|
23天前
|
存储 NoSQL Redis
使用Redis实现反向海淘购物车合并与过期清理
本文面向开发者,介绍如何用Redis实现反向海淘购物车:按店铺分组存储(Hash结构)、支持商品合并与最低起批量校验、7天自动过期+到期前1天提醒,并已落地于Taocarts代购系统。
68 0

热门文章

最新文章