淘宝订单同步方案 - 丢单终结者

订单管理是很多卖家工具的必备功能之一,而订单同步则是订单管理中的数据来源,如何保证订单同步的实时、高效、低碳和不丢单是非常重要的事情。

订单同步接口
1.    taobao.trades.sold.get,根据订单创建时间查询3个月内已卖出的订单。
2.    taobao.trades.sold.increment.get,根据订单修改时间查询1天内的增量订单。
3.    taobao.trade.fullinfo.get,根据订单ID查询订单的详细信息。

丢单原因分析
一、没有检查订单同步接口的返回值是否成功。
二、只使用taobao.trades.sold.get同步订单,此接口是按照订单创建时间查询的,一个订单创建后何时被修改(付款、发货、确认收货)是不确定的,所以采用这种方案无法确定该同步哪个时段内的订单,除非你每次都同步3个月内的订单(严重浪费资源,应该没人会这么做),否则不管选择什么时段同步都有丢单的可能。
三、没有记录每次订单同步成功后的时间点。比如每10分钟增量同步一次订单,如果系统恰好在某个同步时刻出现异常,则这次的同步就有可能被中止。
四、整点误差(时/分/秒)。比如每10分钟增量同步一次订单:第一次同步00:00:00 ~ 00:10:00时段的订单,第二次同步00:10:01 ~ 00:20:00时段的订单。这种方式就有可能丢失00:10:00的一部分订单,特别是店铺参加聚划算活动时更容易出现。
五、按状态同步订单,这种方式的问题在于订单状态过多,有可能会出现状态遗漏,而且性能低效。

推荐同步方案
同步流程图

流程图解释
1.    用户第一次登录时使用taobao.trades.sold.get同步3个月内的订单,并把用户登录的时间做为之后增量同步的时间起点。
2.    同时后台启动定时任务进行增量订单同步,根据店铺订单量的不同和客户来访时间,可设置不同的同步频率,每次增量同步完毕后,需要把增量同步的时间点记录下来,以做为下次增量同步的起点。

订单同步技巧
1.    使用taobao.trades.sold.get同步3个月内的订单时,最好把3个月分隔成若干个时段来查询,否则很容易出现超时。由于订单的创建时间不会变化,所以分页时从前翻页还是从后面翻页都无所谓(前提是翻页的过程中不能改变查询时间)。
2.    使用taobao.trades.sold.increment.get增量同步订单时,查询到的订单是按修改时间倒序返回的,所以分页时必须从最后一页开始翻页,否则有可能出现丢单。这是因为如果从第一页开始翻页,则翻页过程中发生变更的订单就会减少订单总数,使翻页出现误差。
3.    使用taobao.trades.sold.increment.get增量同步订单时,可以先通过只查询tid字段得到指定时段的订单总数,然后计算出分页数,后继采用倒序翻页时,设置use_has_next=true可以禁止API接口去统计订单总数,减少每次查询时都做统计的开销,可以大大提高查询性能。
4.    根据订单量的不同,需要采用不同的同步时段。对于日均订单量在1000左右的店铺,如果设置每页查询50条记录,每10分钟同步一次,则每次同步基本上只需要一次分页查询就能完成同步。
5.    时刻记录每次成功同步的时间点(比如存储到数据库中),避免重复劳动。
6.    对于用户量较大,实时性要求较高的应用,最好采用多线程同步的方式。可建立一个固定大小的线程池(需要根据硬件条件和网络状况不同设置不同的线程池大小),为每个用户启动一个线程去同步订单。
7.    由于API调用是有频率限制的,采用多线程同步订单时,有可能需要每次API调用后做一些短暂的停顿,以免调用超频,造成长时间不可访问API。
8.    如果批量订单查询返回的数据不够,需要通过订单详情接口获取时,强烈推荐批量查询订单时,只查询tid字段,然后通过taobao.trade.fullinfo.get查询订单详情。
9.    使用taobao.time.get获取的时间作为当前时间。否则,如果ISV服务器的时间比淘宝服务器的时间快,则有可能提前同步订单导致丢单。
10.    使用taobao.trades.sold.increment.get接口时,设置的查询时间段返回的总记录数最好不要超过2万,否则很容易发生超时。

特别提醒:针对光棍节大促,由于订单量很大,如果使用倒序需要返回total_results,建议大商家抓单时间间隔设置小于5分钟,使每次抓单尽量不要超过2万单,避免数目过多导致的性能和超时问题。

附JAVA示例代码:http://tbtop.googlecode.com/files/TradeSync.java

