Authored by ccbikai

SALE 头部颜色修改

1 'use strict'; 1 'use strict';
2 2
3 const mRoot = '../models'; 3 const mRoot = '../models';
4 -const library = '../../../library';  
5 4
6 const couponModel = require(`${mRoot}/coupon`); // 领取优惠券 model 5 const couponModel = require(`${mRoot}/coupon`); // 领取优惠券 model
7 -const log = require(`${library}/logger`);  
8 6
9 exports.index = (req, res, next) => { 7 exports.index = (req, res, next) => {
10 var renderData = { 8 var renderData = {
1 -var API = require('../../../library/api').API;  
2 -var api = new API(); 1 +var api = global.yoho.API;
3 2
4 /** 3 /**
5 * 分享页面基础参数 4 * 分享页面基础参数
@@ -8,8 +8,8 @@ @@ -8,8 +8,8 @@
8 const request = require('request-promise'); 8 const request = require('request-promise');
9 const Promise = require('bluebird'); 9 const Promise = require('bluebird');
10 const crypto = require('crypto'); 10 const crypto = require('crypto');
11 -const logger = require('../../../library/logger');  
12 -const cache = require('../../../library/cache'); 11 +const logger = global.yoho.logger;
  12 +const cache = global.yoho.cache;
13 13
14 // 此处请勿使用有货公众号的 appId, 此处使用的是 女生志 的appId 14 // 此处请勿使用有货公众号的 appId, 此处使用的是 女生志 的appId
15 const appId = 'wxb52ec6a352f0b090'; 15 const appId = 'wxb52ec6a352f0b090';
@@ -71,14 +71,16 @@ let index = (req, res, next) => { @@ -71,14 +71,16 @@ let index = (req, res, next) => {
71 Object.assign(params.renderData.pageHeader, { 71 Object.assign(params.renderData.pageHeader, {
72 saleNav: saleModel.saleNav(req.yoho.channel) 72 saleNav: saleModel.saleNav(req.yoho.channel)
73 }); 73 });
  74 +
  75 + // 此处 channel 需要读取 cookies 的 channel
  76 + req.cookies._Channel && (res.locals.pageChannel[req.cookies._Channel] = true);
74 res.render('sale/index', Object.assign(params.renderData, { 77 res.render('sale/index', Object.assign(params.renderData, {
75 content: result, 78 content: result,
76 floorHeader: { 79 floorHeader: {
77 title: { 80 title: {
78 title: '最新降价' 81 title: '最新降价'
79 } 82 }
80 - },  
81 - pageChannel: false // 需求中,头部不需要跟随频道变颜色 83 + }
82 })); 84 }));
83 }).catch(next); 85 }).catch(next);
84 }; 86 };
@@ -3,7 +3,7 @@ @@ -3,7 +3,7 @@
3 * @return {[type]} 3 * @return {[type]}
4 */ 4 */
5 const headerModel = require('../models/header'); 5 const headerModel = require('../models/header');
6 -const logger = require('../../library/logger'); 6 +const logger = global.yoho.logger;
7 7
8 exports.notFound = () => { 8 exports.notFound = () => {
9 return (req, res) => { 9 return (req, res) => {
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 - _requestFromAPI(options, cacheOption, reqId) {  
61 - const timer = new Timer();  
62 - const method = options.method || 'get';  
63 -  
64 - timer.put('getApi');// 统计时间开始  
65 - return rp(options).then((result) => {  
66 - const duration = timer.put('getApi');// 统计时间结束  
67 -  
68 - // 数据校验  
69 - if (!result) {  
70 - log.error(`error: ${API_BAD_RETSULT.message}`);  
71 - return Promise.reject(API_BAD_RETSULT);  
72 - }  
73 -  
74 - // 写缓存, 否则返回 Slave 缓存服务器的数据  
75 - if (config.useCache && cacheOption) {  
76 - const cacheTime = _.isNumber(cacheOption) ? cacheOption : 60;  
77 - const catchErr = (err) => {  
78 - log.error(`cache: ${err.toString()}`);  
79 - };  
80 -  
81 - reqId = reqId || this._getReqId(options);  
82 - cache.set(`apiCache:${reqId}`, result, cacheTime).catch(catchErr);  
83 - cache.setSlave(`apiCache:${reqId}`, result, 86400).catch(catchErr); // 二级缓存存储一天  
84 - }  
85 -  
86 - let reqData = options.qs || options.form;  
87 -  
88 - log.info(`use: ${duration}ms for ${method} api: ${options.url}?${qs.stringify(reqData)} `);  
89 - return result;  
90 - }).catch((err)=> {  
91 - const duration = timer.put('getApi');// 统计时间结束  
92 -  
93 - log.error(`${method} api fail: use: ${duration}ms, code:${err.statusCode}, error: ${err.message}`);  
94 - log.error(`API: ${options.url}?${qs.stringify(options.qs)}`);  
95 -  
96 - // 使用缓存的时候,读取二级缓存  
97 - if (config.useCache && cacheOption) {  
98 - return this._requestFromCache(options, true);  
99 - }  
100 - return Promise.resolve(API_CALL_FAIL);  
101 - });  
102 - }  
103 -  
104 - /**  
105 - * 读取缓存  
106 - * @param {[object]} options  
107 - * @param {[boolean]} slave true: 读取二级缓存  
108 - * @return {[type]}  
109 - */  
110 - _requestFromCache(options, slave) {  
111 - const reqId = this._getReqId(options);  
112 - const getCache = slave ? cache.getFromSlave : cache.get;  
113 -  
114 - log.info(`get ${slave ? 'slave' : 'master'} cache: ${reqId}, url: ${options.url}?${qs.stringify(options.qs)}`);  
115 - return getCache(`apiCache:${reqId}`).then((result) => {  
116 - if (!_.isNil(result)) {  
117 - try {  
118 - result = JSON.parse(result);  
119 - } finally {  
120 - log.info(slave ? SLAVE_CACHE_SUCCESS : MASTER_CACHE_SUCCESS);  
121 - return result;  
122 - }  
123 - }  
124 -  
125 - // 读取缓存失败,并且不是二级缓存的时候,调用 API  
126 - if (!slave) {  
127 - return this._requestFromAPI(options, true, reqId);  
128 - }  
129 - }).catch(() => {  
130 - log.error(slave ? SLAVE_CACHE_FAIL : MASTER_CACHE_FAIL);  
131 -  
132 - // 读取缓存失败,并且不是二级缓存的时候,调用 API  
133 - if (!slave) {  
134 - return this._requestFromAPI(options, true, reqId);  
135 - }  
136 -  
137 - return Promise.resolve(API_CALL_FAIL);  
138 - });  
139 - }  
140 -  
141 - /**  
142 - * 使用 get 请求获取接口  
143 - * @param {[string]} url  
144 - * @param {[object]} data  
145 - * @param {[bool or number]} cacheOption 使用数字时,数字表示缓存时间  
146 - * @return {[type]}  
147 - */  
148 - get(url, data, cacheOption) {  
149 - const options = {  
150 - url: `${this.ApiUrl}${url}`,  
151 - qs: data.client_secret ? data : sign.apiSign(data),  
152 - json: true,  
153 - gzip: true,  
154 - timeout: 3000  
155 - };  
156 -  
157 - // 从缓存获取数据  
158 - if (config.useCache && cacheOption) {  
159 - return this._requestFromCache(options);  
160 - }  
161 -  
162 - return this._requestFromAPI(options, cacheOption);  
163 - }  
164 -  
165 - /**  
166 - * post  
167 - * @param url String  
168 - * @param data Obejct  
169 - */  
170 - post(url, data) {  
171 - const options = {  
172 - url: `${this.ApiUrl}${url}`,  
173 - form: data.client_secret ? data : sign.apiSign(data),  
174 - method: 'post',  
175 - json: true,  
176 - timeout: 3000  
177 - };  
178 -  
179 - return this._requestFromAPI(options);  
180 - }  
181 -  
182 - all(list) {  
183 - if (_.isArray(list)) {  
184 - return Promise.all(list);  
185 - } else {  
186 - return Promise.reject(Error(API_ALL_METHOD_ERROR));  
187 - }  
188 - }  
189 -}  
190 -  
191 -class API extends Http {  
192 - constructor() {  
193 - super(api);  
194 - }  
195 -}  
196 -  
197 -class ServiceAPI extends Http {  
198 - constructor() {  
199 - super(serviceApi);  
200 - }  
201 -}  
202 -  
203 -class SearchAPI extends Http {  
204 - constructor() {  
205 - super(searchApi);  
206 - }  
207 -}  
208 -  
209 -exports.API = API;  
210 -exports.ServiceAPI = ServiceAPI;  
211 -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 - url = url.replace(/{width}/g, width).replace(/{height}/g, height).replace(/{mode}/g, mode);  
24 -  
25 - return url.replace('http:', '');  
26 -};  
27 -  
28 -/**  
29 - * 条件判断  
30 - * @param {[string]} v1  
31 - * @param {[string]} v2  
32 - * @param {[object]} options 上下文环境,一般不手动传  
33 - * @return {[boolen]}  
34 - */  
35 -exports.isEqual = (v1, v2, _options) => {  
36 - if (_.isEqual(v1, v2)) {  
37 - return _options.fn(this); // eslint-disable-line  
38 - }  
39 -  
40 - return _options.inverse(this); // eslint-disable-line  
41 -};  
42 -  
43 -/**  
44 - * 站内地址格式化  
45 - * @param {[string]} uri 路径  
46 - * @param {[object]} qs 查询字符串  
47 - * @param {[string]} module 模块  
48 - * @return {[string]}  
49 - */  
50 -exports.urlFormat = (uri, qs, module) => {  
51 - const subDomain = '.m.yohobuy.com';  
52 - const subName = {  
53 - default: config.siteUrl,  
54 - guang: `//guang${subDomain}`,  
55 - list: `//list${subDomain}`,  
56 - search: `//search${subDomain}`,  
57 - huodong: `//huodong${subDomain}`,  
58 - activity: '//activity.yohobuy.com',  
59 - index: config.siteUrl  
60 - };  
61 - let url;  
62 -  
63 - module = module || 'default';  
64 - if (subName[module]) {  
65 - url = subName[module];  
66 - } else {  
67 - url = `//${module}${subDomain}`; // 规则没匹配到就把模块当作子域名  
68 - }  
69 -  
70 - url += uri;  
71 - if (qs) {  
72 - url += `?${querystring.stringify(qs)}`;  
73 - }  
74 -  
75 - return url;  
76 -};  
77 -  
78 -/**  
79 - * 大写转小写处理  
80 - * @param {[string]} str 转换字符  
81 - */  
82 -exports.lowerCase = (str) => {  
83 - str = str || '';  
84 - return str.toLowerCase();  
85 -};  
86 -  
87 -/**  
88 - * 小写转大写处理  
89 - * @param {[string]} str 转换字符  
90 - */  
91 -exports.upperCase = (str) => {  
92 - str = str || '';  
93 - return str.toUpperCase();  
94 -};  
95 -  
96 -  
97 -/**  
98 - * 四舍五入  
99 - * @param {[type]} num 数字  
100 - * @param {[type]} precision 精度  
101 - * @return {[type]}  
102 - */  
103 -exports.round = (num, precision) => {  
104 - precision = _.isNumber(precision) ? precision : 2;  
105 - num = _.isInteger(num) ? (+num).toFixed(precision) : _.round(num, precision);  
106 - return num;  
107 -};  
108 -  
109 -/**  
110 - * 链接改成自适应链接  
111 - * @return {[type]}  
112 - */  
113 -exports.https = (url) => {  
114 - return url.replace('http:', '');  
115 -};  
116 -  
117 -/**  
118 - * 时间格式化  
119 - * @param format 格式化token @see{http://momentjs.cn/docs/#/displaying/format/}  
120 - * @param date 日期或者数字  
121 - * @return string  
122 - *  
123 - */  
124 -exports.dateFormat = (format, date) => {  
125 - if (typeof format !== 'string' || typeof date === 'undefined') {  
126 - return '';  
127 - } else {  
128 - if (date instanceof Date) {  
129 - return moment(date).format(format);  
130 - } else {  
131 - const d = moment.unix(date);  
132 -  
133 - return moment(d).utc().format(format);  
134 - }  
135 - }  
136 -};  
137 -  
138 -/**  
139 - * 时间差格式化  
140 - * @param {[string]} format 格式化字符串  
141 - * @param {[number]} diff 相差值  
142 - * @param {[string]} type diff时间类型 默认ms  
143 - *  
144 - * Key Shorthand  
145 - * years y  
146 - * quarters Q  
147 - * months M  
148 - * weeks w  
149 - * days d  
150 - * hours h  
151 - * minutes m  
152 - * seconds s  
153 - * milliseconds ms  
154 - *  
155 - * @example  
156 - * let diff = 60 * 60 * 24 * (1.3) + 2;  
157 - *  
158 - * let s = helpers.dateDiffFormat('{d}天{h}小时', diff, 's');  
159 - * >>> 1天7小时  
160 - */  
161 -exports.dateDiffFormat = (format, diff, type) => {  
162 - if (typeof format !== 'string' || typeof diff === 'undefined') {  
163 - return '';  
164 - } else {  
165 - type = type || 'ms';  
166 - const m = moment.duration(diff, type);  
167 -  
168 - format.match(/(\{.*?\})/g).forEach((s) => {  
169 - format = format.replace(s, m.get(s.substring(1, s.length - 1)));  
170 - });  
171 -  
172 - return format;  
173 - }  
174 -};  
175 -  
176 -/**  
177 - * 验证邮箱是否合法  
178 - *  
179 - * @param string email  
180 - * @return boolean  
181 - */  
182 -exports.verifyEmail = (email) => {  
183 - if (!email) {  
184 - return false;  
185 - }  
186 -  
187 - const emailRegExp = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;  
188 -  
189 - return emailRegExp.test(email);  
190 -};  
191 -  
192 -/**  
193 - * 各国手机号规则  
194 - */  
195 -function areaMobileVerify(phone, area) {  
196 - area = area || '86';  
197 - phone = phone.trim();  
198 -  
199 - let verify = {  
200 - 86: {  
201 - name: '中国',  
202 - match: /^1[3|4|5|8|7][0-9]{9}$/.test(phone)  
203 - },  
204 - 852: {  
205 - name: '中国香港',  
206 - match: /^[9|6|5][0-9]{7}$/.test(phone)  
207 - },  
208 - 853: {  
209 - name: '中国澳门',  
210 - match: /^[0-9]{8}$/.test(phone)  
211 - },  
212 - 886: {  
213 - name: '中国台湾',  
214 - match: /^[0-9]{10}$/.test(phone)  
215 - },  
216 - 65: {  
217 - name: '新加坡',  
218 - match: /^[9|8][0-9]{7}$/.test(phone)  
219 - },  
220 - 60: {  
221 - name: '马来西亚',  
222 - match: /^1[1|2|3|4|6|7|9][0-9]{8}$/.test(phone)  
223 - },  
224 - 1: {  
225 - name: '加拿大&美国',  
226 - match: /^[0-9]{10}$/.test(phone)  
227 - },  
228 - 82: {  
229 - name: '韩国',  
230 - match: /^01[0-9]{9}$/.test(phone)  
231 - },  
232 - 44: {  
233 - name: '英国',  
234 - match: /^7[7|8|9][0-9]{8}$/.test(phone)  
235 - },  
236 - 81: {  
237 - name: '日本',  
238 - match: /^0[9|8|7][0-9]{9}$/.test(phone)  
239 - },  
240 - 61: {  
241 - name: '澳大利亚',  
242 - match: /^[0-9]{11}$/.test(phone)  
243 - }  
244 - };  
245 -  
246 - if (verify[area]) {  
247 - return verify[area].match;  
248 - } else {  
249 - return false;  
250 - }  
251 -}  
252 -  
253 -/**  
254 - * 验证国际手机号是否合法  
255 - */  
256 -exports.verifyAreaMobile = (areaMobile) => {  
257 - if (!areaMobile) {  
258 - return false;  
259 - }  
260 -  
261 - let mobile = {  
262 - area: '86',  
263 - phone: ''  
264 - };  
265 -  
266 - let splitMobile = areaMobile.split('-');  
267 -  
268 - if (splitMobile.length === 2) {  
269 - mobile.area = splitMobile[0];  
270 - mobile.phone = splitMobile[1];  
271 - } else {  
272 - mobile.phone = splitMobile[0];  
273 - }  
274 -  
275 - return areaMobileVerify(mobile.phone, mobile.area);  
276 -};  
277 -  
278 -/**  
279 - * 验证手机是否合法  
280 - */  
281 -exports.verifyMobile = (phone) => {  
282 - if (!phone) {  
283 - return false;  
284 - }  
285 -  
286 - return /^1[3|4|5|8|7][0-9]{9}$/.test(phone);  
287 -};  
288 -  
289 -/**  
290 - * 验证密码规则  
291 - */  
292 -exports.verifyPassword = (password) => {  
293 - if (!password) {  
294 - return false;  
295 - }  
296 -  
297 - return /^([a-zA-Z0-9\-\+_!@\#$%\^&\*\(\)\:\;\.=\[\]\\\',\?]){6,20}$/.test(password);  
298 -};  
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 || 'h5';  
53 -  
54 - /* eslint-disable */  
55 - let sign = packageSort(Object.assign({  
56 - client_type: clientType,  
57 - private_key: privateKey[clientType],  
58 - app_version: '4.6.0',  
59 - os_version: 'yohobuy:h5',  
60 - screen_size: '720x1280',  
61 - v: '7'  
62 - }, params));  
63 - /* eslint-enable */  
64 -  
65 - sign = Object.assign(sign, {  
66 - client_secret: makeSign(sign) // eslint-disable-line camelcase  
67 - });  
68 - delete sign.private_key;  
69 - return sign;  
70 -};  
71 -  
72 -// 检查签名,APP 访问 H5 页面的时候需要检查  
73 -exports.checkSign = (params) => {  
74 - const // eslint-disable-line camelcase  
75 - clientSecret = params.client_secret;  
76 -  
77 - let sortedParams;  
78 -  
79 - // 忽略部分参数  
80 - delete params.client_secret;  
81 - delete params.q;  
82 - delete params.debug_data;  
83 - delete params['/api'];  
84 -  
85 - params.private_key = privateKey[params.client_type]; // eslint-disable-line camelcase  
86 - sortedParams = packageSort(params);  
87 -  
88 - return clientSecret === makeSign(sortedParams);  
89 -};  
90 -  
91 -// 检查签名,APP 访问 H5 页面的时候需要检查, 有可能不同于上边的签名方式  
92 -exports.webSign = (params) => {  
93 - const webPrivateKey = 'yohobuyapp';  
94 -  
95 - return params.key === md5(md5(webPrivateKey) + params.uid);  
96 -};  
97 -  
98 -// 生成 token  
99 -exports.makeToken = (string) => {  
100 - return md5(md5(string + '#@!@#'));  
101 -};  
102 -  
103 -// 校验 token  
104 -exports.verifyToken = (string, token) => {  
105 - return exports.makeToken(string) === token;  
106 -};  
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;  
1 -/**  
2 - * http api 测试  
3 - *  
4 - * @author: jiangfeng<jeff.jiang@yoho.cn>  
5 - * @date: 2016/05/17  
6 - */  
7 -'use strict';  
8 -  
9 -const test = require('ava');  
10 -const sign = require('../../library/sign');  
11 -  
12 -  
13 -const API = require('../../library/api').API;  
14 -const ServiceAPI = require('../../library/api').ServiceAPI;  
15 -const SearchAPI = require('../../library/api').SearchAPI;  
16 -  
17 -const getUrl = 'operations/api/v6/category/getCategory';  
18 -  
19 -test('api constructor test', (t) => {  
20 - let api = new ServiceAPI();  
21 - let api2 = new API();  
22 - let api3 = new SearchAPI();  
23 -  
24 - t.true(api !== null);  
25 - t.true(api2 !== null);  
26 - t.true(api3 !== null);  
27 -});  
28 -  
29 -test('api get test', t => {  
30 - let api = new ServiceAPI();  
31 -  
32 - return api.get(getUrl, sign.apiSign({})).then(result => {  
33 - if (result && result.code) {  
34 - t.pass();  
35 - } else {  
36 - t.fail();  
37 - }  
38 - });  
39 -});  
40 -  
41 -test('api get test, api return an error', t => {  
42 - let api = new ServiceAPI();  
43 -  
44 - return api.get(getUrl + '/error', sign.apiSign({})).catch(err => {  
45 -  
46 - // 故意调用一个错误的接口  
47 - if (err && err.code === 500) {  
48 - t.pass();  
49 - } else {  
50 - t.fail();  
51 - }  
52 - });  
53 -});  
54 -  
55 -test('api get use cache test', t => {  
56 - let api = new ServiceAPI();  
57 -  
58 - return api.get(getUrl, sign.apiSign({}), true).then(result => {  
59 - if (result && result.code) {  
60 - t.pass();  
61 - } else {  
62 - t.fail();  
63 - }  
64 - });  
65 -});  
66 -  
67 -test('api post test', t => {  
68 - let api = new ServiceAPI();  
69 -  
70 - return api.post(getUrl, sign.apiSign({})).then(result => {  
71 - if (result && result.code) {  
72 - t.pass();  
73 - } else {  
74 - t.fail();  
75 - }  
76 - });  
77 -});  
78 -  
79 -test('api multiple call test', (t) => {  
80 - let api = new ServiceAPI();  
81 - let multi = [api.get(getUrl, sign.apiSign({})), api.get(getUrl, sign.apiSign({}))];  
82 -  
83 - return api.all(multi).then(result => {  
84 - if (result.length === 2) {  
85 - t.pass();  
86 - } else {  
87 - t.fail();  
88 - }  
89 - });  
90 -});  
91 -  
92 -test('api multiple fail call test', (t) => {  
93 - let api = new ServiceAPI();  
94 -  
95 - return api.all(1).catch((e) => {  
96 - if (e) {  
97 - t.pass();  
98 - } else {  
99 - t.fail();  
100 - }  
101 - });  
102 -});  
1 -/**  
2 - * cache 测试  
3 - *  
4 - * @author: jf<jeff.jiang@yoho.cn>  
5 - * @date: 2016/5/18  
6 - */  
7 -  
8 -'use strict';  
9 -  
10 -import test from 'ava';  
11 -  
12 -import cache from '../../library/cache';  
13 -  
14 -let testKey = 'test_unit_key:' + (new Date()).getTime();  
15 -let testValue = 'anotherValue';  
16 -let anotherKey = 'test_unit_key2:' + (new Date()).getTime();  
17 -let anotherValue = {a: 1};  
18 -  
19 -let slaveTestKey = 'test_unit_key3:' + (new Date()).getTime();  
20 -let slaveTestValue = 'anotherValue3';  
21 -  
22 -test.before('set test key', (t) => {  
23 - cache.set(testKey, testValue);  
24 - cache.set(anotherKey, anotherValue);  
25 - t.pass();  
26 -});  
27 -  
28 -test.after('del test key', (t) => {  
29 - cache.del(testKey);  
30 - cache.del(anotherKey);  
31 - t.pass();  
32 -});  
33 -  
34 -test('cache get test', (t) => {  
35 - return cache.get(testKey).then((v) => {  
36 - t.is(v, testValue);  
37 - });  
38 -});  
39 -  
40 -test('cache get multi test', (t) => {  
41 - cache.set(anotherKey, anotherValue);  
42 - return cache.getMulti([testKey, anotherKey]).then((values) => {  
43 - t.is(values[testKey], testValue);  
44 - t.is(values[anotherKey], JSON.stringify(anotherValue));  
45 - });  
46 -});  
47 -  
48 -test('cache get from slave test', (t) => {  
49 - return cache.getFromSlave(testKey).then((v) => {  
50 - t.is(v, testValue);  
51 - });  
52 -});  
53 -  
54 -test('cache get multi from slave test', (t) => {  
55 - cache.set(anotherKey, anotherValue);  
56 - return cache.getMultiFromSlave([testKey, anotherKey]).then((values) => {  
57 - t.is(values[testKey], testValue);  
58 - t.is(values[anotherKey], JSON.stringify(anotherValue));  
59 - });  
60 -});  
61 -  
62 -test('cache set to slave', (t) => {  
63 - return cache.setSlave(slaveTestKey, {  
64 - value: slaveTestValue  
65 - }).then(() => {  
66 - return cache.getFromSlave(slaveTestKey);  
67 - }).then((v) => {  
68 - v = JSON.parse(v);  
69 - t.is(v.value, slaveTestValue);  
70 - cache.del(slaveTestKey);  
71 - });  
72 -});  
73 -  
74 -test('cache get test, key is not a string', (t) => {  
75 - return cache.get(123).then((v) => {  
76 - t.notOk(v);  
77 - });  
78 -});  
79 -  
80 -test('cache get multi test, key is not an array', (t) => {  
81 - return cache.getMulti(123).then((v) => {  
82 - t.notOk(v);  
83 - });  
84 -});  
85 -  
86 -test('cache get from slave test, key is not a string', (t) => {  
87 - return cache.getFromSlave(123).then((v) => {  
88 - t.notOk(v);  
89 - });  
90 -});  
91 -  
92 -test('cache get multi from slave test, key is not an array', (t) => {  
93 - return cache.getMultiFromSlave(123).then((v) => {  
94 - t.notOk(v);  
95 - });  
96 -});  
97 -  
98 -test('cache set test, key is not a string', (t) => {  
99 - return cache.set(123).then((v) => {  
100 - t.notOk(v);  
101 - });  
102 -});  
103 -  
104 -test('cache set multi test, key is not an array', (t) => {  
105 - return cache.setSlave(123).then((v) => {  
106 - t.notOk(v);  
107 - });  
108 -});  
109 -  
110 -test('cache del test, key is not a string', (t) => {  
111 - return cache.del(123).then((v) => {  
112 - t.notOk(v);  
113 - });  
114 -});  
1 -/**  
2 - * 对象键名驼峰测试  
3 - *  
4 - * @author: jiangfeng<jeff.jiang@yoho.cn>  
5 - * @date: 2016/05/17  
6 - */  
7 -  
8 -import {test} from 'ava';  
9 -  
10 -const camelCase = require('../../library/camel-case');  
11 -  
12 -test('camel case object', t => {  
13 - let o = {  
14 - A_B: 'ab_cd'  
15 - };  
16 -  
17 - t.is(camelCase(o).aB, 'ab_cd');  
18 -});  
19 -  
20 -test('camel case array', t => {  
21 - let arr = [{  
22 - A_B: 'ab_cd'  
23 - }, {  
24 - A_B: 'ab_cd'  
25 - }];  
26 -  
27 - t.is(camelCase(arr)[1].aB, 'ab_cd');  
28 -});  
1 -/**  
2 - * library helpers 类单元测试  
3 - * @author jeff.jiang<jeff.jiang@yoho.cn>  
4 - * @date 2016/05/17  
5 - */  
6 -  
7 -'use strict';  
8 -  
9 -const test = require('ava');  
10 -const helpers = require('../../library/helpers');  
11 -  
12 -test('qiniu image url handle', t => {  
13 - let url = 'http://img11.static.yhbimg.com/yhb-img01/2016/04/18/03/016d50b20cfdec5a91c614b68546bc9d72.jpg?imageView2/{mode}/w/{width}/h/{height}';  
14 - let expected = 'http://img11.static.yhbimg.com/yhb-img01/2016/04/18/03/016d50b20cfdec5a91c614b68546bc9d72.jpg?imageView2/2/w/400/h/300';  
15 -  
16 - t.is(helpers.image(url, 400, 300), expected);  
17 -});  
18 -  
19 -test('uri format', t => {  
20 - let uri = '/test';  
21 - let qs = { name: 'yoho' };  
22 - let mod = 'list';  
23 - let expected = '//list.m.yohobuy.com/test?name=yoho';  
24 -  
25 - t.is(helpers.urlFormat(uri, qs, mod), expected);  
26 -});  
27 -  
28 -test('upper char to lowercase', t => {  
29 - let str = 'ABc';  
30 - let expected = 'abc';  
31 -  
32 - t.is(helpers.lowerCase(str), expected);  
33 -});  
34 -  
35 -test('lower char to uppercase', t => {  
36 - let str = 'abc!';  
37 - let expected = 'ABC!';  
38 -  
39 - t.is(helpers.upperCase(str), expected);  
40 -});  
41 -  
42 -test('date format test', (t) => {  
43 - let seconds = 60 * 60 * 3 + 2;  
44 - let str = helpers.dateFormat('HH:mm:ss', seconds, 'ss');  
45 - let expected = '03:00:02';  
46 -  
47 - t.is(str, expected);  
48 -});  
49 -  
50 -test('date diff format test', (t) => {  
51 - let seconds = 60 * 60 * 24 * (1.3) + 2;  
52 -  
53 - let str = helpers.dateDiffFormat('{d}天{h}小时', seconds, 's');  
54 -  
55 - console.log(str);  
56 - t.pass();  
57 -});  
1 -/**  
2 - * logger 工具类测试  
3 - */  
4 -  
5 -const test = require('ava');  
6 -const logger = require('../../library/logger');  
7 -  
8 -test('logger error test', t => {  
9 - logger.error('error test', () => {  
10 - t.pass();  
11 - });  
12 -});  
13 -  
14 -test('logger info test', t => {  
15 - logger.info('info test', () => {  
16 - t.pass();  
17 - });  
18 -});  
1 -/**  
2 - * 签名类测试  
3 - *  
4 - * @author: jiangfeng<jeff.jiang@yoho.cn>  
5 - * @date: 2016/05/17  
6 - */  
7 -  
8 -const test = require('ava');  
9 -const sign = require('../../library/sign');  
10 -  
11 -test('app sign test', t => {  
12 - let params = {  
13 - client_type: 'h5', // eslint-disable-line  
14 - a: 1,  
15 - b: 'b'  
16 - };  
17 - let signedParams = sign.apiSign(params);  
18 -  
19 - t.true(sign.checkSign(signedParams));  
20 -});  
21 -  
22 -test('app sign test webSign', t => {  
23 - let params = {  
24 - uid: '123',  
25 - key: '3fc5a9fcea9fea49cce5432202a167ad'  
26 - };  
27 -  
28 - t.true(sign.webSign(params));  
29 -});  
1 -/**  
2 - * Timer 计时类测试  
3 - *  
4 - * @author: jiangfeng<jeff.jiang@yoho.cn>  
5 - * @date: 2016/05/17  
6 - */  
7 -  
8 -const test = require('ava');  
9 -const Timer = require('../../library/timer');  
10 -  
11 -const sleep = (timeout) => {  
12 - return new Promise((resolve) => {  
13 - setTimeout(() => {  
14 - resolve();  
15 - }, timeout);  
16 - });  
17 -};  
18 -  
19 -test.cb('timer class ', t => {  
20 - let timer = new Timer();  
21 -  
22 - timer.put('test');  
23 - sleep(300).then(() => {  
24 - let diff = timer.put('test');  
25 -  
26 - t.true(diff >= 300);  
27 - t.end();  
28 - });  
29 -});