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

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

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

推荐同步方案
同步流程图


百度api接口 

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

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

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


附JAVA示例代码:lecode/files/TradeSync.java
package p.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 urrent.ExecutorService;
import urrent.Executors;
import org.apachemons.logging.Log;
import org.apachemons.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.quest.TimeGetRequest;
import com.quest.TradeFullinfoGetRequest;
import com.quest.TradesSoldGetRequest;
import com.quest.TradesSoldIncrementGetRequest;
import com.sponse.TimeGetResponse;
import com.taobao.api.response.TradeFullinfoGetResponse;
import com.sponse.TradesSoldGetResponse;
import com.sponse.TradesSoldIncrementGetResponse;
import p.util.TestData;
public class TradeSync {
    private static final Log log = Log(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 = wFixedThreadPool(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 = LastSyncTime();
                    threadPool.submit(new Runnable() {
                        public void run() {
                            try {
                                getIncrementSoldTradesByPeriod(start, end, SessionKey());
                                user.setLastSyncTime(end);
                                updateUserToDB(user);
                            } catch (ApiException e) {
                                ("同步" + UserId() + "的增量订单失败:" + 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) {