Authored by 徐祁xuqi

Merge branch 'release/4.6' of http://git.yoho.cn/fe/yohobuy-node into release/4.6

Conflicts:
	doraemon/models/header.js
... ... @@ -53,7 +53,7 @@ exports.getListData = (params, channel) => {
filters: publicHandler.handleSaleFilterData(result[1].data.filter, params),
opts: publicHandler.handleSaleOptsData(params, result[1].data.total),
totalCount: result[1].data.total,
pager: publicHandler.handleSalePagerData(result[1].data.total, params),
footPager: publicHandler.handlePagerData(result[1].data.total, params),
goods: productProcess.processProductList(result[1].data.product_list)
});
}
... ...
... ... @@ -99,8 +99,6 @@ exports.getOutletsIndexData = (params, channel) => {
finalResult.goodsBoard.sort.nextHref += '#otspool';
}
}
// finalResult.goodsBoard.pager = publicHandler.handleSalePagerData(result[5].data.total, params);
finalResult.goodsBoard.footPager = publicHandler.handlePagerData(result[5].data.total, params);
}
... ... @@ -174,7 +172,7 @@ exports.getOutletsChannelData = (params, channel) => {
channelData.goodsBoard.sort = publicHandler.handleSaleOptsData(params, result[3].data.total);
channelData.goodsBoard.sort.newPage = true;
channelData.goodsBoard.list = productProcess.processProductList(result[3].data.product_list);
channelData.goodsBoard.pager = publicHandler.handleSalePagerData(result[3].data.total, params);
channelData.goodsBoard.footPager = publicHandler.handlePagerData(result[3].data.total, params);
// 添加锚点
if (channelData.goodsBoard.sort.sortType) {
... ...
... ... @@ -198,87 +198,6 @@ const handleBrandCheckedData = (params, origin) => {
};
/**
* 处理分页数据
* @param {[type]} total [description]
* @param {[type]} params [description]
* @return {[type]} [description]
*/
exports.handleSalePagerData = (total, params) => {
// 当前页
let currentPage = parseInt((_.isEmpty(params.page) ? 1 : params.page), 10);
let perPageCount = parseInt((_.isEmpty(params.limit) ? 60 : params.limit), 10);
let totalPage = parseInt(total / perPageCount, 10) + 1;
let dest = '<a href ="' + handleFilterUrl(params, {page: (currentPage)}) +
'" class="cur">' + currentPage + '</a>';
// 先处理大多数情况
if ((currentPage > 2) && (currentPage < (parseInt(total, 10) / perPageCount) - 1)) {
for (let i = 1; i < 3; i++) {
dest = '<a href ="' + handleFilterUrl(params, {page: (currentPage - i)}) + '">' +
(currentPage - i) + '</a> ' + dest + ' <a href ="' +
handleFilterUrl(params, {page: (currentPage + i)}) +
'">' + (currentPage + i) + '</a>';
}
// 处理页码小于2的情况
} else if (currentPage <= 2) {
for (let i = currentPage - 1; i > 0; i--) {
dest = '<a href ="' + handleFilterUrl(params, {page: i}) +
'">' + i + '</a>' + dest;
}
for (let i = currentPage + 1; i < (totalPage < 6 ? totalPage : 6); i++) {
dest += '<a href ="' + handleFilterUrl(params, {page: i}) +
'">' + i + '</a>';
}
// 处理页码大于最大页-2的情况
} else if (currentPage > totalPage - 2) {
for (let i = currentPage + 1; i < totalPage + 1; i++) {
dest += '<a href ="' + handleFilterUrl(params, {page: i}) +
'">' + i + '</a>';
}
for (let i = currentPage - 1; i > (currentPage - 5 > 1 ? currentPage - 5 : 1); i--) {
dest = '<a href ="' + handleFilterUrl(params, {page: i}) +
'">' + i + '</a>' + dest;
}
}
// 处理后省略号的情况
if ((totalPage > 5) && (currentPage < (totalPage - 2))) {
dest += ' ... <a href ="' + handleFilterUrl(params, {page: totalPage}) +
'">' + totalPage + '</a>';
}
// 处理前省略号的情况
if (currentPage > 3) {
dest = '<a href ="' + handleFilterUrl(params, {page: 1}) +
'">1</a> ... ' + dest;
}
// 处理上一页下一页
if (currentPage !== 1) {
dest = '<a href="' + handleFilterUrl(params, {page: currentPage - 1}) +
'"><span class="iconfont">&#xe60e;上一页</span></a>' + dest;
}
if (currentPage !== totalPage) {
dest += '<a href="' + handleFilterUrl(params, {page: currentPage + 1}) +
'"><span class="iconfont">下一页&#xe60c;</span></a>';
}
return dest;
};
/**
* 处理 opts 排序数据
* @param params
* @param total
... ... @@ -445,8 +364,8 @@ exports.handleSaleSortData = (origin, params, extra) => {
leftContent.allSort.list = [];
// 删除 msort misort
delete params.msort;
delete params.misort;
// delete params.msort;
// delete params.misort;
if (extra === 'discount') {
let tempParams = _.cloneDeep(params);
... ... @@ -834,9 +753,10 @@ exports.handlePagerData = (total, params) => {
start: (currentPage ? currentPage - 1 : 0) * perPageCount + 1
};
dest.tip.end = (total < perPageCount) ? total : ((dest.pageCount === parseInt(params.page, 10)) ?
total : parseInt(dest.tip.start, 10) + perPageCount - 1);
let endPageNum = (totalPage === currentPage) ?
total : parseInt(dest.tip.start, 10) + perPageCount - 1;
dest.tip.end = (total < perPageCount) ? total : endPageNum;
dest.pages = _.concat(prevPages, pages, nextPages);
return dest;
... ...
... ... @@ -8,6 +8,8 @@
'use strict';
const library = '../../../library';
const utils = '../../../utils';
const logger = require(`${library}/logger`);
const camelCase = require(`${library}/camel-case`);
const API = require(`${library}/api`).API;
const api = new API();
const saleApi = require('./sale-api');
... ... @@ -67,14 +69,22 @@ exports.getSaleGoodsData = (params) => {
delete finalResult.goods[key].tags.isSale;// 屏蔽 sale 标签
delete finalResult.goods[key].discount; // 屏蔽折扣信息
});
} else {
logger.error('search sale api code no 200');
}
// 处理 VIP 商品数据
if (result[1].code === 200) {
let userInfo = result[1].data.vip_info ? camelCase(result[1].data.vip_info) : {};
if (_.isEmpty(userInfo)) {
logger.info('no user info');
}
if (params.saleType === '2') {
_.forEach(finalResult.goods, (value, key) => {
switch (result[1].curLevel) {
switch (userInfo.curLevel) {
case '1':
finalResult.goods[key].salePrice = value.vip1Price;
finalResult.goods[key].vip1 = true;
... ... @@ -150,12 +160,16 @@ exports.getSaleIndexData = (channel) => {
// 折扣专场活动处理
if (result[1].code === 200) {
finalResult.brandSale = saleHandler.handleSaleActivityData(result[1].data, channel);
} else {
logger.error('discount activities api code no 200');
}
// 资源位数据处理
if (result[2].code === 200 && !_.isEmpty(result[2].data)) {
finalResult.topBanner = saleHandler.handleSaleBannerData(result[2].data);
finalResult.activityEnter = saleHandler.handleSaleBannerSmallData(result[2].data);
} else {
logger.error('content code api code no 200 or no data');
}
// 分类处理
... ... @@ -170,6 +184,8 @@ exports.getSaleIndexData = (channel) => {
{forVip: true}
)
);
} else {
logger.error('index vip category api code no 200');
}
// 断码区分类处理
... ... @@ -198,10 +214,14 @@ exports.getSaleIndexData = (channel) => {
finalResult.saleCategory.push(
saleHandler.handleSaleCategoryData(result[5].data.filter.group_sort, '3', channel)
);
} else {
logger.error('newSale category api code no 200');
}
return finalResult;
});
} else {
logger.error('breakYards category api code no 200');
}
return finalResult;
});
... ... @@ -245,6 +265,8 @@ exports.getSaleOthersData = (params, channel) => {
if (!_.isEmpty(result[1].data.filter)) {
// 顶部筛选条件
finalResult.filters = publicHandler.handleSaleFilterData(result[1].data.filter, params);
} else {
logger.error('data filter is empty');
}
// 处理排序数据
... ... @@ -268,6 +290,8 @@ exports.getSaleOthersData = (params, channel) => {
// 获取焦点图数据
if (result[2].code === 200) {
finalResult.topBanner = saleHandler.handleSaleBannerData(result[2].data);
} else {
logger.error('content code api code no 200');
}
// 获取分类筛选数据
... ... @@ -276,14 +300,22 @@ exports.getSaleOthersData = (params, channel) => {
// 获取左侧类目数据
finalResult.leftContent = publicHandler.handleSaleSortData(result[3].data.filter.group_sort, params);
} else {
logger.error('left content category api code no 200');
}
if (!_.isEmpty(result[4])) {
// 处理 VIP 商品数据
if (result[4].code === 200 && parseInt(params.saleType, 10) === 2) {
let userInfo = result[4].data.vip_info ? camelCase(result[4].data.vip_info) : {};
if (_.isEmpty(userInfo)) {
logger.info('no user info');
}
_.forEach(finalResult.goods, (value, key) => {
switch (result[4].curLevel) {
switch (userInfo.curLevel) {
case '1':
finalResult.goods[key].salePrice = value.vip1_price;
finalResult.goods[key].vip1 = true;
... ... @@ -303,7 +335,11 @@ exports.getSaleOthersData = (params, channel) => {
}
});
} else {
logger.error('user api code no 200 or no vip category');
}
} else {
logger.error('no user info get from api');
}
return finalResult;
... ... @@ -331,6 +367,8 @@ exports.getSaleDiscountData = (params, channel) => {
opts: publicHandler.handleSaleOptsData(params, result[1].data.total)
}
});
} else {
logger.error('category api no 200');
}
// 处理折扣专场标题 banner 数据
... ... @@ -345,6 +383,8 @@ exports.getSaleDiscountData = (params, channel) => {
finalResult.topBanner.list[0] = {
img: result[2].data[0].web_url
};
} else {
logger.error('discount activity data[0] web_url empty');
}
}
... ... @@ -361,10 +401,14 @@ exports.getSaleDiscountData = (params, channel) => {
delete finalResult.saleList.goods[key].tags.isSale;// 屏蔽 sale 标签
delete finalResult.saleList.goods[key].discount; // 屏蔽折扣信息
});
} else {
logger.error('discount goods list api code no 200');
}
return finalResult;
});
} else {
logger.error('discount activity api code no 200');
}
return finalResult;
});
... ... @@ -387,6 +431,8 @@ exports.getSalebreakingYardsData = (params, channel) => {
// 处理 banner 数据
if (result[1].code === 200) {
finalResult.topBanner = saleHandler.handleSaleBannerData(result[1].data);
} else {
logger.error('content code api code no 200');
}
// 断码区产品筛选需要断码区尺码数据,改为串行处理
... ... @@ -411,6 +457,8 @@ exports.getSalebreakingYardsData = (params, channel) => {
if (subResult[0].code === 200) {
finalResult.leftContent =
publicHandler.handleSaleSortData(subResult[0].data.filter.group_sort, params);
} else {
logger.error('breakYards left content api code no 200');
}
// 处理商品数据
... ... @@ -439,9 +487,13 @@ exports.getSalebreakingYardsData = (params, channel) => {
delete finalResult.goods[key].tags.isSale;// 屏蔽 sale 标签
delete finalResult.goods[key].discount; // 屏蔽折扣信息
});
} else {
logger.error('breakYards goods list code no 200');
}
return finalResult;
});
} else {
logger.error('breakYards size api code no 200');
}
});
};
... ...
... ... @@ -2,13 +2,13 @@
<div class="extra-pack">
<div class="extra-title">{{title}}</div>
<div class="brand-list">
<ul class="list-page">
<div class="list-page">
{{# brands}}
<li class="brand-item">
<div class="brand-item">
<img class="lazy" data-original="{{image logo 112 77}}">
</li>
</div>
{{/ brands}}
</ul>
</div>
</div>
<div class="brand-page-btns page-btns" onselectstart="return false">
<label class="iconfont pre-page-btn">&#xe609;</label>
... ...
... ... @@ -61,6 +61,7 @@ const getNavBar = (data, type) => {
obj.link = item.sort_url;
obj.cn = item.sort_name;
obj.en = item.sort_name_en;
obj.isNewPage = item.is_new_page === 'Y' ? true : false;
if (type === lowEn) {
obj.active = true;
... ...
... ... @@ -68,13 +68,13 @@
{{# navbars}}
<li {{#if active}} class="cure"{{/if}}{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}>
{{#if ico}}
<a href="{{link}}" class="menu-ico"></a>
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"></a>
{{^}}
<h3 class="name-cn">
<a href="{{link}}">{{cn}}</a>
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}>{{cn}}</a>
</h3>
<h3 class="name-en">
<a href="{{link}}">{{en}}</a>
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}>{{en}}</a>
</h3>
{{/if}}
</li>
... ...
... ... @@ -17,6 +17,29 @@ const api = config.domains.api;
const serviceApi = config.domains.service;
const searchApi = config.domains.search;
// 错误返回
const API_BAD_RETSULT = {
code: 500,
message: 'API result is not JSON string or null.'
};
// 调用失败
const API_CALL_FAIL = {
code: 500,
message: 'Call API failed.'
};
// all 方法错误的传参
const API_ALL_METHOD_ERROR = 'the parameters of api all method should be Array!';
// 获取缓存数据失败
const SLAVE_CACHE_FAIL = 'get slave cache fail';
const MASTER_CACHE_FAIL = 'get master cache fail';
// 获取缓存数据成功
const SLAVE_CACHE_SUCCESS = 'get slave cache success';
const MASTER_CACHE_SUCCESS = 'get master cache success';
class Http {
constructor(baseUrl) {
... ... @@ -37,18 +60,14 @@ class Http {
const timer = new Timer();
const method = options.method || 'get';
log.info(`${method} api: ${options.url}?${qs.stringify(options.qs)}`);
timer.put('getApi');// 统计时间开始
return rp(options).then((result) => {
const duration = timer.put('getApi');// 统计时间结束
// 数据校验
if (!result) {
log.error('error: 接口返回的数据结构错误,非 JSON');
return Promise.reject({
statusCode: 500,
message: '接口返回内容格式错误'
});
log.error(`error: ${API_BAD_RETSULT.message}`);
return Promise.reject(API_BAD_RETSULT);
}
// 写缓存, 否则返回 Slave 缓存服务器的数据
... ... @@ -63,7 +82,7 @@ class Http {
cache.setSlave(`apiCache:${reqId}`, result, 86400).catch(catchErr); // 二级缓存存储一天
}
log.info(`get api success: use: ${duration}ms`);
log.info(`use: ${duration}ms for ${method} api: ${options.url}?${qs.stringify(options.qs)} `);
return result;
}).catch((err)=> {
const duration = timer.put('getApi');// 统计时间结束
... ... @@ -75,10 +94,7 @@ class Http {
if (config.useCache && cacheOption) {
return this._requestFromCache(options, true);
}
return Promise.resolve({
code: 500,
message: '接口调用失败'
});
return Promise.resolve(API_CALL_FAIL);
});
}
... ... @@ -98,7 +114,7 @@ class Http {
try {
result = JSON.parse(result);
} finally {
log.info(slave ? 'get slave cache success' : 'get master cache success');
log.info(slave ? SLAVE_CACHE_SUCCESS : MASTER_CACHE_SUCCESS);
return result;
}
}
... ... @@ -108,17 +124,14 @@ class Http {
return this._requestFromAPI(options, true, reqId);
}
}).catch(() => {
log.error(slave ? 'get slave cache fail' : 'get master cache fail');
log.error(slave ? SLAVE_CACHE_FAIL : MASTER_CACHE_FAIL);
// 读取缓存失败,并且不是二级缓存的时候,调用 API
if (!slave) {
return this._requestFromAPI(options, true, reqId);
}
return Promise.resolve({
code: 500,
message: '接口调用失败'
});
return Promise.resolve(API_CALL_FAIL);
});
}
... ... @@ -167,7 +180,7 @@ class Http {
if (_.isArray(list)) {
return Promise.all(list);
} else {
return Promise.reject(Error('the parameters of api all method should be Array!'));
return Promise.reject(Error(API_ALL_METHOD_ERROR));
}
}
}
... ...
... ... @@ -282,7 +282,7 @@
li {
float: left;
padding: 8px 23px 5px;
padding: 8px 22px 5px;
a {
font-size: 12px;
... ... @@ -324,7 +324,7 @@
background: resolve('layout/logo-en.png') no-repeat center center;
width: 182px;
height: 53px;
left: 42%;
left: 43%;
margin-top: 22px;
position: absolute;
animation: logoflip 20s infinite;
... ...