Authored by 毕凯

Merge branch 'develop' of git.yoho.cn:fe/yohobuy-node into develop

... ... @@ -17,8 +17,8 @@ const path = require('path');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const favicon = require('serve-favicon');
const session = require('yoho-express-session');
const memcached = require('yoho-connect-memcached');
const session = require('express-session');
const memcached = require('connect-memcached');
const hbs = require('express-handlebars');
const pkg = require('./package.json');
... ...
... ... @@ -10,7 +10,7 @@ const _ = require('lodash');
const channelModel = require('../models/index');
exports.index = (req, res) => {
exports.index = (req, res, next) => {
let channelType = req.path.substring(1) || 'boys';
// 将woman转换为girls,以便model层进行处理
... ... @@ -18,18 +18,18 @@ exports.index = (req, res) => {
channelModel.getContent(channelType).then(data => {
res.render('channel', data);
});
}).catch(next);
};
exports.getbrandFloorDataAjax = (req, res) => {
exports.getbrandFloorDataAjax = (req, res, next) => {
const channelType = req.query.channelType || 'boys';
channelModel.getbrandFloorDataAjax(channelType).then(data => {
res.json(data);
});
}).catch(next);
};
exports.getNewArrival = (req, res) => {
exports.getNewArrival = (req, res, next) => {
let reqBody = req.body,
pageIndex = reqBody.pageIndex,
pageCount = reqBody.pageCount,
... ... @@ -54,5 +54,5 @@ exports.getNewArrival = (req, res) => {
};
}
res.send(result);
});
}).catch(next);
};
... ...
/**
* girls model
* 频道页 model
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/05/17
*/
... ... @@ -7,11 +7,15 @@
const _ = require('lodash');
const dataMap = require('../../../config/data-map');
const ServiceAPI = require(`${global.library}/api`).ServiceAPI;
const SearchAPI = require(`${global.library}/api`).SearchAPI;
const sign = require(`${global.library}/sign`);
const helpers = require(`${global.library}/helpers`);
const images = require(`${global.utils}/images`);
// const processProduct = require(`${global.utils}/product-process`).processProductList;
const log = require(`${global.library}/logger`);
... ... @@ -25,9 +29,7 @@ const getShelveTime = duration => {
todayMil = today.getTime().toString().substr(0, 10),
startDayMil = (today.setMonth(today.getMonth() - duration)).toString().substr(0, 10);
return `${startDayMil},${todayMil}`;
// `
return `${startDayMil},${todayMil}`; // `返回时间
};
// 获取可用的标题
... ... @@ -46,112 +48,15 @@ const getText = data => {
return text;
};
const channelMap = {
boys: {
code: '79372627eee75d73afe7f9bac91e5ce6',
gender: '1,3'
},
girls: {
code: '75215008957605c05e8cd375eac4f817',
gender: '2,3'
},
kids: {
code: 'd71f4b27f2a7229fbb31a4bc490a6f36',
gender: '2,3'
},
lifestyle: {
code: '8a341ca7eacc069ba80f02dec80eaf34',
// code: '380c38155fd8beee10913a3f5b462da6',
// code: '665f7c2fb9d037ee820766953ee34bf7',
gender: '2,3'
}
};
const channelMap = dataMap.channel;
const sortMap = {
boys: [
{sort: 147, viewNum: 5}, // 休闲运动鞋
{sort: 129, viewNum: 5}, // 休闲裤
{sort: 152, viewNum: 5}, // 双肩包
{misort: 11, viewNum: 5}, // T恤
{sort: 115, viewNum: 5}, // 衬衫
{sort: 130, viewNum: 5}, // 牛仔裤
{misort: 60, viewNum: 5}, // 帽子
{sort: 124, viewNum: 5}, // 夹克
{sort: 119, viewNum: 5}, // 卫衣
{sort: 162, viewNum: 5}, // 手表
{sort: 148, viewNum: 5}, // 靴子
{misort: 65, viewNum: 5}, // 首饰
{sort: 151, viewNum: 5}, // 时装鞋
{misort: 61, viewNum: 5}, // 太阳镜
{misort: 39, viewNum: 5}, // 袜子
{sort: 346, viewNum: 5}, // 运动裤
{sort: 131, viewNum: 5}, // 短裤
{misort: 66, viewNum: 5}, // 配饰
{misort: 309, viewNum: 5}, // 内裤
{misort: 30, viewNum: 5}, // 打底裤/紧身裤
{sort: 342, viewNum: 5} // 邮差包
],
girls: [
{misort: 16, viewNum: 4}, // 卫衣
{misort: 12, viewNum: 4}, // 衬衫
{misort: 44, viewNum: 4}, // 休闲/运动鞋
{misort: 11, viewNum: 4}, // T恤
{misort: 21, viewNum: 4}, // 夹克
{misort: 257, viewNum: 4}, // 毛衣/针织
{misort: 22, viewNum: 4}, // 大衣/风衣
{misort: 26, viewNum: 4}, // 休闲裤
{misort: 27, viewNum: 4}, // 牛仔裤
{misort: 31, viewNum: 4}, // 连衣裙
{misort: 32, viewNum: 4}, // 半身裙
{misort: 48, viewNum: 4}, // 时装鞋
{misort: 49, viewNum: 4}, // 双肩包
{misort: 50, viewNum: 4}, // 手拎包/单肩包
{misort: 60, viewNum: 4}, // 帽子
{misort: 65, viewNum: 4}, // 首饰
{misort: 59, viewNum: 4}, // 手表
{misort: 61, viewNum: 4}, // 太阳镜
{misort: 66, viewNum: 4} // 配饰
],
kids: [
{misort: 366, viewNum: 4}, // T恤
{misort: 367, viewNum: 4}, // 衬衫
{misort: 396, viewNum: 4}, // 卫衣
{misort: 400, viewNum: 4}, // // 毛衣/针织
{misort: 404, viewNum: 4}, // 夹克
{misort: 369, viewNum: 4}, // 休闲裤
{misort: 388, viewNum: 4}, // 牛仔裤
{misort: 371, viewNum: 4}, // 连衣裙
{misort: 370, viewNum: 4}, // 半身裙
{misort: 368, viewNum: 4}, // 休闲/运动鞋
{misort: 392, viewNum: 4}, // 双肩包
{misort: 414, viewNum: 4}, // 帽子
{misort: 372, viewNum: 4}, // 短裤
{misort: 384, viewNum: 4}, // 打底裤/紧身裤
{misort: 382, viewNum: 4}, // 凉鞋/拖鞋
{misort: 402, viewNum: 4}, // 马甲
{misort: 386, viewNum: 4}, // 背心
{misort: 406, viewNum: 4}, // 大衣/风衣
{misort: 430, viewNum: 4}, // 羽绒服
{misort: 423, viewNum: 4}, // 棉衣
{misort: 417, viewNum: 4} // 套装
],
lifestyle: [
{sort: 171, viewNum: 5}, // 耳机
{sort: 398, viewNum: 5}, // 只能装备
{sort: 185, viewNum: 5}, // 相机
{misort: 259, viewNum: 5}, // 美妆
{sort: 267, viewNum: 5}, // 杯子/水壶
{sort: 313, viewNum: 5}, // 手机/ipad壳套
{sort: 211, viewNum: 5}, // 数码配件
{sort: 292, viewNum: 5}, // 玩偶
{sort: 272, viewNum: 5}, // 储物收纳
{sort: 183, viewNum: 5}, // 启用家居
{sort: 273, viewNum: 5}, // 厨具/餐具
{sort: 271, viewNum: 5} // 靠枕/靠垫/抱枕
]
};
const sortMap = dataMap.sort;
/**
* 获取导航信息
* @param {[Object]} 原始数据
* @return {[Object]} 转换后的数据
*/
const getNavs = rawNavs => {
const navs = rawNavs;
let list = [];
... ... @@ -279,23 +184,29 @@ const formatProduct = (productData, showTags, showNew, showSale, width, height,
return result;
};
const getBannerList = data => {
let list = [];
_.forEach(data, (bannerData) => {
let obj = {};
obj.href = bannerData.url;
obj.img = bannerData.src;
obj.name = bannerData.title;
/**
* 获取普通banner
* @param {[Object]} data 原始数据
* @return {[Object]} 转换后的数据
*/
const getBannerList = data => {
list.push(obj);
return _.map(data, item => {
return Object.assign(item, {
href: item.url,
img: item.src,
name: item.title
});
});
return list;
};
/**
* 获取带小图的banner
* @param {[Object]} data 原始数据
* @return {[Object]} 转换后的数据
*/
const getDebrisSlide = data => {
let floorData = {
debrisSlider: {
... ... @@ -319,6 +230,12 @@ const getDebrisSlide = data => {
return floorData;
};
/**
* 获取广告位
* @param {Object} data 原始数据
* @return {Object} 转换后的数据
*/
const getadbannerData = data => {
const obj = {
adbanner: {
... ... @@ -335,6 +252,11 @@ const getadbannerData = data => {
return obj;
};
/**
* 生成banner模板数据
* @param {Object} srcData 原始数据
* @return {Object} 转换后的数据
*/
const getSlideData = srcData => {
const slideData = {
slide: {
... ... @@ -359,6 +281,11 @@ const getSlideData = srcData => {
};
/**
* 获取最新速报模板数据
* @param {Object} srcData 原始数据
* @return {Object} 转换后的数据
*/
const getNewReportFloorData = args => {
const title = args[0].data.text;
let item = args[1].data;
... ... @@ -412,11 +339,23 @@ const getNewReportFloorData = args => {
return floorDatas;
};
/**
* 给目标对象绑定频道属性
* @param {Object} obj 需要绑定频道属性的对象
* @param {String} type 需要设置的频道类型
* @return undefined
*/
const setChannelType = (obj, type) => {
obj[type + 'Channel'] = true;
};
// 优选品牌
/**
* 获取优选品牌模板数据
* @param {[Object]} args 参数列表
* @param {String} type 频道类型
* @return {Object}
*/
const getPreBrandTopData = (args, type) => {
const title = args[0].data.text;
let item = args[1].data;
... ... @@ -443,7 +382,12 @@ const getPreBrandTopData = (args, type) => {
return data;
};
// 热门品类
/**
* 获取热门分类模板数据
* @param {[Object]} args 参数列表
* @param {String} type 频道类型
* @return {Object}
*/
const getHotGoodsFloorData = (args, type) => {
let item = args[0];
let nextItem = args[1];
... ... @@ -523,6 +467,12 @@ const getHotGoodsFloorData = (args, type) => {
};
/**
* 获取boys人气单品模版数据
* @param {[Object]} args 参数列表
* @param {String} type 频道类型
* @return {Object}
*/
const getBoysSingleHot = (args, type) => {
const len = 10;
const data = {
... ... @@ -568,6 +518,12 @@ const getBoysSingleHot = (args, type) => {
return floorDatas;
};
/**
* 获取girls人气单品模版数据
* @param {[Object]} args 参数列表
* @param {String} type 频道类型
* @return {Object}
*/
const getGirlsSingleHot = args => {
let goods = args[2].data;
let skns = '';
... ... @@ -591,7 +547,12 @@ const getGirlsSingleHot = args => {
};
// 人气单品
/**
* 人气单品入口
* @param {[Object]} args 参数列表
* @param {String} type 频道类型
* @return {Object}
*/
const getSingleHotFloorData = (args, type) => {
if (type === 'boys') {
return getBoysSingleHot(args, type);
... ... @@ -600,6 +561,13 @@ const getSingleHotFloorData = (args, type) => {
}
};
/**
* 处理异步获取的人气单品数据
* @param {[Object]} args 参数列表
* @param {Object} queryResult 异步获取的数据
* @param {String} type 频道类型
* @return {Object}
*/
const getAsyncSingleHot = (args, queryResult, type) => {
const data = {
singlehot: {
... ... @@ -622,32 +590,42 @@ const getAsyncSingleHot = (args, queryResult, type) => {
});
}
_.forEach(queryResult.data.product_list, (it, index) => {
let obj = {};
const formatData = formatProduct(it, true, true, true, 280, 373);
if (index > 12) {
return;
}
if (queryResult.data) {
_.forEach(queryResult.data.product_list || [], (it, index) => {
let obj = {};
const formatData = formatProduct(it, true, true, true, 280, 373);
if (index > 12) {
return;
}
obj.price = formatData.salePrice;
obj.href = formatData.url;
obj.img = formatData.thumb;
obj.name = formatData.name;
if (index < 3) {
obj.tip = 'TOP' + (index + 1);
}
obj.price = formatData.salePrice;
obj.href = formatData.url;
obj.img = formatData.thumb;
obj.name = formatData.name;
data.singlehot.imgHot.push(obj);
});
if (index < 3) {
obj.tip = 'TOP' + (index + 1);
}
data.singlehot.imgHot.push(obj);
});
}
data.singlehot.navs = getNavs(args[1].data);
setChannelType(data.singlehot, type);
return data;
};
/**
* 异步获取人气单品
* @param {[Object]} rawData 接口返回的原始数据
* @param {[Object]} floorData 已经经过处理的楼层数据
* @param {Object} queryResult 接口中用于请求人气单品的数据
* @param {String} title 人气单品楼层的标题
* @param {Type} type 人气单品楼层的类型
* @return {Object}
*/
const processFloorDataWithQueryReusult = (rawData, floorData, queryResult, title, type) => {
let data = {};
... ... @@ -716,7 +694,7 @@ exports.getNewArrival = channel => {
result = [];
_.forEach(sortList, (it, index) => {
if (res[index].data.product_list.length === sortList[index].viewNum) {
if (res[index].data.product_list && res[index].data.product_list.length === sortList[index].viewNum) {
data = data.concat(res[index].data.product_list);
}
});
... ... @@ -825,8 +803,9 @@ const processFloorData = (rawData, type) => {
let floorList = [];
let searchPromise = [];
let singlehotFloorIndex = [];
let singlehotFloorTitile = [];
let singlehotFloorTitle = [];
// 定义各种楼层需要用到的接口返回的数组中元素的个数
const bigFloorLength = 5;
const normalFloorLength = 3;
const hotCategoryLength = 2;
... ... @@ -876,7 +855,7 @@ const processFloorData = (rawData, type) => {
singlehotFloorIndex.push(floorList.length);
// 记住楼层标题, 以便后面promise获取数据后插入楼层数据
singlehotFloorTitile.push(getText(data.data.text));
singlehotFloorTitle.push(getText(data.data.text));
} else if (!_.isNil(floorData)) {
_.isArray(floorData) ?
floorList = floorList.concat(floorData) :
... ... @@ -887,7 +866,7 @@ const processFloorData = (rawData, type) => {
return {
floors: floorList,
promise: _.reverse(searchPromise),
singlehotFloorTitile: _.reverse(singlehotFloorTitile),
singlehotFloorTitle: _.reverse(singlehotFloorTitle),
singlehotFloorIndex: _.reverse(singlehotFloorIndex)
};
};
... ... @@ -895,8 +874,8 @@ const processFloorData = (rawData, type) => {
/**
* 获取频道页数据
* @param {string} type 传入频道页类型,值可以是: boys, girls, kids, lifestyle
* @return {object}
* @param {String} type 传入频道页类型,值可以是: boys, girls, kids, lifestyle
* @return {Object}
*/
exports.getContent = type => {
return Promise.all([headerModel.requestHeaderData(type), requestContent(type)]).then(res => {
... ... @@ -921,7 +900,7 @@ exports.getContent = type => {
floorData: data,
searchPromise: processResult.promise,
singlehotFloorIndex: processResult.singlehotFloorIndex,
singlehotFloorTitile: processResult.singlehotFloorTitile,
singlehotFloorTitle: processResult.singlehotFloorTitle,
channelType: type
};
... ... @@ -934,7 +913,7 @@ exports.getContent = type => {
result.floorData.channel
.splice(result.singlehotFloorIndex[index], 0,
processFloorDataWithQueryReusult(result.rawData,
result.floorData, data, result.singlehotFloorTitile[index], result.channelType));
result.floorData, data, result.singlehotFloorTitle[index], result.channelType));
});
return result.floorData;
... ...
/**
* 共用的map数据常量定义
* @author: 赵彪<bill.zhao@yoho.cn>
* @date: 2016/06/23
*/
'use strict';
const channelMap = {
boys: {
code: '79372627eee75d73afe7f9bac91e5ce6',
gender: '1,3'
},
girls: {
code: '75215008957605c05e8cd375eac4f817',
gender: '2,3'
},
kids: {
code: 'd71f4b27f2a7229fbb31a4bc490a6f36',
gender: '2,3'
},
lifestyle: {
code: '8a341ca7eacc069ba80f02dec80eaf34',
// code: '380c38155fd8beee10913a3f5b462da6',
// code: '665f7c2fb9d037ee820766953ee34bf7',
gender: '2,3'
}
};
const sortMap = {
boys: [
{sort: 147, viewNum: 5}, // 休闲运动鞋
{sort: 129, viewNum: 5}, // 休闲裤
{sort: 152, viewNum: 5}, // 双肩包
{misort: 11, viewNum: 5}, // T恤
{sort: 115, viewNum: 5}, // 衬衫
{sort: 130, viewNum: 5}, // 牛仔裤
{misort: 60, viewNum: 5}, // 帽子
{sort: 124, viewNum: 5}, // 夹克
{sort: 119, viewNum: 5}, // 卫衣
{sort: 162, viewNum: 5}, // 手表
{sort: 148, viewNum: 5}, // 靴子
{misort: 65, viewNum: 5}, // 首饰
{sort: 151, viewNum: 5}, // 时装鞋
{misort: 61, viewNum: 5}, // 太阳镜
{misort: 39, viewNum: 5}, // 袜子
{sort: 346, viewNum: 5}, // 运动裤
{sort: 131, viewNum: 5}, // 短裤
{misort: 66, viewNum: 5}, // 配饰
{misort: 309, viewNum: 5}, // 内裤
{misort: 30, viewNum: 5}, // 打底裤/紧身裤
{sort: 342, viewNum: 5} // 邮差包
],
girls: [
{misort: 16, viewNum: 4}, // 卫衣
{misort: 12, viewNum: 4}, // 衬衫
{misort: 44, viewNum: 4}, // 休闲/运动鞋
{misort: 11, viewNum: 4}, // T恤
{misort: 21, viewNum: 4}, // 夹克
{misort: 257, viewNum: 4}, // 毛衣/针织
{misort: 22, viewNum: 4}, // 大衣/风衣
{misort: 26, viewNum: 4}, // 休闲裤
{misort: 27, viewNum: 4}, // 牛仔裤
{misort: 31, viewNum: 4}, // 连衣裙
{misort: 32, viewNum: 4}, // 半身裙
{misort: 48, viewNum: 4}, // 时装鞋
{misort: 49, viewNum: 4}, // 双肩包
{misort: 50, viewNum: 4}, // 手拎包/单肩包
{misort: 60, viewNum: 4}, // 帽子
{misort: 65, viewNum: 4}, // 首饰
{misort: 59, viewNum: 4}, // 手表
{misort: 61, viewNum: 4}, // 太阳镜
{misort: 66, viewNum: 4} // 配饰
],
kids: [
{misort: 366, viewNum: 4}, // T恤
{misort: 367, viewNum: 4}, // 衬衫
{misort: 396, viewNum: 4}, // 卫衣
{misort: 400, viewNum: 4}, // // 毛衣/针织
{misort: 404, viewNum: 4}, // 夹克
{misort: 369, viewNum: 4}, // 休闲裤
{misort: 388, viewNum: 4}, // 牛仔裤
{misort: 371, viewNum: 4}, // 连衣裙
{misort: 370, viewNum: 4}, // 半身裙
{misort: 368, viewNum: 4}, // 休闲/运动鞋
{misort: 392, viewNum: 4}, // 双肩包
{misort: 414, viewNum: 4}, // 帽子
{misort: 372, viewNum: 4}, // 短裤
{misort: 384, viewNum: 4}, // 打底裤/紧身裤
{misort: 382, viewNum: 4}, // 凉鞋/拖鞋
{misort: 402, viewNum: 4}, // 马甲
{misort: 386, viewNum: 4}, // 背心
{misort: 406, viewNum: 4}, // 大衣/风衣
{misort: 430, viewNum: 4}, // 羽绒服
{misort: 423, viewNum: 4}, // 棉衣
{misort: 417, viewNum: 4} // 套装
],
lifestyle: [
{sort: 171, viewNum: 5}, // 耳机
{sort: 398, viewNum: 5}, // 只能装备
{sort: 185, viewNum: 5}, // 相机
{misort: 259, viewNum: 5}, // 美妆
{sort: 267, viewNum: 5}, // 杯子/水壶
{sort: 313, viewNum: 5}, // 手机/ipad壳套
{sort: 211, viewNum: 5}, // 数码配件
{sort: 292, viewNum: 5}, // 玩偶
{sort: 272, viewNum: 5}, // 储物收纳
{sort: 183, viewNum: 5}, // 启用家居
{sort: 273, viewNum: 5}, // 厨具/餐具
{sort: 271, viewNum: 5} // 靠枕/靠垫/抱枕
]
};
module.exports = {
channel: channelMap,
sort: sortMap
};
... ...
... ... @@ -33,9 +33,11 @@
"dependencies": {
"bluebird": "^3.4.0",
"body-parser": "^1.15.0",
"connect-memcached": "^0.2.0",
"cookie-parser": "^1.4.3",
"express": "^4.13.1",
"express-handlebars": "^3.0.0",
"express-session": "^1.13.0",
"influxdb-winston": "^1.0.1",
"lodash": "^4.13.1",
"md5": "^2.1.0",
... ...