Taocarts 知识

📅 2026-02-24 系统功能介绍

1688自动采购实战:从0到1搭建高可用订单同步

批量翻译商品信息时,翻译API超时了将近四成,结果商品列表里一半韩文一半中文,客户直接懵了。这个场景在代购系统里很常见——1688的商品信息需要抓取、翻译、映射到本地SKU,任何一个环节不稳定,都会导致前端展示错乱,进而影响采购准确率。

1688自动采购的核心,不是“能下单”,而是“稳定地下单、不漏单、不重复下单”。本文从架构设计到代码实现,逐步拆解一个可落地的1688采购同步方案。

对接1688开放平台前,先确认账号类型。企业认证账号的QPS上限大约50次/秒,日调用量约5000次。如果是个人开发者,只有5-10次/秒,日调用量仅100次左右,基本只够测试。

限流维度有三个:应用级(单AppKey约1000次/分钟)、用户级(单用户约5次/秒)、IP级。超限后首次封5-10分钟,多次触发可能封24小时。

解决方案:所有API调用前做本地限流,用Redis的令牌桶算法控制频率。采购高峰期一天300多单,手动下单到凌晨两点漏单率5%(案例2),自动化后必须把QPS压到安全线内。

// Redis令牌桶限流器(PHP)
function rateLimit($key, $capacity, $rate, $tokens_per_request = 1) {

$now = microtime(true);

$bucket = redis()->get($key);

if (!$bucket) {

$bucket = ['tokens' => $capacity, 'last' => $now];

} else {

$bucket = json_decode($bucket, true);

$elapsed = $now - $bucket['last'];

$bucket['tokens'] = min($capacity, $bucket['tokens'] + $elapsed * $rate);

$bucket['last'] = $now;

}

if ($bucket['tokens'] >= $tokens_per_request) {

$bucket['tokens'] -= $tokens_per_request;

redis()->setex($key, 60, json_encode($bucket));

return true;

}

redis()->setex($key, 60, json_encode($bucket));

return false;
}

每个1688 API调用前执行rateLimit('1688:app', 50, 0.83)(50 tokens,每秒补充0.83个,即50次/分钟)。超过限制时让请求休眠200ms后重试。

1688订单回调的丢包率在高峰期可能到1%-3%,重复回调的概率也不低。解决方案:本地维护一张purchase_tasks表,用request_id唯一索引防重。

CREATE TABLE purchase_tasks (

id INT AUTO_INCREMENT PRIMARY KEY,

request_id VARCHAR(64) NOT NULL UNIQUE,

order_id INT NOT NULL,

supplier_order_id VARCHAR(64),

status ENUM('pending','processing','done','failed') DEFAULT 'pending',

retry_count TINYINT DEFAULT 0,

created_at DATETIME
);

每次调用1688 API前生成request_id = md5($order_id . time() . rand()),先插入purchase_tasks(状态pending)。如果插入时唯一索引冲突,说明该请求已存在,直接返回。插入成功后调用API,更新状态为processing/done。

配合指数退避重试:处理超时或429状态码时,间隔1秒、2秒、4秒重试,最多3次。重试时携带相同的request_id

高并发库存预扣:Redis分布式锁+Lua脚本

代购抢购场景最怕超卖。下单时不能直接扣1688库存(因为查询有延迟),应该先扣本地虚拟库存。虚拟库存阈值设置为真实库存的90%左右,留出缓冲。

-- 原子预扣虚拟库存(Redis Lua)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or 0)
if current < limit then

redis.call('incr', key)

return 1
end
return 0

预扣成功后创建订单,状态为“待采购”。支付完成后,再由后台任务串行调用1688下单接口。串行化通过Redis分布式锁实现,锁住商品ID,超时5秒。

// 分布式锁(PHP)
$lockKey = "lock:purchase:{$productId}";
$lockValue = uniqid();
if (!$redis->set($lockKey, $lockValue, ['nx', 'ex' => 5])) {

return ['code' => 429, 'msg' => '系统繁忙'];
}
// 调用1688 API,下单成功后释放锁
$redis->eval("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end", [$lockKey, $lockValue]);

释放锁时必须校验锁的值是否当前请求持有,防止误删。

踩坑记录:1688签名算法静默升级

某次1688接口的签名算法突然变了,官方文档没同步。结果一整天的订单都没同步到仓库(案例3)。解决方案有两个:一是日志里记录每次请求的完整报文和签名,出问题时能快速定位;二是实现一个签名降级开关,当发现签名失败比例突增时,手动切换到备用签名算法版本。

另外,1688的商品库存同步延迟5-30分钟。如果依赖实时查询来预扣,大概率会超卖。所以前面强调的虚拟库存缓冲系数非常关键。

在taocarts 的1688自动采购模块中,上述限流、幂等、预扣、分布式锁已经封装为可配置的管道。从几十单到几百单的跨越过程中,这套方案保障了采购链路的稳定性。

回头想想,自动采购的可靠性不是靠一个完美的API调用,而是靠限流、幂等、重试、兜底这四个环节的组合。下篇我们将聊仓库管理和合包策略——当多个客户的包裹混在一起时,如何用系统保证不串货、不丢件。如果你在对接1688过程中踩过其他坑,欢迎在评论区分享。

wechat wechat qr