Authored by weiqingting

Merge branch 'develop' into feature/personal

@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 const utils = '../../../utils'; 8 const utils = '../../../utils';
9 const helpers = global.yoho.helpers; 9 const helpers = global.yoho.helpers;
10 const _ = require('lodash'); 10 const _ = require('lodash');
11 -const camelCase = require('../../../library/camel-case'); 11 +const camelCase = global.yoho.camelCase;
12 const productProcess = require(`${utils}/product-process`); 12 const productProcess = require(`${utils}/product-process`);
13 const url = require('url'); 13 const url = require('url');
14 const queryString = require('querystring'); 14 const queryString = require('querystring');
1 -/**  
2 - * 接口公共方法  
3 - * @author: xuqi<qi.xu@yoho.cn>  
4 - * @date: 2016/4/25  
5 - */  
6 -  
7 -'use strict';  
8 -const rp = require('request-promise');  
9 -const qs = require('querystring');  
10 -const md5 = require('md5');  
11 -const _ = require('lodash');  
12 -const log = require('./logger');  
13 -const cache = require('./cache');  
14 -const Timer = require('./timer');  
15 -const sign = require('./sign');  
16 -const config = require('../config/common');  
17 -const api = config.domains.api;  
18 -const serviceApi = config.domains.service;  
19 -const searchApi = config.domains.search;  
20 -  
21 -// 错误返回  
22 -const API_BAD_RETSULT = {  
23 - code: 500,  
24 - message: 'API result is not JSON string or null.'  
25 -};  
26 -  
27 -// 调用失败  
28 -const API_CALL_FAIL = {  
29 - code: 500,  
30 - message: 'Call API failed.'  
31 -};  
32 -  
33 -// all 方法错误的传参  
34 -const API_ALL_METHOD_ERROR = 'the parameters of api all method should be Array!';  
35 -  
36 -// 获取缓存数据失败  
37 -const SLAVE_CACHE_FAIL = 'get slave cache fail';  
38 -const MASTER_CACHE_FAIL = 'get master cache fail';  
39 -  
40 -// 获取缓存数据成功  
41 -const SLAVE_CACHE_SUCCESS = 'get slave cache success';  
42 -const MASTER_CACHE_SUCCESS = 'get master cache success';  
43 -  
44 -class Http {  
45 -  
46 - constructor(baseUrl) {  
47 - this.ApiUrl = baseUrl;  
48 - }  
49 -  
50 - /**  
51 - * 获取请求 ID  
52 - */  
53 - _getReqId(options) {  
54 - return md5(`${options.url}?${qs.stringify(options.qs || options.form)}`);  
55 - }  
56 -  
57 - /**  
58 - * 处理接口返回状态码  
59 - */  
60 - _handleDataCode(data, code, url) {  
61 - let result = {};  
62 -  
63 - code = code || 200;  
64 - if (_.toNumber(data.code) === _.toNumber(code)) {  
65 - _.unset(data, 'code');  
66 - _.forEach(data, (value, key) => {  
67 - _.set(result, key, value);  
68 - });  
69 - } else {  
70 - log.error(`API: ${url} return code not ${code}`);  
71 - }  
72 - return result;  
73 - }  
74 -  
75 -  
76 - /**  
77 - * 调用接口  
78 - */  
79 - _requestFromAPI(options, param, reqId) {  
80 - const timer = new Timer();  
81 - const method = options.method || 'get';  
82 -  
83 - timer.put('getApi');// 统计时间开始  
84 - return rp(options).then((result) => {  
85 - const duration = timer.put('getApi');// 统计时间结束  
86 -  
87 - // 数据校验  
88 - if (!result) {  
89 - log.error(`error: ${API_BAD_RETSULT.message}`);  
90 - return Promise.reject(API_BAD_RETSULT);  
91 - }  
92 -  
93 - // 处理返回数据状态码  
94 - if (param && param.code) {  
95 - result = this._handleDataCode(result, param.code, options.url);  
96 - }  
97 -  
98 - // 写缓存, 否则返回 Slave 缓存服务器的数据  
99 - if (options.method === 'get' && config.useCache &&  
100 - param && param.cache) {  
101 - const cacheTime = _.isNumber(param.cache) ? param.cache : 60;  
102 - const catchErr = (err) => {  
103 - log.error(`cache: ${err.toString()}`);  
104 - };  
105 -  
106 - reqId = reqId || this._getReqId(options);  
107 - cache.set(`apiCache:${reqId}`, result, cacheTime).catch(catchErr);  
108 - cache.setSlave(`apiCache:${reqId}`, result, 86400).catch(catchErr); // 二级缓存存储一天  
109 - }  
110 -  
111 - log.info(`use: ${duration}ms for ${method} api: ${options.url}?${qs.stringify(options.qs)} `);  
112 - return result;  
113 - }).catch((err)=> {  
114 - const duration = timer.put('getApi');// 统计时间结束  
115 -  
116 - log.error(`${method} api fail: use: ${duration}ms, code:${err.statusCode}, error: ${err.message}`);  
117 - log.error(`API: ${options.url}?${qs.stringify(options.qs)}`);  
118 -  
119 - // 使用缓存的时候,读取二级缓存  
120 - if (config.useCache && param && param.cache) {  
121 - return this._requestFromCache(options, true);  
122 - }  
123 - return Promise.resolve(API_CALL_FAIL);  
124 - });  
125 - }  
126 -  
127 - /**  
128 - * 读取缓存  
129 - * @param {[object]} options  
130 - * @param {[boolean]} slave true: 读取二级缓存  
131 - * @param {[object]} param 请求API处理参数  
132 - * @return {[type]}  
133 - */  
134 - _requestFromCache(options, slave, param) {  
135 - const reqId = this._getReqId(options);  
136 - const getCache = slave ? cache.getFromSlave : cache.get;  
137 -  
138 - log.info(`get ${slave ? 'slave' : 'master'} cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`);  
139 - return getCache(`apiCache:${reqId}`).then((result) => {  
140 - if (!_.isNil(result)) {  
141 - try {  
142 - result = JSON.parse(result);  
143 - } finally {  
144 - log.info(slave ? SLAVE_CACHE_SUCCESS : MASTER_CACHE_SUCCESS);  
145 - return result;  
146 - }  
147 - }  
148 -  
149 - // 读取缓存失败,并且不是二级缓存的时候,调用 API  
150 - if (!slave) {  
151 - return this._requestFromAPI(options, param, reqId);  
152 - }  
153 - }).catch(() => {  
154 - log.error(slave ? SLAVE_CACHE_FAIL : MASTER_CACHE_FAIL);  
155 -  
156 - // 读取缓存失败,并且不是二级缓存的时候,调用 API  
157 - if (!slave) {  
158 - return this._requestFromAPI(options, param, reqId);  
159 - }  
160 -  
161 - return Promise.resolve(API_CALL_FAIL);  
162 - });  
163 - }  
164 -  
165 - /**  
166 - * 使用 get 请求获取接口  
167 - * @param {[string]} url  
168 - * @param {[object]} data  
169 - * @param {[object]} param 数据处理参数  
170 - * @return {[type]}  
171 - */  
172 - get(url, data, param) {  
173 - const options = {  
174 - url: `${this.ApiUrl}${url}`,  
175 - qs: data.client_secret ? data : sign.apiSign(data),  
176 - json: true,  
177 - gzip: true,  
178 - timeout: 3000  
179 - };  
180 -  
181 - // 从缓存获取数据  
182 - if (config.useCache && param && param.catch) {  
183 - return this._requestFromCache(options, false, param);  
184 - }  
185 -  
186 - return this._requestFromAPI(options, param);  
187 - }  
188 -  
189 - /**  
190 - * 使用 post 请求获取接口  
191 - * @param {[string]} url  
192 - * @param {[object]} data  
193 - * @param {[object]} param 数据处理参数  
194 - * @return {[type]}  
195 - */  
196 - post(url, data, param) {  
197 - const options = {  
198 - url: `${this.ApiUrl}${url}`,  
199 - form: data.client_secret ? data : sign.apiSign(data),  
200 - method: 'post',  
201 - json: true,  
202 - timeout: 3000  
203 - };  
204 -  
205 - return this._requestFromAPI(options, param);  
206 - }  
207 -  
208 - all(list) {  
209 - if (_.isArray(list)) {  
210 - return Promise.all(list);  
211 - } else {  
212 - return Promise.reject(Error(API_ALL_METHOD_ERROR));  
213 - }  
214 - }  
215 -}  
216 -  
217 -class API extends Http {  
218 - constructor() {  
219 - super(api);  
220 - }  
221 -}  
222 -  
223 -class ServiceAPI extends Http {  
224 - constructor() {  
225 - super(serviceApi);  
226 - }  
227 -}  
228 -  
229 -class SearchAPI extends Http {  
230 - constructor() {  
231 - super(searchApi);  
232 - }  
233 -}  
234 -  
235 -exports.API = API;  
236 -exports.ServiceAPI = ServiceAPI;  
237 -exports.SearchAPI = SearchAPI;  
1 -/**  
2 - * 缓存封装  
3 - * 前期使用 memcache, 写方法的时候考虑一下如何转换为 redis  
4 - * @author bikai kai.bi@yoho.cn  
5 - * @date 2016/05/16  
6 - */  
7 -'use strict';  
8 -const Promise = require('bluebird');  
9 -const Memcached = require('memcached');  
10 -const _ = require('lodash');  
11 -const config = require('../config/common');  
12 -  
13 -let master = new Memcached(config.memcache.master, config.memcache);  
14 -let slave = new Memcached(config.memcache.slave, config.memcache);  
15 -  
16 -master = Promise.promisifyAll(master);  
17 -slave = Promise.promisifyAll(slave);  
18 -  
19 -/**  
20 - * 获取缓存  
21 - * @param {[string]} key 键  
22 - * @return {[type]}  
23 - */  
24 -exports.get = (key) => {  
25 - if (_.isString(key)) {  
26 - return master.getAsync(key);  
27 - }  
28 -  
29 - return Promise.resolve();  
30 -};  
31 -  
32 -/**  
33 - * 批量获取缓存  
34 - * @param {[array]} list 字符串数组  
35 - * @return {[type]}  
36 - */  
37 -exports.getMulti = (list) => {  
38 - if (_.isArray(list)) {  
39 - return master.getMultiAsync(list);  
40 - }  
41 - return Promise.resolve();  
42 -};  
43 -  
44 -/**  
45 - * 获取缓存(从 Slave 服务器)  
46 - * @param {[string]} key 键  
47 - * @return {[type]}  
48 - */  
49 -exports.getFromSlave = (key) => {  
50 - if (_.isString(key)) {  
51 - return slave.getAsync(key);  
52 - }  
53 - return Promise.resolve();  
54 -};  
55 -  
56 -/**  
57 - * 批量获取缓存(从 Slave 服务器)  
58 - * @param {[array]} list 字符串数组  
59 - * @return {[type]}  
60 - */  
61 -exports.getMultiFromSlave = (list) => {  
62 - if (_.isArray(list)) {  
63 - return slave.getMultiAsync(list);  
64 - }  
65 - return Promise.resolve();  
66 -};  
67 -  
68 -/**  
69 - * 写缓存  
70 - * @param {[type]} key 键  
71 - * @param {[type]} value 值  
72 - * @param {[type]} lifetime 生命周期  
73 - * @return {[type]}  
74 - */  
75 -exports.set = (key, value, lifetime) => {  
76 - lifetime = lifetime || 86400;  
77 -  
78 - if (_.isObject(value)) {  
79 - value = JSON.stringify(value);  
80 - }  
81 -  
82 - if (_.isString(key)) {  
83 - return master.setAsync(key, value, lifetime);  
84 - }  
85 - return Promise.resolve();  
86 -};  
87 -  
88 -/**  
89 - * 写缓存(到 Slave 服务器)  
90 - * @param {[type]} key 键  
91 - * @param {[type]} value 值  
92 - * @param {[type]} lifetime 生命周期  
93 - * @return {[type]}  
94 - */  
95 -exports.setSlave = (key, value, lifetime) => {  
96 - lifetime = lifetime || 86400;  
97 -  
98 - if (_.isObject(value)) {  
99 - value = JSON.stringify(value);  
100 - }  
101 -  
102 - if (_.isString(key)) {  
103 - return slave.setAsync(key, value, lifetime);  
104 - }  
105 - return Promise.resolve();  
106 -};  
107 -  
108 -/**  
109 - * 删除缓存  
110 - * @param {[string]} key 键  
111 - * @return {[type]}  
112 - */  
113 -exports.del = (key) => {  
114 - if (_.isString(key)) {  
115 - return master.delAsync(key);  
116 - }  
117 - return Promise.resolve();  
118 -};  
1 -/**  
2 - * 对象键名驼峰化  
3 - * @author: Bi Kai<kai.bi@yoho.cn>  
4 - * @date: 2016/05/09  
5 - */  
6 -'use strict';  
7 -const _ = require('lodash');  
8 -  
9 -let camelCase,  
10 - camelCaseObject,  
11 - camelCaseArray;  
12 -  
13 -camelCaseObject = (obj) => {  
14 - _.forEach(Object.keys(obj), (k) => {  
15 - obj[k] = camelCase(obj[k]);  
16 - if (/[_-]/.test(k)) {  
17 - obj[_.camelCase(k)] = obj[k];  
18 - delete obj[k];  
19 - }  
20 - });  
21 - return obj;  
22 -};  
23 -  
24 -camelCaseArray = (list) => {  
25 - _.forEach(list, (k) => {  
26 - k = camelCase(k);  
27 - });  
28 - return list;  
29 -};  
30 -  
31 -camelCase = (data) => {  
32 - if (_.isArray(data)) {  
33 - data = camelCaseArray(data);  
34 - } else if (_.isObject(data)) {  
35 - data = camelCaseObject(data);  
36 - }  
37 -  
38 - return data;  
39 -};  
40 -  
41 -module.exports = camelCase;  
1 -/**  
2 - * 获取 UID  
3 - * @param {[object]} req  
4 - * @return {[string]}  
5 - */  
6 -'use strict';  
7 -const sign = require('./sign');  
8 -  
9 -exports.getUid = (req) => {  
10 - const cookie = req.cookies._UID;  
11 - let _uid = 0;  
12 - let cookieList;  
13 -  
14 - if (req.isApp) {  
15 - return req.query.uid || 0;  
16 - }  
17 -  
18 - if (cookie) {  
19 - cookieList = cookie.split('::');  
20 - if (cookieList[1] && !isNaN(cookieList[1])) {  
21 - _uid = cookieList[1];  
22 - }  
23 - }  
24 -  
25 - // 校验 cookie 的 uid 有没有被修改  
26 - if (req.cookies._TOKEN !== sign.makeToken(_uid)) {  
27 - _uid = 0;  
28 - }  
29 -  
30 - return _uid;  
31 -};  
32 -  
33 -exports.getShoppingKey = (req) => {  
34 - return req.cookies['_SPK'] ? req.cookies['_SPK'] : ''; // eslint-disable-line  
35 -};  
1 -/**  
2 - * Handlebars helpers  
3 - * bikai kai.bi@yoho.cn  
4 - * 2016-05-10  
5 - */  
6 -'use strict';  
7 -const querystring = require('querystring');  
8 -const _ = require('lodash');  
9 -const moment = require('moment');  
10 -const config = require('../config/common');  
11 -  
12 -/**  
13 - * 七牛图片路径处理  
14 - * @param {[string]} url  
15 - * @param {[string]} width  
16 - * @param {[string]} height  
17 - * @param {[string]} mode  
18 - * @return {[string]}  
19 - */  
20 -exports.image = (url, width, height, mode) => {  
21 - mode = _.isNumber(mode) ? mode : 2;  
22 - url = url || '';  
23 - return url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);  
24 -};  
25 -  
26 -/**  
27 - * 条件判断  
28 - * @param {[string]} v1  
29 - * @param {[string]} v2  
30 - * @param {[object]} options 上下文环境,一般不手动传  
31 - * @return {[boolen]}  
32 - */  
33 -exports.isEqual = (v1, v2, _options) => {  
34 - if (_.isEqual(v1, v2)) {  
35 - return _options.fn(this); // eslint-disable-line  
36 - }  
37 -  
38 - return _options.inverse(this); // eslint-disable-line  
39 -};  
40 -  
41 -/**  
42 - * 站内地址格式化  
43 - * @param {[string]} uri 路径  
44 - * @param {[object]} qs 查询字符串  
45 - * @param {[string]} module 模块  
46 - * @return {[string]}  
47 - */  
48 -exports.urlFormat = (uri, qs, module) => {  
49 - const subDomain = '.yohobuy.com';  
50 - const subName = {  
51 - default: config.siteUrl,  
52 - guang: `//guang${subDomain}`,  
53 - list: `//list${subDomain}`,  
54 - search: `//search${subDomain}`,  
55 - huodong: `//huodong${subDomain}`,  
56 - activity: '//activity.yohobuy.com',  
57 - index: config.siteUrl  
58 - };  
59 - let url;  
60 -  
61 - module = module || 'default';  
62 - if (subName[module]) {  
63 - url = subName[module];  
64 - } else {  
65 - url = `//${module}${subDomain}`; // 规则没匹配到就把模块当作子域名  
66 - }  
67 -  
68 - url += uri;  
69 - if (qs) {  
70 - url += `?${querystring.stringify(qs)}`;  
71 - }  
72 -  
73 - return url;  
74 -};  
75 -  
76 -/**  
77 - * 站内地址格式化  
78 - * @param {[string]} uri 路径  
79 - * @param {[object]} qs 查询字符串  
80 - * @param {[string]} module 模块  
81 - * @return {[string]}  
82 - */  
83 -exports.fakeUrlFormat = (uri, qs, module) => {  
84 - const subDomain = 'http://localhost:6001';  
85 - const subName = {  
86 - default: subDomain,  
87 - guang: `${subDomain}`,  
88 - list: `${subDomain}`,  
89 - search: `${subDomain}`,  
90 - huodong: `${subDomain}`,  
91 - index: subDomain  
92 - };  
93 - let url;  
94 -  
95 - module = module || 'default';  
96 - if (subName[module]) {  
97 - url = subName[module];  
98 - } else {  
99 - url = `//${module}${subDomain}`; // 规则没匹配到就把模块当作子域名  
100 - }  
101 -  
102 - url += uri;  
103 - if (qs) {  
104 - url += `?${querystring.stringify(qs)}`;  
105 - }  
106 -  
107 - return url;  
108 -};  
109 -  
110 -/**  
111 - * 大写转小写处理  
112 - * @param {[string]} str 转换字符  
113 - */  
114 -exports.lowerCase = (str) => {  
115 - str = str || '';  
116 - return str.toLowerCase();  
117 -};  
118 -  
119 -/**  
120 - * 小写转大写处理  
121 - * @param {[string]} str 转换字符  
122 - */  
123 -exports.upperCase = (str) => {  
124 - str = str || '';  
125 - return str.toUpperCase();  
126 -};  
127 -  
128 -/**  
129 - * 四舍五入  
130 - * @param {[type]} num 数字  
131 - * @param {[type]} precision 精度  
132 - * @return {[type]}  
133 - */  
134 -exports.round = (num, precision) => {  
135 - precision = _.isNumber(precision) ? precision : 2;  
136 - num = _.isInteger(num) ? (+num).toFixed(precision) : _.round(num, precision);  
137 - return num;  
138 -};  
139 -  
140 -/**  
141 - * 时间格式化  
142 - * @param format 格式化token @see{http://momentjs.cn/docs/#/displaying/format/}  
143 - * @param date 日期或者数字  
144 - * @return string  
145 - *  
146 - */  
147 -exports.dateFormat = (format, date) => {  
148 - if (typeof format !== 'string' || typeof date === 'undefined') {  
149 - return '';  
150 - } else {  
151 - if (date instanceof Date) {  
152 - return moment(date).format(format);  
153 - } else {  
154 - const d = moment.unix(date);  
155 -  
156 - return moment(d).utc().format(format);  
157 - }  
158 - }  
159 -};  
160 -  
161 -/**  
162 - * 时间差格式化  
163 - * @param {[string]} format 格式化字符串  
164 - * @param {[number]} diff 相差值  
165 - * @param {[string]} type diff时间类型 默认ms  
166 - *  
167 - * Key Shorthand  
168 - * years y  
169 - * quarters Q  
170 - * months M  
171 - * weeks w  
172 - * days d  
173 - * hours h  
174 - * minutes m  
175 - * seconds s  
176 - * milliseconds ms  
177 - *  
178 - * @example  
179 - * let diff = 60 * 60 * 24 * (1.3) + 2;  
180 - *  
181 - * let s = helpers.dateDiffFormat('{d}天{h}小时', diff, 's');  
182 - * >>> 1天7小时  
183 - */  
184 -exports.dateDiffFormat = (format, diff, type) => {  
185 - if (typeof format !== 'string' || typeof diff === 'undefined') {  
186 - return '';  
187 - } else {  
188 - type = type || 'ms';  
189 - const m = moment.duration(diff, type);  
190 -  
191 - format.match(/(\{.*?\})/g).forEach((s) => {  
192 - format = format.replace(s, m.get(s.substring(1, s.length - 1)));  
193 - });  
194 -  
195 - return format;  
196 - }  
197 -};  
198 -  
199 -/**  
200 - * 验证邮箱是否合法  
201 - *  
202 - * @param string email  
203 - * @return boolean  
204 - */  
205 -exports.verifyEmail = (email) => {  
206 - if (!email) {  
207 - return false;  
208 - }  
209 -  
210 - const emailRegExp = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;  
211 -  
212 - return emailRegExp.test(email);  
213 -};  
214 -  
215 -/**  
216 - * 各国手机号规则  
217 - */  
218 -function areaMobileVerify(phone, area) {  
219 - area = area || '86';  
220 - phone = phone.trim();  
221 -  
222 - let verify = {  
223 - 86: {  
224 - name: '中国',  
225 - match: /^1[3|4|5|8|7][0-9]{9}$/.test(phone)  
226 - },  
227 - 852: {  
228 - name: '中国香港',  
229 - match: /^[9|6|5][0-9]{7}$/.test(phone)  
230 - },  
231 - 853: {  
232 - name: '中国澳门',  
233 - match: /^[0-9]{8}$/.test(phone)  
234 - },  
235 - 886: {  
236 - name: '中国台湾',  
237 - match: /^[0-9]{10}$/.test(phone)  
238 - },  
239 - 65: {  
240 - name: '新加坡',  
241 - match: /^[9|8][0-9]{7}$/.test(phone)  
242 - },  
243 - 60: {  
244 - name: '马来西亚',  
245 - match: /^1[1|2|3|4|6|7|9][0-9]{8}$/.test(phone)  
246 - },  
247 - 1: {  
248 - name: '加拿大&美国',  
249 - match: /^[0-9]{10}$/.test(phone)  
250 - },  
251 - 82: {  
252 - name: '韩国',  
253 - match: /^01[0-9]{9}$/.test(phone)  
254 - },  
255 - 44: {  
256 - name: '英国',  
257 - match: /^7[7|8|9][0-9]{8}$/.test(phone)  
258 - },  
259 - 81: {  
260 - name: '日本',  
261 - match: /^0[9|8|7][0-9]{9}$/.test(phone)  
262 - },  
263 - 61: {  
264 - name: '澳大利亚',  
265 - match: /^[0-9]{11}$/.test(phone)  
266 - }  
267 - };  
268 -  
269 - if (verify[area]) {  
270 - return verify[area].match;  
271 - } else {  
272 - return false;  
273 - }  
274 -}  
275 -  
276 -/**  
277 - * 验证国际手机号是否合法  
278 - */  
279 -exports.isAreaMobile = (areaMobile) => {  
280 - if (!areaMobile) {  
281 - return false;  
282 - }  
283 -  
284 - let mobile = {  
285 - area: '86',  
286 - phone: ''  
287 - };  
288 -  
289 - let splitMobile = areaMobile.split('-');  
290 -  
291 - if (splitMobile.length === 2) {  
292 - mobile.area = splitMobile[0];  
293 - mobile.phone = splitMobile[1];  
294 - } else {  
295 - mobile.phone = splitMobile[0];  
296 - }  
297 -  
298 - return areaMobileVerify(mobile.phone, mobile.area);  
299 -};  
300 -  
301 -/**  
302 - * 验证手机是否合法  
303 - */  
304 -exports.verifyMobile = (phone) => {  
305 - if (!phone) {  
306 - return false;  
307 - }  
308 -  
309 - return /^1[3|4|5|8|7][0-9]{9}$/.test(phone);  
310 -};  
311 -  
312 -/**  
313 - * 组合国际手机号  
314 - */  
315 -exports.makeAreaMobile = (area, mobile) => {  
316 - if (!area || area === '86') {  
317 - return mobile;  
318 - }  
319 -  
320 - return `${area}-${mobile}`;  
321 -};  
322 -  
323 -exports.isPassword = (pwd) => {  
324 - if (!pwd) {  
325 - return false;  
326 - }  
327 -  
328 - let pwdRegexp = /^([a-zA-Z0-9\-\+_!@\#$%\^&\*\(\)\:\;\.=\[\]\\\',\?]){6,20}$/;  
329 -  
330 - return pwdRegexp.test(_.trim(pwd));  
331 -};  
1 -/**  
2 - * 日志工具类  
3 - * @author: hbomb<qiqi.zhou@yoho.cn>  
4 - * @date: 2016/05/06  
5 - */  
6 -'use strict';  
7 -  
8 -const winston = require('winston');  
9 -const config = require('../config/common');  
10 -const FileTransport = require('winston-daily-rotate-file');  
11 -  
12 -require('influxdb-winston');  
13 -  
14 -const logger = new (winston.Logger)({  
15 - transports: [  
16 - new (FileTransport)(config.loggers.infoFile),  
17 - new (FileTransport)(config.loggers.errorFile),  
18 - new (winston.transports.UdpTransport)(config.loggers.udp),  
19 - new (winston.transports.Console)(config.loggers.console)  
20 - ],  
21 - exitOnError: false  
22 -});  
23 -  
24 -module.exports = logger;  
1 -/**  
2 - * 签名  
3 - * @author: bikai  
4 - * @date: 2016/5/6  
5 - */  
6 -  
7 -'use strict';  
8 -const _ = require('lodash');  
9 -const md5 = require('md5');  
10 -  
11 -const privateKey = {  
12 - android: 'fd4ad5fcfa0de589ef238c0e7331b585',  
13 - iphone: 'a85bb0674e08986c6b115d5e3a4884fa',  
14 - ipad: 'ad9fcda2e679cf9229e37feae2cdcf80',  
15 - web: '0ed29744ed318fd28d2c07985d3ba633',  
16 - yoho: 'fd4ad5fcsa0de589af23234ks1923ks',  
17 - h5: 'fd4ad5fcfa0de589ef238c0e7331b585'  
18 -};  
19 -  
20 -/**  
21 - * 排序参数  
22 - * @param {Object} argument 需要排序的参数对象  
23 - * @return {Object} 排序之后的参数对象  
24 - */  
25 -const packageSort = argument => {  
26 - const newObj = {};  
27 -  
28 - for (const k of Object.keys(argument).sort()) {  
29 - newObj[k] = argument[k];  
30 - }  
31 -  
32 - return newObj;  
33 -};  
34 -  
35 -/**  
36 - * 生成签名  
37 - * @param {Object} argument 需要签名的数据  
38 - * @return {string} 生成的签名字符串  
39 - */  
40 -const makeSign = argument => {  
41 - const qs = [];  
42 -  
43 - _.forEach(argument, (value, key) => {  
44 - qs.push(`${key}=${_.trim(value)}`);  
45 - });  
46 -  
47 - return md5(qs.join('&')).toLowerCase();  
48 -};  
49 -  
50 -// 生成API签名,调用后端接口的时候有私钥校验  
51 -exports.apiSign = (params) => {  
52 - const clientType = params.client_type || 'web';  
53 -  
54 - /* eslint-disable */  
55 - let sign = packageSort(Object.assign({  
56 - client_type: clientType,  
57 - private_key: privateKey[clientType],  
58 - app_version: '3.8.2',  
59 - os_version: 'yohobuy:web',  
60 - screen_size: '720x1280',  
61 - v: '7'  
62 - }, params));  
63 - /* eslint-enable */  
64 -  
65 - sign = Object.assign(sign, {  
66 - client_secret: makeSign(sign)  
67 - });  
68 - delete sign.private_key;  
69 - return sign;  
70 -};  
71 -  
72 -// 检查签名,APP 访问 H5 页面的时候需要检查  
73 -exports.checkSign = (params) => {  
74 - const clientSecret = params.client_secret;  
75 -  
76 - let sortedParams;  
77 -  
78 - // 忽略部分参数  
79 - delete params.client_secret;  
80 - delete params.q;  
81 - delete params.debug_data;  
82 - delete params['/api'];  
83 -  
84 - params.private_key = privateKey[params.client_type];  
85 - sortedParams = packageSort(params);  
86 -  
87 - return clientSecret === makeSign(sortedParams);  
88 -};  
89 -  
90 -// 检查签名,APP 访问 H5 页面的时候需要检查, 有可能不同于上边的签名方式  
91 -exports.webSign = (params) => {  
92 - const webPrivateKey = 'yohobuyapp';  
93 -  
94 - return params.key === md5(md5(webPrivateKey) + params.uid);  
95 -};  
96 -  
97 -exports.makeToken = (string) => {  
98 - return md5(md5(string + '#@!@#'));  
99 -};  
1 -/**  
2 - * 计时类  
3 - * @example  
4 - * let timer = new Timer();  
5 - * timer.put('profile');  
6 - * timer.put('proflie'); // console output: 12.14  
7 - *  
8 - * @author: hbomb<qiqi.zhou@yoho.cn>  
9 - * @date: 2016/05/07  
10 - */  
11 -'use strict';  
12 -class Timer {  
13 - constructor() {  
14 - this.timers = {};  
15 - }  
16 -  
17 - /**  
18 - * 打点计时  
19 - */  
20 - put(label) {  
21 - const labelTime = this.timers[label];  
22 -  
23 - if (labelTime) {  
24 - const duration = process.hrtime(labelTime);  
25 -  
26 - return this._round(duration[0], duration[1]);  
27 - } else {  
28 - this.timers[label] = process.hrtime();  
29 - }  
30 - }  
31 -  
32 - /**  
33 - * 格式化成毫秒  
34 - * @param {Number} value 纳秒  
35 - */  
36 - _round(seconds, nanoseconds) {  
37 - return Math.round((seconds * 1e9 + nanoseconds) / 10000) / 100;  
38 - }  
39 -  
40 -}  
41 -  
42 -module.exports = Timer;