项目背景
公司电商平台需要江订单数据下载到本地数据库,让后将对订单进行拆分转化为公司内部的产品组合。实现电商平台订单转移到公司内部平台进行处理。由于淘宝API依赖于网络,存在着网络不稳定和同步时间长的问题,所以应用必须把淘宝的订单数据同步到本地。如何才能 快速、完整的把订单同步到本地是本方案将要讨论的问题。
要点
同步订单:同步最近三个月的订单
增量同步:对于已经同步的订单,发生修改或者新增的订单就是增量订单
淘宝API
taobao.trades.sold.get
获取当前商家已经卖出的商品订单数据(最近三个月),返回的顺序是订单创建时间的倒序排列,包含了订单的部分数据,详细叙述需要taobao.trade.fullinfo.get获取订单详情
taobao.trades.sold.increment.get
查询卖家已卖出的增量交易数据(根据修改时间),一次请求只能查询时间跨度为一天的增量交易记录,即end_modified - start_modified <= 1天。 返回的数据结果是以订单的修改时间倒序排列的,通过从后往前翻页的方式可以避免漏单问题。返回的数据结果只包含了订单的部分数据,可通过taobao.trade.fullinfo.get获取订单详情。
taobao.trade.fullinfo.get
获取单笔交易的详细信息
解决方案
全量同步:同步最近三个月的订单数据
增量同步:同步时间段内修改的数据
初始化:通过taobao.trades.sold.get获取最近三个月的订单数据,再通过taobao.trade.fullinfo.get获取详细的订单数据,保存到本地
增量:通过定时轮询(10min)taobao.trades.sold.increment.get获取修改过的数据,再通过taobao.trade.fullinfo.get获取详细的订单数据保存到本地数据库
初步代码构建
1、创建配置类,配置APPKey和secert以及链接淘宝API的Client
import com.taobao.api.DefaultTaobaoClient;
import com.taobao.api.TaobaoClient;
public class TaobaoApiConfig {
public static final String APP_KEY = "your_app_key";
public static final String APP_SECRET = "your_app_secret";
public static TaobaoClient getTaobaoClient() {
return new DefaultTaobaoClient("https://eco.taobao.com/router/rest", APP_KEY, APP_SECRET);
}
}
2、获取Token
@Component
public class TaobaoAuthService {
public String getAccessToken(String code) throws ApiException {
TaobaoClient client = new DefaultTaobaoClient(URL, APP_KEY, APP_SECRET);
TopAuthTokenCreateRequest req = new TopAuthTokenCreateRequest();
req.setCode(code);
req.setGrantType("authorization_code");
TopAuthTokenCreateResponse rsp = client.execute(req);
return rsp.getToken().getAccessToken(); // 返回有效期为3小时的Token
}
}
3、记录每次增量的时间
public String getLastTime(){
}
4、增量同步
@Service
public class OrderSyncService {
@Autowired private RedisTemplate<String, String> redisTemplate;
@Autowired private TaobaoClient taobaoClient;
// 每10分钟执行增量同步
@Scheduled(fixedDelay = 10 * 60 * 1000)
public void syncIncrementalOrders() throws ApiException {
String shopId = "SHOP_123";
String sessionKey = getSessionKey(shopId); // 从DB获取店铺授权
// 1. 获取时间窗口(防漏单关键)
Date endModified = getTaobaoTime(); // 必须用淘宝服务器时间[1,2](@ref)
Date startModified = getLastSyncTime(shopId); // 从Redis读取上次同步时间
startModified = DateUtils.addMinutes(startModified, -5); // 时间前移5分钟,避免边界漏单[2](@ref)
// 2. 构建增量请求(仅拉取订单ID提升性能)
TradesSoldIncrementGetRequest req = new TradesSoldIncrementGetRequest();
req.setStartModified(startModified);
req.setEndModified(endModified);
req.setUseHasNext(true); // 禁用COUNT(*)优化性能[1](@ref)
req.setPageSize(100L);
req.setFields("tid"); // 只获取订单ID
// 3. 倒序分页处理(从最后一页向前翻)
boolean hasNext = true;
long pageNo = 1L;
List<String> orderIds = new ArrayList<>();
while (hasNext) {
req.setPageNo(pageNo);
TradesSoldIncrementGetResponse rsp = taobaoClient.execute(req, sessionKey);
// 收集当前页订单ID
orderIds.addAll(rsp.getTrades().stream()
.map(Trade::getTid)
.collect(Collectors.toList())
);
// 关键:计算下一页页码(倒序)
long totalPages = rsp.getTotalPages();
hasNext = rsp.getHasNext();
pageNo = totalPages - pageNo + 1; // 例:总5页时,第1页→第5页,第5页→第1页[1](@ref)
}
// 4. 批量获取订单详情(减少API调用)
batchFetchOrderDetails(orderIds, sessionKey);
// 5. 更新检查点(Redis记录本次同步结束时间)
redisTemplate.opsForValue().set("sync:taobao:" + shopId, endModified.toString());
}
// 批量获取订单详情(异步提升吞吐)
@Async("orderTaskExecutor")
public void batchFetchOrderDetails(List<String> orderIds, String sessionKey) {
for (String tid : orderIds) {
TradeFullinfoGetRequest detailReq = new TradeFullinfoGetRequest();
detailReq.setTid(Long.parseLong(tid));
TradeFullinfoGetResponse detailRsp = taobaoClient.execute(detailReq, sessionKey);
saveOrderToDB(detailRsp.getTrade()); // 存储到数据库
}
}
// 获取淘宝服务器时间(避免时差导致漏单)
private Date getTaobaoTime() throws ApiException {
TimeGetRequest req = new TimeGetRequest();
TimeGetResponse rsp = taobaoClient.execute(req);
return rsp.getTime();
}
}
注意
1、使用全量同步时,一次性同步三个月的订单数据可能会出现超时,可以通过按天来同步或者按照小时段来同步。
2、使用增量同步时,是按照修改时间倒序返回数据,所以分页时必须从最后一页开始翻页,否则有可能出现丢单。这是因为如果从第一页开始翻页,则翻页过程中发生变更的订单就会减少订单总数,使翻页出现误差。
3、记录上次的同步时间,防止重复劳动。