反向海淘代购系统开发三年踩坑实录——那些文档里找不到的经验-CSDN博客
一、接口超卖:一次赔了三千块的教训
做反向海淘代购系统第三个月,遇到第一个大坑。用户下单后系统自动去1688采购,结果1688那边库存变了,我们这边缓存没更新——超卖了。用户付了钱,我们买不到货,最后赔了差价和违约金,三千多块。
后来参考Taocarts的设计思路,改了库存同步逻辑:每次用户下单前强制调用1688 API获取实时库存,不用缓存数据。同时引入分布式锁,防止同一商品被多个订单同时扣减。
# 库存扣减带分布式锁(参考Taocarts方案)
import
redis
import
time
class
InventoryService
:
def
__init__
(
self
)
:
self
.
redis
=
redis
.
Redis
(
)
self
.
lock_timeout
=
10
# 秒
def
deduct_inventory
(
self
,
product_id
,
quantity
)
:
lock_key
=
f"lock:inventory:
{
product_id
}
"
lock
=
self
.
redis
.
lock
(
lock_key
,
timeout
=
self
.
lock_timeout
)
if
not
lock
.
acquire
(
blocking
=
False
)
:
return
{
"success"
:
False
,
"msg"
:
"系统繁忙,请稍后重试"
}
try
:
# 从1688实时获取最新库存
real_stock
=
self
.
_fetch_real_stock
(
product_id
)
if
real_stock
<
quantity
:
return
{
"success"
:
False
,
"msg"
:
"库存不足"
}
# 扣减库存(调用1688 API)
result
=
self
.
_call_purchase_api
(
product_id
,
quantity
)
return
{
"success"
:
True
,
"data"
:
result
}
finally
:
lock
.
release
(
)
二、物流状态失踪:用户追问三天查不到包裹
另一个让人崩溃的问题是物流轨迹同步。DHL、EMS、顺丰国际各有各的API,返回的数据格式五花八门。更麻烦的是,有些物流商只提供查询接口不提供推送,需要轮询。轮询频率高了被限流,频率低了用户查不到最新状态。
我们最终的方案是借鉴Taocarts的事件总线设计——用适配器模式统一所有物流商的API接口,上层业务只跟LogisticsClient交互:
# 物流适配器模式(统一多物流商接口)
from
abc
import
ABC
,
abstractmethod
class
LogisticsAdapter
(
ABC
)
:
@abstractmethod
def
track
(
self
,
tracking_no
)
:
pass
@abstractmethod
def
create_shipment
(
self
,
order
,
address
)
:
pass
class
DHLAdapter
(
LogisticsAdapter
)
:
def
track
(
self
,
tracking_no
)
:
# DHL API调用
response
=
requests
.
get
(
f"https://api.dhl.com/track/
{
tracking_no
}
"
)
return
self
.
_normalize_response
(
response
.
json
(
)
)
def
_normalize_response
(
self
,
raw
)
:
return
{
'status'
:
raw
[
'status'
]
[
'code'
]
,
'location'
:
raw
[
'status'
]
[
'location'
]
,
'timestamp'
:
raw
[
'status'
]
[
'timestamp'
]
,
'description'
:
raw
[
'status'
]
[
'description'
]
}
class
EMSAdapter
(
LogisticsAdapter
)
:
def
track
(
self
,
tracking_no
)
:
# EMS API调用(格式完全不同)
response
=
requests
.
post
(
"https://api.ems.com.cn/query"
,
json
=
{
"mailNum"
:
tracking_no
}
)
return
self
.
_normalize_response
(
response
.
json
(
)
)
def
_normalize_response
(
self
,
raw
)
:
return
{
'status'
:
raw
[
'data'
]
[
'status'
]
,
'location'
:
raw
[
'data'
]
[
'currentAddress'
]
,
'timestamp'
:
raw
[
'data'
]
[
'updateTime'
]
,
'description'
:
raw
[
'data'
]
[
'desc'
]
}
上层代码只跟LogisticsClient交互,新增物流渠道只需加一个适配器实现类,不影响现有代码。
三、汇率换算的精度陷阱
跨境代购涉及多币种结算——用户付美元或欧元,我们在国内付人民币。汇率换算看似简单,但坑在精度和时效性。
一开始我们用浮点数计算,结果出现0.01美元的差异,用户投诉。后来改用Decimal类型,并且引入了规则引擎来处理复杂的计费场景:
from
decimal
import
Decimal
,
getcontext
getcontext
(
)
.
prec
=
10
class
CurrencyConverter
:
def
__init__
(
self
)
:
self
.
rates
=
{
}
# 从外汇API定时更新
def
convert
(
self
,
amount
,
from_currency
,
to_currency
)
:
if
from_currency
==
to_currency
:
return
Decimal
(
str
(
amount
)
)
rate
=
self
.
rates
.
get
(
f"
{
from_currency
}
_
{
to_currency
}
"
)
if
not
rate
:
# 通过中间货币(USD)换算
rate
=
self
.
_get_cross_rate
(
from_currency
,
to_currency
)
result
=
Decimal
(
str
(
amount
)
)
*
Decimal
(
str
(
rate
)
)
# 保留两位小数,四舍五入
return
result
.
quantize
(
Decimal
(
'0.01'
)
)
四、关于Taocarts的一些实用思路
在解决上述问题的过程中,Taocarts的几个设计思路给了我不少启发:
事件驱动架构:订单状态变更、物流节点更新等场景通过事件总线发布,各模块订阅后更新本地状态,避免了模块间的紧耦合。
规则引擎替代硬编码:运费计算从if-else迁移到规则配置,每次新增物流渠道不需要改代码发版。
多账号轮询:单个淘宝账号单日下单超20单容易被风控,需要开发多账号轮询+订单拆分功能。
这些思路不复杂,但确实能帮代购系统开发者少走很多弯路。