20261016 从定时任务到主明细入库:Spring Boot 对接亚马逊订单的数据同步设计

20261016 从定时任务到主明细入库:Spring Boot 对接亚马逊订单的数据同步设计

对接亚马逊订单,最容易想到的实现是:定时调用订单接口,把返回结果写进数据库。

但订单会持续变化。一笔订单可能先处于待处理状态,随后变为已发货,也可能被取消;订单列表与商品明细需要分别获取,两类接口还可能分页。只完成一次请求,并不代表一笔订单已经同步完整。

本文结合一个 Spring Boot 项目的实际源码,梳理一套由 XXL-JOB 驱动的订单同步方案:先生成时间窗口任务,再拉取订单主单到暂存表,最后逐单获取商品明细,批量写入正式表。

一、利用XXLJOB来定时触发拉去订单

它使用的核心接口是:

接口 获取内容 在项目中的用途
getOrders 指定条件下的订单列表 按创建时间或最后更新时间采集主单
getOrderItems 指定订单的商品列表 补齐 SKU、数量、价格、税费、促销等明细
getOrder 单笔订单 指定订单补拉

Agent 层还封装了 getOrderAddress,但在本文分析的 Job 主流程中,没有发现对该方法的调用。地址主要取自订单响应中的 ShippingAddress,响应未提供的字段不会凭空补齐。

这里的"先列表、后明细"是两次不同的接口调用:getOrders 获取订单头信息,getOrderItems 获取订单商品。不能将它理解成只对订单列表返回的 JSON 进行本地拆表。

二、为什么分成三个阶段

订单同步被拆成三个职责:

对应的主要 XXL-JOB 入口是:

阶段 按更新时间处理 按创建时间处理
生成窗口任务 initOrderJob initOrderJobByCreateTime
拉取订单主单 syncOrderJob syncOrderJobByCreateTime
拉取商品明细 syncOrderDetailJob 共用同一套明细流程

这些是独立入口。源码声明了它们可以被调度,并不意味着一次调用就自动串行执行三个 Job。

拆分后的价值在于:主单采集不必等待所有订单商品查询完成,明细采集也有明确的待处理数据来源。主单已经落入暂存表后,后续处理可以继续从数据库推进。

三、六张表如何协作

表 数据粒度 主要职责
amzn_job_sync_time 卖家与业务类型的进度记录 保存时间窗口生成到哪里
amzn_order_job 一条时间窗口任务 保存查询范围、分页 token 和任务状态
amzn_order_stash 一次采集得到的单笔订单快照 暂存主单,等待商品明细采集
amzn_order 一笔正式订单 保存订单当前业务数据
amzn_order_item 一条订单商品 保存数量、价格、税费及商品编码
amzn_order_address 订单对应的地址记录 保存响应中已有的地址信息

另外,amzn_api_logs 保存接口请求与响应,用于排查调用问题。它与订单暂存表不是同一种数据:暂存表保存结构化订单字段,日志表保存调用记录。

整体流程如下:

bash 复制代码
flowchart TD
    A["同步进度表"] --> B["生成时间窗口任务"]
    B --> C["amzn_order_job"]
    C --> D["getOrders 分页查询"]
    D --> E["amzn_order_stash"]
    D --> F["amzn_order_address"]
    E --> G["getOrderItems 逐单分页"]
    G --> H["amzn_order"]
    G --> I["amzn_order_item"]
    G --> J["暂存状态更新为 DONE"]

四、任务生成:将时间进度变为可执行窗口

OrderJobHandler 获取卖家及其站点后,通过 CycleTimeService 查询 amzn_job_sync_time。

该表使用两种业务类型,分别维护进度:

  • • ORDER_BY_UPDATETIME:按订单最后更新时间查询。
  • • ORDER_BY_CREATETIME:按订单创建时间查询。

两者的意义不同。创建时间用于发现相应时间内创建的订单,更新时间用于发现已经存在但后来发生变化的订单。只查询新创建的订单,无法持续刷新旧订单的后续状态。

源码中的时间规则如下:

规则 按更新时间 按创建时间
查询截止时间 当前时间减 5 分钟 当前时间减 1 小时
首次初始化的默认范围 截止时间往前 7 天 截止时间往前 7 天
根据已有进度生成窗口时 开始时间再往前回退 1 小时 开始时间再往前回退 1 分钟
积压范围的拆分周期 7 天 7 天

例如,更新时间进度停在 10:00,本次截止时间是 10:25,则生成的实际查询范围是 09:00 至 10:25。回退一小时使相邻窗口存在重叠,后续通过订单更新时间判断是否需要重新处理。

这些时间是我们项目配置逻辑,不是所有订单系统都必须遵循的通用参数。

生成的任务写入 amzn_order_job,初始状态为 NEW,next_token 为空,记录卖家、站点、任务类型和起止时间。

五、主单采集:分页结果先进入暂存表

OrderSyncHandler 读取待执行的窗口任务,再由 OrderJobService.syncOrder 执行。

任务按卖家分组,卖家之间通过线程池并行,同一分组内顺序处理窗口任务。

按更新时间的任务最终调用 getOrders 的更新时间条件;按创建时间的任务则填入创建时间条件。远程调用经过:

Job 服务的 Feign 客户端 → Gateway → Agent SP → 亚马逊 Orders API。

