Authored by 周少峰

Merge branch 'feature/qps' into gray

Conflicts:
	.gitignore
Showing 47 changed files with 1162 additions and 599 deletions
{
"extends": "yoho",
"parserOptions": {
"sourceType": "module"
"sourceType": "module",
"ecmaVersion": 2017
}
}
\ No newline at end of file
... ...
... ... @@ -150,6 +150,5 @@ library
### foreman ###
Procfile
.env
yarn.lock
package-json.lock
... ...
... ... @@ -132,7 +132,7 @@ try {
const mobileCheck = require('./doraemon/middleware/mobile-check');
const user = require('./doraemon/middleware/user');
const seo = require('./doraemon/middleware/seo');
const errorHanlder = require('./doraemon/middleware/error-handler');
const errorHandler = require('./doraemon/middleware/error-handler');
const setPageInfo = require('./doraemon/middleware/set-pageinfo');
const layoutTools = require('./doraemon/middleware/layout-tools');
const pageCache = require('./doraemon/middleware/page-cache');
... ... @@ -162,10 +162,10 @@ try {
require('./dispatch')(app);
app.all('*', errorHanlder.notFound()); // 404
app.all('*', errorHandler.notFound()); // 404
// YOHO 后置中间件
app.use(errorHanlder.serverError());
app.use(errorHandler.serverError());
} catch (err) {
logger.error(err);
}
... ...
... ... @@ -86,6 +86,11 @@ exports.getIndexGuide = (req, res, next) => {
};
exports.hasNewUserFloor = (req, res, next) => {
let uid = req.user.uid;
if (!uid) {
return res.send({code: 200, isNewUser: false});
}
req.ctx(ChannelModel).hasNewUserFloor(req.yoho.channel, req.user.uid).then(data => {
res.send(data);
... ...
... ... @@ -44,7 +44,7 @@ exports.index = (req, res, next) => {
reqCtx.getTopList(gender, uid, udid, page, true)
]).then(ret => {
if (_.get(ret[2], 'msgs') || _.get(ret[7], 'msgs')) {
if (!_.get(ret[2], 'msgs')) {
res.set('Cache-Control', 'no-cache');
}
... ...
... ... @@ -14,8 +14,20 @@ const getCommonHeader = (req, res, next) => {
}).catch(next);
};
const getCommonHeaderNew = (req, res, next) => {
let channel = req.query.channel;
let uid = req.user.uid;
let clientService = _.get(req.app.locals.pc, 'clientService.new', false);
let result = req.ctx(headerModel).getHomeNavNew(uid, channel, req.originalUrl, clientService);
_.merge(res.locals, result);
next();
};
module.exports = {
getCommonHeader
getCommonHeader,
getCommonHeaderNew
};
... ...
'use strict';
const indexService = require('../models/index-service');
const _ = require('lodash');
const index = (req, res, next)=>{
let uid = req.user.uid;
... ... @@ -8,17 +9,45 @@ const index = (req, res, next)=>{
let channel = req.yoho.channel;
let isStudent = req.user.isStudent;
req.ctx(indexService).index(uid, udid, channel, isStudent).then(result => {
req.ctx(indexService).indexNew(uid, udid, channel, isStudent).then(result => {
_.merge(res.locals, result.header);
return res.render('index', {
module: 'home',
page: 'index',
meIndexPage: true,
me: result
me: result.content
});
}).catch(next);
};
// 头像和左侧菜单我的消息
async function newsAvatar(req, res) {
let uid = req.user.uid;
let result = await req.ctx(indexService).newsAvatar(uid);
res.json(result);
}
async function asyncData(req, res) {
let uid = req.user.uid;
let udid = req.user.uid + req.yoho.udid;
let channelNum = req.yoho.channelNum;
let data = await Promise.props({
numbers: req.ctx(indexService).numbers(uid),
brands: req.ctx(indexService).brands(),
products: req.ctx(indexService).newArrive(),
rec: req.ctx(indexService).recommend(uid, udid, channelNum)
});
res.json(data);
}
module.exports = {
index
index,
newsAvatar,
asyncData
};
... ...
... ... @@ -149,6 +149,43 @@ module.exports = class extends global.yoho.BaseModel {
});
}
getHomeNavNew(uid, channel, url, clientSwitcher) {
let navs = this._getActiveNav(url, clientSwitcher, 1);
let activeNav = navs.activeNav;
let bread = [{href: helpers.urlFormat('/'), name: 'YOHO!BUY 有货首页'}];
if (activeNav) {
bread.push({
name: '个人中心',
href: helpers.urlFormat('/home')
});
bread.push({
name: activeNav.name
});
// 订单详情
if (activeNav.curHref === '/home/orders/detail') {
Object.assign(_.last(bread), {
href: helpers.urlFormat('/home/orders')
});
bread.push({
name: '订单详情'
});
}
} else {
bread.push({
name: '个人中心'
});
}
return {
path: bread,
homeNav: navs.homeNav,
userThumb: defaultAvatar
};
}
getHomeNav(uid, channel, url, clientSwitcher) {
let that = this;
... ...
... ... @@ -38,7 +38,7 @@ module.exports = class extends global.yoho.BaseModel {
method: 'web.search.favorBrand'
};
return this.get({data: options});
return this.get({data: options, param: {cache: 86400}});
}
newArrival() {
... ... @@ -84,13 +84,13 @@ module.exports = class extends global.yoho.BaseModel {
* @param string $mode
* @return mixed
*/
getByNodeContent(node, mode) {
getByNodeContent(node, mode, params) {
let options = {
method: 'web.html.content',
mode: mode,
node: node
};
return this.get({data: options});
return this.get({data: options, param: params || {}});
}
};
... ...
... ... @@ -9,6 +9,20 @@ const helpers = global.yoho.helpers;
const OrderService = require('./orders-service');
const IndexApi = require('./index-api');
const UserApi = require('./user-api');
const MsgApi = require('./message');
const headerModel = require('../../../doraemon/models/header');
const cache = global.yoho.cache;
const cachePreKey = 'HOME_INDEX_';
const defaultAvatar = '//img10.static.yhbimg.com/headimg/' +
'2013/11/28/09/01cae078abe5fe320c88cdf4c220212688.gif?imageView/2/w/100/h/100';
const numbersKey = {
pending: 'pengding',
unread: 'unread',
needComment: 'needComment'
};
const CHANNEL_NUM = {
boys: 1,
... ... @@ -164,7 +178,7 @@ module.exports = class extends global.yoho.BaseModel {
_footerBanner() {
const CODE = '20110609-152143';
return new IndexApi(this.ctx).getByNodeContent(CODE).then(banner => {
return new IndexApi(this.ctx).getByNodeContent(CODE, {}, {cache: 86400}).then(banner => {
return _.get(banner, 'data', '').replace(/http:\/\//g, '//');
});
}
... ... @@ -176,6 +190,138 @@ module.exports = class extends global.yoho.BaseModel {
return new OrderService(this.ctx).closeReason();
}
// 个人中心首页 异步获取新品上架数据
async newArrive() {
let arriveCaceKey = `${cachePreKey}arrive`;
let arrive = await cache.get(arriveCaceKey).catch(() => {
return {};
});
if (_.isEmpty(arrive)) {
let arriveOrigin = await new IndexApi(this.ctx).newArrival();
arrive = this._handleProduct(_.get(arriveOrigin, 'data.product_list', []));
if (!_.isEmpty(arrive)) {
cache.set(arriveCaceKey, arrive, 3600);
}
} else {
arrive = JSON.parse(arrive);
}
return Promise.resolve({newArrive: arrive});
}
// 推荐数据
async recommend(uid, udid, channelNum) {
let recommendCaceKey = `${cachePreKey}recommend`;
let recommend = await cache.get(recommendCaceKey).catch(() => {
return {};
});
if (_.isEmpty(recommend)) {
let recommendOrigin = await new IndexApi(this.ctx).recommend(channelNum, uid, udid, '100004', 30);
recommend = this._handleProduct(_.get(recommendOrigin, 'data.product_list', []));
if (!_.isEmpty(recommend)) {
cache.set(recommendCaceKey, recommend, 3600);
}
} else {
recommend = JSON.parse(recommend);
}
return Promise.resolve({recommend: recommend});
}
// 个人中心首页 异步获取品牌数据
async brands() {
let brandsCaceKey = `${cachePreKey}brands`;
let brand = await cache.get(brandsCaceKey).catch(() => {
return {};
});
if (_.isEmpty(brand)) {
let brandOrigin = await new IndexApi(this.ctx).guessBrand();
brand = this._handleBrand(_.get(brandOrigin, 'data', []), 6);
if (!_.isEmpty(brand)) {
cache.set(brandsCaceKey, brand, 86400);
}
} else {
brand = JSON.parse(brand);
}
return Promise.resolve({brands: brand, more: '/brands'});
}
// 个人中心异步查询我的消息和头像
async newsAvatar(uid) {
let result = await Promise.props(
{
profile: new UserApi(this.ctx).getUserInfo(uid),
msg: new MsgApi(this.ctx).unreadTotal(uid)
}
).catch(() => {
return {};
});
let avatar = helpers.image(_.get(result, 'profile.data.head_ico', ''), 100, 100) || defaultAvatar;
let msgCount = _.get(result, 'msg.data.total', 0);
return Promise.resolve({avatar: avatar, msg: msgCount});
}
// 首页顶部数量
async numbers(uid, udid) {
let result = {};
let numbers = await Promise.props({
pending: new IndexApi(this.ctx).pendingOrderCount(uid), // 待处理订单
unread: new IndexApi(this.ctx).unreadMessageCount(uid, udid), // 未读消息
needComment: new IndexApi(this.ctx).needCommentCount(uid) // 待评论商品
}).catch(() => {
return {};
});
result[numbersKey.pending] = _.get(numbers, 'pending.data.count', 0);
result[numbersKey.unread] = _.get(numbers, 'unread.data.inbox_total', 0);
result[numbersKey.needComment] = _.get(numbers, 'needComment.data', 0);
return Promise.resolve(result);
}
// 个人中心首页同步数据
async indexNew(uid, udid, channel, isStudent) {
let indexData = await Promise.props({
header: headerModel.requestHeaderData(channel),
recentOrder: this._recentOrder(uid),
footerBanner: this._footerBanner(),
reason: this._cancelReason()
}).catch(() => {
return {};
});
return Object.assign(
{
content: {
content: {
messages: [
{href: helpers.urlFormat('/home/orders'), name: '待处理订单', id: numbersKey.pending, count: 0},
{href: helpers.urlFormat('/home/message'), name: '未读消息', id: numbersKey.unread, count: 0},
{href: helpers.urlFormat('/home/comment'), name: '待评论商品',
id: numbersKey.needComment, count: 0}
],
certifiedName: +isStudent ? '学生身份已验证' : '身份验证',
certifiedUrl: helpers.urlFormat('/product/students/'),
latestOrders: Object.assign(indexData.recentOrder, {cancelReason: indexData.reason})
},
banner: indexData.footerBanner
}
},
{
header: indexData.header
}
);
}
index(uid, udid, channel, isStudent) {
let that = this;
... ...
... ... @@ -4,11 +4,8 @@ const api = global.yoho.API;
exports.getUserSuccessfulOrders = (uid)=>{
return api.get('', {
method: 'app.SpaceOrders.list',
uid: uid,
type: 5,
page: 1,
limit: 10
method: 'app.resources.isNewUser',
uid: uid
});
};
... ...
... ... @@ -9,7 +9,7 @@ const check = (uid) => {
return {};
}
if (_.get(result, 'data.total', 0) !== 0) {
if (!_.get(result, 'data', false)) {
return {
isNew: false
};
... ...
... ... @@ -159,7 +159,11 @@ module.exports = class extends global.yoho.BaseModel {
method: 'app.SpaceOrders.closeReasons'
};
return this.get({data: options});
return this.get({data: options,
param: {
cache: 86400
}
});
}
/**
... ...
... ... @@ -742,7 +742,7 @@ module.exports = class extends global.yoho.BaseModel {
let orderInfo = yield new OrderApi(that.ctx).getOrderDetail(uid, orderId);
let detail = {};
if (orderInfo.code === 400) {
if (_.get(orderInfo, 'code', 400) !== 200) {
return orderInfo;
}
... ...
... ... @@ -52,7 +52,9 @@ const meGiftController = require(`${cRoot}/me-gift`);
// 首页
router.get(['/index', '/'], tabsMiddleware.getCommonHeader, indexController.index);
router.get(['/index', '/'], tabsMiddleware.getCommonHeaderNew, indexController.index);
router.get('/index/newsAvatar', indexController.newsAvatar);
router.get('/index/async', indexController.asyncData);
// 查看二维码
router.get('/QRcode', tabsMiddleware.getCommonHeader, personalController.QRcode);
... ...
... ... @@ -10,7 +10,7 @@
消息提示:
{{# messages}}
<a href="{{href}}">{{name}}</a>
<b>({{count}})</b>
<b id="{{id}}">({{count}})</b>
{{/ messages}}
{{#unless @root.pc.user.removeStudentIdentification}}
... ... @@ -36,88 +36,86 @@
{{> home/orders/order-block}}
</div>
{{/ latestOrders}}
{{# favBrand}}
<div class="brands block">
<h2 class="title">
<a class="more-orders more" href="{{more}}">
更多品牌
</a>
</h2>
<ul class="clearfix">
{{# brands}}
<li>
<a href="{{href}}">
<img class="brand-logo" src="{{logo}}">
<span class="brand-name">{{name}}</span>
</a>
</li>
{{/ brands}}
</ul>
<div class="brands block" id="home-brands-box">
</div>
{{/ favBrand}}
{{#if newArrival}}
<div class="new-arrival block">
<h2 class="title">
<p class="na-pager-wrap">
<span class="na-pager pre no-visible">
<i class="iconfont">&#xe618;</i>
</span>
<span class="na-pager next">
<i class="iconfont">&#xe619;</i>
</span>
</p>
</h2>
<ul class="clearfix">
{{# newArrival}}
<li>
<a href="{{href}}">
<img class="thumb" src="{{thumb}}">
<p class="name">{{name}}</p>
<span class="price">¥{{price}}</span>
</a>
</li>
{{/ newArrival}}
</ul>
<div class="new-arrival block" id="home-new-box">
</div>
{{/if}}
{{/ content}}
</div>
<!--recommend-for-you-->
{{#if recommend}}
<div class="recommend block">
<h2 class="title">
<p class="na-pager-wrap">
<span class="rc-pager pre no-visible">
<i class="iconfont">&#xe618;</i>
</span>
<span class="rc-pager next">
<i class="iconfont">&#xe619;</i>
</span>
</p>
</h2>
<ul class="clearfix">
{{# recommend}}
<li>
<a href="{{href}}" data-id='{{productId}}' target="_blank">
<img class="thumb" src="{{thumb}}">
<p class="name">{{name}}</p>
<span class="price">¥{{price}}</span>
</a>
</li>
{{/ recommend}}
</ul>
<div class="recommend block" id="home-recommend-box">
</div>
{{/if}}
<!--recommend-for-you-->
<div class="ho-btm">
{{{banner}}}
{{> help-us}}
</div>
{{/ me}}
<script type="text/html" id="home-brands-tpl">
<h2 class="title">
<a class="more-orders more" href="\{{more}}">
更多品牌
</a>
</h2>
<ul class="clearfix">
\{{# brands}}
<li>
<a href="\{{href}}">
<img class="brand-logo" src="\{{logo}}">
<span class="brand-name">\{{name}}</span>
</a>
</li>
\{{/ brands}}
</ul>
</script>
<script type="text/html" id="home-new-tpl">
\{{#if newArrive}}
<h2 class="title">
<p class="na-pager-wrap">
<span class="na-pager pre no-visible">
<i class="iconfont">&#xe618;</i>
</span>
<span class="na-pager next">
<i class="iconfont">&#xe619;</i>
</span>
</p>
</h2>
<ul class="clearfix">
\{{# newArrive}}
<li>
<a href="\{{href}}">
<img class="thumb" src="\{{thumb}}">
<p class="name">\{{name}}</p>
<span class="price">¥\{{price}}</span>
</a>
</li>
\{{/ newArrive}}
</ul>
\{{/if}}
</script>
<script type="text/html" id="home-recommend-tpl">
\{{#if recommend}}
<h2 class="title">
<p class="na-pager-wrap">
<span class="rc-pager pre no-visible">
<i class="iconfont">&#xe618;</i>
</span>
<span class="rc-pager next">
<i class="iconfont">&#xe619;</i>
</span>
</p>
</h2>
<ul class="clearfix">
\{{# recommend}}
<li>
<a href="\{{href}}" data-id='\{{productId}}' target="_blank">
<img class="thumb" src="\{{thumb}}">
<p class="name">\{{name}}</p>
<span class="price">¥\{{price}}</span>
</a>
</li>
\{{/ recommend}}
</ul>
\{{/if}}
</script>
</div>
... ...
... ... @@ -2,9 +2,7 @@
<p class="title ucenter"></p>
<div class="user-thumb">
<div class="thumb-bg">
{{#if userThumb}}
<img id="user-thumb" src="{{image2 userThumb w=100 h=100}}">
{{/if}}
<img id="user-thumb" src="{{image2 userThumb w=100 h=100}}">
</div>
</div>
{{# homeNav}}
... ... @@ -18,7 +16,7 @@
<li class="row{{#if @first}} first{{/if}}{{#if active}} active{{/if}}">
<a href="{{href}}" target="{{#if isBlank}}_blank{{/if}}" >{{name}}</a>
{{#if count}}
<span class="new-count">{{count}}</span>
<span class="new-count hide" id="new-count">{{count}}</span>
{{/if}}
</li>
{{/each}}
... ...
... ... @@ -30,7 +30,7 @@ exports.index = (req, res, next) => {
logger.debug(`decodeURIComponent query fail:${e.toString()}`);
}
req.ctx(list).getListData(Object.assign(req.query, {uid: req.user.uid, prid: req.user.prid}),
req.ctx(list).getListDataPre(Object.assign(req.query, {uid: req.user.uid, prid: req.user.prid}),
req.yoho.channel).then(result => {
Object.assign(resData, result);
... ... @@ -65,7 +65,7 @@ exports.index = (req, res, next) => {
exports.new = (req, res, next) => {
let resData = {};
req.ctx(list).getListNewData(Object.assign({order: 's_t_desc'}, req.query), req.yoho.channel).then(result => {
req.ctx(list).getListNewDataPre(Object.assign({order: 's_t_desc'}, req.query), req.yoho.channel).then(result => {
Object.assign(resData, result, {
hideInfo: {from: 'newProduct'}
});
... ...
... ... @@ -8,6 +8,7 @@
const mRoot = '../models';
const sale = require(`${mRoot}/sale`); // sale model
const detailHelper = require(`${mRoot}/detail-helper`);
const channelList = ['boys', 'girls', 'kids', 'lifestyle'];
... ... @@ -170,10 +171,13 @@ exports.special = (req, res, next) => {
*/
exports.getGoodsList = (req, res, next) => {
let params = req.query;
let vipLevel = detailHelper.vipLevel(req.user.vip);
params.uid = req.user.uid || 0;
if (params.refresh && !vipLevel) {
return res.send('');
}
req.ctx(sale).getSaleGoodsData(params).then(result => {
req.ctx(sale).getSaleGoodsData(params, vipLevel).then(result => {
let responseData = {};
responseData.footerTop = false;
... ...
... ... @@ -32,7 +32,7 @@ const index = (req, res, next) => {
return;
}
return req.ctx(search).getSearchData(Object.assign(params, {uid: req.user.uid, prid: req.user.prid}),
return req.ctx(search).getSearchDataPre(Object.assign(params, {uid: req.user.uid, prid: req.user.prid}),
req.yoho.channel).then(result => {
let queryKey = req.query.query;
... ...
const cache = global.yoho.cache;
const logger = global.yoho.logger;
const ONE_MINUTE = 60;
const FIVE_MINUTE = 5 * ONE_MINUTE;
function _cacheGet(key) {
return cache.get(key).then((data) => {
if (data) {
return JSON.parse(data);
}
return Promise.reject();
});
}
function _cacheSave(key, value, ttl) {
return cache.set(key, value, ttl)
.catch(err => logger.error(`save cache data fail:${err.toString()}`));
}
function defaultKey() {
const args = Array.prototype.slice.call(arguments);
return JSON.stringify(args || '[]');
}
function _cached(fn, keyFn = defaultKey, ctx = null, ttl = FIVE_MINUTE) {
const keyGen = function() {
const args = Array.prototype.slice.call(arguments);
return '_cache:' + (fn.name || '') + ':' + keyFn.apply(null, args);
};
return function() {
const args = Array.prototype.slice.call(arguments);
const key = keyGen.apply(null, args);
return _cacheGet(key).catch(() => {
return fn.apply(ctx, args).then(result => {
_cacheSave(key, result, ttl);
return result;
});
});
};
}
module.exports = _cached;
... ...
... ... @@ -26,24 +26,29 @@ function _cacheGet(key) {
});
}
// 半个月
const HALF_MONTH = 60 * 60 * 24 * 15;
const ONE_HOUR = 60 * 60; // 一小时
const ONE_DAY = ONE_HOUR * 24; // 一天
const HALF_MONTH = ONE_DAY * 15; // 半个月
function _cacheSave(key, value) {
return cache.set(key, value, HALF_MONTH)
function _cacheSave(key, value, ttl) {
return cache.set(key, value, ttl)
.catch(err => logger.error(`save cache data fail:${err.toString()}`));
}
function _cached(fn, ctx) {
function _cached(fn, ctx, ttl = HALF_MONTH) {
const pre = 'recommend-cache:' + (fn.name || 'random:' + uuid.v4()) + ':';
return function() {
const args = Array.prototype.slice.call(arguments);
const key = pre + JSON.stringify(args || '[]');
if (!_.get(this.ctx.req.app.locals.pc, 'ci.tdk', false)) {
return Promise.resolve();
}
return _cacheGet(key).catch(() => {
return fn.apply(ctx, args).then(result => {
_cacheSave(key, result);
_cacheSave(key, result, ttl);
return result;
});
});
... ... @@ -61,6 +66,7 @@ module.exports = class extends global.yoho.BaseModel {
this.getNewProduct = _cached(this._getNewProduct, this);
this.getGuangArticles = _cached(this._getGuangArticles, this);
this.getRecommendKeywords = _cached(this._getRecommendKeywords, this);
this.getShopRecommendAsync = _cached(this._getShopRecommendAsync, this, ONE_DAY);
}
/**
... ... @@ -191,13 +197,13 @@ module.exports = class extends global.yoho.BaseModel {
/**
* 店铺推荐
*/
getShopRecommendAsync(skn, page, limit) {
_getShopRecommendAsync(skn) {
return this.get({
data: {
method: 'web.product.shopRecommend',
product_skn: skn,
page: page || 1,
limit: limit || 20
page: 1,
limit: 20
}
});
}
... ...
... ... @@ -17,6 +17,7 @@ const logger = global.yoho.logger;
const detailHelper = require('./detail-helper');
const _cached = require('./cache-utils');
const ProductModel = require('./detail-product-api');
const ConsultServiceModel = require('./detail-consult-service');
const CommentServiceModel = require('./detail-comment-service');
... ... @@ -36,29 +37,14 @@ const BUNDLE_PACKAGE = 1; // 套餐
const tdk = require('../../../utils/getTDK');
const productProcess = require('../../../utils/product-process');
function _getProductAdditionInfoAsync(data) {
return co(function* () {
let productId = _.get(data, 'product_id', 0);
let brandId = _.get(data, 'brand_info.brand_id', 0);
// 获取相关数据
let promiseData = {
productBanner: this.productAPI.getProductBannerAsync(productId),
bannerInfo: this.brandService.getBannerInfoAsync(brandId)
};
let result = yield Promise.props(promiseData);
return result;
}).bind(this)();
}
function _getProductIntroAsync(productId, productSkn) {
return co(function* () {
let result = yield Promise.props({
sizeInfo: this.productAPI.sizeInfoAsync(productSkn),
productComfort: this.productAPI.getProductComfortAsync(productId),
productModelTry: this.productAPI.getProductModelTryAsync(productSkn)
productModelTry: this.productAPI.getProductModelTryAsync(productSkn),
banner: this.productAPI.getProductBannerAsync(productId)
});
return result;
... ... @@ -185,9 +171,7 @@ function _getVipDataByProductBaseInfo(data, vipLevel, uid) {
}
// 活动
function _getProductActivityBanner(additionalData) {
let data = additionalData.productBanner;
function _getProductActivityBanner(data) {
if (_.isEmpty(data) ||
_.get(data, 'code', 400) !== 200 ||
!_.get(data, 'data.bannerImg')) {
... ... @@ -1057,7 +1041,6 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
result.bundleType = propOrigin('bundle_type', 0);
let requestApi = {
addition: _getProductAdditionInfoAsync.call(this, origin), // 预处理所有的数据
fav: _getProductFavoriteDataAsync.call(this, uid, result.productId), // 处理收藏喜欢数据
promotion: this.productAPI.getPromotionAsync(result.skn), // 打折信息
coupon: this.couponService.listAsync(propOrigin('brand_info.brand_id'), result.skn, uid) // 优惠券
... ... @@ -1096,7 +1079,6 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
let requestData = yield Promise.props(requestApi);
let additionalData = requestData.addition;
let favoriteData = requestData.fav;
let promotionData = requestData.promotion;
let coupon = requestData.coupon;
... ... @@ -1170,9 +1152,6 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
}
}
// 促销活动图片
result.imageBanner = _getProductActivityBanner(additionalData);
// 促销活动,虚拟商品无促销
if (propOrigin('attribute') !== 3) {
result.activity = _getActivityDataByProductBaseInfo(promotionData);
... ... @@ -1551,6 +1530,9 @@ function showMainAsync(req, data) {
result.deatil = Object.assign(result.detail, intro);
// 促销活动图片, 在页面最底部
result.detail.goodsInfo.imageBanner = _getProductActivityBanner(productDescription.banner);
// seo
result.seo = _getSeoByGoodsInfo(productInfo.goodsInfo, sortNavigator);
... ... @@ -1737,7 +1719,9 @@ module.exports = class extends global.yoho.BaseModel {
this.usefulAsync = this.consultService.usefulAsync.bind(this.consultService);
// 获取某一个商品详情主页面
this.showMainAsync = showMainAsync.bind(this);
this.showMainAsync = _cached(showMainAsync, function keyGen(req, {skn, channel, gender}) {
return skn + (channel || '') + (gender || '');
}, this);
// 获取某一个商品详情主页面
this.showMainBackAsync = showMainBackAsync.bind(this);
... ...
... ... @@ -14,6 +14,11 @@ const shopHandler = require('./shop-handler');
const helpers = global.yoho.helpers;
const crypto = global.yoho.crypto;
const _ = require('lodash');
const Fn = require('lodash/fp');
const md5 = require('md5');
const cache = global.yoho.cache;
const config = global.yoho.config;
const logger = global.yoho.logger;
// const limitNum = 60; // 商品每页显示数目
const needParams = ['query', 'msort', 'misort', 'category_id', 'gender', 'shelveTime'];
... ... @@ -29,6 +34,15 @@ const positionId = 10;
// 获取分类左侧广告id
const sortAdsId = 79;
const CACHE_TIME_S = 60;
function _getCacheKey(params, page) {
let sortByKey = Fn.pipe(Fn.toPairs, Fn.sortBy(0), Fn.flatten);
let genKey = Fn.pipe(Fn.cloneDeep, sortByKey, Fn.join('_'));
return `render_cache_${page}_${md5(genKey(params))}`;
}
/**
* 获取商品分类列表数据
*/
... ... @@ -101,7 +115,7 @@ function getListData(params, channel) {
_.get(finalResult, 'list.leftContent.sort', {}));
filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions,
finalResult.list.leftContent.checked);
_.get(finalResult, 'list.leftContent.checked', []));
Object.assign(finalResult.list, {
filters: filters,
... ... @@ -137,6 +151,49 @@ function getListData(params, channel) {
});
}
function getListDataPre(params, channel) {
let cKey = _getCacheKey(Object.assign({channel: channel}, params), 'list');
let getOriginData = () => {
return this.getListData(params, channel).then(result => {
// 查询结果为空则不cache
if (config.useCache && !_.isEmpty(_.get(result, 'list.goods', []))) {
cache.set(cKey, result, CACHE_TIME_S)
.catch(err => logger.debug(`list render cache data save fail:${err.toString()}`));
}
return result;
});
};
if (config.useCache) {
return cache.get(cKey).catch(err => {
logger.debug(`get list render cache data fail:${err.toString()}`);
return getOriginData();
}).then(cdata => {
let hasCache = false;
if (cdata) {
try {
hasCache = true;
cdata = JSON.parse(cdata);
} catch (e) {
logger.debug('list render cache data parse fail.');
}
}
if (hasCache) {
return cdata;
} else {
return getOriginData();
}
});
}
return getOriginData();
}
/**
* 获取新品到着数据
*/
... ... @@ -183,7 +240,7 @@ function getListNewData(params, channel) {
finalResult.list.leftContent.sort);
filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions,
finalResult.list.leftContent.checked);
_.get(finalResult, 'list.leftContent.checked', []));
Object.assign(finalResult.list, {
filters: filters,
... ... @@ -215,6 +272,49 @@ function getListNewData(params, channel) {
});
}
function getListNewDataPre(params, channel) {
let cKey = _getCacheKey(Object.assign({channel: channel}, params), 'listnew');
let getOriginData = () => {
return this.getListNewData(params, channel).then(result => {
// 查询结果为空则不cache
if (config.useCache && !_.isEmpty(_.get(result, 'list.goods', []))) {
cache.set(cKey, result, CACHE_TIME_S)
.catch(err => logger.debug(`list_new render cache data save fail:${err.toString()}`));
}
return result;
});
};
if (config.useCache) {
return cache.get(cKey).catch(err => {
logger.debug(`get list_new render cache data fail:${err.toString()}`);
return getOriginData();
}).then(cdata => {
let hasCache = false;
if (cdata) {
try {
hasCache = true;
cdata = JSON.parse(cdata);
} catch (e) {
logger.debug('list_new render cache data parse fail.');
}
}
if (hasCache) {
return cdata;
} else {
return getOriginData();
}
});
}
return getOriginData();
}
/**
* 根据品牌域名查询品牌信息
*/
... ... @@ -314,7 +414,7 @@ function getBrandData(params, extra, channel) {
finalResult.brand.leftContent.sort);
filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions,
finalResult.brand.leftContent.checked);
_.get(finalResult, 'brand.leftContent.checked', []));
Object.assign(finalResult.brand, {
filters: filters,
... ... @@ -588,7 +688,9 @@ module.exports = class extends global.yoho.BaseModel {
this.shopApi = new ShopApiModel(ctx);
this.getListData = getListData.bind(this);
this.getListDataPre = getListDataPre.bind(this);
this.getListNewData = getListNewData.bind(this);
this.getListNewDataPre = getListNewDataPre.bind(this);
this.getBrandInfo = getBrandInfo.bind(this);
this.getBrandData = getBrandData.bind(this);
this.getBrandAbout = getBrandAbout.bind(this);
... ...
... ... @@ -8,6 +8,7 @@
'use strict';
const api = global.yoho.API;
const Promise = require('bluebird');
const querystring = require('querystring');
const SaleApiModel = require('./sale-api');
const SearchApiModel = require('./search-api');
... ... @@ -61,16 +62,12 @@ const contentCode = {
* 获取Sale首页商品列表数据 仅 Ajax 调用 Controller 调用
* @return {[type]} [description]
*/
function getSaleGoodsData(params) {
return api.all([
this.saleApi.getSaleGoodsList(params),
this.saleApi.getUserProfile(params.uid)
]).then(result => {
function getSaleGoodsData(params, vipLevel) {
return this.saleApi.getSaleGoodsList(params).then(result => {
let finalResult = {};
if (result[0].code === 200) {
finalResult.goods = productProcess.processProductList(result[0].data.product_list);
if (result.code === 200) {
finalResult.goods = productProcess.processProductList(result.data.product_list);
_.forEach(finalResult.goods, (value, key) => {
delete finalResult.goods[key].tags.isNew; // 屏蔽 new 标签
delete finalResult.goods[key].tags.isSale;// 屏蔽 sale 标签
... ... @@ -79,33 +76,28 @@ function getSaleGoodsData(params) {
}
// 处理 VIP 商品数据
if (result[1].code === 200) {
let vipInfo = _.get(result, '[1].data.vip_info', {});
if (params.saleType === '2') {
_.forEach(finalResult.goods, (value, key) => {
switch (vipInfo.cur_level) {
case '1':
finalResult.goods[key].sales_price = value.vip1_price;
finalResult.goods[key].vip1 = true;
break;
case '2':
finalResult.goods[key].sales_price = value.vip2_price;
finalResult.goods[key].vip2 = true;
break;
case '3':
finalResult.goods[key].sales_price = value.vip3_price;
finalResult.goods[key].vip3 = true;
break;
default:
finalResult.goods[key].vip = true;
delete finalResult.goods[key].sales_price;
break;
}
if (params.saleType === '2') {
_.forEach(finalResult.goods, (value, key) => {
});
}
switch (_.toString(vipLevel)) {
case '1':
finalResult.goods[key].sales_price = value.vip1_price;
finalResult.goods[key].vip1 = true;
break;
case '2':
finalResult.goods[key].sales_price = value.vip2_price;
finalResult.goods[key].vip2 = true;
break;
case '3':
finalResult.goods[key].sales_price = value.vip3_price;
finalResult.goods[key].vip3 = true;
break;
default:
finalResult.goods[key].vip = true;
delete finalResult.goods[key].sales_price;
break;
}
});
}
// 需要判断是否为今日推荐,今日推荐需要添加最后的全部商品图片
... ... @@ -147,7 +139,6 @@ function getSaleGoodsData(params) {
* @return {[type]} [description]
*/
function getSaleIndexData(channel) {
return api.all([
headerModel.requestHeaderData(channel),
this.saleApi.getSaleActivityList('', channel),
... ... @@ -155,7 +146,7 @@ function getSaleIndexData(channel) {
this.saleApi.getSaleGoodsList({channel: channel, saleType: '2', limit: '1'}), // 会员专享分类
this.saleApi.getSalebreakingYardsSortList({channel: channel}), // 断码区尺码数据
this.saleApi.getSaleGoodsList({channel: channel, limit: '1'}) // 最新降价分类
]).then(result => {
]).then(async result => {
var finalResult = result[0];
// 折扣专场活动处理
... ... @@ -176,7 +167,6 @@ function getSaleIndexData(channel) {
// 会员专享分类处理
if (result[3].code === 200) {
finalResult.saleCategory.push(
Object.assign(
saleHandler.handleSaleCategoryData(result[3].data.filter.group_sort, '2', channel),
... ... @@ -189,37 +179,42 @@ function getSaleIndexData(channel) {
if (result[4].code === 200) {
let breakingSizeSort = saleHandler.handleSaleBreakingSizeData(result[4].data);
return api.all([
this.saleApi.getSaleGoodsList({
channel: channel,
saleType: '5', // app 与 pc 有冲突,为 PC 加一个 5 的选项
limit: '1',
breakSize: breakingSizeSort.breakSize,
breakSort: breakingSizeSort.breakSort
})
]).then(subResult => {
if (subResult[0].code === 200) {
finalResult.saleCategory.push(
saleHandler.handleSaleCategoryData(
subResult[0].data.filter.group_sort,
'5',
channel,
breakingSizeSort
)
);
}
// 最新降价分类处理
if (result[5].code === 200) {
finalResult.saleCategory.push(
saleHandler.handleSaleCategoryData(result[5].data.filter.group_sort, '3', channel)
);
}
return finalResult;
let subResult = await this.saleApi.getSaleGoodsList({
channel: channel,
saleType: '5', // app 与 pc 有冲突,为 PC 加一个 5 的选项
limit: '1',
breakSize: breakingSizeSort.breakSize,
breakSort: breakingSizeSort.breakSort
});
if (subResult.code === 200) {
finalResult.saleCategory.push(
saleHandler.handleSaleCategoryData(
subResult.data.filter.group_sort,
'5',
channel,
breakingSizeSort
)
);
}
}
// 最新降价分类处理
if (result[5].code === 200) {
finalResult.saleCategory.push(
saleHandler.handleSaleCategoryData(result[5].data.filter.group_sort, '3', channel)
);
}
let saleGoods = await Promise.all(_.cloneDeep(finalResult.saleCategory).map(value => {
return this.getSaleGoodsData(querystring.parse(_.trim(value.urlLocation, '?')));
}));
_.zipWith(finalResult.saleCategory, saleGoods, function(category, goods) {
category.defaultGoods = goods.goods || [];
return category;
});
return finalResult;
});
}
... ...
... ... @@ -481,6 +481,7 @@ function getArticleByBrand(brand, udid, limit) {
return this.get({
url: relateArticleUrl,
data: params,
param: config.apiCache,
api: global.yoho.ServiceAPI
});
}
... ... @@ -517,7 +518,7 @@ function lessRecommend(channelNum, uid, udid, recPos, limit) {
param.uid = uid;
}
return this.get({data: param});
return this.get({data: param, param: config.apiCache});
}
module.exports = class extends global.yoho.BaseModel {
... ...
... ... @@ -440,7 +440,7 @@ exports.handleOptsData = (params, total, extra) => {
// 上下翻页数据处理
dest.pageCounts = [{
href: handleFilterUrl(params, {limit: 60}, {page: true}),
href: handleFilterUrl(params, {limit: 40}, {page: true}),
count: 60
}, {
href: handleFilterUrl(params, {limit: 100}, {page: true}),
... ... @@ -453,7 +453,7 @@ exports.handleOptsData = (params, total, extra) => {
dest.curPage = _.isEmpty(params.page) ? 1 : params.page; // 当前页码数
// 每页商品数量
dest.countPerPage = _.isEmpty(params.limit) ? 60 : params.limit;
dest.countPerPage = _.isEmpty(params.limit) ? 40 : params.limit;
// 全部页码数量
dest.pageCount = _.ceil(total / (dest.countPerPage - 1));
... ... @@ -1132,7 +1132,7 @@ exports.handlePagerData = (total, params, noNextBtn) => {
};
let currentPage = parseInt(_.get(params, 'page', 1), 10); // 当前页
let perPageCount = parseInt(_.get(params, 'limit', noNextBtn === true ? 60 : 59), 10); // 每页商品数
let perPageCount = parseInt(_.get(params, 'limit', noNextBtn === true ? 40 : 39), 10); // 每页商品数
let totalPage = Math.ceil(total / perPageCount); // 总页数
if (noNextBtn !== true) {
... ... @@ -1144,7 +1144,7 @@ exports.handlePagerData = (total, params, noNextBtn) => {
perPageCount = 99;
break;
default :
perPageCount = 59;
perPageCount = 39;
break;
}
}
... ... @@ -1654,7 +1654,7 @@ exports.getCriteo = (glist) => {
// handlePagerData
exports.getSearchParams = params => {
let mlimit = 59;
let mlimit = 39;
if (params && params.limit) {
... ... @@ -1668,7 +1668,7 @@ exports.getSearchParams = params => {
mlimit = 199;
break;
default:
mlimit = 59;
mlimit = 39;
}
}
... ...
... ... @@ -19,8 +19,23 @@ const headerModel = require('../../../doraemon/models/header');
const productProcess = require(`${utils}/product-process`);
const searchHandler = require('./search-handler');
const _ = require('lodash');
const Fn = require('lodash/fp');
const md5 = require('md5');
const cache = global.yoho.cache;
const config = global.yoho.config;
const logger = global.yoho.logger;
const needParams = ['query', 'msort', 'misort'];
const CACHE_TIME_S = 60;
function _getCacheKey(params, page) {
let sortByKey = Fn.pipe(Fn.toPairs, Fn.sortBy(0), Fn.flatten);
let genKey = Fn.pipe(Fn.cloneDeep, sortByKey, Fn.join('_'));
return `render_cache_${page}_${md5(genKey(params))}`;
}
function getKeyActivity(query) {
return this.searchApi.getKeyActivityAsync(query).then(result => {
return _.get(result, 'data.urlobj.pcUrl', '');
... ... @@ -159,6 +174,49 @@ function getSearchData(params, channel) {
});
}
function getSearchDataPre(params, channel) {
let cKey = _getCacheKey(Object.assign({channel: channel}, params), 'search');
let getOriginData = () => {
return this.getSearchData(params, channel).then(result => {
// 查询结果为空则不cache
if (config.useCache && !_.isEmpty(_.get(result, 'search.goods', []))) {
cache.set(cKey, result, CACHE_TIME_S)
.catch(err => logger.debug(`search render cache data save fail:${err.toString()}`));
}
return result;
});
};
if (config.useCache) {
return cache.get(cKey).catch(err => {
logger.debug(`get search render cache data fail:${err.toString()}`);
return getOriginData();
}).then(cdata => {
let hasCache = false;
if (cdata) {
try {
hasCache = true;
cdata = JSON.parse(cdata);
} catch (e) {
logger.debug('search render cache data parse fail.');
}
}
if (hasCache) {
return cdata;
} else {
return getOriginData();
}
});
}
return getOriginData();
}
/**
* 搜索提示
*/
... ... @@ -472,6 +530,7 @@ module.exports = class extends global.yoho.BaseModel {
this.getKeyActivity = getKeyActivity.bind(this);
this.getSearchData = getSearchData.bind(this);
this.getSearchDataPre = getSearchDataPre.bind(this);
this.getSuggest = getSuggest.bind(this);
this.getListBrandsFilter = getListBrandsFilter.bind(this);
this.getBrands4Filter = getBrands4Filter.bind(this);
... ...
... ... @@ -279,7 +279,7 @@ function _getBaseShopData(channel, params, shopInfo) {
_.get(resData, 'brand.leftContent.sort', {}));
filters.checkedConditions.conditions = _.concat(filters.checkedConditions.conditions,
resData.brand.leftContent.checked);
_.get(resData, 'brand.leftContent.checked', []));
Object.assign(resData.brand, {
filters: filters,
... ...
... ... @@ -12,9 +12,11 @@
<i class="iconfont">&#xe62a;</i>
品牌介绍
</a>
{{#unless @root.pc.cart.removeFavStatus}}
<span id="brand-favor" class="brand-favor" data-id="{{dataId}}">
<i class="iconfont{{#if coled}} coled{{/if}}">&#xe611;</i>
</span>
{{/unless}}
</p>
</div>
</div>
... ... @@ -36,9 +38,11 @@
<i class="iconfont">&#xe62a;</i>
店铺介绍
</a>
{{#unless @root.pc.cart.removeFavStatus}}
<span id="shop-favor" class="brand-favor shop-favor" data-id="{{shopId}}">
<i class="iconfont{{#if coled}} coled{{/if}}">&#xe611;</i>
</span>
{{/unless}}
<div class="qrcode-hover-box" data-id="{{shopId}}">
<span class="qrcode-shop">
<i class="qrcode-icon"></i>
... ...
... ... @@ -13,6 +13,7 @@
<i class="shop-intro-ico"></i>
店铺介绍
</div>
{{#unless @root.pc.cart.removeFavStatus}}
<div class="shop-collect" data-id="{{brandIntro.shopId}}">
<i class="shop-collect-ico {{#if brandIntro.isFavorite}}on{{/if}}"></i>
<span class="shop-collect-text">
... ... @@ -23,6 +24,7 @@
{{/if}}
</span>
</div>
{{/unless}}
<div class="qrcode-hover-box qrcode-decoration" data-id="{{brandIntro.shopId}}">
<i class="qrcode-icon"></i>
<div class="qrcode-box">
... ...
... ... @@ -10,12 +10,15 @@
<a class="home" href="{{homeUrl}}" title="{{brandName}}">
<span class="iconfont">&#xe61a;</span>
</a>
{{#unless @root.pc.cart.removeFavStatus}}
{{#if brandId}}
<span id="brand-favour" class="brand-fav{{#if isCollect}} coled{{/if}}" data-id="{{brandId}}">
<i class="iconfont">&#xe641;</i>
<span class="fav-num"></span>
</span>
{{/if}}
{{/unless}}
</div>
</div>
{{/if}}
... ...
... ... @@ -11,6 +11,9 @@
</ul>
<div class="commodity-list">
<ul class="clearfix goods">
{{# defaultGoods}}
{{> product/good}}
{{/ defaultGoods}}
</ul>
</div>
</div>
... ...
... ... @@ -18,9 +18,9 @@ const cachePage = {
'/product/^\\/([\\d]+)(.*)/': 5 * MINUTE,
// 逛
'/guang/': 1 * MINUTE,
// '/guang/info/index': 10 * MINUTE,
'/guang/': 10 * MINUTE,
'/guang/info/detailData': 10 * MINUTE,
'/guang/^\\/([\\d]+)(.*)/': 10 * MINUTE,
'/guang/detail/:id': 10 * MINUTE,
'/guang/Index/editor': 1 * MINUTE,
'/guang/tags/index': 1 * MINUTE,
... ... @@ -29,8 +29,8 @@ const cachePage = {
'/coupon/': 5 * MINUTE,
// 商品列表
// '/product/list/index': 5 * MINUTE,
// '/product/index/index': 5 * MINUTE,
'/product/list/index': 10 * MINUTE,
'/product/index/index': 10 * MINUTE,
// 秒杀列表
'/product/seckill': 30 * SECOND,
... ... @@ -38,6 +38,7 @@ const cachePage = {
// 秒杀详情
// sale
'/product/\\/(.*)-sale/': 5 * MINUTE,
'/product/sale': 5 * MINUTE,
'/product/sale/vip': 5 * MINUTE,
'/product/sale/breakingYards': 5 * MINUTE,
... ... @@ -47,20 +48,25 @@ const cachePage = {
'/product/outlet': 30 * SECOND,
'/product/index/brand': 2 * MINUTE,
'/product/index/brand': 5 * MINUTE,
'/product/index/about': 10 * MINUTE,
'/product/shoplist': 2 * MINUTE,
'/product/shoplist': 5 * MINUTE,
'/product/shop': 30 * MINUTE, // 店铺首页
'/product/list/new': 30 * SECOND,
// 品牌一览
'/brands/': 5 * MINUTE,
'/brands': 5 * MINUTE,
'/\\/(boys|girls|kids|lifestyle)-brands(\\/)?$/': 5 * MINUTE,
'/brands/plusstar': 5 * MINUTE,
'/^\\/special\\/(\\d+)_(.*)\\.html$/': 5 * MINUTE,
'/product/search/keyword/:id': 7 * DAY
'/product/search/keyword/:id': 7 * DAY,
// 帮助
'/help/': 1 * DAY,
'/help/detail': 1 * DAY,
};
module.exports = cachePage;
... ...
... ... @@ -9,6 +9,9 @@
const isProduction = process.env.NODE_ENV === 'production';
const isTest = process.env.NODE_ENV === 'test';
// 修改 sockets 默认设置
require('http').globalAgent.maxSockets = 10;
module.exports = {
app: 'web',
appVersion: '5.8.0', // 调用api的版本
... ... @@ -43,8 +46,8 @@ module.exports = {
// gray
// singleApi: 'http://single.gray.yohops.com/',
// api: 'http://api.gray.yohops.com/',
// service: 'http://api.gray.yohops.com/',
// api: 'http://apigray.yoho.cn/',
// service: 'http://apigray.yoho.cn/',
// platformApi: 'http://172.16.6.210:8088/',
// dev
... ... @@ -74,7 +77,7 @@ module.exports = {
db: 'web-apm'
},
useOneapm: false,
useCache: false,
useCache: true,
memcache: {
master: ['127.0.0.1:11211'],
slave: ['127.0.0.1:11211'],
... ... @@ -107,7 +110,7 @@ module.exports = {
port: '4444' // influxdb port
},
console: {
level: 'info',
level: 'error',
colorize: 'all',
prettyPrint: true
}
... ... @@ -144,7 +147,7 @@ module.exports = {
},
UNIVERSAL_CAPTCHA: 'yoho4946abcdef#$%&!@',
apiCache: {
cache: false
cache: true
},
zookeeperServer: '192.168.102.168:2188',
sessionMemcachedPrefix: 'yohobuy_session:',
... ... @@ -199,9 +202,12 @@ if (isProduction) {
yohoNowApi: 'http://new.yohoboys.com/',
},
memcache: {
master: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'],
slave: ['memcache1.yohoops.org:12112', 'memcache2.yohoops.org:12112', 'memcache3.yohoops.org:12112'],
session: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111', 'memcache3.yohoops.org:12111'],
master: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111',
'memcache3.yohoops.org:12111', 'memcache4.yohoops.org:12111'],
slave: ['memcache1.yohoops.org:12112', 'memcache2.yohoops.org:12112',
'memcache3.yohoops.org:12112', 'memcache4.yohoops.org:12112'],
session: ['memcache1.yohoops.org:12111', 'memcache2.yohoops.org:12111',
'memcache3.yohoops.org:12111', 'memcache4.yohoops.org:12111'],
poolSize: 100,
reconnect: 5000,
timeout: 300,
... ... @@ -257,11 +263,30 @@ if (isProduction) {
useOneapm: true,
useCache: true,
memcache: {
master: ['127.0.0.1:12111'],
slave: ['127.0.0.1:12112'],
session: ['127.0.0.1:12111'],
master: ['192.168.104.15:12111', '192.168.104.29:12111', '192.168.104.32:12111'],
slave: ['192.168.104.15:12112', '192.168.104.29:12112', '192.168.104.32:12112'],
session: ['192.168.104.15:12111', '192.168.104.29:12111', '192.168.104.32:12111'],
timeout: 1000,
retries: 0
},
redis: {
connect: {
host: '192.168.104.32',
port: '6379',
retry_strategy(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
// console.log('redis连接不成功');
}
if (options.total_retry_time > 1000 * 60 * 60 * 6) {
// console.log('redis连接超时');
return;
}
if (options.attempt > 10) {
return 1000 * 60 * 60 * 0.5;
}
return Math.min(options.attempt * 100, 1000);
}
}
}
});
}
... ...
... ... @@ -11,6 +11,10 @@ const _ = require('lodash');
const api = global.yoho.API;
const serviceApi = global.yoho.ServiceAPI;
const helpers = global.yoho.helpers;
const Handlebars = require('handlebars');
const path = require('path');
const headerHtml = require('fs').readFileSync(path.resolve(__dirname, '../views/partial/headerNew.hbs')).toString();
const template = Handlebars.compile(headerHtml);
/**
* 获取菜单
... ... @@ -214,31 +218,33 @@ const getHeaderNavAsync = () => {
* @param {String} 频道类型
* @return {promise}
*/
exports.requestHeaderData = (type) => {
let cacheHeaderHtml = {};
async function requestHeaderData(type) {
let resData = {};
type = type || 'boys';
let channelNum = (function() {
switch (type) {
case 'boys':
return 1;
case 'girls':
return 2;
case 'kids':
return 3;
case 'lifestyle':
return 4;
default:
return 1;
}
}());
if (_.isEmpty(cacheHeaderHtml[type])) {
let channelNum = (function() {
switch (type) {
case 'boys':
return 1;
case 'girls':
return 2;
case 'kids':
return 3;
case 'lifestyle':
return 4;
default:
return 1;
}
}());
return Promise.all([
getHeaderNavAsync(),
getHotSearchAsync(channelNum)
let res = await Promise.all([
getHeaderNavAsync(),
getHotSearchAsync(channelNum)
]);
]).then(res => {
resData.headerData = {};
if (res[0] && res[0].data) {
... ... @@ -258,6 +264,23 @@ exports.requestHeaderData = (type) => {
});
}
return resData;
});
resData = template(resData);
if (res[0] && res[0].data && res[1] && res[1].data) {
cacheHeaderHtml[type] = resData;
}
} else {
resData = cacheHeaderHtml[type];
}
return Promise.resolve({headerData: resData});
}
setInterval(function() {
cacheHeaderHtml = {};
}, 60 * 10 * 1000);
module.exports = {
requestHeaderData
};
... ...
{{# headerData}}
<div id="yoho-header" class="yoho-header" data-type="{{headType}}">
<div class="tool-wrapper clearfix" data-role="tool-wrapper">
<div class="center-content">
<!--<div class="yoho-group-map left">
<span class="icon-hamburger"></span>
<a href="//www.yohobuy.com"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>YOHO!BUY 有货</a>
{{#if yohoGroup}}
<ul class="yoho-group-list">
{{# yohoGroup}}
<li>
<a href="{{link}}" class="yoho-group"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} data-en="{{en}}" data-cn="{{cn}}">{{en}}</a>
</li>
{{/ yohoGroup}}
</ul>
{{/if}}
</div>-->
<div class="left swindle-info">
<span class="icon-note"></span>
<a href="//www.yohobuy.com/help/detail?id=103&contId=147"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">
关于防诈骗的重要提醒
</a>
</div>
<div class="left yoho-cert-tip"></div>
<div class="yoho-buy-tools right">
<ul>
<li id="loginBox">
<span class="hi">Hi~</span>
[ <a id="signin-url" href="//www.yohobuy.com/signin.html" class="loginbar" rel="nofollow">请登录</a> ]
[ <a id="reg-url" href="//www.yohobuy.com/reg.html" class="registbar" rel="nofollow">免费注册</a> ]
</li>
<li class="myyoho" id="myYohoBox">
<span class="tag-seprate"></span>
<a href="//www.yohobuy.com/home?t=1453340799.4986" rel="nofollow">MY有货</a>
<span class="icon-bottomarrow"></span>
{{#unless @root.pc.common.disMyYohoHover}}
<div class="simple-user-center"></div>
{{/unless}}
</li>
<li class="myorder">
<span class="tag-seprate"></span>
<span class="icon-papers"></span>
<a href="//www.yohobuy.com/home/orders?t=1453168898.0176" rel="nofollow">我的订单</a>
</li>
<li class="mycollect">
<span class="tag-seprate"></span>
<span class="icon-heart"></span>
<a href="//www.yohobuy.com/home/favorite?t=1453168898.0176" rel="nofollow">我的收藏</a>
</li>
<li class="message">
<span class="tag-seprate"></span>
<span class="icon-mail"></span>
<a href="//www.yohobuy.com/home/message?t=1453168898.0176" rel="nofollow">消息</a>
</li>
<li class="help nav-drop-down-container">
<span class="tag-seprate"></span>
<span class="iconfont">&#xe702;</span>
<a href="#">客户服务</a>
<ul class="nav-drop-down">
<li>
<a href="{{#if @root.pc.clientService.new}}http://chat8.live800.com/live800/chatClient/chatbox.jsp?companyID=620092&configID=149091&jid=8732423409{{else}}//www.yohobuy.com/service/client{{/if}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">在线客服</a>
</li>
<li>
<a href="//www.yohobuy.com/help"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>帮助中心</a>
</li>
</ul>
</li>
<li class="we-chat download-code">
<span class="tag-seprate"></span>
<span class="iconfont">&#xe704;</span>
<a href="">关注有货</a>
<div class="download-app-box">
<div class="we-chat-img code-img"></div>
<h5 class="qr-words">微信扫一扫</h5>
<h5 class="qr-words">关注公众号,赢免单</h5>
</div>
</li>
<li class="phoneapp download-code" id="phoneApp">
<span class="tag-seprate"></span>
<span class="icon-phone"></span>
<a href="//www.yohobuy.com/download/app"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>手机版</a>
<div class="download-app-box">
<div class="qr-img code-img"></div>
<h5 class="qr-words">下载手机客户端</h5>
<h5 class="qr-more">
<a href="javascript:void(0);">更多客户端</a>
</h5>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="head-wrapper clearfix">
<div class="center-content">
<div class="outlets-logo"></div>
<div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div>
<ul class="main-nav-list">
{{# navbars}}
<li class="{{type}}"{{#if ico}}
style="background: url({{image2 ico w=54 h=32}}) no-repeat center center"{{/if}}>
{{#if ico}}
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}></a>
{{^}}
<h5 class="name-cn">
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{cn}}</a>
</h5>
<h5 class="name-en">
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{en}}</a>
</h5>
{{/if}}
</li>
{{/ navbars}}
</ul>
<div class="func-area hide">
{{#unless @root.pc.common.disSearchAssociation}}
<ul class="search-suggest"></ul>
{{/unless}}
<ul class="search-suggest-history"></ul>
<div class="search-2016">
<form action="//search.yohobuy.com" method="get" id="search-form">
<input type="hidden" id="defaultsearch" value="{{defaultSearch}}">
<input class="search-key" type="text" name="query" id="query-key" autocomplete="off" x-webkit-speech="" lang="zh-CN" x-webkit-grammar="builtin:translate" value="" maxlength="30">
<a class="search-btn" href="javascript:submitSearch();"></a>
</form>
<ul class="search-hot">
{{#hotTerms}}
<li>
<a style="display: block;" href="{{href}}" title="{{content}}"
act="{{href}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">
<span class="searchvalue">{{content}}</span>
</a>
</li>
{{/hotTerms}}
</ul>
</div>
<div class="go-cart">
<a href="//www.yohobuy.com/cart/cart">
<span class="iconfont ">&#xe600;</span>
{{#unless @root.pc.common.removeCartCount}}
<span class="goods-num-tip hide">0</span>
{{/unless}}
</a>
{{#unless @root.pc.common.disCartHover}}
<div class="mini-cart-wrapper">
<div class="loading-cart">
<h3>加载中,请稍后</h3>
</div>
<div class="empty-cart">
<h3>您的购物车暂无商品</h3>
</div>
</div>
{{/unless}}
</div>
</div>
</div>
</div>
<div class="nav-wrapper clearfix">
<div class="center-content">
{{# subNavGroup}}
<ul class="sub-nav-list {{subType}}">
{{# subNav}}
<li {{#if thirdNav}}class="contain-third"{{/if}}>
<a href="{{link}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{name}}
{{#if isNew}}
<span class="newlogo"></span>
{{/if}}
</a>
{{#if thirdNav}}
<div class="third-nav-wrapper">
<div class="center-content">
<dl class="hide-list hide">
{{# thirdNav}}
<dt>
<h3 class=""><a href="{{link}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{title}}</a></h3>
</dt>
{{#brandItems}}
<dd>
<a href="{{link}}"{{#if hot}}
class="hot"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{brandName}}</a>
</dd>
{{/brandItems}}
{{/thirdNav}}
</dl>
<div class="show-detail" data-code="{{imgCode}}">
<a{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}><img src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="></a>
<a class="title">热门小物优选</a>
</div>
</div>
</div>
{{/if}}
</li>
{{/ subNav}}
</ul>
{{/ subNavGroup}}
</div>
</div>
</div>
<div id="hide-template">
<script type="text/html" id="header-login-info-tpl">
<span>Hi~ <a href="\{{usercenter}}" class="nick-name">\{{nickname}}</a></span>
&nbsp;[ <a href="\{{signout}}">退出</a> ]
</script>
<script type="text/html" id="simple-account-info-tpl">
<div class="account-info-header">
<div class="user-img">
\{{#if headIco}}
<img src="\{{headIco}}">
\{{/if}}
</div>
<div class="user-name">
<a href="//www.yohobuy.com/home?t=\{{random}}">\{{profileName}}</a>
</div>
<h4 class="user-level">
VIP: <span>\{{curTitle}}</span>
</h4>
\{{#unless vip3}}
<div class="level-detail">
<div class="level-view-bar">
<div class="text-span">
\{{curYearCost}}/\{{nextVipNeedCost}}
</div>
<p class="\{{#if curYearCostPer}}integrate\{{/if}}" style="width: \{{curYearCostPer}}%;"></p>
</div>
<span>\{{nextVipTitle}}</span>
</div>
\{{/unless}}
</div>
<ul class="account-info-content">
<li>
<a href="//www.yohobuy.com/home/orders?t=\{{timestamp}}">待处理的订单</a>
<span class="right">\{{order}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/favorite?t=\{{timestamp}}">我的收藏</a>
<span class="right"></span>
</li>
<li>
<a href="//www.yohobuy.com/home/coupons?t=\{{timestamp}}">我的优惠券</a>
<span class="right">\{{coupon}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/currency?t=\{{timestamp}}">我的有货币</a>
<span class="right">\{{coin}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/returns?t=\{{timestamp}}">我的退货换货</a>
<span class="right">\{{return}}</span>
</li>
</ul>
<div class="account-info-footer">
<a href="//www.yohobuy.com/home/user?t=\{{timestamp}}">完善资料
{{#unless @root.pc.user.removeStudentIdentification}}
<span>(学生认证)</span>
{{/unless}}
</a>
</div>
</script>
<script type="text/html" id="search-suggest-tml">
\{{#data}}
<li>
<a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}"
act="\{{href}}">
<span class="searchvalue">\{{keyword}}</span>
<span class="valuenum">约\{{count}}个商品</span>
</a>
</li>
\{{/data}}
</script>
<script type="text/html" id="search-suggest-history">
\{{#if data}}<p class="search-suggest-title">最近搜过<a class="search-del" href="#">清空</a></p>
\{{#data}}
<li>
<a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}"
act="\{{href}}">
<span class="searchvalue">\{{keyword}}</span>
</a>
</li>
\{{/data}}
\{{/if}}
</script>
</div>
<input id="api-domain" type="hidden" value="{{apiDomain}}">
<div class="code-down-box">
<div class="code-img"></div>
<h5 class="code-title">下载手机客户端</h5>
<i class="iconfont icon-del"></i>
</div>
{{/ headerData}}
{{{headerData}}}
... ...
{{# headerData}}
<div id="yoho-header" class="yoho-header" data-type="{{headType}}">
<div class="tool-wrapper clearfix" data-role="tool-wrapper">
<div class="center-content">
<!--<div class="yoho-group-map left">
<span class="icon-hamburger"></span>
<a href="//www.yohobuy.com"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>YOHO!BUY 有货</a>
{{#if yohoGroup}}
<ul class="yoho-group-list">
{{# yohoGroup}}
<li>
<a href="{{link}}" class="yoho-group"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} data-en="{{en}}" data-cn="{{cn}}">{{en}}</a>
</li>
{{/ yohoGroup}}
</ul>
{{/if}}
</div>-->
<div class="left swindle-info">
<span class="icon-note"></span>
<a href="//www.yohobuy.com/help/detail?id=103&contId=147"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">
关于防诈骗的重要提醒
</a>
</div>
{{#unless @root.pc.common.removeCertTip}}
<div class="left yoho-cert-tip"></div>
{{/unless}}
<div class="yoho-buy-tools right">
<ul>
<li id="loginBox">
<span class="hi">Hi~</span>
[ <a id="signin-url" href="//www.yohobuy.com/signin.html" class="loginbar" rel="nofollow">请登录</a> ]
[ <a id="reg-url" href="//www.yohobuy.com/reg.html" class="registbar" rel="nofollow">免费注册</a> ]
</li>
<li class="myyoho" id="myYohoBox">
<span class="tag-seprate"></span>
<a href="//www.yohobuy.com/home?t=1453340799.4986" rel="nofollow">MY有货</a>
<span class="icon-bottomarrow"></span>
{{#unless @root.pc.common.disMyYohoHover}}
<div class="simple-user-center"></div>
{{/unless}}
</li>
<li class="myorder">
<span class="tag-seprate"></span>
<span class="icon-papers"></span>
<a href="//www.yohobuy.com/home/orders?t=1453168898.0176" rel="nofollow">我的订单</a>
</li>
<li class="mycollect">
<span class="tag-seprate"></span>
<span class="icon-heart"></span>
<a href="//www.yohobuy.com/home/favorite?t=1453168898.0176" rel="nofollow">我的收藏</a>
</li>
<li class="message">
<span class="tag-seprate"></span>
<span class="icon-mail"></span>
<a href="//www.yohobuy.com/home/message?t=1453168898.0176" rel="nofollow">消息</a>
</li>
<li class="help nav-drop-down-container">
<span class="tag-seprate"></span>
<span class="iconfont">&#xe702;</span>
<a href="#">客户服务</a>
<ul class="nav-drop-down">
<li>
<a href="{{#if @root.pc.clientService.new}}http://chat8.live800.com/live800/chatClient/chatbox.jsp?companyID=620092&configID=149091&jid=8732423409{{else}}//www.yohobuy.com/service/client{{/if}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">在线客服</a>
</li>
<li>
<a href="//www.yohobuy.com/help"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>帮助中心</a>
</li>
</ul>
</li>
<li class="we-chat download-code">
<span class="tag-seprate"></span>
<span class="iconfont">&#xe704;</span>
<a href="">关注有货</a>
<div class="download-app-box">
<div class="we-chat-img code-img"></div>
<h5 class="qr-words">微信扫一扫</h5>
<h5 class="qr-words">关注公众号,赢免单</h5>
</div>
</li>
<li class="phoneapp download-code" id="phoneApp">
<span class="tag-seprate"></span>
<span class="icon-phone"></span>
<a href="//www.yohobuy.com/download/app"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>手机版</a>
<div class="download-app-box">
<div class="qr-img code-img"></div>
<h5 class="qr-words">下载手机客户端</h5>
<h5 class="qr-more">
<a href="javascript:void(0);">更多客户端</a>
</h5>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="head-wrapper clearfix">
<div class="center-content">
<div class="outlets-logo"></div>
<div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div>
<ul class="main-nav-list">
{{# navbars}}
<li class="{{type}}">
{{#if ico}}
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}></a>
{{^}}
<h5 class="name-cn">
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{cn}}</a>
</h5>
<h5 class="name-en">
<a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{en}}</a>
</h5>
{{/if}}
</li>
{{/ navbars}}
</ul>
<div class="func-area hide">
{{#unless @root.pc.common.disSearchAssociation}}
<ul class="search-suggest"></ul>
{{/unless}}
<ul class="search-suggest-history"></ul>
<div class="search-2016">
<form action="//search.yohobuy.com" method="get" id="search-form">
<input type="hidden" id="defaultsearch" value="{{defaultSearch}}">
<input class="search-key" type="text" name="query" id="query-key" autocomplete="off" x-webkit-speech="" lang="zh-CN" x-webkit-grammar="builtin:translate" value="" maxlength="30">
<a class="search-btn" href="javascript:submitSearch();"></a>
</form>
<ul class="search-hot">
{{#hotTerms}}
<li>
<a style="display: block;" href="{{href}}" title="{{content}}"
act="{{href}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}} target="_blank">
<span class="searchvalue">{{content}}</span>
</a>
</li>
{{/hotTerms}}
</ul>
</div>
<div class="go-cart">
<a href="//www.yohobuy.com/cart/cart">
<span class="iconfont ">&#xe600;</span>
{{#unless @root.pc.common.removeCartCount}}
<span class="goods-num-tip hide">0</span>
{{/unless}}
</a>
{{#unless @root.pc.common.disCartHover}}
<div class="mini-cart-wrapper">
<div class="loading-cart">
<h3>加载中,请稍后</h3>
</div>
<div class="empty-cart">
<h3>您的购物车暂无商品</h3>
</div>
</div>
{{/unless}}
</div>
</div>
</div>
</div>
<div class="nav-wrapper clearfix">
<div class="center-content">
{{# subNavGroup}}
<ul class="sub-nav-list {{subType}}">
{{# subNav}}
<li {{#if thirdNav}}class="contain-third"{{/if}}>
<a href="{{link}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{name}}
{{#if isNew}}
<span class="newlogo"></span>
{{/if}}
</a>
{{#if thirdNav}}
<div class="third-nav-wrapper">
<div class="center-content">
<dl class="hide-list hide">
{{# thirdNav}}
<dt>
<h3 class=""><a href="{{link}}"{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{title}}</a></h3>
</dt>
{{#brandItems}}
<dd>
<a href="{{link}}"{{#if hot}}
class="hot"{{/if}}{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}>{{brandName}}</a>
</dd>
{{/brandItems}}
{{/thirdNav}}
</dl>
<div class="show-detail" data-code="{{imgCode}}">
<a{{#if @root.pageNoFollow}} rel="nofollow"{{/if}}><img src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="></a>
<a class="title">热门小物优选</a>
</div>
</div>
</div>
{{/if}}
</li>
{{/ subNav}}
</ul>
{{/ subNavGroup}}
</div>
</div>
</div>
<div id="hide-template">
<script type="text/html" id="header-login-info-tpl">
<span>Hi~ <a href="\{{usercenter}}" class="nick-name">\{{nickname}}</a></span>
&nbsp;[ <a href="\{{signout}}">退出</a> ]
</script>
<script type="text/html" id="simple-account-info-tpl">
<div class="account-info-header">
<div class="user-img">
\{{#if headIco}}
<img src="\{{headIco}}">
\{{/if}}
</div>
<div class="user-name">
<a href="//www.yohobuy.com/home?t=\{{random}}">\{{profileName}}</a>
</div>
<h4 class="user-level">
VIP: <span>\{{curTitle}}</span>
</h4>
\{{#unless vip3}}
<div class="level-detail">
<div class="level-view-bar">
<div class="text-span">
\{{curYearCost}}/\{{nextVipNeedCost}}
</div>
<p class="\{{#if curYearCostPer}}integrate\{{/if}}" style="width: \{{curYearCostPer}}%;"></p>
</div>
<span>\{{nextVipTitle}}</span>
</div>
\{{/unless}}
</div>
<ul class="account-info-content">
<li>
<a href="//www.yohobuy.com/home/orders?t=\{{timestamp}}">待处理的订单</a>
<span class="right">\{{order}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/favorite?t=\{{timestamp}}">我的收藏</a>
<span class="right"></span>
</li>
<li>
<a href="//www.yohobuy.com/home/coupons?t=\{{timestamp}}">我的优惠券</a>
<span class="right">\{{coupon}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/currency?t=\{{timestamp}}">我的有货币</a>
<span class="right">\{{coin}}</span>
</li>
<li>
<a href="//www.yohobuy.com/home/returns?t=\{{timestamp}}">我的退货换货</a>
<span class="right">\{{return}}</span>
</li>
</ul>
<div class="account-info-footer">
<a href="//www.yohobuy.com/home/user?t=\{{timestamp}}">完善资料
{{#unless @root.pc.user.removeStudentIdentification}}
<span>(学生认证)</span>
{{/unless}}
</a>
</div>
</script>
<script type="text/html" id="search-suggest-tml">
\{{#data}}
<li>
<a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}"
act="\{{href}}">
<span class="searchvalue">\{{keyword}}</span>
<span class="valuenum">约\{{count}}个商品</span>
</a>
</li>
\{{/data}}
</script>
<script type="text/html" id="search-suggest-history">
\{{#if data}}<p class="search-suggest-title">最近搜过<a class="search-del" href="#">清空</a></p>
\{{#data}}
<li>
<a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}"
act="\{{href}}">
<span class="searchvalue">\{{keyword}}</span>
</a>
</li>
\{{/data}}
\{{/if}}
</script>
</div>
<input id="api-domain" type="hidden" value="{{apiDomain}}">
<div class="code-down-box">
<div class="code-img"></div>
<h5 class="code-title">下载手机客户端</h5>
<i class="iconfont icon-del"></i>
</div>
{{/ headerData}}
\ No newline at end of file
... ...
{
"name": "yohobuy-node",
"version": "6.1.5",
"version": "5.9.92",
"private": true,
"description": "A New Yohobuy Project With Express",
"repository": {
... ... @@ -33,6 +33,7 @@
"cookie-parser": "^1.4.3",
"csurf": "^1.9.0",
"express": "^4.13.1",
"handlebars": "4.0.5",
"lodash": "^4.13.1",
"md5": "^2.1.0",
"moment": "^2.14.1",
... ... @@ -67,9 +68,9 @@
"eslint-config-yoho": "^1.0.1",
"eslint-loader": "^1.6.3",
"extract-text-webpack-plugin": "^2.1.0",
"happypack": "^3.0.3",
"handlebars": "4.0.5",
"handlebars-loader": "^1.4.0",
"happypack": "^3.0.3",
"husky": "^0.11.4",
"jquery-ui": "^1.12.1",
"nodemon": "1.9.2",
... ... @@ -84,6 +85,7 @@
"postcss-opacity": "^4.0.0",
"postcss-position": "^0.5.0",
"postcss-pxtorem": "^4.0.0",
"postcss-scss": "^1.0.2",
"postcss-short": "^3.0.3",
"postcss-sprites": "^4.2.0",
"postcss-use": "^2.3.0",
... ... @@ -93,8 +95,8 @@
"style-loader": "^0.16.0",
"stylelint": "^7.9.0",
"stylelint-config-yoho": "^1.2.8",
"stylelint-webpack-plugin": "^0.7.0",
"stylelint-formatter-table": "^1.0.3",
"stylelint-webpack-plugin": "^0.7.0",
"webpack": "^2.3.1",
"webpack-dev-server": "^2.4.2",
"yoho-eventproxy": "^0.3.6",
... ...
... ... @@ -3,7 +3,7 @@
{
"name": "yohobuy-node",
"script": "app.js",
"instances": "6",
"instances": "8",
"exec_mode": "cluster",
"merge_logs": true,
"log_date_format": "YYYY-MM-DD HH:mm Z",
... ...
... ... @@ -1075,7 +1075,9 @@ giftCard.init();
refund.init();
// 获取用户是否新客(品众统计)写cookie
$.ajax({type: 'GET', url: '/home/newuser'});
if (!window.cookie('__NEW_USER').length) {
$.ajax({type: 'GET', url: '/home/newuser'});
}
// 订单确认页默认埋点
yas.givePoint('YB_SC_ORDER_ENSURE');
... ...
... ... @@ -70,14 +70,15 @@ $('.new-user-prolist').slider2({
isCircle: false
});
if (window.cookie('_UID')) {
// 判断是否有新人专享
// 判断是否是新客,展示新客楼层
if (window.cookie('__NEW_USER')) {
$('.new-user').addClass('hide');
} else {
$.ajax({
type: 'GET',
url: '/channel/isNewUserAjax',
data: {},
url: '/home/newuser',
success: function(res) {
if (!res.isNewUser) {
if (res.code === 200 && res.data && res.data.isNew) {
$('.new-user').addClass('hide');
}
}
... ...
... ... @@ -135,8 +135,10 @@ if ($rightFloatingLayer.hasClass('hide')) {
}
// 初始化
actionSubscription();
actionhomeFootChange();
if ($('.yoho-footer .footertop').length) {
actionSubscription();
actionhomeFootChange();
}
// 暴露给有可能通AJAX改变内容的页面去用
window.rePosReturnTop = rePosReturn;
... ...
... ... @@ -948,11 +948,16 @@ function actionCover() {
window.submitSearch = submitSearch;
function emailUserCertTip() {
var $certTip = $('#yoho-header .yoho-cert-tip');
if (!$certTip.length) {
return;
}
return $.getJSON('//www.yohobuy.com/passport/cert/headerTip?callback=?', function(jsonData) {
if (jsonData && jsonData.data === 'Y') {
$('#yoho-header .swindle-info').addClass('hide');
$('#yoho-header .yoho-cert-tip').html(
$certTip.html(
'<i class="iconfont left">&#xe63f;</i> ' +
'您的账号安全等级较低,建议您立即<a href="//www.yohobuy.com/passport/cert/index">绑定手机号</a>');
}
... ...
... ... @@ -4,12 +4,14 @@
* @date: 2016/2/17
*/
var $ = require('yoho-jquery');
var $ = require('yoho-jquery'),
handlebars = require('yoho-handlebars');
var yas = require('../common/data-yas');
var RECID = (new Date().getTime() + '_PC_YOHOBUY_' +
Math.floor(Math.random() * 1000000 + 1000000) +
'_' + Math.floor(Math.random() * 1000000 + 1000000));
require('./orders/order-block');
require('../common');
... ... @@ -76,6 +78,13 @@ function pageChange(self, $ul, page, itemWith, curPage, num) {
rcCurPage = 1,
PRDID = [];
var $brandsBox = $('#home-brands-box'),
$newBox = $('#home-new-box'),
$recommendBox = $('#home-recommend-box'),
$brandsTpl = $('#home-brands-tpl').html(),
$newTpl = $('#home-new-tpl').html(),
$recommendTpl = $('#home-recommend-tpl').html();
$naUl.width($naUl.width() * naPage);
$rcUl.width($rcUl.width() * rcPage);
... ... @@ -167,4 +176,40 @@ function pageChange(self, $ul, page, itemWith, curPage, num) {
// 为您优选埋点 end
// 我的消息、我的头像
$.getJSON('//www.yohobuy.com/home/index/newsAvatar', function(data) {
$('#user-thumb').attr('src', data.avatar);
if (+data.msg > 0) {
$('#new-count').html(data.msg).removeClass('hide');
}
});
// 异步数据
$.getJSON('//www.yohobuy.com/home/index/async', function(data) {
// 首页异步数据待处理订单,未读消息,待分享商品数量
if (data.numbers) {
$.each(data.numbers, function(key, val) {
$('#' + key).html(val);
});
}
// 喜欢的品牌
if (data.brands) {
$brandsBox.html(handlebars.compile($brandsTpl)(data.brands));
}
// 新品上架
if (data.products) {
$newBox.html(handlebars.compile($newTpl)(data.products));
}
// 推荐
if (data.rec) {
$recommendBox.html(handlebars.compile($recommendTpl)(data.rec));
}
});
}());
... ...
... ... @@ -1414,14 +1414,14 @@ function loadConsult() {
baseUrl = '/product/detail/consult?productId=' + id;
if (totalNum > 20) {
$('.consults-pager').empty().append(
setPager({
baseUrl: baseUrl,
totalRecords: totalNum,
page: nowPage,
type: 'ellipsis',
theme: 'msg-pager'
})
);
setPager({
baseUrl: baseUrl,
totalRecords: totalNum,
page: nowPage,
type: 'ellipsis',
theme: 'msg-pager'
})
);
}
}
}).always(function() {
... ... @@ -1945,16 +1945,19 @@ bindEvent.fire();
}
});
favorite.getFavNum(shopId, brandId).then(function(result) {
if (result === 0) {
return;
}
$('#brand-favour').find('span').html(result);
});
favorite.statusFav(shopId, brandId).then(function() {
$('#brand-favour').addClass('coled');
});
if ($('#brand-favour').length) {
favorite.getFavNum(shopId, brandId).then(function(result) {
if (result === 0) {
return;
}
$('#brand-favour').find('span').html(result);
});
favorite.statusFav(shopId, brandId).then(function() {
$('#brand-favour').addClass('coled');
});
}
// 数据懒加载
var dataLoad = dataLazyLoad.init({cls: '.datalazyload', threshold: 0}); //eslint-disable-line
... ...
... ... @@ -9,8 +9,9 @@ var $ = require('yoho-jquery'),
var limitTime = [];
var $saleBox = $('.sale-box'),
$navItem = $('.sale-nav li');
var $navItem = $('.sale-nav li');
var htmlCache = {};
require('../common');
require('./sale/banner');
... ... @@ -25,7 +26,7 @@ lazyLoad($('img.lazy'));
$('.slide-container').slider();
// 请求带导航的列表数据
function getNaviData(items) {
function getNaviData(items, refresh) {
var location, url, goodItems, navTitle;
navTitle = items.attr('urlLocation');
... ... @@ -35,15 +36,28 @@ function getNaviData(items) {
goodItems = items.parent().parent('.sale-box').find('.goods');
url = location + navTitle;
if (refresh) {
url += '&refresh=' + true;
}
// 本地有缓存数据,则取本地数据
if (htmlCache[url]) {
goodItems.html(htmlCache[url]);
lazyLoad(items.closest('.sale-box').find('.lazy'));
return;
}
$.ajax({
type: 'GET',
url: url,
data: ''
}).then(function(res) {
goodItems.html('');
goodItems.append(res);
lazyLoad(items.closest('.sale-box').find('.lazy'));
if (res) {
htmlCache[url] = res;
goodItems.html(res);
lazyLoad(items.closest('.sale-box').find('.lazy'));
}
});
}
... ... @@ -52,21 +66,15 @@ $navItem.on('click', function() {
$(this).addClass('active');
getNaviData($(this));
});
// 初始化页面数据
(function() {
$saleBox.each(function() {
var item = $(this).find($navItem).eq(0);
getNaviData(item);
});
if (window.cookie('_UID')) {
getNaviData($('.sale-vip-box .sale-nav li').eq(0), true);
}
}());
function convertTime(time) {
var anHour = 3600000,
aMinute = 60000,
... ...