package com.taobao.top.tool;import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;import com.taobao.api.ApiException;
import com.taobao.api.DefaultTaobaoClient;
import com.taobao.api.TaobaoClient;
import com.taobao.api.domain.Trade;
import com.taobao.api.request.TimeGetRequest;
import com.taobao.api.request.TradeFullinfoGetRequest;
import com.taobao.api.request.TradesSoldGetRequest;
import com.taobao.api.request.TradesSoldIncrementGetRequest;
import com.taobao.api.response.TimeGetResponse;
import com.taobao.api.response.TradeFullinfoGetResponse;
import com.taobao.api.response.TradesSoldGetResponse;
import com.taobao.api.response.TradesSoldIncrementGetResponse;
import com.taobao.top.util.TestData;public class TradeSync {private static final Log log = LogFactory.getLog(TradeSync.class);private static final String TOP_URL = TestData.ONLINE_SERVER_URL;private static final String APP_KEY = TestData.TEST_APP_KEY;private static final String APP_SECRET = TestData.TEST_APP_SECRET;private static final ExecutorService threadPool = Executors.newFixedThreadPool(12);private static final TaobaoClient client = new DefaultTaobaoClient(TOP_URL, APP_KEY, APP_SECRET);public static void main(String[] args) throws Exception {// 新用户登录后调用此方法// getLast3MonthSoldTrades(null);// 系统启动后创建此定时任务Timer timer = new Timer();timer.schedule(new TimerTask() {public void run() {// 每个卖家启动一个线程去同步增量订单final Date end = getTaobaoTime();List<UserInfo> users = getUsersFromDB();for (final UserInfo user : users) {final Date start = user.getLastSyncTime();threadPool.submit(new Runnable() {public void run() {try {getIncrementSoldTradesByPeriod(start, end, user.getSessionKey());user.setLastSyncTime(end);updateUserToDB(user);} catch (ApiException e) {log.error("同步" + user.getUserId() + "的增量订单失败:" + start + "-" + end, e);}}});}}}, 0, 1 * 60 * 1000L); // 每10分钟增量同步一次Thread.sleep(100000);}private static List<UserInfo> getUsersFromDB() {// TODO 从数据库中查询已授权的用户信息List<UserInfo> users = new ArrayList<UserInfo>();UserInfo user = new UserInfo();user.setUserId(123456789L);user.setSessionKey("410253676dfef08550cce6f76ac549da2e2a5679429OOd5HfMv88371");users.add(user);return users;}private static void updateUserToDB(UserInfo user) {// TODO 保存更新后的用户信息到数据库}/*** 新用户登录后调用:同步三个月内的订单。*/public static void getLast3MonthSoldTrades(final UserInfo user) {Date end = getTaobaoTime();Date start = addMonths(end, -3); // 最多只能查询3个月内的订单// 切隔时间(公式为:24*每页记录数[推荐50]/日均订单量),如日均订单量为100的店铺,可按每24*50/100=12小时切割一段List<Date[]> dateList = splitTimeByHours(start, end, 24);for (final Date[] dates : dateList) {// 由于3个月的订单数量较大,建议采用多线程的方式同步,但是要注意APP的调用频率threadPool.submit(new Runnable() {public void run() {try {getSoldTradesByPeriod(dates[0], dates[1], user.getSessionKey());} catch (ApiException e) {log.error("同步" + user.getUserId() + "的已卖出订单失败:" + dates[0] + "-" + dates[1], e);}}});}// 把获取3个月内已卖出订单的结束时间做为下次增量订单同步的开始时间user.setLastSyncTime(end);updateUserToDB(user);}private static void getSoldTradesByPeriod(Date start, Date end, String sessionKey) throws ApiException {TradesSoldGetRequest req = new TradesSoldGetRequest();req.setFields("tid");req.setStartCreated(start);req.setEndCreated(end);req.setPageNo(1L);req.setPageSize(50L);long pageCount = 0L;TradesSoldGetResponse rsp = null;do {rsp = client.execute(req, sessionKey);log.info(rsp.getTotalResults() + "=>>" + req.getPageNo());if (rsp.isSuccess()) {for (Trade t : rsp.getTrades()) {Trade trade = getTradeFullInfo(t.getTid(), sessionKey);if (trade != null) {// TODO 更新订单数据到本地数据库}}req.setPageNo(req.getPageNo() + 1);pageCount = getPageCount(rsp.getTotalResults(), req.getPageSize());} else {// 错误响应直接重试}} while (req.getPageNo() <= pageCount);}/*** 后台线程定时调用:增量同步订单。*/public static void getIncrementSoldTradesByPeriod(Date start, Date end, String sessionKey) throws ApiException {TradesSoldIncrementGetRequest req = new TradesSoldIncrementGetRequest();req.setFields("tid");req.setStartModified(start);req.setEndModified(end);req.setPageNo(1L);req.setPageSize(50L);TradesSoldIncrementGetResponse rsp = client.execute(req, sessionKey);if (rsp.isSuccess()) {long pageCount = getPageCount(rsp.getTotalResults(), req.getPageSize());while (pageCount > 0) {req.setPageNo(pageCount);req.setUseHasNext(true);rsp = client.execute(req, sessionKey);if (rsp.isSuccess()) {log.info(rsp.getTotalResults() + " >> " + req.getPageNo());for (Trade t : rsp.getTrades()) {Trade trade = getTradeFullInfo(t.getTid(), sessionKey);if (trade != null) {// TODO 更新订单数据到本地数据库}}pageCount--;} else {// 错误响应直接重试}}} else {getIncrementSoldTradesByPeriod(start, end, sessionKey);}}private static Trade getTradeFullInfo(Long tid, String sessionKey) throws ApiException {TradeFullinfoGetRequest req = new TradeFullinfoGetRequest();req.setFields("tid,buyer_nick,seller_nick,status,payment,created");req.setTid(tid);TradeFullinfoGetResponse rsp = client.execute(req, sessionKey);if (rsp.isSuccess()) {return rsp.getTrade();} else {// API服务不可用或超时,则重试if ("520".equals(rsp.getErrorCode())) {return getTradeFullInfo(tid, sessionKey);} else {log.error("查询订单详情失败:" + tid);return null;}}}/*** 获取淘宝服务器时间作为当前时间,避免部分ISV机器时间提前时导致同步漏单现象。*/private static Date getTaobaoTime() {TimeGetRequest req = new TimeGetRequest();try {TimeGetResponse rsp = client.execute(req);if (rsp.isSuccess()) {return rsp.getTime();}} catch (ApiException e) {}return new Date();}private static List<Date[]> splitTimeByHours(Date start, Date end, int hours) {List<Date[]> dl = new ArrayList<Date[]>();while (start.compareTo(end) < 0) {Date _end = addHours(start, hours);if (_end.compareTo(end) > 0) {_end = end;}Date[] dates = new Date[] { (Date) start.clone(), (Date) _end.clone() };dl.add(dates);start = _end;}return dl;}private static long getPageCount(long totalCount, long pageSize) {return (totalCount + pageSize - 1) / pageSize;}private static Date addMonths(Date date, int amount) {Calendar c = Calendar.getInstance();c.setTime(date);c.add(Calendar.MONTH, amount);return c.getTime();}private static Date addHours(Date date, int amount) {Calendar c = Calendar.getInstance();c.setTime(date);c.add(Calendar.HOUR_OF_DAY, amount);return c.getTime();}
}class UserInfo {private Long userId; // 用户IDprivate String sessionKey; // 访问授权码private Date lastSyncTime; // 上次增量订单同步时间public Long getUserId() {return this.userId;}public void setUserId(Long userId) {this.userId = userId;}public String getSessionKey() {return this.sessionKey;}public void setSessionKey(String sessionKey) {this.sessionKey = sessionKey;}public Date getLastSyncTime() {return this.lastSyncTime;}public void setLastSyncTime(Date lastSyncTime) {this.lastSyncTime = lastSyncTime;}
}

淘宝订单同步方案 - 丢单终结者相关推荐

  1. 淘宝服务市场 淘宝订单同步方案 - 丢单终结者

    淘宝订单同步方案 - 丢单终结者 订单管理是很多卖家工具的必备功能之一,而订单同步则是订单管理中的数据来源,如何保证订单同步的实时.高效.低碳和不丢单是非常重要的事情. 订单同步接口 1.    ta ...

  2. 淘宝订单API-获取订单详情接口应用

    项目背景 最近做一个电子商务平台的投标工作,写技术标过程中,配到客户一些和淘宝集成的接口,其中有一个需求就是需要将目前ERP系统中的定的那和淘宝店铺中订单进行同步,具体需求如下描述: 1.零售.批销. ...

  3. 获取淘宝订单的解决方案

    项目需求: 需求很简单,就是想获取淘宝的订单: 获取淘宝订单的几种方式: 聚石塔: 首先是该商家必须已经入驻了聚石塔,因为聚石塔可以共享改商家的淘宝.天猫.阿里云.支付宝等信息.所以你可以通过该商家的 ...

  4. 淘宝订单接口|订单插旗备注,淘宝开放平台最稳定的店铺订单接口

    使用淘宝订单接口,可以对订单数据进行同步.同步后,可以做订单金额.订单状态等信息的同步. 服务器集群,R2资质. 在官方基础上接口做了二次封装,完善的API文档,一般团队1天内即可完成从开发接入的完整 ...

  5. taobao.logistics.dummy.send( 无需物流发货处理 )接口,淘宝店铺发货API接口,淘宝r2接口,淘宝oAu2.0接口,淘宝订单发货接口

    taobao.logistics.dummy.send( 无需物流发货处理 )接口,淘宝店铺发货API接口,淘宝r2接口,淘宝oAu2.0接口,淘宝订单发货接口,接口可适用于,店铺发货,店铺订单同步, ...

  6. C#编写Windows服务程序 (服务端),客户端使用 消息队列 实现淘宝 订单全链路效果

    需求: 针对 淘宝提出的 订单全链路 产品接入 .http://open.taobao.com/doc/detail.htm?id=102423&qq-pf-to=pcqq.group oms ...

  7. C#编写Windows服务程序 (服务端),client使用 消息队列 实现淘宝 订单全链路效果

    需求: 针对 淘宝提出的 订单全链路 产品接入 .http://open.taobao.com/doc/detail.htm?id=102423&qq-pf-to=pcqq.group oms ...

  8. taobao.logistics.dummy.send( 无需物流发货处理 )接口,淘宝r2接口,淘宝oAu2.0接口,淘宝订单发货接口

    taobao.logistics.dummy.send( 无需物流发货处理 )接口,淘宝r2接口,淘宝oAu2.0接口,淘宝订单物流接口,接口可以用于店铺订单同步,ERP系统,订单推送,店铺上传商品等 ...

  9. 弘辽科技:淘宝订单编号会透露个人信息吗?淘宝如何查看订单编号

    在淘宝平台上每成交一个订单都会有对应的订单信息和订单编号,那么有一些朋友在淘宝平台上下单购物的时候,有非常强的安全意识,所以想要确认一个问题就是这个淘宝订单编号会透露个人信息吗? 会. 淘宝订单编号即 ...

最新文章

  1. 2017各银行贷款利息表及P2P平台贷款利率比较
  2. C++学习笔记(三)
  3. 最简单的6种防止数据重复提交的方法!(干货)
  4. easyx鼠标放置前按钮颜色_七种正确使用鼠标的好习惯,让你摆脱鼠标手的痛苦...
  5. linux系统硬盘数量,Linux ext4文件系统划分磁盘inode数量
  6. NumPy Cookbook 带注释源码 十一、NumPy 的底牌
  7. HDFS文件权限不足导致Sqoop执行失败
  8. EDA技术实用教程 | 复习一 | IP核的概念和分类
  9. 从CVPR2019 看计算机视觉最新趋势
  10. 计算机农业应用与3S技术论文,3S技术在精细农业中的应用实例分析
  11. rslogix5000pide实例_用RSLogix5000梯形图实现一种高级PID运算
  12. 普通类创建获取session 方式_博物馆类建筑的空调与通风工程设计(附图纸参考,要图的看文本获取方式)...
  13. 零基础学习 自动化编程- 第一天 计算机语言
  14. Scratch编程与数学:会编程的孩子是怎样解奥数题的?
  15. word2010怎样显示分节符?
  16. char可以是负数吗
  17. 域名whois查询接口代码
  18. 梦断代码读后感 (一)
  19. 视觉SLAM小知识——叉乘的物理意义
  20. java 反射为何耗性能_Java反射的性能成本

热门文章

  1. C#获取字符串的拼音和首字母
  2. PDF用什么软件可以修改
  3. matlab中regress函数教程,regress()函数
  4. 截取Chrome下载的mp3
  5. 【它山之石,可以攻玉】关于求职(实习)面试经验(2)
  6. Meteor android apk打包两种方式
  7. 上下文无关文法的分析树(Context-Free Grammar, CFG)的分析树--编译原理
  8. 【polar】协作polar码和非协作polar码的误码率性能matlab仿真
  9. 计算机里设备和驱动器下面有个没有名字的文件夹怎么删除它
  10. java 公式计算_java 实现的公式计算