一、增量拉取的时间窗口设计
订单同步的典型策略是增量拉取。以小于科技的订单列表接口为例,filter 支持 create_time_start 和 create_time_end 两个时间戳参数。
实际编码时有几个直接影响数据准确性的细节:
时间窗口留缓冲。create_time_end 取当前时间减 60 秒,而非当前时刻。原因在于接口数据可能存在秒级延迟,若以“当前时刻”作为窗口终点,边界订单可能在下次拉取时被遗漏。
分页循环。单次请求的 pageSize 通常有上限,订单量大的店铺需要循环翻页。建议按时间窗口分批拉取,避免单次请求数据量过大导致超时。
import requests
import time
def fetch_orders(app_id, app_secret, shop_id, last_sync_time, page=1, page_size=100):
url = "https://${host_prefix}/api/doudian/order/searchlist"
end_time = int(time.time()) - 60 # 留 60 秒缓冲
payload = {
"appId": app_id,
"appSecret": app_secret,
"platformShopId": shop_id,
"pageIndex": page,
"pageSize": page_size,
"filter": {
"create_time_start": str(last_sync_time),
"create_time_end": str(end_time),
"order_by": "create_time",
"sort": "asc"
}
}
resp = requests.post(url, json=payload, timeout=30)
return resp.json()
二、幂等入库与状态映射
幂等判断是订单同步的基本要求。增量拉取不可避免地会出现时间窗口重叠,没有幂等机制会导致重复入库。以 order_id 作为唯一键,写入前先查库判断是否已存在。
状态映射需要兜底处理。抖店订单状态(unpaid/stock_up/on_delivery/received/closed)与内部系统的状态并非一一对应。需要维护一张映射表,并对未知状态做兜底处理——例如记录告警而非直接入库异常状态。
STATUS_MAP = {
"unpaid": "待支付",
"stock_up": "待发货",
"on_delivery": "已发货",
"received": "已完成",
"closed": "已关闭"
}
def map_status(doudian_status):
return STATUS_MAP.get(doudian_status, f"未知状态:{doudian_status}")
对于状态映射中未覆盖的值,建议记录到异常队列,由人工确认后再决定如何处理,避免因状态枚举变更导致数据丢失。
三、同步频率与补偿策略
不同数据类型的同步频率应有差异:
| 数据类型 | 同步方式 | 频率建议 |
|---|---|---|
| 订单 | 增量 | 每 10 分钟一次 |
| 售后 | 增量 | 每 10~30 分钟一次 |
| 商品 | 全量 + 增量 | 首次全量,后续按更新时间增量 |
订单和售后数据在增量拉取时,需要保证及时性;商品变更频率低,全量拉取后按更新时间增量即可。异常补偿定时任务建议每小时执行一次,处理失败队列中的积压数据。
四、完整调用示例
将上述逻辑整合,一个完整的增量同步循环如下:
def sync_orders_incremental(app_id, app_secret, shop_id, last_sync_time):
page = 1
all_orders = []
while True:
result = fetch_orders(app_id, app_secret, shop_id,
last_sync_time, page=page)
rows = result.get("data", {
}).get("rows", [])
if not rows:
break
for order in rows:
order_id = order["order_id"]
if not order_exists(order_id): # 幂等判断
process_order(order) # 写入内部系统
all_orders.append(order)
if len(rows) < 100: # 不足一页,说明已拉完
break
page += 1
return all_orders
工程实现的核心原则是:时间窗口宁可重叠不可遗漏,写入操作必须幂等,状态映射需要兜底。