api.js 4.88 KB
/**
 * 接口公共方法
 * @author: xuqi<qi.xu@yoho.cn>
 * @date: 2016/4/25
 */

'use strict';

const rp = require('request-promise');
const qs = require('querystring');
const md5 = require('md5');
const _ = require('lodash');
const log = require('./logger');
const cache = require('./cache');
const Timer = require('./timer');
const config = require('../config/common');
const api = config.domains.api;
const serviceApi = config.domains.service;
const searchApi = config.domains.search;


class Http {

    constructor(baseUrl) {
        this.ApiUrl = baseUrl;
    }

    /**
     * 获取请求 ID
     */
    _getReqId(options) {
        return md5(`${options.url}?${qs.stringify(options.qs || options.form)}`);
    }

    /**
     * 调用接口
     */
    _requestFromAPI(options, cacheOption, reqId) {
        let timer = new Timer();
        let method = options.method || 'get';

        timer.put('getApi');// 统计时间开始

        log.info(`${method} api: ${options.url}?${qs.stringify(options.qs)}`);
        return rp(options).then((result) => {
            let duration = timer.put('getApi');// 统计时间结束

            log.info(`get api success: use: ${duration}ms`);
            if (config.useCache && cacheOption) {
                reqId = reqId || this._getReqId(options);

                // 数据校验无误,写缓存, 否则返回 Slave 缓存服务器的数据
                if (result && result.code) {
                    let cacheTime = _.isNumber(cacheOption) ? cacheOption : 60;

                    cache.set(`apiCache:${reqId}`, result, cacheTime);
                    cache.setSlave(`apiCache:${reqId}`, result, cacheTime);
                } else {
                    return this._requestFromCache(options, true);
                }
            }
            return result;
        }).catch((error)=> {
            let duration = timer.put('getApi');// 统计时间结束

            log.error(`${method} api fail: use: ${duration}ms, statusCode:
                ${error.statusCode}, error: ${error.message}`);

            // 使用缓存的时候,读取二级缓存
            if (config.useCache) {
                return this._requestFromCache(options, true);
            }
            return Promise.reject({
                error: '接口调用失败'
            });
        });
    }

    /**
     * 读取缓存
     * @param  {[object]} options
     * @param  {[boolean]} slave true: 读取二级缓存
     * @return {[type]}
     */
    _requestFromCache(options, slave) {
        let reqId = this._getReqId(options);
        let getCache = slave ? cache.getFromSlave : cache.get;

        log.info(`get cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`);
        return getCache(`apiCache:${reqId}`).then((result) => {
            if (!_.isNil(result)) {
                try {
                    result = JSON.parse(result);
                } finally {
                    log.info(slave ? 'get slave cache success' : 'get master cache success');
                    return result;
                }
            }

            if (!slave) {
                return this._requestFromAPI(options, true, reqId);
            }
        }).catch(() => {
            log.error(slave ? 'get slave cache fail' : 'get master cache fail');
            if (!slave) {
                return this._requestFromAPI(options, true, reqId);
            }
        });
    }

    /**
     * 使用 get 请求获取接口
     * @param  {[string]} url
     * @param  {[object]} data
     * @param  {[bool or number]} cacheOption 使用数字时,数字表示缓存时间
     * @return {[type]}
     */
    get(url, data, cacheOption) {
        let options = {
            url: `${this.ApiUrl}${url}`,
            qs: data,
            json: true,
            timeout: 3000
        };

        // console.log('in api : ' + config.useCache);

        // 从缓存获取数据
        if (config.useCache && cacheOption) {
            return this._requestFromCache(options);
        }

        return this._requestFromAPI(options, cacheOption);
    }

    /**
     * post
     * @param url String
     * @param data Obejct
     */
    post(url, data) {
        let options = {
            url: `${this.ApiUrl}${url}`,
            form: data,
            method: 'post',
            json: true,
            timeout: 3000
        };

        return this._requestFromAPI(options);
    }

    all(list) {
        if (_.isArray(list)) {
            return Promise.all(list);
        } else {
            return Promise.reject(Error('the parameters of api all method should be Array!'));
        }
    }
}

class API extends Http {
    constructor() {
        super(api);
    }
}

class ServiceAPI extends Http {
    constructor() {
        super(serviceApi);
    }
}

class SearchAPI extends Http {
    constructor() {
        super(searchApi);
    }
}

exports.API = API;
exports.ServiceAPI = ServiceAPI;
exports.SearchAPI = SearchAPI;