酒店

订单增量更新时间:2019/11/07 11:08

涉及接口 hotel.incr.order、hotel.order.detail
demo语言: Java
开发环境:
后端  jdk1.7以上、 tomcat8以上、spring + mybatis


非主线程异步轮询调用订单增量接口获取增量,根据增量修改订单表


增量任务类

定义task继承Thread类或实现Runnable接口,实现InitializingBean用以spring容器初始化该任务时可以调用相关方法
@Service
public class IncrOrderTask extends Thread implements InitializingBean {
    @Autowired
    private IIncrOrderManagerDao incrOrderManagerDao;  // 订单增量管理表访问Dao对象
    
    @Autowired
    private IOrderDao orderDao;            // 订单访问Dao对象
    
    @Autowired
    private HotelIncrOrderApi incrOrderApi;    // 订单增量api调用对象
    
    @Autowired
    private HotelOrderDetailApi hotelOrderDetailApi; // 订单详情访问api对象
    ...
    ...
    // 重写Thread的run    
    public void run() {
        while (true) {
            readTask();
        }
    }
    ...
    ...
    // 初始化bean之后,自动执行此方法,开始执行轮询任务
    @Override
    public void afterPropertiesSet() throws Exception {
        this.start();
    }
}


任务实现1

具体增量轮询任务实现, 如果应用部署在单台机器,则只需在新线程进行拉取增量,更新数据。
@Transactional
public void readTask() {
    try {
        updateOrder(managerDo);
        sleep(CommonService.INCR_ORDER_FREQUENCY);      
    } catch (Exception e) {
        // 吃掉异常,避免影响正常业务
        logger.error(e);
    }
}


任务实现2

具体增量轮询任务实现, 如果应用部署在多台机器,为了保证增量在单线程拉取,则需要抢占执行
@Transactional
public void readTask() {
    try {
        IncrOrderManagerDo managerDo = incrOrderManagerDao.getManagerInfo();
        // 首先判断是否要抢占更新任务资源
        // mac地址为空 或者 心跳时间为1970-01-01 或者 心跳时间超过设置的更新频率
        // 间隔的四倍并且在5分钟以上 则认为原有的更新任务死掉了 需要抢占
        if (StringUtils.isBlank(managerDo.getMacAddr())
            || managerDo.getBeatTime().getTime() == -28800000
            || ((new Date().getTime() - managerDo.getBeatTime()
                .getTime()) / CommonService.INCR_ORDER_FREQUENCY > 4
            && (new Date().getTime() - managerDo
                .getBeatTime().getTime()) / 60000 > 5)) {
            // 此时需要抢占
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("newMacAddr", CommonService.MAC_ADDR);
            params.put("beatTime", new Date());
            params.put("oldMacAddr", managerDo.getMacAddr());
            incrOrderManagerDao.updateMacAddr(params);
        } else {
            // 此时不需要抢占 如果mac地址是本机地址,那么执行更新任务,否则睡眠等待
            if (managerDo.getMacAddr().equals(CommonService.MAC_ADDR)) {
                updateOrder(managerDo);
                sleep(CommonService.INCR_ORDER_FREQUENCY);
            } else {
                sleep(noTaskSleep);
            }
        }
    } catch (Exception e) {
        // 吃掉异常,避免影响正常业务
        logger.error(e);
    }
}


增量api

调用艺龙订单增量api接口
public class HotelOrderIncrApi {
    private static double version = 1.50;
    private static EnumLocal locale = EnumLocal.zh_CN;
    private static boolean isHttps = true; // 测试环境为false,使用http
    ...
    ...
    
    /**
     * 通过http工具类调用艺龙接口,将前端参数拼接为URL
     */
    public OrderIncrResult getStateIncr(GetOrderIncrRequest condition,
        String userName, String appKey, String secretKey) {
        String url = "";
        String responseData = "";
        OrderIncrResult result = new OrderIncrResult();
        try{
            BaseRequest<GetOrderIncrRequest> req = new BaseRequest<GetOrderIncrRequest>();
            req.Version = version;
            req.Local = locale;
            req.Request = condition;
    
            //请求参数转换为Json字符串
            String str = JsonUtil.entity2Json(req);
            
            //产生签名
            long epoch = System.currentTimeMillis()/1000;
            String sig = Tool.md5(epoch + Tool.md5(str + appKey) + secretKey);
            
            //产生请求链接
            url = "http"+(this.isHttps ? "s": "")+ "://" 
                + serverHost + "/rest?format=json&method=hotel.incr.order";
            url += "&user="+ userName +"&timestamp=";
            url += epoch;
            url += "&signature=";
            url += sig;
            url += "&data=" + Tool.encodeUri(str);
            
            //发送请求
            responseData = HttpUtil.Send("GET", url, "", "application/json");
            
            //返回值处理
            result = JsonUtil.jsonToObject(responseData, OrderIncrResult.class);
        } catch (Exception e) {
            log.info("[HotelOrderIncrApi] 异常: " + e);
            log.info("发送的url: " + url);
            log.info("返回值: " + responseData);
        }
        return result;
    }

    ...
    ...
}



更新订单信息

如果有增量,则先请求订单详情接口,根据订单详情更新本地订单信息
private void updateOrder(IncrOrderManagerDo managerDo) {
    // 先调用订单详情接口
    IncrCondition condition = new IncrCondition();
    condition.setLastId(managerDo.getLastId());
    OrderIncrResult incrResult = null;
    try {
        incrResult = incrOrderApi.Invoke(condition);
    } catch (Exception e) {
        logger.error(e);
    }
    // 更新订单
    if (incrResult != null && incrResult.getCode().equals("0")
        && incrResult.getResult() != null 
        && incrResult.getResult().getOrders() != null
        && incrResult.getResult().getOrders().size() > 0) {
        List<Order> orderList = incrResult.getResult().getOrders();
        for (int i = 0; i< orderList.size(); i++) {
            Order order = orderList.get(i);
            // 创建数据库对象
            OrderDo orderDo = new OrderDo();
            orderDo.setOrderId(order.getOrderId());
            orderDo.setStatus(order.getStatus());
            orderDo.setArrivalDate(order.getArrivalDate());
            orderDo.setDepartureDate(order.getDepartureDate());
            orderDo.setAffiliateConfirmationId(order.getAffiliateConfirmationId());
            orderDo.setTotalPrice(order.getTotalPrice().doubleValue());
            orderDo.setRoomNum(order.getNumberOfRooms());
            orderDo.setPayStatus(order.getPayStatus());
            ...
            ...
     ...       
}