摘要:本文围绕企业采购系统与外部商品详情 API 的集成实践展开,从整体架构、接口对接、数据建模、供应商比价算法到自动下单流程,给出一套可落地的技术方案,并重点讨论幂等、限流、缓存、异常补偿等工程细节,帮助读者把「比价 + 下单」真正嵌入内部采购流程。
一、为什么要把商品详情 API 嵌入采购流程
传统企业采购往往依赖人工询价、Excel 比价和邮件下单,存在三个明显痛点:
信息滞后:供应商价格、库存、交期分散在各平台,采购员需要反复登录核对。
比价口径不统一:不同供应商的含税价、运费、阶梯价、最小起订量(MOQ)规则不同,人工比价容易出错。
下单链路割裂:比价结果需要手工录入 ERP 或采购系统,重复劳动多,且难以追溯。
把商品详情 API 嵌入内部采购流程,本质上是把「外部商品数据」变成「内部可计算的结构化数据」,让比价和下单由系统自动完成。这样做的收益包括:
采购员只需在内部系统发起需求,系统自动拉取多家供应商报价;
比价规则统一配置,结果可审计、可复现;
选定供应商后自动创建采购单,减少人工录入错误。
二、整体架构设计
一个典型的集成架构可以分为四层:采购前端、采购服务、集成适配层、外部供应商 API。
flowchart TD
A[采购前端 / 需求单] --> B[采购服务 Purchase Service]
B --> C[比价引擎 Price Engine]
B --> D[下单编排 Order Orchestrator]
C --> E[集成适配层 Adapter]
D --> E
E --> F[供应商 A 商品详情 API]
E --> G[供应商 B 商品详情 API]
E --> H[供应商 C 商品详情 API]
B --> I[(采购数据库)]
C --> I
D --> I
D --> J[ERP / 财务系统]
各层职责如下:
层级 核心职责
采购前端 提交采购需求、展示比价结果、确认下单
采购服务 需求管理、流程编排、权限与审批
比价引擎 统一价格口径、计算综合成本、排序推荐
集成适配层 封装各供应商 API 差异、限流、重试、缓存
关键设计原则是:采购服务不直接调用外部 API,而是通过集成适配层统一出口。这样供应商接口变更、鉴权方式调整、限流策略变化都收敛在一处。
三、商品详情 API 对接要点
3.1 统一商品模型
不同供应商返回的字段命名、单位、价格口径差异很大,需要先定义内部统一模型:
public class SupplierProduct {
private String supplierCode; // 供应商编码
private String supplierSku; // 供应商侧 SKU
private String internalSku; // 内部物料编码
private String productName;
private BigDecimal unitPrice; // 单价(不含税)
private BigDecimal taxRate; // 税率
private BigDecimal shippingFee; // 运费
private Integer moq; // 最小起订量
private Integer stock; // 可用库存
private Integer leadTimeDays; // 交期(天)
private String currency; // 币种
private Instant fetchedAt; // 数据抓取时间
}
适配层负责把供应商原始响应映射为 SupplierProduct,并统一单位(如把「元/千件」换算为「元/件」)。
3.2 鉴权与限流
常见鉴权方式包括 API Key、OAuth2 客户端凭证、签名(HMAC)。建议在适配层统一封装:
凭证集中管理,避免散落在业务代码中;
按供应商维度做令牌桶限流,防止触发对方风控;
对 429、5xx 做指数退避重试,并设置最大重试次数。
public class RateLimitedClient {
private final RateLimiter limiter = RateLimiter.create(5.0); // 每秒 5 次
public String get(String url, Map<String, String> headers) {
limiter.acquire();
// 使用 HttpClient 发起请求,省略具体实现
return httpGet(url, headers);
}
}
3.3 缓存策略
商品详情中的价格、库存变化频率不同,建议分层缓存:
商品基础信息(名称、规格):缓存 1 小时以上;
价格:缓存 5 到 15 分钟,比价前可强制刷新;
库存:缓存时间尽量短,下单前必须实时校验。
缓存键建议使用 supplierCode + supplierSku,并记录 fetchedAt,在比价结果中展示数据新鲜度。
四、供应商比价引擎实现
4.1 综合成本计算
单纯比较单价并不准确,应计算「综合采购成本」:
public BigDecimal totalCost(SupplierProduct p, int quantity) {
BigDecimal goods = p.getUnitPrice()
.multiply(BigDecimal.valueOf(quantity))
.multiply(BigDecimal.ONE.add(p.getTaxRate()));
BigDecimal shipping = p.getShippingFee() == null
? BigDecimal.ZERO : p.getShippingFee();
return goods.add(shipping);
}
如果供应商提供阶梯价,需要先根据采购数量匹配对应价格区间,再计算总成本。
4.2 比价排序与推荐
比价结果通常需要综合多个维度,而不仅是价格。可以采用加权评分:
维度 权重示例 说明
综合成本 0.5 越低越好,归一化后取反
交期 0.2 天数越短越好
库存满足率 0.2 库存 / 需求量,上限为 1
历史履约评分 0.1 来自内部供应商评价
public double score(SupplierProduct p, int quantity, double maxCost) {
double costScore = 1 - totalCost(p, quantity).doubleValue() / maxCost;
double leadScore = 1.0 / (1 + p.getLeadTimeDays());
double stockScore = Math.min(1.0, (double) p.getStock() / quantity);
double ratingScore = supplierRating(p.getSupplierCode());
return 0.5 costScore + 0.2 leadScore
+ 0.2 * stockScore + 0.1 * ratingScore;
}
权重应做成可配置项,不同品类(如办公用品与生产物料)可以采用不同策略。
4.3 比价结果落库
每次比价都应保存快照,包括参与比价的供应商、原始报价、计算参数和最终推荐结果,便于后续审计与复盘。
五、自动下单流程设计
5.1 下单编排
自动下单不是简单调用一次接口,而是一个带状态机的流程:
stateDiagram-v2
[*] --> 待确认
待确认 --> 库存校验: 采购员确认
库存校验 --> 创建订单: 库存充足
库存校验 --> 待确认: 库存不足
创建订单 --> 支付或账期: 订单创建成功
支付或账期 --> 已完成: 支付成功
创建订单 --> 失败补偿: 调用异常
失败补偿 --> 创建订单: 重试
失败补偿 --> 待确认: 超过重试上限
5.2 幂等设计
下单接口必须支持幂等,避免网络重试导致重复采购。推荐使用业务幂等键:
String idempotentKey = purchaseRequestId + ":" + supplierCode + ":" + internalSku;
// 下单前先查询幂等表,若已存在则直接返回历史结果
if (idempotentRepository.exists(idempotentKey)) {
return idempotentRepository.get(idempotentKey);
}
幂等表需要设置合理过期时间,并与订单状态保持一致。
5.3 异常补偿
外部下单可能遇到超时、库存变化、价格变动等情况,建议:
下单前再次实时校验价格与库存,若变化超过阈值则回到待确认状态;
对超时请求先查询订单状态,再决定是否重试;
引入对账任务,定期比对内部采购单与供应商订单状态。
六、工程实践与注意事项
接口契约先行:为每家供应商定义适配器接口,新增供应商只需实现适配器,不改动比价与下单主流程。
可观测性:记录每次外部调用的耗时、状态码、重试次数,便于定位问题。
灰度上线:先对少量品类开启自动比价,验证数据准确性后再扩大范围。
权限与审批:自动下单应设置金额阈值,超过阈值仍需人工审批。
数据合规:遵守供应商 API 使用条款,不超范围抓取或转售数据。
七、总结
把商品详情 API 嵌入企业采购流程,核心在于三件事:用统一模型抹平供应商差异,用比价引擎把价格与交期等维度转化为可比较的综合成本,用带幂等和补偿的下单编排保证流程可靠。落地时建议从单一品类、少量供应商开始,逐步完善适配层、缓存与监控,再扩展到全品类自动比价与自动下单。如有任何疑问,欢迎留言探讨!