Agent 复用 ApiClientFactory 获取卖家对应的 ApiClient,执行鉴权后的 SDK 调用。

每得到一页订单,代码依次转换订单字段、写入暂存表、保存已有地址,再更新窗口任务状态。

job_status 含义
NEW 尚未完成采集
HAS_NEXT 已处理部分页面,还有下一页
DONE 窗口内的订单列表采集结束
ERROR 调用返回失败

有下一页时,将返回的 token 保存到任务记录;后续可以从该分页位置继续。这个 DONE 仅说明订单列表采集结束,不能据此判断商品明细已经获取完整。

六、暂存表:决定哪些订单需要继续拉明细

OrderService.saveDataToStash 并不是无条件触发商品查询。

它先根据"卖家 + 站点 + 亚马逊订单号"查正式表,再比较订单最后更新时间:

正式表情况 新暂存记录的状态
正式表没有这笔订单 NEW
本次订单的更新时间更新 NEW
正式表更新时间大于等于本次快照 NOT_DEAL

无论是否需要处理,源码都会新增一条暂存记录。因此,暂存表可以保留多次采集快照,而正式表用于保存业务数据。

例如,同一订单被重叠窗口再次查到,但更新时间没有变化,就将这次快照标为 NOT_DEAL,避免重复请求商品接口。若更新时间变新,则标为 NEW,交给明细任务处理。

这让 amzn_order_stash 同时承担两个作用:保存订单采集过程,以及充当数据库中的待处理队列。

七、明细采集:逐单查询商品,再推进正式表

syncOrderDetailJob 查询暂存表中 NEW 和 WAIT_UPDATE 的记录,按订单更新时间降序,单轮最多取 10,000 条。

记录按卖家分组后,每个分组再拆成每批 200 条。这里的 200 是本地处理批次,不代表一个亚马逊请求能获取 200 个订单的商品。

对每笔订单,代码调用 getOrderItems,并继续获取该订单商品列表的所有页面。

因此,系统存在两种不同的分页:

  • • 订单列表分页:一个时间范围内可能有很多订单。
  • • 商品列表分页:一个订单下可能有很多商品。

正常成功路径上,获取商品后才准备写入正式主表和明细表:

主单的业务匹配键是"卖家 + 站点 + 订单号",商品明细在此基础上增加 orderItemId。代码使用的是这些业务键,而非只按数据库自增或分布式 ID 判断是否存在。

批处理方法 syncOrdersDetailSplit 使用独立事务,将该批正式主单、商品明细和暂存状态一起写入。(这里有个考点 就是 this调用事务注释的问题)

八、入库不仅是字段复制,还包含业务加工(后续ERP系统要根据订单表的数据去推送金蝶)

商品明细入库前,系统使用"卖家 + 站点 + sellerSku"匹配内部商品映射,补充 sku_id、sku、sku_name 和 fn_sku。

匹配成功标记 FILL_SUCCESS,初次未匹配到则标记 WAIT_FILL,后续可由 fillSku 任务补充。这一步将平台商品编码连接到企业内部商品体系。

订单字段还包含项目自己的识别规则,例如:

  • • 根据 fulfillmentChannel 是否为 MFN,标记自配送订单。
  • • 根据订单号前缀,标记多渠道订单。
  • • 根据 sellerOrderId 的约定前缀,推断其他销售渠道。
  • • 根据促销标识,标记 Vine 商品。
  • • 根据国家时区配置,生成站点下单时间。

其中,前缀识别是项目业务约定,不应直接当作所有亚马逊订单都适用的平台规则。

九、如何判断"一笔订单同步完成"

这套设计里有三种进度,必须分开理解:

位置 回答的问题
amzn_job_sync_time 时间范围是否已经生成任务?
amzn_order_job.job_status 这个窗口的订单列表是否已经采集结束?
amzn_order_stash.pull_detail_status 这份订单快照是否已经完成后续处理?

例如,窗口任务已经 DONE,但暂存记录仍为 NEW,说明主单已经拿到,商品明细还在等待处理。

暂存记录为 NOT_DEAL,表示它在进入暂存表时,被判定为不需要重复处理;它不是一次新的商品查询成功记录。

正常成功路径中,暂存记录 DONE 表示已经准备将该订单主单及商品明细写入正式表。

相关推荐
Ysx1 小时前
比"本地全绿"更可怕的坑:"交付了"不等于"生效了"
java·后端
武子康1 小时前
ESP32-S3 分核后仍报 AFE 缓冲区满?先查输入合同
人工智能·后端·agent
Zheone1 小时前
《Java开发者视角:利用AI辅助审计Spring Boot接口越权漏洞的初步实践》
后端
god_cvz1 小时前
第三章:4、补充:对话记忆与会话管理
后端·ai编程
弈栈录1 小时前
用户、会话与消息表应该如何设计
数据库·后端
maozhenzhong1 小时前
接口加解密与安全通信方案
后端
god_cvz1 小时前
第三章:5、MCP协议入门
后端·ai编程
RaaS1002 小时前
AI网关选型指南:MAI Gateway(魔芋企业级AI网关)vs 开源方案全面对比
前端·后端
明月_清风2 小时前
ClickHouse 实战指南:为什么它能做到"亿级数据秒级查询"?
后端·数据分析