Too many changes to show.

To preserve performance only 28 of 28+ files are displayed.

... ... @@ -12,7 +12,7 @@ const path = require('path');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const favicon = require('serve-favicon');
const session = require('express-session');
const session = require('yoho-express-session');
const memcached = require('connect-memcached');
const hbs = require('express-handlebars');
... ... @@ -34,6 +34,7 @@ global.utils = path.resolve('./utils');
app.locals.devEnv = app.get('env') === 'development';
app.locals.version = pkg.version;
app.set('subdomain offset', 2);
app.set('view engine', '.hbs');
app.set('views', './doraemon/views');
app.set('view cache', true);
... ... @@ -85,6 +86,7 @@ const logger = global.yoho.logger;
// dispatcher
try {
const subDomain = require('./doraemon/middleware/sub-domain');
const itemNameHandler = require('./doraemon/middleware/item-name-handler');
const mobileCheck = require('./doraemon/middleware/mobile-check');
const user = require('./doraemon/middleware/user');
... ... @@ -94,8 +96,8 @@ try {
const setPageInfo = require('./doraemon/middleware/set-pageinfo');
// YOHO 前置中间件
app.use(subDomain());
app.use(itemNameHandler);
app.use(mobileCheck());
app.use(setYohoData());
app.use(user());
... ...
... ... @@ -14,20 +14,11 @@ const helpers = global.yoho.helpers;
const processProduct = require(`${global.utils}/product-process`).processProduct;
const api = global.yoho.API;
const serviceApi = global.yoho.ServiceAPI;
const searchApi = global.yoho.SearchAPI;
const headerModel = require('../../../doraemon/models/header');
// 获取上线时间
const _getShelveTime = duration => {
let today = new Date(),
todayMil = today.getTime().toString().substr(0, 10),
startDayMil = (today.setMonth(today.getMonth() - duration)).toString().substr(0, 10);
return `${startDayMil},${todayMil}`; // `返回时间
};
// 获取可用的标题
const _getText = data => {
let text = data.split(' ')[0];
... ... @@ -52,11 +43,10 @@ const channelMap = dataMap.channel;
const sortMap = dataMap.sort;
// 构建url
const _httpBuildQuery = data => {
return searchApi.get('search.json', data, {
cache: true
});
const webSearchAsync = data => {
return api.get('', Object.assign({
method: 'web.search.newshelve'
}, data), {cache: true});
};
/**
... ... @@ -339,7 +329,7 @@ const _getGirlsSingleHot = (args, type) => {
return;
}
return searchApi.get('search.json', {
return webSearchAsync({
client_type: 'web',
query: skns,
order: 'shelve_time:desc',
... ... @@ -349,9 +339,7 @@ const _getGirlsSingleHot = (args, type) => {
stocknumber: 1,
page: 1,
gender: channelMap[type].gender,
viewNum: 60
}, {
cache: true
limit: 60
});
};
... ... @@ -739,7 +727,8 @@ const _formatParams = (channel, data) => {
if (data) {
params = Object.assign(data);
}
return searchApi.get('search.json', params).then(result => {
return webSearchAsync(params).then(result => {
let ret = result.data;
if (ret) {
... ... @@ -811,16 +800,16 @@ const getNewArrival = channel => {
sales: 'Y',
attribute_not: 2,
stocknumber: 3,
shelve_time: _getShelveTime(2)
dayLimit: 4
};
params.gender = channelMap[channel].gender;
_.forEach(sortList, (item) => {
let data = Object.assign(item, params);
let data = Object.assign(item, params, {limit: item.viewNum});
rel.push(_httpBuildQuery(data));
rel.push(webSearchAsync(data));
});
return Promise.all(rel).then(res => {
... ... @@ -828,10 +817,10 @@ const getNewArrival = channel => {
result = [];
_.forEach(sortList, (it, index) => {
if (res[index].data &&
res[index].data.product_list.length === sortList[index].viewNum) {
let list = _.get(res[index], 'data.product_list', []);
data = data.concat(res[index].data.product_list);
if (list.length === sortList[index].viewNum) {
data = data.concat(list);
}
});
... ...
... ... @@ -15,8 +15,6 @@ const DoubanStrategy = require('passport-douban').Strategy;
const RenrenStrategy = require('passport-renren').Strategy;
const AlipayStrategy = require('./models/passport-alipay').Strategy;
const md5 = require('md5');
const AuthHelper = require('./models/auth-helper');
const config = global.yoho.config;
... ... @@ -37,21 +35,11 @@ passport.use('local', new LocalStrategy({
let area = req.body.areaCode || '86';
if (isNaN(_.parseInt(area)) || _.isEmpty(username) || _.isEmpty(password)) {
if (_.isEmpty(password)) {
logger.info(`【Passport Loginbad params, area:${area} account:${username} password:${password}`);
return done({message: '登录参数错误'}, null);
}
let verifyEmail = helpers.verifyEmail(username);
// 国际号码验证取消
let verifyMobile = area === '86' ? helpers.verifyAreaMobile(area + '-' + username) : true;
if (!verifyEmail && !verifyMobile) {
logger.info(`【Passport Loginbad account, email:${verifyEmail} mobile:${verifyMobile}`);
return done({message: '登录账号格式错误'}, null);
}
let verifyCode = req.body.captcha;
if (verifyCode && verifyCode !== req.session.captcha) {
... ... @@ -62,7 +50,6 @@ passport.use('local', new LocalStrategy({
let account = req.body.account;
let type = req.body.loginType;
let ip = req.ip;
let errorLoginKey = 'account_errorlogin_' + account;
let accountKey = 'account_signin_' + account;
... ...
... ... @@ -74,6 +74,10 @@ const getUserInfoAPI = (req, res, next) => {
.catch(next);
};
const fakeGetUserInfoAPI = (req, res) => {
return res.json({code: 200, message: '验证成功'});
};
const sendCodePage = (req, res, next) => {
let inputInfo = req.inputInfo;
... ... @@ -357,6 +361,7 @@ const validateMobileInSession = (req, res, next) => {
module.exports = {
index, // 首页
fakeGetUserInfoAPI, // 通过邮箱或手机号获得用户信息
getUserInfoAPI, // 通过邮箱或手机号获得用户信息
sendCodePage, // 发送验证码到邮箱或者手机,然后跳转页面
saveInSession, // 保存状态到session中
... ...
... ... @@ -44,7 +44,7 @@ const generate = (req, res) => {
res.end(captcha.image);
};
const checkAPI = (req, res, next) => {
const checkAPI = (req, res) => {
let captchaToken = req.body.verifyCode || '';
if (captchaToken === req.session.captcha) {
... ...
... ... @@ -12,7 +12,6 @@ const qs = require('querystring');
const passport = require('passport');
const uuid = require('uuid');
const md5 = require('md5');
const cookie = global.yoho.cookie;
const helpers = global.yoho.helpers;
const log = global.yoho.logger;
... ... @@ -30,7 +29,7 @@ function doPassportCallback(req, res, user) {
let shoppingKey = cookie.getShoppingKey(req);
let refer = cookie.getRefer(req, config.siteUrl);
if (/sign|login/.test(refer)) {
if (/sign|login|reg|passport/.test(refer)) {
refer = config.siteUrl;
}
if (user.openId) {
... ... @@ -122,6 +121,13 @@ const local = {
areaName = area ? area.name : '';
}
let refer = req.query.refer;
refer && res.cookie('refer', encodeURI(refer), {
domain: 'yohobuy.com'
});
PassportHelper.getLeftBannerAsync(SIGNIN_LEFT_BANNER_CODE).then(cover => {
res.render('login', {
loginPage: true,
... ... @@ -133,7 +139,7 @@ const local = {
countryName: areaName,
countryList: areaArr,
forgetPwd: helpers.urlFormat('/passport/back/index'),
fastReg: helpers.urlFormat('/passport/reg/index'),
fastReg: helpers.urlFormat('/reg.html', {refer: refer}),
weixinLogin: helpers.urlFormat('/passport/autosign/wechat'),
qqLogin: helpers.urlFormat('/passport/autosign/qq'),
weiboLogin: helpers.urlFormat('/passport/autosign/sina'),
... ... @@ -168,10 +174,10 @@ const local = {
}
let refer = (function() {
if (_.has(req, 'cookies.refer')) {
return decodeURI(req.cookies.refer);
} else if (/sign|login|passport/.test(_.get(req, 'cookies.refer', ''))) {
if (/sign|login|reg|passport/.test(_.get(req, 'cookies.refer', ''))) {
return `${config.siteUrl}/home`;
} else if (_.has(req, 'cookies.refer')) {
return decodeURI(req.cookies.refer);
} else {
return `${config.siteUrl}/home`;
}
... ... @@ -284,6 +290,86 @@ const local = {
}
}).catch(next);
}
},
qrcode: {
refresh: (req, res, next) => {
AuthHelper.fetchByQrCode().then((result) => {
if (result.code === 200) {
return res.json({
code: 200,
data: {
qrcode: `http://m.yohobuy.com/signin.html?qr=${result.data.code}`
}
});
} else {
return res.json({
code: 400,
message: '生成二维码错误'
});
}
}).catch(next);
},
check: (req, res, next) => {
let qrcode = req.body.qrcode || '';
if (!qrcode) {
return res.json({
code: 400,
message: '输入错误,请重新刷新二维码'
});
}
if (!_.includes(qrcode, '=')) {
return res.json({
code: 400,
message: '输入错误,请重新刷新二维码'
});
}
let token = qrcode.substring(qrcode.indexOf('=') + 1);
AuthHelper.checkByQrCode(token).then((result) => {
if (_.isEmpty(result)) {
return res.json({
code: 400,
message: '输入错误,请重新刷新二维码'
});
}
if (result.code === 402) {
return res.json({
code: 402,
message: '二维码为空'
});
}
if (result.code === 403) {
return res.json({
code: 403,
message: '二维码未失效,不可登录'
});
}
if (result.code === 404) {
return res.json({
code: 404,
message: '二维码失效,需要重新刷新'
});
}
if (result.code === 200) {
return res.json({
code: 200,
message: '可以登录'
});
}
return res.json({
code: 400,
message: '服务器问题'
});
}).catch(next);
}
}
};
... ...
... ... @@ -87,7 +87,7 @@ let index = (req, res, next) => {
captchaUrl: helpers.urlFormat('/passport/imagesNode', {t: Date.now()}),
itemUrl: helpers.urlFormat('/help/', {category_id: 143}),
referUrl: refer,
loginUrl: helpers.urlFormat('/passport/login', {refer: refer}),
loginUrl: helpers.urlFormat('/signin.html', {refer: refer}),
coverHref: result.url,
coverImg: result.img,
regBtnText: '立即注册'
... ...
'use strict';
const md5 = require('md5');
const aes = require('./aes-pwd');
const cache = global.yoho.cache;
const sign = global.yoho.sign;
... ... @@ -11,7 +12,8 @@ const Auth = {
let _that = this;
let loginBy = {
password: _that.signinByPasswordWithAes,
sms: _that.signinBySMS
sms: _that.signinBySMS,
qrcode: _that.signinByQrCode
};
return loginBy[type](area, profile, password, shoppingKey);
... ... @@ -44,6 +46,33 @@ const Auth = {
return api.post('', param);
},
signinByQrCode(__1, __2, code, shoppingKey) { // eslint-disable-line
let param = {
method: 'app.twoDimen.qrCodeLogin',
code: code.substring(code.indexOf('=') + 1)
};
if (shoppingKey) {
param.shopping_key = shoppingKey;
}
return api.post('', param);
},
fetchByQrCode() {
let param = {
method: 'app.twoDimen.getCode'
};
return api.post('', param);
},
checkByQrCode(code) {
let param = {
method: 'app.twoDimen.loginCheck',
code: code
};
return api.post('', param);
},
sendPasswordBySMS(area, mobile) {
let param = {
method: 'app.message.sendSms',
... ...
... ... @@ -249,8 +249,8 @@ const getUserInfo = (area, mobile) => {
return UserService.findByMobileAsync(area, mobile).then(user => {
let profile = (user.profile_name || mobile).toString();
if ((profile.length === 11 && profile.indexOf('*') < 0) || (profile.indexOf('-') >= 0
&& profile.indexOf('*') < 0)) {
if ((profile.length === 11 && profile.indexOf('*') < 0) || (profile.indexOf('-') >= 0 &&
profile.indexOf('*') < 0)) {
profile = profile.substring(0, 3) + '****' + profile.substring(7, 11);
}
... ...
... ... @@ -27,7 +27,11 @@ router.get('/logout.html', login.local.logout);
router.post('/passport/login/sms/send', login.local.sms.send); // 发短信验证码
router.post('/passport/login/sms/auth', login.local.sms.auth); // 验证短信验证码
router.post('/passport/login/sms/checkuser', login.local.sms.checkUser); // 短信检查用户是否注册
router.post('/passport/login/password/checkuser', back.validateInputAPI, back.getUserInfoAPI);// 密码登录检查用户是否注册
router.post('/passport/login/password/checkuser', back.fakeGetUserInfoAPI);// 假的,密码登录检查用户是否注册
// 二维码登录
router.post('/passport/login/qrcode/refresh', login.local.qrcode.refresh); // 刷新二维码
router.post('/passport/login/qrcode/check', login.local.qrcode.check); // 验证二维码的状态
router.get('/passport/login', login.common.beforeLogin, login.local.loginPage);
router.post('/passport/login/auth', login.local.login);
... ...
... ... @@ -4,8 +4,17 @@
<form method='post' onsubmit='return false;'>
<div class="content">
<ul class="login-ul">
<li class="relative clearfix">
<h2 class="title">会员登录</h2>
<div class="switch-login-type" data-type="DesktopLogin">
<div id="device-bg" class="type-mobile-bg"></div>
<div class="type-tip tip-title">
<div id="device-tip" style="margin-left: 15px;margin-top: 8px">扫码登录更安全</div>
</div>
</div>
<div class="desktop-login">
<li class="relative clearfix">
<h2 class="title">会员登录</h2>
<span id="country-code" class="country-code right">
<em>{{countryName}} {{countryCode}}</em>
<i class="iconfont">&#xe60b;</i>
... ... @@ -16,70 +25,73 @@
{{/each}}
</ul>
</span>
</li>
</li>
<li class="relative">
<div class="switch">
<div class="left selected" data-type="PasswordLogin">普通登录</div>
<div class="right" data-type="SMSLogin">手机验证码登录</div>
</div>
</li>
<li class="relative">
<div class="switch">
<div class="left selected" data-type="PasswordLogin">普通登录</div>
<div class="right" data-type="SMSLogin">手机验证码登录</div>
</div>
</li>
<li class="relative password-login">
<input id="account1" class="account input va" name="account" value="{{bindMobile}}" type="text"
placeholder="邮箱/手机号码" autocomplete="off">
<li class="relative password-login">
<input id="account1" class="account input va" name="account" value="{{bindMobile}}"
type="text"
placeholder="邮箱/手机号码" autocomplete="off">
<span class="err-tip hide">
<i></i>
<em></em>
</span>
</li>
<li class="relative password-login">
<input id="password" class="password input va" name="password" type="password" placeholder="密码"
autocomplete="off" maxlength="20">
<span id="caps-lock" class="caps-lock hide">大写状态开启</span>
</li>
<li class="relative password-login">
<input id="password" class="password input va" name="password" type="password"
placeholder="密码"
autocomplete="off" maxlength="20">
<span id="caps-lock" class="caps-lock hide">大写状态开启</span>
<span class="err-tip hide">
<i></i>
<em>请输入密码</em>
</span>
</li>
</li>
<li class="relative clearfix sms-login hide">
<input id="account2" class="account input va" name="account2" value="{{bindMobile}}" type="text"
placeholder="手机号码" autocomplete="off">
<li class="relative clearfix sms-login hide">
<input id="account2" class="account input va" name="account2" value="{{bindMobile}}"
type="text"
placeholder="手机号码" autocomplete="off">
<span class="err-tip hide">
<i></i>
<em></em>
</span>
</li>
</li>
<li class="relative clearfix sms-login hide">
<input id="captcha-sms" class="input va captcha-sms-input" type="text" name="captcha"
placeholder="短信验证码"
autocomplete="off" maxlength="4">
<div class="change-captcha-sms btn">获取短信验证码</div>
<li class="relative clearfix sms-login hide">
<input id="captcha-sms" class="input va captcha-sms-input" type="text" name="captcha"
placeholder="短信验证码"
autocomplete="off" maxlength="4">
<div class="change-captcha-sms btn">获取短信验证码</div>
<span class="err-tip hide">
<i></i>
<em></em>
</span>
</li>
</li>
<li class="clearfix captcha-wrap hide">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码"
autocomplete="off" maxlength="4">
<img id="captcha-img" class="captcha-img" alt="">
<a class="link change-captcha">换一张</a>
<li class="clearfix captcha-wrap hide">
<input id="captcha" class="input va captcha" type="text" name="captcha" placeholder="图形验证码"
autocomplete="off" maxlength="4">
<img id="captcha-img" class="captcha-img" alt="">
<a class="link change-captcha">换一张</a>
<span class="err-tip hide">
<i></i>
<em></em>
</span>
</li>
</li>
<li>
<span id="login-btn" class="login-btn btn">登录</span>
</li>
<li class="other-opts">
<li class="desktop-login">
<span id="login-btn" class="login-btn btn">登录</span>
</li>
<li class="other-opts">
<span class="remember-me">
<i class="iconfont">&#xe613;</i>
记住登录状态
... ... @@ -89,30 +101,67 @@
|
<a class="fast-reg" href="{{fastReg}}">快速注册</a>
</span>
</li>
<li class="third-party-login">
<a href="{{weixinLogin}}">
<span class="icon weixin"></span>
</a>
<a href="{{qqLogin}}">
<span class="icon qq"></span>
</a>
<a href="{{weiboLogin}}">
<span class="icon weibo"></span>
</a>
<a href="{{alipayLogin}}">
<span class="icon alipay"></span>
</a>
<a href="{{doubanLogin}}">
<span class="icon douban"></span>
</a>
<a href="{{renrenLogin}}">
<span class="icon renren"></span>
</a>
</li>
</li>
<li class="third-party-login">
<a href="{{weixinLogin}}">
<span class="icon weixin"></span>
</a>
<a href="{{qqLogin}}">
<span class="icon qq"></span>
</a>
<a href="{{weiboLogin}}">
<span class="icon weibo"></span>
</a>
<a href="{{alipayLogin}}">
<span class="icon alipay"></span>
</a>
<a href="{{doubanLogin}}">
<span class="icon douban"></span>
</a>
<a href="{{renrenLogin}}">
<span class="icon renren"></span>
</a>
</li>
</div>
<div class="mobile-login hide">
<li class="relative clearfix center qrcode-login-title">
<div>
手机扫码,安全登录
</div>
</li>
<li class="relative clearfix center">
<div id="qrcode-placeholder" style="width: 270px;height: 140px;"></div>
<div id="qrcode-container" class="qrcode"></div>
<div id="qrcode-helper" class="qrcode-helper hide"></div>
<div id="qrcode-hover" class="qrcode-hover"></div>
<div id="qrcode-overlay"></div>
</li>
<li class="relative clearfix">
<div>
<i class="scan"></i>
<p class="scan-font">打开手机Yoho!Buy有货</p>
<p class="scan-font">扫一扫登录</p>
</div>
</li>
<li class="relative clearfix center">
<div>
<a class="download-link"
href="http://www.yohobuy.com/download/app">下载Yoho!Buy有货手机APP</a>
</div>
</li>
</div>
</ul>
<input id="country-code-hide" name="countryCode" type="hidden" value="{{countryCode}}">
<input id="captcha-sms-token-hide" name="token" type="hidden">
<input id="qrcode" name="qrcode" type="hidden" value="//m.yohobuy.com/signin.html">
<input id="uid" type="hidden">
</div>
</form>
{{/ passport}}
... ...
... ... @@ -6,6 +6,7 @@
const helpers = global.yoho.helpers;
const brandService = require('../models/favorite-brand-service');
const productService = require('../models/favorite-product-service');
const fav = require('../models/favorite');
/**
* 收藏品牌ajax请求
... ... @@ -85,7 +86,36 @@ const collectProduct = (req, res, next) => {
}
};
const collectShop = (req, res, next) => {
let uid = req.user.uid || '';
let shopId = req.body.shopId;
let isadd = req.body.isFavorite * 1 ? true : false;
// needColloect 说明刚登录状态 是cookie传的值
if (req.body.needColloect * 1 === 1) {
isadd = true;
}
if (!uid) {
res.json({
code: 401,
message: '用户没有登录',
data: {url: helpers.urlFormat('/signin.html')}
});
} else if (!shopId) {
res.json({
code: 400,
message: '收藏失败'
});
} else {
fav.toggleFavShop(shopId, uid, isadd).then(result => {
res.json(result);
}).catch(next);
}
};
module.exports = {
changeFavoriteBrand,
collectProduct
collectProduct,
collectShop
};
... ...
/*
* @Author: Targaryen
* @Date: 2016-05-21 10:59:20
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-08 13:37:17
* @Author: sefon
* @Date: 2016-07-24 11:40:21
*/
'use strict';
const mRoot = '../models';
// list 页 model
const list = require(`${mRoot}/list`);
const headerModel = require('../../../doraemon/models/header');
const helpers = global.yoho.helpers;
// 搜索相关接口
const searchApi = require(`${mRoot}/search-api`);
exports.getProductPic = (req, res) => {
// 店铺页
const shop = (shopId, req, res, next, brandInfo) => {
shopId = parseInt(shopId, 10);
res.jsonp();
list.getShopInfo(shopId, req.user.id).then(shopInfo => {
if (+shopInfo.shopTemplateType === 2) { // 经典模板
list.getShopData(shopId, req.yoho.channel, req.query, shopInfo).then(result => {
Object.assign(result, {
page: 'shop',
shopId: shopId
});
res.render('list/shop-index', result);
}).catch(next);
} else { // 基础模板
list.getBaseShopData(req.query, Object.assign({uid: req.user.uid}, brandInfo),
req.yoho.channel, shopId).then(result => {
Object.assign(result, {page: 'list'});
res.render('list/brand', result);
}).catch(next);
}
}).catch(next);
};
/**
* 商品列表页
* 商品分类列表页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.index = (req, res, next) => {
let params = Object.assign({
order: 's_t_asc,s_s_asc'
}, req.query);
let resData = {};
list.getListData(req.query, req.yoho.channel).then(result => {
Object.assign(resData, result);
res.render('list/index', resData);
}).catch(next);
};
/**
* 新品到着
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.new = (req, res, next) => {
let resData = {};
list.getListNewData(Object.assign({order: 's_t_desc'}, req.query), req.yoho.channel).then(result => {
Object.assign(resData, result);
res.render('list/index', resData);
}).catch(next);
};
/**
* 品牌页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.brand = (req, res, next) => {
let brandDomain = req.params.brandDomain;
let shopId = req.query.shopId;
let resData = {};
if (!brandDomain) {
return next();
}
// shopId存在,直接走店铺
if (shopId) {
return shop(shopId, req, res, next);
}
// 获取品牌信息
list.getBrandInfo({domain: brandDomain}).then(brandInfo => {
if (!brandInfo.hasOwnProperty('type')) {
return res.redirect(helpers.urlFormat(''));
}
switch (parseInt(brandInfo.type, 10)) {
case 1: // 搜索
res.redirect(helpers.urlFormat('', {query: brandInfo.brandDomain}, 'search'));
break;
case 2: // 店铺
return shop(brandInfo.shopId, req, res, next, brandInfo);
default: // 品牌
return list.getBrandData(req.query, Object.assign({uid: req.user.uid}, brandInfo),
req.yoho.channel).then(result => {
Object.assign(resData, result, {
page: 'list'
});
res.render('list/brand', resData);
});
}
}).catch(next);
};
/**
* 品牌介绍页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.brandAbout = (req, res, next) => {
let brandDomain = req.params.brandDomain;
let shopId = req.query.shopId;
if (!brandDomain) {
return res.redirect(helpers.urlFormat(''));
}
let channel = req.query.channel || req.cookies._Channel || 'boys';
if (shopId) {
list.getShopAbout(shopId, req.user.uid).then(result => {
res.render('list/brand', Object.assign(result, {page: 'list'}));
}).catch(next);
} else {
list.getBrandAbout(brandDomain, req.user.uid, req.yoho.channel).then(result => {
res.render('list/brand', Object.assign(result, {page: 'list'}));
}).catch(next);
}
};
/**
* 店铺商品列表页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.shopList = (req, res, next) => {
let shopId = req.query.shopId;
if (!shopId) {
return next();
}
list.getShopListData(req.yoho.channel, req.query).then(result => {
Object.assign(result, {
page: 'shop',
shopId: shopId
});
if (req.query.query) {
result.shopKey = req.query.query;
}
res.render('list/shop-list', result);
}).catch(next);
};
params.uid = req.user.uid || 0;
/**
* ajax调用品牌页左侧水牌
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.getNodeContent = (req, res, next) => {
let responseData = {
module: 'product',
page: 'sale',
footerTop: true
};
if (!req.xhr || !req.body.node) {
return next();
}
list.getNodeContentData(req.body).then(result => {
res.json(result);
}).catch(next);
};
let headerData = {};
/**
* ajax调用品牌页左侧series folder
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.getAdnav = (req, res, next) => {
headerModel.requestHeaderData(channel).then(result => {
headerData = result.headerData;
});
if (!req.xhr || !req.body.brandId) {
return next();
}
list.getSaleOthersData(params, channel).then((result) => {
responseData.saleList = result;
responseData.resultShow = JSON.stringify(result, null, 4);
responseData.headerData = headerData;
res.render('sale/other', responseData);
list.getAdnav(req.body).then(result => {
res.json(result);
}).catch(next);
};
/**
* 判断用户是否收藏品牌页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.isFavoriteBrand = (req, res, next) => {
let uid = req.user.uid;
let brandId = req.body.brandId;
if (!req.xhr) {
return next();
}
if (!req.body.brandId || !uid) {
return res.json({code: 400, message: '用户未登录或缺少参数'});
}
searchApi.isFavoriteBrand(uid, brandId).then(result => {
res.json(result);
}).catch(next);
};
... ...
/*
* @Author: Targaryen
* @Date: 2016-05-21 10:59:20
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-08 13:37:17
*/
'use strict';
const mRoot = '../models';
// list 页 model
const list = require(`${mRoot}/outletsList`);
const headerModel = require('../../../doraemon/models/header');
exports.getProductPic = (req, res) => {
res.jsonp();
};
/**
* 商品列表页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.index = (req, res, next) => {
let params = Object.assign({
order: 's_t_asc,s_s_asc'
}, req.query);
let channel = req.query.channel || req.cookies._Channel || 'boys';
params.uid = req.user.uid || 0;
let responseData = {
module: 'product',
page: 'sale',
footerTop: true
};
let headerData = {};
headerModel.requestHeaderData(channel).then(result => {
headerData = result.headerData;
});
list.getListData(params, channel).then((result) => {
responseData.saleList = result;
responseData.resultShow = JSON.stringify(result, null, 4);
responseData.headerData = headerData;
res.render('sale/other', responseData);
}).catch(next);
};
... ...
... ... @@ -136,6 +136,26 @@ exports.breakingYards = (req, res, next) => {
};
/**
* 活动
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.special = (req, res, next) => {
let params = req.query;
let specialId = params.specialsale_id || params.specialsaleId, // 专区ID
promotion = params.promotion; // 促销ID
if (!specialId && !promotion) {
return next();
}
sale.getSaleSpecialData(specialId, params, req.yoho.channel).then(result => {
res.render('sale/special', result);
}).catch(next);
};
/**
* Ajax 获取商品列表
* @param {[type]} req [description]
* @param {[type]} res [description]
... ...
/**
* 商品搜索controller
* @author: sefon
* @date: 2016-7-12 10:09:25
*/
'use strict';
const mRoot = '../models';
const search = require(`${mRoot}/search`);
const _ = require('lodash');
/**
* search 首页
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.index = (req, res, next) => {
let resData = {
title: '潮流商品搜索 | YOHO!BUY 有货',
keywords: 'Yoho! 有货,潮流,时尚,流行,购物,B2C,正品,购物网站,网上购物,货到付款,' +
'品牌服饰,男士护肤,黑框眼镜,匡威,板鞋,i.t,izzue,5cm,eastpak,vans,lylescott,g-shock,' +
'new balance,lacoste,melissa,casio,卡西欧手表,舒雅,jasonwood,odm,AAAA,香港购物,日本潮流',
description: '潮流商品搜索,上衣,衬衫,TEE,卫衣,冲锋衣,风衣,羽绒服,裤子,休闲鞋,板鞋,配饰,复古眼镜'
};
search.getSearchData(req.query, req.yoho.channel).then(result => {
Object.assign(resData, result);
if (!_.get(resData, 'search.goods') || !_.get(resData, 'search.goods').length) {
_.set(resData, 'search.keyWord', req.query.query);
return res.render('search/no-result', resData);
}
res.render('search/index', resData);
}).catch(next);
};
/**
* search 提示
* @param {[type]} req [description]
* @param {[type]} res [description]
* @return {[type]} [description]
*/
exports.suggest = (req, res, next) => {
search.getSuggest(req.query).then(result => {
let dest = {
code: 200,
message: 'suggest',
data: result || ''
};
res.type('text/javascript');
res.send(req.query.callback + '(' + JSON.stringify(dest) + ')');
}).catch(next);
};
exports.suggest4Old = (req, res, next) => {
search.getSuggest(Object.assign({}, req.query, { keyword: req.query.query })).then(result => {
let dest = {
code: 200,
message: 'suggest'
};
let data = [];
if (result) {
for (let it of result) {
let item = '<li>' +
`<a style="display: block;" href="${it.href}" class="clearfix clear search-item" title="${it.keyword}" act="${it.href}">` + // eslint-disable-line
`<span class="searchvalue" >${it.keyword}</span><span class="valuenum">约${it.count}个商品</span>` + // eslint-disable-line
'</a>' +
'</li>';
data.push(item);
}
}
dest.data = data;
res.send(req.query.callback + '(' + JSON.stringify(dest) + ')');
}).catch(next);
};
exports.serachFilterBrands = (req, res, next) => {
search.getBrands4Filter(Object.assign({}, req.query, {
keyword: req.query.query
})).then(result => {
let dest = {
code: 200,
data: result || ''
};
res.type('text/javascript');
res.send(req.query.callback + '(' + JSON.stringify(dest) + ')');
}).catch(next);
};
... ...
... ... @@ -830,16 +830,15 @@ const _getDetailDataBySizeInfo = (sizeInfo) => {
'<img src=': '<img class="lazy" src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///93d3f' +
'///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw==" data-original=',
'<img border="0" src=': '<img border="0" class="lazy" src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP' +
'///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw==" data-original='
'///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw==" data-original=',
'.jpg': '.jpg?imageView2/0/q/90'
};
let intro = _.get(sizeInfo, 'productIntroBo.productIntro', '');
// 详情配图
_(replacePairs).forEach((value, key)=> {
let re = new RegExp(key, 'gm');
intro = _.replace(intro, re, value);
intro = _.replace(intro, new RegExp(key, 'gm'), value);
});
details += intro;
... ...
/**
* 收藏相关接口
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/17
*/
'use strict';
const api = global.yoho.API;
/**
* 收藏API
* @function addFavAsync
* @param { number } uid 用户uid
* @param { number } id 收藏id
* @param { string } type 收藏类型 product--商品 brand--品牌 shop--店铺
* @return { Object } 收藏结果
*/
const addFavAsync = (uid, id, type) => {
return api.get('', {
method: 'app.favorite.add',
id: id,
uid: uid,
type: type
});
};
/**
* 取消收藏API
* @function cancelFavAsync
* @param { number } uid 用户uid
* @param { number } id 收藏id
* @param { string } type 收藏类型 product--商品 brand--品牌 shop--店铺
* @return { Object } 取消收藏结果
*/
const cancelFavAsync = (uid, id, type) => {
return api.get('', {
method: 'app.favorite.cancel',
fav_id: id,
uid: uid,
type: type
});
};
module.exports = {
addFavAsync, // 收藏
cancelFavAsync // 取消收藏
};
... ...
... ... @@ -7,6 +7,10 @@
const api = global.yoho.API;
const favApi = global.yoho.FavAPI;
const _ = require('lodash');
const favoriteApi = require('./favorite-api');
const isFavoriteAsync = (uid, pid) => {
let onNewApi = false;
... ... @@ -27,23 +31,8 @@ const isFavoriteAsync = (uid, pid) => {
}
};
const createAsync = (uid, pid) => {
return api.get('', {
method: 'app.favorite.add',
id: pid,
uid: uid,
type: 'product'
});
};
const deleteAsync = (uid, pid) => {
return api.get('', {
method: 'app.favorite.cancel',
fav_id: pid,
uid: uid,
type: 'product'
});
};
const createAsync = _.partial(favoriteApi.addFavAsync, _, _, 'product');
const deleteAsync = _.partial(favoriteApi.cancelFavAsync, _, _, 'product');
module.exports = {
isFavoriteAsync,
... ...
/**
* 收藏相关接口
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/17
*/
'use strict';
const favAPI = require('./favorite-api');
/**
* 收藏商品
* @function toggleFavProduct
* @param { number } productId 商品id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavProduct = (productId, uid, isadd) => {
if (isadd) {
return favAPI.addFavAsync(uid, productId, 'product');
} else {
return favAPI.cancelFavAsync(uid, productId, 'product');
}
};
/**
* 收藏品牌
* @function toggleFavBrand
* @param { number } brandId 品牌id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavBrand = (brandId, uid, isadd) => {
if (isadd) {
return favAPI.addFavAsync(uid, brandId, 'brand');
} else {
return favAPI.cancelFavAsync(uid, brandId, 'brand');
}
};
/**
* 收藏店铺
* @function toggleFavShop
* @param { number } shopId 店铺id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavShop = (shopId, uid, isadd) => {
if (isadd) {
return favAPI.addFavAsync(uid, shopId, 'shop');
} else {
return favAPI.cancelFavAsync(uid, shopId, 'shop');
}
};
module.exports = {
toggleFavProduct, // 收藏商品
toggleFavBrand, // 收藏品牌
toggleFavShop // 收藏店铺
};
... ...
/*
* @Author: Targaryen
* @Date: 2016-06-02 15:11:15
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-08 14:57:54
* @Author: sefon
* @Date: 2016-07-24 11:48:22
*/
'use strict';
const utils = '../../../utils';
const api = global.yoho.API;
const saleApi = require('./sale-api');
const outletsApi = require('./outlets-api');
const searchApi = require('./search-api');
const headerModel = require('../../../doraemon/models/header');
const productProcess = require(`${utils}/product-process`);
const publicHandler = require('./public-handler');
/**
* 获取商品列表商品数据 Controller 调用
* @param {[type]} params [常规参数]
* @param {[type]} extra [左侧列表额外要拼接的参数]
* @return {[type]} [description]
*/
exports.getListData = (params, channel) => {
let apiArr = [];
if (params.productPool) {
// 奥莱活动页调用app.search.sales
apiArr = [
saleApi.getSaleGoodsList({limit: '1', channel: channel }),
saleApi.getSaleGoodsList(Object.assign(params, { channel: channel }))
];
} else {
// 奥莱品类页调用app.search.li
apiArr = [
outletsApi.getOutletsGoodsList({limit: '1', channel: channel }),
outletsApi.getOutletsGoodsList(Object.assign(params, { channel: channel }))
];
const searchHandler = require('./search-handler');
const shopHandler = require('./shop-handler');
const helpers = global.yoho.helpers;
const _ = require('lodash');
const limitNum = 60; // 商品每页显示数目
// 品牌页folder名称
const brandFolderSeries = '经典系列';
// 搜索分类信息需要的参数
const positionId = 10;
// 获取分类左侧广告id
const sortAdsId = 79;
const _getGender = (channel) => {
let gender;
switch (channel) {
case 'boys':
gender = '1,3';
break;
case 'girls':
gender = '2,3';
break;
default:
gender = '1,2,3';
break;
}
return api.all(apiArr).then(result => {
let finalResult = {};
return gender;
};
/**
* 获取商品分类列表数据
*/
const getListData = (params, channel) => {
let standard = [];
_.forEach(params, (value, key) => {
let s = _.split(key, 'parameter_', 2);
if (s.length > 1) {
standard.push(`${s[1]}_${value}`);
_.unset(params, `${key}`);
}
});
if (standard.length) {
params.standard = standard.join(',');
}
let searchParams = searchHandler.getSearchParams(params);
// 调用接口
let apiMethod = [
headerModel.requestHeaderData(channel),
searchApi.getSortList(Object.assign({}, {msort: '', misort: ''})),
searchApi.getProductList(searchParams)
];
// 搜索分类介绍和广告
if (params.msort && params.misort) {
let IntroParams = {
max_sort_id: params.msort,
middle_sort_id: params.misort,
gender: params.gender || '1,3'
};
apiMethod.push(searchApi.getSortIntro(Object.assign(IntroParams, {position_id: positionId})));
apiMethod.push(searchApi.getSortAds(Object.assign(IntroParams, {position_id: sortAdsId})));
}
return Promise.all(apiMethod).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true
}),
list: {
leftContent: {}
}
};
// 获取左侧类目数据
if (result[1].code === 200) {
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
finalResult.list = Object.assign(
searchHandler.handlePathNavData(result[1].data.sort, params, 'sort', channel), {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
});
}
// 获取商品数据和顶部筛选条件
if (result[0].code === 200) {
finalResult.leftContent = publicHandler.handleSaleSortData(result[0].data.filter.group_sort, params);
finalResult.pathNav = publicHandler.handlePathNavData(result[0].data.filter.group_sort, params);
if (result[2].code === 200) {
Object.assign(finalResult.list, {
filters: searchHandler.handleFilterDataAll(result[2].data, params),
opts: searchHandler.handleOptsData(params, result[2].data.total, result[2].data.filter),
totalCount: result[2].data.total,
footPager: searchHandler.handlePagerData(result[2].data.total, params),
goods: productProcess.processProductList(result[2].data.product_list,
Object.assign({showDiscount: false}, params)),
hasNextPage: searchHandler.handleNextPage(params, result[2].data.total),
// 最近浏览记录
latestWalk: 6
});
}
// 分类介绍
if (result[3] && result[3].code === 200) {
finalResult.list.sortIntro = searchHandler.handleSortIntro(result[3].data);
}
// 分类广告
if (result[4] && result[4].code === 200) {
Object.assign(finalResult.list.leftContent, searchHandler.handleSortAds(result[4].data));
}
finalResult.criteo = {skn: searchHandler.getCriteo(_.get(finalResult.list, 'goods'))};
let seo = searchHandler.getListSeo(channel,
_.get(finalResult, 'list.leftContent.allSort.list'),
_.get(finalResult, 'list.filters.checkedConditions.conditions'));
return Object.assign({}, seo, finalResult);
});
};
/**
* 获取新品到着数据
*/
const getListNewData = (params, channel) => {
let searchParams = searchHandler.getSearchParams(params);
// 调用接口
let apiMethod = [
headerModel.requestHeaderData(channel),
searchApi.getSortList(Object.assign({}, {msort: '', misort: ''})),
searchApi.getProductList(Object.assign({new: 'Y'}, searchParams)),
searchApi.getWeekNew(params)
];
return Promise.all(apiMethod).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true
}),
list: {
leftContent: {}
}
};
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
// 获取左侧类目数据
if (result[1].code === 200) {
Object.assign(finalResult, {
filters: publicHandler.handleSaleFilterData(result[1].data.filter, params),
opts: publicHandler.handleSaleOptsData(params, result[1].data.total),
totalCount: result[1].data.total,
footPager: publicHandler.handlePagerData(result[1].data.total, params),
goods: productProcess.processProductList(result[1].data.product_list)
finalResult.list = Object.assign(
searchHandler.handlePathNavData(result[1].data.sort, params, 'new', channel), {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
});
}
// 获取商品数据和顶部筛选条件
if (result[2].code === 200) {
Object.assign(finalResult.list, {
filters: searchHandler.handleFilterDataAll(result[2].data, params),
opts: searchHandler.handleOptsData(params, result[2].data.total, result[2].data.filter),
totalCount: result[2].data.total,
footPager: searchHandler.handlePagerData(result[2].data.total, params),
goods: productProcess.processProductList(result[2].data.product_list,
Object.assign({showDiscount: false, showNew: false}, params)),
hasNextPage: searchHandler.handleNextPage(params, result[2].data.total),
// 最近浏览记录
latestWalk: 7
});
}
// 新品上架
if (result[3] && result[3].code === 200) {
Object.assign(finalResult.list.leftContent, searchHandler.handleWeekNew(result[3].data, dps));
}
finalResult.criteo = {skn: searchHandler.getCriteo(_.get(finalResult.list, 'goods'))};
let seo = searchHandler.getNewSeo(channel, _.get(finalResult, 'list.leftContent.newSales.list'));
return Object.assign({}, seo, finalResult);
});
};
/**
* 根据品牌域名查询品牌信息
*/
const getBrandInfo = (params) => {
return searchApi.getBrandData(params).then(result => {
if (result.code !== 200 || !result.data) {
return {};
}
return {
brandId: result.data.id,
brandNameEn: result.data.brand_name_en || '',
brandNameCn: result.data.brand_name_cn || '',
brandBanner: result.data.brand_banner || '',
node: result.data.static_content_code || '',
brandAbout: result.data.brand_intro || '',
shopTemplateType: result.data.shop_template_type || 0,
url: helpers.urlFormat('', '', result.data.brand_domain),
name: result.data.brand_name,
shopId: result.data.shop_id || 0,
type: result.data.type || 0,
brandDomain: result.data.brand_domain
};
});
};
/**
* 获取品牌页面数据
*/
const getBrandData = (params, extra, channel) => {
let searchParams = searchHandler.getSearchParams(params);
// 设置品牌默认排序方式
let order = params.order || 'b_w_desc';
// 调用接口
let apiMethod = [
headerModel.requestHeaderData(channel),
searchApi.getSortList(Object.assign({}, {brand: extra.brandId, msort: '', misort: ''})),
searchApi.getProductList(Object.assign({order: order, brand: extra.brandId}, searchParams))
];
return Promise.all(apiMethod).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true
}),
brand: {
brandBanner: searchHandler.handleBrandBanner(extra),
leftContent: {},
node: extra.node || ''
}
};
// 获取左侧类目数据
if (result[1].code === 200) {
let dps = {};
if (params.gender) {
dps.gender = params.gender;
}
Object.assign(finalResult.brand, {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
}, searchHandler.handlePathNavData(extra, params, 'brand', channel));
}
// 获取商品数据和顶部筛选条件
if (result[2].code === 200) {
Object.assign(finalResult.brand, {
filters: searchHandler.handleFilterDataAll(result[2].data, params),
opts: searchHandler.handleOptsData(params, result[2].data.total, result[2].data.filter),
totalCount: result[2].data.total,
footPager: searchHandler.handlePagerData(result[2].data.total, params),
goods: productProcess.processProductList(result[2].data.product_list,
Object.assign({showDiscount: false}, params)),
hasNextPage: searchHandler.handleNextPage(params, result[2].data.total),
// 最近浏览记录
latestWalk: 7
});
}
// 品牌页不显示品牌筛选项
delete finalResult.brand.filters.brand;
finalResult.criteo = {skn: searchHandler.getCriteo(_.get(finalResult.brand, 'goods'))};
let seo = searchHandler.getBrandSeo(channel, extra);
return Object.assign({}, seo, finalResult);
});
};
/**
* 获取品牌介绍页面数据
*/
const getBrandAbout = (domain, uid, channel) => {
channel = channel || 'boys';
return Promise.all([
headerModel.requestHeaderData(channel),
searchApi.getBrandData({domain: domain})
]).then(result => {
let home;
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true
})
};
switch (channel) {
case 'girls':
home = helpers.urlFormat('/woman', {}, 'new');
break;
case 'lifestyle':
home = helpers.urlFormat('/lifestyle', {}, 'new');
break;
case 'kids':
home = helpers.urlFormat('/kids', {}, 'new');
break;
default:
home = helpers.urlFormat('');
break;
}
if (result[1].code === 200) {
let data = result[1].data || {};
let extra = {
brandId: data.id,
brandNameEn: data.brand_name_en || '',
brandNameCn: data.brand_name_cn || '',
brandBanner: data.brand_banner || '',
node: data.static_content_code || '',
brandAbout: data.brand_intro || '',
shopTemplateType: data.shop_template_type || 0,
url: helpers.urlFormat('', '', data.brand_domain),
name: data.brand_name,
shopId: data.shop_id || 0,
type: data.type || 0,
brandDomain: data.brand_domain
};
finalResult.brand = {
brandBanner: searchHandler.handleBrandBanner(extra),
pathNav: [
{
href: home,
name: `${_.toUpper(channel)}首页`,
pathTitle: 'YOHO!BUY 有货'
},
{
href: helpers.urlFormat('/brands'),
name: '品牌一览',
pathTitle: '品牌一览'
},
{
name: `${extra.brandNameEn} ${extra.brandNameCn}`
}
],
brandAbout: {
brandIntro: extra.brandAbout
}
};
}
return finalResult;
});
};
/**
* 获取店铺介绍页面数据
*/
const getShopAbout = (shopId, uid, channel) => {
let resData = {};
return Promise.all([
headerModel.requestHeaderData(channel),
searchApi.getShopDecorator(shopId), // 店铺装修数据
searchApi.getShopInfo(shopId, uid)
]).then(result => {
resData.headerData = Object.assign(result[0].headerData, {
header: true
});
if (result[1].code === 200) {
let data = result[1].data || {},
decorator = shopHandler.getShopDecorator(data, {}, shopId);
Object.assign(decorator.shopTopBannerBase, {
shopId: shopId,
shopHome: `/?shopId=${shopId}`,
shopIntro: `/about?shopId=${shopId}`,
coled: _.get(result, '[1].is_favorite', 'N') === 'Y'
});
_.set(resData, 'brand.shopBanner', decorator.shopTopBannerBase);
_.unset(resData, 'brand.brandBanner');
}
if (result[2].code === 200) {
let data = result[2].data || {};
_.set(resData, 'brand.brandAbout.brandIntro', data.shop_intro);
}
return resData;
});
};
/**
* 获取品牌页面水牌
*/
const getNodeContentData = (params) => {
return searchApi.getNodeContent(params).then(result => {
if (result.code === 200 && result.data) {
return result.data;
}
});
};
/**
* 获取品牌页面系列
*/
const getAdnav = (params) => {
return Promise.all([searchApi.getBrandFolder(params), searchApi.getBrandSeries(params)]).then(result => {
let dest = {
picTitle: '',
list: []
};
if (result[0].code === 200 && result[0].data) {
dest.picTitle = brandFolderSeries;
Object.assign(dest.list, searchHandler.handleFolderData(result[0].data));
}
if (result[1].code === 200 && result[1].data) {
dest.picTitle = brandFolderSeries;
Object.assign(dest.list, searchHandler.handleFolderData(result[0].data));
}
return dest;
});
};
/**
* 获取店铺基本信息
*/
const getShopInfo = (shopId, uid) => {
return searchApi.getShopInfo(shopId, uid).then(result => {
if (result.code === 200) {
return {
brandName: result.data.shop_name,
brandCont: result.data.shop_intro,
domain: result.data.shop_domain,
logo: result.data.shop_logo,
isFavorite: result.data.is_favorite === 'Y',
shopTemplateType: result.data.shop_template_type,
multBrandShopType: result.data.mult_brand_shop_type,
showShopName: result.data.is_show_shop_name === 'Y'
};
} else {
return {};
}
});
};
/**
* 获取经典模板店铺数据
* @param shopId
* @param uid
* @param params
* @param shopInfo 店铺介绍
*/
const getShopData = (shopId, channel, params, shopInfo) => {
let gender = _getGender(channel);
params = params || {};
params.shopId = shopId;
return Promise.all([
headerModel.requestHeaderData(channel), // 头部数据
searchApi.getShopDecorator(shopId), // 店铺装修数据
searchApi.getProductList(Object.assign({
shop_id: shopId
}, params)), // 搜索店铺商品
searchApi.getShopBrands(shopId) // 店铺品牌数据
]).then(result => {
let finalResult = {};
Object.assign(finalResult,
result[0], // 头部数据
searchHandler.handlePathNavData(shopInfo, params, 'shop', channel) // 面包屑导航
);
_.set(finalResult, 'headerData.header', true);
// 店铺装修
if (result[1].code === 200) {
Object.assign(finalResult, shopHandler.getShopDecorator(result[1].data, params, shopId));
_.set(finalResult, 'shopTopBanner.brandIntro', {
shopId: shopId,
brandName: shopInfo.brandName || '',
isFavorite: shopInfo.isFavorite || false,
brandCont: shopInfo.brandCont || ''
});
} else {
return Promise.reject('No ShopDecorator data');
}
// 获取商品数据和顶部筛选条件
if (result[2].code === 200) {
let allGoods = {
name: '全部商品 ALL',
sort: searchHandler.handleOptsData(params, _.get(result[2], 'data.total', 0)),
list: productProcess.processProductList(_.get(result[2], 'data.product_list', []), {
newCoverSort: true,
showDiscount: false,
gender: gender
}),
href: `/product/shoplist?navBar=1&shopId=${shopId}`
};
_.set(allGoods, 'sort.newPage', true); // 启用新的分页导航
finalResult.allGoods = allGoods;
}
let sknList = []; // 资源位配置商品skn
let prodList = _.concat(_.get(finalResult, 'newArrivel.list', []),
_.get(finalResult, 'hotSingle.list', []));
_.forEach(prodList, value => {
sknList.push(value.productSkn);
});
let shopBrandIds = [], // 店铺的所有品牌id
articleApiMethod = [], // 通过品牌获取相关文章接口
apiMethod = [
searchApi.getProductList({
viewNum: sknList.length,
query: _.join(_.uniq(sknList), ',')
})
];
if (result[3].code === 200 && result[3].data) {
_.forEach(result[3].data, value => {
shopBrandIds.push(value.brand_id);
articleApiMethod.push(
searchApi.getArticleByBrand(value.brand_id, 'udid') // 品牌推荐文章
);
});
}
// 根据品牌获取分类 (腾讯云测试没有该接口,暂时不调用分类)
apiMethod.push(searchApi.getSortList({brand: shopBrandIds}));
apiMethod = _.concat(apiMethod, articleApiMethod);
return Promise.all(apiMethod).then(subRes => {
// 设置资源位商品封面图
if (subRes[0].code === 200) {
let list = {};
prodList = productProcess.processProductList(_.get(subRes[0], 'data.product_list', []), {
newCoverSort: true,
showDiscount: false,
gender: gender
});
_.forEach(prodList, value => {
list[value.product_skn] = {
img: helpers.getForceSourceUrl(value.default_images) +
'?imageView/1/w/{width}/h/{height}',
title: value.product_name,
price: ${value.sales_price}`
};
});
if (_.has(finalResult, 'newArrivel.list')) {
_.forEach(finalResult.newArrivel.list, value => {
if (list[value.productSkn]) {
Object.assign(value, list[value.productSkn]);
}
});
}
if (_.has(finalResult, 'hotSingle.list')) {
_.forEach(finalResult.hotSingle.list, value => {
if (list[value.productSkn]) {
Object.assign(value, list[value.productSkn]);
}
});
}
}
if (subRes[1].code === 200) {
let groupSort = _.get(subRes[1], 'data.sort', []);
finalResult.leftContent = searchHandler.handleSortData(groupSort, params);
if (finalResult.allGoods) {
Object.assign(finalResult.allGoods, searchHandler.setShopSort(groupSort, params));
}
}
let articleList = [];
for (let i = 2; i < subRes.length; i++) {
articleList = _.concat(articleList, _.get(subRes[i], 'data', []));
}
if (articleList.length >= 3) {
let trendList = [];
for (let i = 0; i < 3; i++) {
trendList.push({
href: articleList[i].url,
src: helpers.getForceSourceUrl(articleList[i].src) +
'?imageView/1/w/{width}/h/{height}',
mainTitle: articleList[i].title,
Subtitle: articleList[i].intro
});
articleList = _.concat(articleList, _.get(subRes[i], 'data', []));
}
finalResult.trendInfo = {
name: '潮流资讯 HOT ITEMS',
trendList: trendList
};
}
return finalResult;
});
});
};
const getShopListData = (channel, params, uid) => {
let gender = _getGender(channel),
shopId = params.shopId,
navBar = params.navBar || 1;
let apiArr = [
headerModel.requestHeaderData(channel), // 头部数据
searchApi.getShopDecorator(shopId), // 店铺装修数据
searchApi.getShopInfo(shopId, uid), // 店铺介绍
searchApi.getProductList(Object.assign({shop_id: shopId}, params)), // 搜索店铺商品
searchApi.getShopBrands(shopId) // 店铺品牌数据
];
if (_.has(params, 'query')) {
apiArr.push(searchApi.getProductList(Object.assign({shop_id: shopId},
params, {query: ''}))); // 搜索店铺所有商品
}
return Promise.all(apiArr).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {header: true})
};
// 面包屑导航
Object.assign(finalResult, searchHandler.handlePathNavData({}, params, 'shop', channel));
// 店铺装修
if (result[1].code === 200) {
Object.assign(finalResult, shopHandler.getShopDecorator(result[1].data, params, shopId));
// 设置shop nav选中状态
_.set(finalResult, `navigationBar[${navBar}].current`, true);
if (result[2].code === 200) {
let data = _.get(result[2], 'data', {});
_.set(finalResult, 'shopTopBanner.brandIntro', {
shopId: shopId,
brandName: data.shop_name || '',
isFavorite: data.is_favorite === 'Y',
brandCont: data.shop_intro || ''
});
}
} else {
return Promise.reject('No ShopDecorator data');
}
// 获取商品数据和顶部筛选条件
if (result[3].code === 200) {
let info = result[3];
let goodsList = _.get(info, 'data.product_list', []);
if (goodsList.length) {
let tip = {
start: (_.get(info, 'data.page', 1) - 1) * limitNum + 1,
total: _.get(info, 'data.total', 0)
};
tip.end = tip.start + limitNum - 1;
if (tip.end > tip.total) {
tip.end = tip.total;
}
Object.assign(finalResult, {
filters: searchHandler.handleFilterDataAll(info.data, params),
opts: searchHandler.handleOptsData(params, tip.total),
goods: productProcess.processProductList(goodsList, {
newCoverSort: true,
showDiscount: false,
gender: _getGender(channel)
}),
footPager: {tip: tip}
});
} else {
finalResult.searchEmpty = true;
if (result[5] && result[5].code === 200) {
let allGoods = {
name: '全部商品 ALL',
sort: searchHandler.handleOptsData(params, _.get(result[5], 'data.total', 0)),
list: productProcess.processProductList(_.get(result[5], 'data.product_list', []), {
newCoverSort: true,
showDiscount: false,
gender: gender
})
};
_.set(allGoods, 'sort.newPage', true); // 启用新的分页导航
finalResult.allGoods = allGoods;
}
}
}
if (result[4].code === 200) {
let shopBrandIds = []; // 店铺下的所有品牌id数组
_.forEach(_.get(result[4], 'data', []), value => {
shopBrandIds.push(value.brand_id);
});
return searchApi.getSortList({brand: shopBrandIds}).then(subRes => {
if (subRes.code === 200) {
let groupSort = _.get(subRes, 'data.sort', []);
finalResult.leftContent = searchHandler.handleSortData(groupSort, params);
if (finalResult.allGoods) {
Object.assign(finalResult.allGoods, searchHandler.setShopSort(groupSort, params));
}
}
return finalResult;
});
}
finalResult.criteo = {skn: searchHandler.getCriteo(_.get(finalResult, 'goods'))};
return finalResult;
});
};
/**
* 获取基础模板店铺数据
*/
const getBaseShopData = (params, extra, channel, shopId) => {
let resData = {};
return Promise.all([
getBrandData(params, extra, channel),
searchApi.getShopDecorator(shopId), // 店铺装修数据
searchApi.getShopInfo(shopId, extra.uid)
]).then(result => {
let brand = result[0] || {};
Object.assign(resData, brand);
if (result[1].code === 200) {
let data = result[1].data || {},
decorator = shopHandler.getShopDecorator(data, {}, shopId);
Object.assign(decorator.shopTopBannerBase, {
shopId: shopId,
shopHome: `/?shopId=${shopId}`,
shopIntro: `/about?shopId=${shopId}`,
coled: _.get(result, '[2].is_favorite', 'N') === 'Y'
});
_.set(resData, 'brand.shopBanner', decorator.shopTopBannerBase);
_.unset(resData, 'brand.brandBanner');
}
return resData;
});
};
module.exports = {
getListData,
getListNewData,
getBrandInfo,
getBrandData,
getBrandAbout,
getShopAbout,
getNodeContentData,
getAdnav,
getShopInfo,
getShopData,
getShopListData,
getBaseShopData
};
... ...
/*
* @Author: Targaryen
* @Date: 2016-06-02 15:11:15
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-08 14:57:54
*/
'use strict';
const utils = '../../../utils';
const api = global.yoho.API;
const saleApi = require('./sale-api');
const outletsApi = require('./outlets-api');
const productProcess = require(`${utils}/product-process`);
const publicHandler = require('./public-handler');
/**
* 获取商品列表商品数据 Controller 调用
* @param {[type]} params [常规参数]
* @param {[type]} extra [左侧列表额外要拼接的参数]
* @return {[type]} [description]
*/
exports.getListData = (params, channel) => {
let apiArr = [];
if (params.productPool) {
// 奥莱活动页调用app.search.sales
apiArr = [
saleApi.getSaleGoodsList({limit: '1', channel: channel }),
saleApi.getSaleGoodsList(Object.assign(params, { channel: channel }))
];
} else {
// 奥莱品类页调用app.search.li
apiArr = [
outletsApi.getOutletsGoodsList({limit: '1', channel: channel }),
outletsApi.getOutletsGoodsList(Object.assign(params, { channel: channel }))
];
}
return api.all(apiArr).then(result => {
let finalResult = {};
// 获取商品数据和顶部筛选条件
if (result[0].code === 200) {
finalResult.leftContent = publicHandler.handleSaleSortData(result[0].data.filter.group_sort, params);
finalResult.pathNav = publicHandler.handlePathNavData(result[0].data.filter.group_sort, params);
}
// 获取左侧类目数据
if (result[1].code === 200) {
Object.assign(finalResult, {
filters: publicHandler.handleSaleFilterData(result[1].data.filter, params),
opts: publicHandler.handleSaleOptsData(params, result[1].data.total),
totalCount: result[1].data.total,
footPager: publicHandler.handlePagerData(result[1].data.total, params),
goods: productProcess.processProductList(result[1].data.product_list)
});
}
return finalResult;
});
};
... ...
... ... @@ -214,6 +214,21 @@ exports.handleSaleOptsData = (params, total, extra) => {
// 用来标记是否是折扣专场,折扣专场只需要前三个排序参数
let count = (extra === 'discont') ? 3 : 4;
if (extra === 'default') {
let defaultOpt = {};
defaultOpt.href = handleFilterUrl(params, {order: ''});
defaultOpt.name = '默认';
defaultOpt.hasSortOrient = true;
if (_.isEmpty(params.order)) {
defaultOpt.active = true;
} else {
defaultOpt.active = false;
}
defaultOpt.desc = true;
dest.sortType.push(defaultOpt);
}
for (let i = 0; i < count; i++) {
let opt = {};
... ... @@ -229,8 +244,7 @@ exports.handleSaleOptsData = (params, total, extra) => {
}
} else {
opt.name = '最新';
opt.hasSortOrient = true;
if (_.isEmpty(params.order) || params.order === 's_t_desc') {
if (params.order === 's_t_desc') {
opt.active = true;
} else {
opt.active = false;
... ...
... ... @@ -32,18 +32,19 @@ const yhChannel = {
*/
exports.getSaleGoodsList = (params) => {
let tempChannel = _.isEmpty(params.channel) ? 'boys' : params.channel;
let finalParams = {
method: 'app.search.sales',
limit: 60,
order: 's_t_desc',
productSize: '384x511',
yh_channel: yhChannel[tempChannel].channel
productSize: '384x511'
};
Object.assign(finalParams, params);
if (finalParams.order === '') {
delete finalParams.order;
}
return api.get('', finalParams);
};
... ... @@ -113,3 +114,18 @@ exports.getUserProfile = (uid) => {
uid: uid
}, true);
};
/**
* 获取用户数据信息
* @param {[string]} uid
* @return {[array]}
*/
exports.getSaleSpecialAsync = (specialId) => {
return api.get('', {
method: 'app.resources.getOneSpecial',
special_id: specialId
}, {
code: 200,
cache: true
});
};
... ...
... ... @@ -6,16 +6,24 @@
*/
'use strict';
const utils = '../../../utils';
const api = global.yoho.API;
const Promise = require('bluebird');
const saleApi = require('./sale-api');
const searchApi = require('./search-api');
const publicHandler = require('./public-handler');
const saleHandler = require('./sale-handler');
const productProcess = require(`${utils}/product-process`);
const searchHandler = require('./search-handler');
const productProcess = require(`${global.utils}/product-process`);
const pager = require(`${global.utils}/pager`).setPager;
const imageHandler = require(`${global.utils}/images`).getImageUrl;
const _ = require('lodash');
const headerModel = require('../../../doraemon/models/header');
const limitNum = 60; // 商品每页显示数目
// 下一页图片
const nextPageImg = '//img10.static.yhbimg.com/product/2014/01/15/11/01fa01614784f6239760f1b749663016f1.jpg?imageMogr2/thumbnail/235x314/extent/235x314/background/d2hpdGU=/position/center/quality/90'; // eslint-disable-line
/**
* 资源位code码
*/
... ... @@ -335,6 +343,11 @@ exports.getSaleDiscountData = (params, channel) => {
]).then(result => {
let finalResult = result[0];
// 默认order: ''
if (_.isEmpty(params.order)) {
Object.assign(params, {order: ''});
}
// 处理折扣专场标题 banner 数据
if (result[1].code === 200) {
if (_.size(result[1].data) === 1) {
... ... @@ -354,7 +367,8 @@ exports.getSaleDiscountData = (params, channel) => {
channel: channel,
saleType: '3',
limit: '1',
productPool: result[1].data[0].product_pool
productPool: result[1].data[0].product_pool,
order: ''
}),
saleApi.getSaleGoodsList(Object.assign(params, {productPool: result[1].data[0].product_pool}))
]).then(subResult => {
... ... @@ -365,7 +379,7 @@ exports.getSaleDiscountData = (params, channel) => {
leftContent: publicHandler.handleSaleSortData(subResult[0].data.filter.group_sort, params, 'discount'),
saleList: {
footPager: publicHandler.handlePagerData(subResult[0].data.total, params),
opts: publicHandler.handleSaleOptsData(params, subResult[0].data.total)
opts: publicHandler.handleSaleOptsData(params, subResult[0].data.total, 'default')
}
});
}
... ... @@ -379,7 +393,7 @@ exports.getSaleDiscountData = (params, channel) => {
finalResult.saleList.totalCount = subResult[1].data.total;
_.forEach(finalResult.saleList.goods, (value, key) => {
if (sknArr.length < 3) {
sknArr.push(value.productSkn);
sknArr.push(value.product_skn);
}
delete finalResult.saleList.goods[key].tags.is_new; // 屏蔽 new 标签
delete finalResult.saleList.goods[key].tags.is_sale;// 屏蔽 sale 标签
... ... @@ -482,3 +496,107 @@ exports.getSalebreakingYardsData = (params, channel) => {
}
});
};
/**
* 获取sale活动数据(原sale.yohobuy.com)
* @param params
* @param channel
* @returns {*|Promise.<TResult>}
*/
exports.getSaleSpecialData = (id, params, channel) => {
const limit = params.limit ? params.limit - 1 : limitNum - 1; // 最后一个商品是翻页
const page = params.page || 1;
const defaultParam = {specialsale_id: id};
return saleApi.getSaleSpecialAsync(id).then(special => {
let resData = {
latestWalk: 6
},
apiList = [
headerModel.requestHeaderData(channel),
searchApi.getSortList(defaultParam)
];
if (special && special.data) {
let data = special.data,
condition = Object.assign({}, params, {
page: page,
limit: limit,
attribute_not: 2
});
let banner = JSON.parse(data.banner_img) || [];
Object.assign(resData, {
mainBanner: {
src: imageHandler(_.get(banner, '[0].img', ''), 1920, 350, 1, 'couponImg')
},
specialHead: {
title: '全部商品',
count: 0
},
node: data.left_ad_code // 水牌node
});
// 传品牌ID参数
if (special.brand_id) {
condition.brand = data.brand_id;
}
// 传促销id,促销id为空时传专区id
if (data.ispromotion) {
condition.promotion = data.ispromotion;
}
// 搜索sale活动商品
apiList.push(searchApi.getProductList(condition));
}
return Promise.all(apiList).then(result => {
if (!_.isEmpty(result[0])) {
resData.headerData = Object.assign(result[0].headerData, {header: true});
}
if (!_.isEmpty(result[1])) {
resData.leftContent = searchHandler.handleSortData(_.get(result[1],
'data.sort', []), defaultParam);
}
if (!_.isEmpty(result[2])) {
let tip = {
start: (_.get(result[2], 'data.page', 1) - 1) * limitNum + 1,
total: _.get(result[2], 'data.total', 0)
};
tip.end = tip.start + limit - 1;
if (tip.end > tip.total) {
tip.end = tip.total;
}
resData.specialHead.count = tip.total;
Object.assign(resData, {
filters: searchHandler.handleFilterDataAll(_.get(result[2], 'data', {}), params),
opts: searchHandler.handleOptsData(params, tip.total),
goods: productProcess.processProductList(_.get(result[2], 'data.product_list', [])),
footPager: {tip: tip}
});
if (_.has(resData, 'filters.checkedConditions')) {
_.set(resData, 'filters.checkedConditions.clearUrl', `?specialsale_id=${id}`);
}
// 设置分页
Object.assign(resData.footPager,
pager(_.get(result[2], 'data.page_total', 0), params));
if (_.has(resData, 'footPager.nextPage')) {
resData.hasNextPage = {
href: _.get(resData, 'footPager.nextPage.url', ''),
src: nextPageImg
};
}
}
return resData;
});
});
};
... ...
... ... @@ -7,10 +7,533 @@
const api = global.yoho.SearchAPI;
const yohoApi = global.yoho.API;
const serviceApi = global.yoho.ServiceAPI;
const _ = require('lodash');
const helpers = global.yoho.helpers;
const images = require('../../../utils/images.js');
const cache = global.yoho.cache;
const logger = global.yoho.logger;
const md5 = require('md5');
const config = require('../../../config/common');
const getSortByConditionAsync = (condition) => {
return api.get('sortgroup.json', condition);
};
// 获取list页广告
const adsUrl = '/shops/api/v1/ads/getList';
// 判断用户是否收藏品牌
const isFavoriteBrandUrl = '/shops/service/v1/favorite/getUidBrandFav';
// 根据品牌查询相关文章
const relateArticleUrl = 'guang/service/v2/article/getArticleByBrand';
const getSearchCackeKey = params => {
let ks = [];
_.forEach(params, (val, key) => {
if (params.hasOwnProperty(key) && !_.includes(['page', 'limit', 'need_filter', 'order'], key)) {
ks.push(val);
}
});
return md5(ks.join('_'));
};
const getProductListOrig = (finalParams) => {
return yohoApi.get('', finalParams).then(result => {
return result;
});
};
/**
* 获取商品列表
* @return
*/
const getProductList = (params) => {
let finalParams = {
method: 'web.search.search',
sales: 'Y',
outlets: 2,
stocknumber: 1,
order: 's_n_desc',
need_filter: 'yes',
limit: 60
};
Object.assign(finalParams, params);
if (!config.useCache) {
return getProductListOrig(finalParams);
} else {
let cKey = getSearchCackeKey(finalParams);
return cache.get(cKey).catch().then(cdata => {
let hasCache = false;
if (cdata) {
try {
cdata = JSON.parse(cdata);
} catch (e) {
logger.debug('getProductList cache data parse fail.');
}
if (cdata.filter && cdata.standard) {
hasCache = true;
finalParams.need_filter = 'no';
}
}
return getProductListOrig(finalParams).then(result => {
if (hasCache && result && result.data) {
Object.assign(result.data, cdata);
} else {
if (result && result.data && result.data.filter) {
cache.set(cKey, Object.assign({}, {
filter: result.data.filter,
standard: result.data.standard
}), 3600);
}
}
return result;
});
});
}
};
const getSortListOrig = (finalParams) => {
return yohoApi.get('', finalParams).then(ret => {
return ret;
});
};
/**
* 获取分类列表
* @return
*/
const getSortList = (params) => {
let finalParams = {
method: 'web.regular.groupsort',
sales: 'Y', // 在销售商品分类
status: 1, // 上架商品分类
stocknumber: 1 // 过滤掉已售罄
};
Object.assign(finalParams, params);
if (!config.useCache) {
return getSortListOrig(finalParams);
} else {
let cKey = getSearchCackeKey(finalParams);
return cache.get(cKey).catch().then(cdata => {
let cdataObj;
if (cdata) {
try {
cdataObj = JSON.parse(cdata);
} catch (e) {
logger.debug('getSortList cache data parse fail.');
}
}
if (cdataObj) {
return cdataObj;
} else {
return getSortListOrig(finalParams).then(ret => {
if (ret && ret.code === 200) {
cache.set(cKey, ret, 3600);
}
return ret;
});
}
});
}
};
/**
* 获取分类图文介绍
* @return
*/
const getSortIntro = (params) => {
let finalParams = {
method: 'web.search.banner'
};
Object.assign(finalParams, params);
return yohoApi.get('', finalParams);
};
/**
* 获取分类广告
* @return
*/
const getSortAds = (params) => {
return serviceApi.get(adsUrl, params);
};
/**
* 获取品牌系列
* @return
*/
const getBrandSeries = (params) => {
let finalParams = {
method: 'web.brand.series',
brand_id: params.brandId,
status: params.status || 1
};
return yohoApi.get('', finalParams);
};
/**
* 获取品牌folder
* @return
*/
const getBrandFolder = (params) => {
let finalParams = {
method: 'web.brand.folder',
brand_id: params.brandId,
status: params.status || 1
};
return yohoApi.get('', finalParams);
};
/**
* 获取品牌水牌
* @return
*/
const getNodeContent = (params) => {
let finalParams = {
method: 'web.html.content',
mode: params.mode || 'release',
node: params.node || ''
};
return yohoApi.get('', finalParams);
};
/**
* 一周新品上架
* @return
*/
const getWeekNew = (params) => {
let finalParams = {
method: 'web.regular.recent'
};
Object.assign(finalParams, params);
return yohoApi.get('', finalParams);
};
/**
* 根据关键词搜索品牌店铺信息 TODO
* @return
*/
const getBrandShop = (query) => {
let finalParams = {
// method: 'web.regular.groupsort'
method: 'web.search.shopInfo'
};
Object.assign(finalParams, {keyword: query});
let cKey = getSearchCackeKey(finalParams);
return cache.get(cKey).catch().then(cdata => {
let retObj;
try {
if (cdata) {
retObj = JSON.parse(cdata);
}
} catch (e) {
logger.debug('getBrandShop cache data parse fail.');
}
if (retObj) {
return retObj;
} else {
return yohoApi.get('', finalParams).then(ret => {
if (ret && ret.code === 200) {
cache.set(cKey, ret.data, 3600);
return ret.data;
}
return null;
});
}
});
};
/**
* 根据品牌ID获取品牌下的店铺
* @return
*/
const getShopsByBrandId = bid => {
let finalParams = {
method: 'app.shop.queryShopsByBrandId',
brand_id: bid
};
let cKey = getSearchCackeKey(finalParams);
return cache.get(cKey).catch().then(cdata => {
let cdataObj;
if (cdata) {
try {
cdataObj = JSON.parse(cdata);
} catch (e) {
logger.debug('getShopsByBrandId cache data parse fail.');
}
}
if (cdataObj) {
return cdataObj;
} else {
return yohoApi.get('', finalParams).then(ret => {
if (ret && ret.code === 200) {
cache.set(cKey, ret.data, 3600);
return ret.data;
}
return null;
});
}
});
};
const shopFormat = shopDatas => {
let shopEntry = [];
_.forEach(shopDatas, val => {
let sorts = [];
let shopId = _.get(val, 'shop.shop_id', '');
let url = helpers.urlFormat('', {shopId: shopId}, val.shop.brand_domain);
if (val.shopSort) {
for (let ss of val.shopSort) {
if (!ss.sub) {
continue;
}
let isFull = false;
for (let misort of ss.sub) {
if (sorts.length >= 10) {
isFull = true;
break;
}
sorts.push({
href: `${url}&msort=${ss.sort_id}&misort=${misort.sort_id}`,
name: misort.sort_name
});
}
if (isFull) {
break;
}
}
}
shopEntry.push({
home: url,
logo: images.getImageUrl(val.shop.brand_ico, 80, 50, 1, 'brandLogo'),
shopName: val.shop.brand_name,
sort: sorts
});
});
return shopEntry;
};
/**
* 根据搜索得到店铺/品牌 列表
*/
const getShopList = params => {
if (!params || !params.query) {
return;
}
let brandShop;
let brandShops;
return getBrandShop(params.query).then(shop => { // 获取品牌,并根据品牌获取品牌下的所有店铺
if (!shop || !shop.id) {
return;
}
brandShop = shop;
return getShopsByBrandId(shop.id).then(shops => {
if (shops && shops.length) {
return shops;
} else {
return shop;
}
});
}).then(shops => { // 获取品牌/品牌店下所有的分类
if (_.isArray(shops)) {
brandShops = shops;
let promises = [];
for (let s of shops) {
promises.push(getSortList({
brand: s.brand_id
}));
}
return Promise.all(promises);
} else if (shops && shops.id) {
return getSortList({
brand: shops.id
});
}
}).then(sorts => { // 数组则为品牌店列表,否则为品牌
let shopData = [];
if (_.isArray(sorts)) {
_.forEach(sorts, (val, index) => {
shopData.push({
shop: brandShops[index],
shopSort: val && val.data && !_.isFunction(val.data.sort) && val.data.sort
});
});
} else if (sorts && sorts.code === 200 && sorts.data) {
shopData.push({
shop: brandShop,
shopSort: !_.isFunction(sorts.data.sort) && sorts.data.sort
});
}
return shopFormat(shopData);
});
};
/**
* 搜索提示
* @return
*/
const getSuggest = (params) => {
let finalParams = {
method: 'app.search.fuzzy',
keyword: params.keyword || ''
};
return yohoApi.get('', finalParams);
};
/**
* 根据品牌域名获取品牌信息
* @return
*/
const getBrandData = (params) => {
let finalParams = {
method: 'web.brand.byDomain',
domain: params.domain || ''
};
return yohoApi.get('', finalParams);
};
/**
* 根据uid和品牌id判断品牌是否收藏
* @return
*/
const isFavoriteBrand = (uid, brandId) => {
return serviceApi.get(isFavoriteBrandUrl, {
uid: uid,
brandId: brandId
});
};
/**
* 根据shopId获取店铺基本信息
* @return
*/
const getShopInfo = (shopId, uid) => {
let finalParams = {
method: 'app.shops.getIntro',
shop_id: shopId || 0,
uid: uid || 0
};
return yohoApi.get('', finalParams);
};
/**
* 查询店铺下面的所有品牌
*/
const getShopBrands = (shopId) => {
return yohoApi.get('', {method: 'app.shops.getShopsBrands', shop_id: shopId || 0});
};
/**
* 查询店铺装修
*/
const getShopDecorator = (shopId) => {
return yohoApi.get('', {method: 'app.shopsdecorator.getList', shop_id: shopId || 0});
};
/**
* 通过品牌获取相关文章
*/
const getArticleByBrand = (brand, udid, limit) => {
let params = {
brand_id: brand || 0,
udid: udid,
limit: limit || 6
};
return serviceApi.get(relateArticleUrl, params);
};
const getBrands4Filter = (params) => {
let finalParams = {
method: 'web.regular.aggBrand'
};
return yohoApi.get('', Object.assign({}, params, finalParams));
};
module.exports = {
getSortByConditionAsync
getSortByConditionAsync,
getProductList,
getSortList,
getSortIntro,
getSortAds,
getBrandFolder,
getBrandSeries,
getWeekNew,
getBrandShop,
getSuggest,
getBrandData,
getNodeContent,
isFavoriteBrand,
getShopInfo,
getShopBrands,
getShopDecorator,
getArticleByBrand,
getShopList,
getBrands4Filter
};
... ...
/*
* @Author: sefon
* @Date: 2016-06-02 15:50:47
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-22 18:36:26
*/
'use strict';
const _ = require('lodash');
const helpers = global.yoho.helpers;
const queryString = require('querystring');
// 打折、新品、限量
const checksName = {
new: '新品',
specialoffer: '打折',
limited: '限量'
};
/**
* 处理用于筛选的 URL , 拼接 URL 参数
* @param originParam 当前 URL 中的参数
* @param newParam 要拼接的 参数
* @returns {string}
*/
const handleFilterUrl = (originParam, newParam, delParam) => {
let dest = '?';
let tempOriginParam = {};
delParam = delParam || {};
tempOriginParam = Object.assign(tempOriginParam, originParam, newParam);
delete tempOriginParam.uid;
_.forEach(tempOriginParam, function(value, key) {
if (!delParam[key]) {
dest += `${key}=${value}&`;
}
});
return _.trim(dest, '&');
};
/**
* 处理选中数据
* @param {[type]} params [description]
* @param {[type]} origin [description]
* @param {[type]} param [description]
* @return {[type]} [description]
*/
const handleCheckedData = (params, origin, param) => {
let dest = [];
if (!_.isEmpty(origin)) {
_.forEach(origin, value => {
if (value.checked) {
let tempPatam = _.cloneDeep(params);
// 删除选中
delete tempPatam[param];
dest.push({
name: value.name,
href: handleFilterUrl(tempPatam),
itemType: param
});
}
});
}
return dest;
};
/**
* 处理品牌筛选数据
* @param params
* @param origin
* @returns {{}}
*/
const handleBrandCheckedData = (params, origin) => {
let dest = [];
// 分拆品牌参数
let brands = _.split(params.brand, ',');
let intBrands = [];
_.forEach(brands, value => {
intBrands.push(parseInt(value, 10));
});
let checkedCount = 0;
// 遍历品牌数据,如果在参数中,那么加 checked,将此数据加入到 checked 数组中
if (!_.isEmpty(origin)) {
_.forEach(origin, (value) => {
if (typeof _.find(intBrands, o => {
return _.isEqual(o, value.id);
}) !== 'undefined') {
let checked = {
name: value.name
}; // push 到已选择数组
// 为了不污染原始数据
let tempIntBrands = _.cloneDeep(intBrands);
// 去掉当前选项
_.remove(tempIntBrands, n => {
return n === value.id;
});
// 拼接参数
let brand = '';
// 建议保留,需要品牌可以单个删除时放开注释即可
// _.forEach(tempIntBrands, subValue => {
// brand += subValue + ',';
// });
// 清除参数,保留没有去掉的参数
let tempParam = {
brand: brand
};
checked.href = handleFilterUrl(params, tempParam);
checked.itemType = 'brand';
if (checkedCount === 0) {
dest[0] = checked;
} else if (checkedCount === 1) {
dest[0].name += '、' + checked.name.substring(0, 3) + '...';
}
checkedCount++;
// 建议保留,需要品牌可以单个删除时放开注释即可
// dest.push(checked);
}
});
}
return dest;
};
/**
* 新品、折扣、限量
*/
const hadleChecks = (name, params, filter) => {
let tempParam = _.cloneDeep(params),
checked = false;
if (!params[name] && !filter[name]) {
return null;
}
if (params[name] && params[name] === 'Y') {
checked = true;
delete tempParam[name];
} else {
tempParam[name] = 'Y';
}
return {
name: checksName[name],
checked: checked,
href: handleFilterUrl(tempParam)
};
};
/**
* 获取standard
*/
const getStandard = (qStandard, addOne, delOne) => {
let s = [];
qStandard = Object.assign({}, qStandard);
if (addOne && addOne.k) {
qStandard[addOne.k] = addOne.v;
}
if (delOne && delOne.k && qStandard[delOne.k] && qStandard[delOne.k] === delOne.v) {
qStandard;
}
_.each(qStandard, (val, key) => {
if (!delOne || !delOne.k || delOne.v !== val) {
s.push([key, val].join('_'));
}
});
return s.join(',');
};
const formatterFilterBrands = (source, paramBrand, params) => {
let dbrand = {
default: [],
brandsShow: [],
brandIndex: [{
index: 'all',
name: '全部'
}, {
index: '0-9',
name: '0~9'
}],
selectedBrands: [],
showMore: true,
showMulti: true
};
// 品牌索引数据处理
for (let i = 65; i < 91; i++) {
dbrand.brandIndex.push({
index: String.fromCharCode(i).toLowerCase(),
name: String.fromCharCode(i)
});
}
// 品牌数据处理
// 分拆品牌参数
let brands = _.split(params.brand, ',');
let intBrands = [];
_.forEach(brands, value => {
intBrands.push(parseInt(value, 10));
});
if (source) {
let count = 0;
_.forEach(source, function(value) {
let brand = {
checked: (typeof _.find(intBrands, o => {
return _.isEqual(o, value.id);
}) !== 'undefined'),
href: handleFilterUrl(params, {brand: value.id}),
name: value.brand_name,
key: value.brand_domain,
id: value.id
};
if (!_.isEmpty(value.brand_alif)) {
if (!_.isNaN(parseInt(value.brand_alif, 10))) {
brand.index = '0-9';
} else {
brand.index = value.brand_alif.toLowerCase();
}
}
if (count < 10) {
dbrand.default.push(brand);
}
dbrand.brandsShow.push(brand);
count++;
});
if (paramBrand) {
_.forEach(paramBrand, value => {
let brand = {
href: handleFilterUrl(params, {brand: value.id}),
name: value.brand_name,
key: value.brand_domain,
id: value.id
};
dbrand.selectedBrands.push(brand);
});
}
// 没有品牌的情况下将 brand 设置为 false,前端不显示 品牌
if (_.size(dbrand.brandsShow) <= 0) {
return false;
} else {
// 品牌排序
dbrand.brandsShow = _.sortBy(dbrand.brandsShow, o => {
return o.index;
});
}
}
return dbrand;
};
/**
* 处理 opts 排序数据
* @param params
* @param total
* @param extra 什么都可以传进来,多个参数传Object
* @returns {{}}
*/
exports.handleOptsData = (params, total, extra) => {
let dest = {
sortType: [],
checks: []
};
// 用来标记是否是折扣专场,折扣专场只需要前三个排序参数
let count = (extra === 'discont') ? 3 : 4;
for (let i = 0; i < count; i++) {
let opt = {};
switch (i) {
case 0:
opt.href = handleFilterUrl(params, {order: ''}, {page: true});
if (extra === 'discont') { // 如果是折扣专场
opt.name = '全部';
if (_.isEmpty(params.order) || params.order === 's_t_desc') {
opt.active = true;
} else {
opt.active = false;
}
} else {
opt.name = '默认';
if (_.isEmpty(params.order) || params.order === 's_n_desc') {
opt.active = true;
} else {
opt.active = false;
}
}
break;
case 1:
if (params.order !== 's_t_desc' && params.order !== 's_t_asc') {
opt.href = handleFilterUrl(params, {order: 's_t_desc'}, {page: true});
opt.hasSortOrient = true;
} else {
opt.hasSortOrient = true;
opt.active = true;
if (params.order === 's_t_desc') {
opt.href = handleFilterUrl(params, {order: 's_t_asc'}, {page: true});
opt.desc = false;
} else {
opt.href = handleFilterUrl(params, {order: 's_t_desc'}, {page: true});
opt.desc = true;
}
}
opt.name = '最新';
break;
case 2:
if (params.order !== 's_p_desc' && params.order !== 's_p_asc') {
opt.href = handleFilterUrl(params, {order: 's_p_desc'}, {page: true});
opt.hasSortOrient = true;
} else {
opt.hasSortOrient = true;
opt.active = true;
if (params.order === 's_p_desc') {
opt.href = handleFilterUrl(params, {order: 's_p_asc'}, {page: true});
opt.desc = false;
} else {
opt.href = handleFilterUrl(params, {order: 's_p_desc'}, {page: true});
opt.desc = true;
}
}
opt.name = '价格';
break;
case 3:
if (params.order !== 'p_d_desc' && params.order !== 'p_d_asc') {
opt.href = handleFilterUrl(params, {order: 'p_d_desc'}, {page: true});
opt.hasSortOrient = true;
} else {
opt.hasSortOrient = true;
opt.active = true;
if (params.order === 'p_d_desc') {
opt.href = handleFilterUrl(params, {order: 'p_d_asc'}, {page: true});
opt.desc = false;
} else {
opt.href = handleFilterUrl(params, {order: 'p_d_desc'}, {page: true});
opt.desc = true;
}
}
opt.name = '折扣';
break;
default:
break;
}
dest.sortType.push(opt);
}
// 上下翻页数据处理
dest.pageCounts = [{
href: handleFilterUrl(params, {limit: 60}, {page: true}),
count: 60
}, {
href: handleFilterUrl(params, {limit: 100}, {page: true}),
count: 100
}, {
href: handleFilterUrl(params, {limit: 200}, {page: true}),
count: 200
}];
dest.curPage = _.isEmpty(params.page) ? 1 : params.page; // 当前页码数
// 每页商品数量
dest.countPerPage = _.isEmpty(params.limit) ? 60 : params.limit;
// 全部页码数量
dest.pageCount = parseInt(total / (dest.countPerPage - 1), 10) + 1;
// 每页多少商品
let paramsLimit = parseInt((_.isEmpty(params.limit) ? 60 : params.limit), 10);
// 上一页下一页
let preHref = (!_.isEmpty(params.page) && parseInt(params.page, 10) > 1) ?
parseInt(params.page, 10) - 1 : 1;
let nextHref = (!_.isEmpty(params.page)) ? parseInt(params.page, 10) + 1 : 2;
params.page = _.isEmpty(params.page) ? 1 : params.page;
if (dest.pageCount > 1 && (parseInt(params.page, 10) !== 1) &&
(parseInt(params.page, 10) !== dest.pageCount)) {
dest.preHref = handleFilterUrl(params, {page: preHref});
dest.nextHref = handleFilterUrl(params, {page: nextHref});
} else if (dest.pageCount > 1 && (parseInt(params.page, 10) === 1 || _.isEmpty(params.page))) {
dest.nextHref = handleFilterUrl(params, {page: nextHref});
} else if (dest.pageCount > 1 && (parseInt(params.page, 10) === dest.pageCount)) {
dest.preHref = handleFilterUrl(params, {page: preHref});
}
// 全部商品数
dest.totalCount = total;
// 商品开始结束数
dest.start = (!_.isEmpty(params.page) ? (params.page - 1) : 0) * paramsLimit + 1;
if (total < paramsLimit) {
dest.end = total;
} else {
dest.end = (dest.pageCount === parseInt(params.page, 10)) ?
total : parseInt(dest.start, 10) + paramsLimit - 1;
}
if (extra) {
// 新品
dest.checks.push(hadleChecks('new', params, extra));
// 打折
dest.checks.push(hadleChecks('specialoffer', params, extra));
// 限量
dest.checks.push(hadleChecks('limited', params, extra));
dest.checks = _.filter(dest.checks, chk => {
return chk !== null;
});
}
return dest;
};
/**
* 处理页面左侧分类筛选数据
* @param origin 分类原始数据,一般是接口返回数据中的 group_sort 字段
* @param params 当前 URL 中已有的参数
* @param extra 可以任意传值用来处理特殊情况
* @returns {{}}
*/
exports.handleSortData = (origin, params) => {
let leftContent = {};
let list = [],
allCount = 0;
leftContent.allSort = {};
leftContent.allSort.all = [{
name: '全部品类',
num: allCount,
href: handleFilterUrl(params, {}, {msort: true, misort: true})
}];
//
_.forEach(origin, value => {
let category = {
name: value.sort_name,
num: value.count,
childList: [
{
name: `全部${value.sort_name}`,
num: value.count,
href: handleFilterUrl(params, {msort: value.sort_id}, {misort: true}),
childActive: (params.msort === value.sort_id) && !params.misort,
sortId: value.sort_id
}
],
href: handleFilterUrl(params, {msort: value.sort_id}, {misort: true}),
active: params.msort === value.sort_id,
sortId: value.sort_id
};
allCount += parseInt(value.count, 10);
_.forEach(value.sub, subValue => {
category.childList.push({
name: subValue.sort_name,
num: subValue.count,
href: handleFilterUrl(params, {msort: value.sort_id, misort: subValue.sort_id}),
childActive: params.misort === subValue.sort_id,
sortId: subValue.sort_id
});
if (params.misort === subValue.sort_id) {
category.active = true;
}
});
list.push(category);
});
leftContent.allSort.list = list;
return leftContent;
};
/**
* 店铺品类聚合
* @param data 分类数据
* @returns {{}}
*/
exports.setShopSort = (data, params) => {
let resData = {};
if (!_.isEmpty(data)) {
let list = [];
_.forEach(data, value => {
_.forEach(value.sub, subValue => {
list.push({
name: subValue.sort_name,
href: handleFilterUrl(params, {msort: value.sort_id, misort: subValue.sort_id}),
curMenu: params.misort === subValue.sort_id
});
});
});
if (list.length) {
_.set(resData, 'goodsMenu.menuList', list);
}
}
return resData;
};
/**
* 处理一般筛选数据
* @param origin 要处理的筛选数据 filter
* @param params 当前 URL 中已有的参数,处理选中状态使用
* @returns {{}}
*/
exports.handleFilterData = (origin, params, total) => {
let dest = {
ageLevel: [],
brand: {
default: [],
brandsShow: [],
brandIndex: [{
index: 'all',
name: '全部'
}, {
index: '0-9',
name: '0~9'
}],
showMore: true,
showMulti: true
},
price: [],
gender: [],
color: [],
checkedConditions: {
conditions: []
}
};
dest.brand = formatterFilterBrands(origin.brand, origin.paramBrand, params);
// 处理价格筛选数据
let priceRangechecked = false;
if (!_.isEmpty(origin.priceRange)) {
let customPrice = _.split(params.price, ',');
_.forEach(origin.priceRange, (value, key) => {
if (params.price === key) {
priceRangechecked = true;
}
let price = {
checked: params.price === key,
href: handleFilterUrl(params, {price: key}),
name: value,
order: +key.split(',')[0]
};
dest.price.push(price);
});
// 价格排序
dest.price = _.sortBy(dest.price, (item) => {
return item.order;
});
dest.customPrice = {
min: customPrice[0],
max: customPrice[1]
};
}
// 处理用户自主填写的价格区间
if (!priceRangechecked && params.price) {
let customPrice = _.split(params.price, ','),
min = customPrice[0] || 0,
name;
customPrice[0] = customPrice[0];
name = ${min}-${customPrice[1]}`;
if (!customPrice[1]) {
customPrice[1] = '';
name = ${min}以上`;
}
dest.customPrice = {
min: customPrice[0],
max: customPrice[1]
};
dest.checkedConditions.conditions.push({
name: name,
href: handleFilterUrl(params, {}, {price: true})
});
}
// 处理性别数据
dest.gender = [{
name: 'BOYS',
href: handleFilterUrl(params, {gender: '1,3'}),
checked: params.gender === '1,3'
}, {
name: 'GIRLS',
href: handleFilterUrl(params, {gender: '2,3'}),
checked: params.gender === '2,3'
}];
// 尺码处理
if (!_.isEmpty(origin.size) && (params.msort || params.misort || params.sort)) {
dest.size = [];
if (_.isArray(origin.size)) {
_.forEach(origin.size, value => {
dest.size.push({
checked: parseInt(params.size, 10) === parseInt(value.size_id, 10),
href: handleFilterUrl(params, {
size: value.size_id
}),
name: value.size_name
});
});
} else {
dest.size.push({
checked: parseInt(params.size, 10) === parseInt(origin.size.size_id, 10),
href: handleFilterUrl(params, {
size: origin.size.size_id
}),
name: origin.size.size_name
});
}
dest.checkedConditions.conditions = _.union(dest.checkedConditions.conditions,
handleCheckedData(params, dest.size, 'size'));
}
// 处理颜色筛选数据
if (!_.isEmpty(origin.color)) {
_.forEach(origin.color, (value) => {
let color = {
checked: parseInt(params.color, 10) === parseInt(value.color_id, 10),
href: handleFilterUrl(params, {color: value.color_id}),
name: value.color_name,
rgb: value.color_value ? 'url(' + value.color_value + ')' : '#' + value.color_code
};
// 处理颜色选中数据
if (color.checked) {
let href = handleFilterUrl(params, null, {color: value.color_id});
dest.checkedConditions.conditions.push({
href: href,
color: color.rgb
});
}
dest.color.push(color);
});
}
// 处理年龄段
if (!_.isEmpty(origin.ageLevel)) {
// 只有一个默认选中
let isChecked = origin.ageLevel.length === 1;
do { // eslint-disable-line
if (isChecked && origin.ageLevel[0].name === '成人' && +params.age_level !== 1) {
break;
}
_.forEach(origin.ageLevel, (value) => {
let ageLevel = {
checked: params.age_level === value.id || isChecked,
href: handleFilterUrl(params, {age_level: value.id}),
name: value.name
};
// 处理颜色年龄段数据
if (ageLevel.checked || isChecked) {
ageLevel.href = handleFilterUrl(params, null, {age_level: value.id});
dest.checkedConditions.conditions.push(ageLevel);
}
dest.ageLevel.push(ageLevel);
});
} while (false);
}
// 清除所有选中数据
let remainParams = {};
if (params.id) {
remainParams.id = params.id;
}
dest.checkedConditions.clearUrl = '?' + queryString.stringify(remainParams);
// 处理频道筛选数据
dest.checkedConditions.conditions = _.union(dest.checkedConditions.conditions,
handleCheckedData(params, dest.gender, 'gender'));
// 处理品牌筛选数据
if (dest.brand.brandsShow) {
dest.checkedConditions.conditions = _.union(dest.checkedConditions.conditions,
handleBrandCheckedData(params, dest.brand.selectedBrands));
}
// 处理价格筛选数据
dest.checkedConditions.conditions = _.union(dest.checkedConditions.conditions,
handleCheckedData(params, dest.price, 'price'));
// 商品记录小于10,不显示价格区间
if (total && total < 10) {
dest.price = null;
}
return dest;
};
/**
* 处理高级选项数据
* @param origin 要处理的筛选数据 filter
* @param params 当前 URL 中已有的参数,处理选中状态使用
* @returns {{}}
*/
exports.handleSeniorFilterData = (data, params) => {
let resData = {};
if (data) {
let chose = [];
let conditions = [];
if (!_.isEmpty(data.style)) {
let sub = [],
conName = [];
let styles = _.split(_.get(params, 'style', ''), ',');
_.forEach(data.style, value => {
let ched = styles.indexOf(`${value.style_id}`) >= 0;
if (ched && conName.length < 2) {
conName.push(value.style_name);
}
sub.push({
id: value.style_id,
checked: ched,
href: handleFilterUrl(params, {style: value.style_id}),
name: value.style_name
});
});
if (conName.length) {
conditions.push({
name: _.join(conName, '、'),
href: handleFilterUrl(params, {}, {style: true})
});
}
if (sub && sub.length > 1) {
chose.push({
name: '风格',
showMulti: true,
sub: sub,
attr: 'style'
});
}
}
if (!_.isEmpty(data.standard)) {
let qStandardStr = (params && params.standard) || '';
let qStandard = {};
_.forEach(qStandardStr.split(','), value => {
let val = value.split('_');
if (val && val.length >= 2) {
qStandard[parseInt(val[0], 10)] = parseInt(val[1], 10);
}
});
_.forEach(data.standard, value => {
let sub = [];
// let parKey = `parameter_${value.standard_id}`;
_.forEach(value.sub, subValue => {
let ched = qStandard[value.standard_id] === parseInt(subValue.standard_id, 10);
if (ched) {
conditions.push({
name: subValue.standard_name,
href: handleFilterUrl(params, {
standard: getStandard(qStandard, null, {
k: value.standard_id,
v: subValue.standard_id
})
})
});
}
sub.push({
id: value.standard_id,
checked: ched,
href: handleFilterUrl(params, {
standard: getStandard(qStandard, {
k: value.standard_id,
v: subValue.standard_id
})
}),
name: subValue.standard_name
});
});
// 只有一条不显示
if (sub && sub.length > 1) {
chose.push({
name: value.standard_name,
sub: sub
});
}
});
}
if (!_.isEmpty(chose)) {
resData.seniorChose = chose;
}
if (!_.isEmpty(conditions)) {
_.set(resData, 'checkedConditions.conditions', conditions);
}
}
return resData;
};
exports.handleFilterDataAll = (data, qs) => {
let params = _.cloneDeep(qs);
_.unset(params, 'page'); // 去除筛选项page
let baseFilter = this.handleFilterData(data.filter, params, data.total);
let seniorFilter = this.handleSeniorFilterData({
style: _.get(data, 'filter.style', []),
standard: _.get(data, 'standard', [])
}, params);
let conditions = _.union(_.get(baseFilter, 'checkedConditions.conditions'),
_.get(seniorFilter, 'checkedConditions.conditions'));
let destFilter = Object.assign({}, seniorFilter, baseFilter);
if (!destFilter.checkedConditions) {
destFilter.checkedConditions = {};
}
destFilter.checkedConditions.conditions = conditions;
return destFilter;
};
/**
* 根据页面设置面包屑导航
* @type {[type]}
*/
exports.handlePathNavData = (data, params, page, channel) => {
let rootName = '首页';
if (channel && _.isString(channel)) {
rootName = channel.toUpperCase() + rootName;
}
let pathNav = [{
href: '/',
name: rootName, // '首页', // TODO 从根据cookie获取频道
pathTitle: rootName // '首页'
}];
switch (page) {
case 'search':
// 搜索结果提示
if (params.query || params.keywords) {
params.query = params.query || params.keywords;
pathNav.push({
name: '“<span id="nav_keyword">' +
params.query + '</span>”&nbsp;&nbsp;共<span id="nav_keyword_count">' +
data.total + '</span>个结果'
});
} else {
pathNav.push({
name: '所有商品'
});
}
break;
case 'brand':
// 品牌
pathNav.push(
{
name: '品牌一览',
pathTitle: '品牌一览',
href: helpers.urlFormat('/brands')
},
{
name: data.brandNameEn + data.brandNameCn,
pathTitle: data.brandNameEn + data.brandNameCn
}
);
break;
case 'shop':
// 店铺
pathNav.push(
{
name: data.brandName,
pathTitle: data.brandName
}
);
break;
case 'new':
// 新品到着
pathNav.push(
{
name: '新品到着',
pathTitle: '新品到着'
}
);
break;
default :
// 分类
_.forEach(data, (sort) => {
if (sort.sort_id === params.msort) {
pathNav.push({
name: sort.sort_name,
href: helpers.urlFormat('/product/outlets/list', {msort: sort.sort_id}),
pathTitle: sort.sort_name
});
}
if (!_.isEmpty(sort.sub)) {
_.forEach(sort.sub, misort => {
if (misort.sort_id === params.misort) {
pathNav.push({
name: misort.sort_name,
pathTitle: misort.sort_name
});
}
});
}
});
break;
}
return {
pathNav: pathNav
};
};
/**
* 分页
* @param {[type]} total 总页数
* @param {[type]} params 筛选条件
* @param {[noNextBtn]} 列表是否有下一页的按钮,如果有则实际查询数比传递参数的少一个,入60 => 59
* @return {[type]} [description]
*/
exports.handlePagerData = (total, params, noNextBtn) => {
let dest = {
prePage: {
url: ''
},
nextPage: {
url: ''
},
pages: []
};
let currentPage = parseInt(_.get(params, 'page', 1), 10); // 当前页
let perPageCount = parseInt(_.get(params, 'limit', noNextBtn === true ? 60 : 59), 10); // 每页商品数
let totalPage = Math.ceil(total / perPageCount); // 总页数
if (noNextBtn !== true) {
switch (perPageCount) {
case 200:
perPageCount = 199;
break;
case 100:
perPageCount = 99;
break;
default :
perPageCount = 59;
break;
}
}
if (currentPage === 1) {
// 当前页为 1,一定没有上一页
delete dest.prePage;
} else {
dest.prePage.url = handleFilterUrl(params, {page: currentPage - 1});
}
if (currentPage === totalPage) {
// 当前页为最后一页,一定没有下一页
delete dest.nextPage;
} else {
dest.nextPage.url = handleFilterUrl(params, {page: currentPage + 1});
}
// 页码临时数据
let pages = [];
if (currentPage > 2 && currentPage <= totalPage - 2) {
for (let i = currentPage - 2; i <= ((currentPage + 2) > totalPage ? totalPage : (currentPage + 2)); i++) {
pages.push({
url: handleFilterUrl(params, {page: i}),
num: i,
cur: currentPage === i
});
}
// 处理页码小于等于 2 的情况
} else if (currentPage <= 2) {
for (let i = 1; i <= (totalPage < 5 ? totalPage : 5); i++) {
pages.push({
url: handleFilterUrl(params, {page: i}),
num: i,
cur: currentPage === i
});
}
} else if (currentPage > totalPage - 2) {
for (let i = totalPage; i >= totalPage - 4; i--) {
if (i > 0) {
pages.push({
url: handleFilterUrl(params, {page: i}),
num: i,
cur: currentPage === i
});
}
}
pages = _.sortBy(pages, ['num']);
}
let prevPages = [];
let nextPages = [];
if (_.size(pages) === 5) {
if (currentPage > 4) {
prevPages.push({
url: handleFilterUrl(params, {page: 1}),
num: 1
});
prevPages.push({
num: '...'
});
}
if (currentPage < totalPage - 2 && totalPage > 5) {
nextPages.push({
num: '...'
});
nextPages.push({
url: handleFilterUrl(params, {page: totalPage}),
num: totalPage
});
}
}
// 商品开始结束数
dest.tip = {
total: total,
start: (currentPage ? currentPage - 1 : 0) * 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;
};
/**
* 处理分类介绍
* @type {[type]}
*/
exports.handleSortIntro = (data) => {
let sortIntro = {
name: data.title,
enName: data.subtitle,
description: data.intro,
img: data.logo,
keyEntry: []
};
_.forEach(data.keyword, function(value) {
sortIntro.keyEntry.push({
name: value.word,
url: value.url
});
});
return sortIntro;
};
/**
* 处理分类页左侧广告
* @type {[type]}
*/
exports.handleSortAds = (data) => {
let sortAds = {
picLink: {
picTitle: '',
list: []
}
};
_.forEach(data, function(value) {
sortAds.picLink.list.push({
href: value.ads_url,
src: value.ads_image
});
});
return sortAds;
};
/**
* 处理一周新品上架
* @type {[type]}
*/
exports.handleWeekNew = (data, params) => {
let list = [];
let dest = {
newSales: {
name: '',
list: []
}
};
_.forEach(data.recent, function(value, key) {
let t = _.split(key, '-'),
dayStart = new Date(t[0], _.parseInt(t[1]) - 1, t[2]).getTime() / 1000,
day = `${dayStart},${dayStart + 86400}`;
list.push({
name: `${t[1] || ''}${t[2] || ''}日`,
href: handleFilterUrl(params, {shelveTime: day}),
active: day === params.shelveTime,
sort: +t.join('')
});
});
_.set(dest, 'newSales.list', _.sortBy(list, function(o) {
return -o.sort;
}));
return dest;
};
/**
* 处理搜索提示
* @type {[type]}
*/
exports.handleSuggest = (data) => {
let suggest = [];
_.forEach(data, function(value) {
suggest.push({
href: helpers.urlFormat('', {query: value.keyword}, 'search'),
keyword: value.keyword,
count: value.count
});
});
return suggest;
};
/**
* 处理品牌页banner
* @type {[type]}
*/
exports.handleBrandBanner = (data) => {
return {
bannerHeight: 150,
banner: helpers.getForceSourceUrl(data.brandBanner),
brandHome: data.url,
brandIntro: helpers.urlFormat('/about', '', data.brandDomain),
dataId: data.brandId
};
};
/**
* 处理品牌系列
* @type {[type]}
*/
exports.handleFolderData = (data) => {
let dest = [];
_.forEach(data, (value) => {
dest.push({
href: '?folder_id=' + value.id,
src: value.brand_sort_ico
});
});
return dest;
};
/**
* 筛选类链接处理的对外接口
* @type {[type]}
*/
exports.handleFilterUrl = handleFilterUrl;
/**
* 处理选中数据的对外接口
* @type {[type]}
*/
exports.handleCheckedData = handleCheckedData;
exports.handleNextPage = (params, total) => {
let href;
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; // 总页数
if (currentPage === totalPage) {
return null;
} else {
href = handleFilterUrl(params, {page: currentPage + 1});
}
return {
href: href,
src: '//img10.static.yhbimg.com/product/2014/01/15/11/01fa01614784f6239760f1b749663016f1.jpg?' +
'imageMogr2/thumbnail/235x314/extent/235x314/background/d2hpdGU=/position/center/quality/90'
};
};
const getChannelName = channel => {
let channelName = '';
switch (channel) {
case 'boys':
channelName = '男生';
break;
case 'girls':
channelName = '女生';
break;
case 'kids':
channelName = '潮童';
break;
case 'lifestyle':
channelName = '创意生活';
break;
default:
channelName = '男生';
}
return channelName;
};
/**
* 列表页seo
* @param channel
* @param sorts
* @param checked
* @returns {{title: string, keywords: string, description: string}}
*/
exports.getListSeo = (channel, sorts, checked) => {
let channelName = getChannelName(channel),
brandName = '',
priceName = '',
msortName = '',
misortName = '';
_.forEach(sorts, val => {
if (val.active) {
msortName = val && val.name;
_.forEach(val.childList, sub => {
if (sub.childActive && sub.sortId !== val.sortId) {
misortName = sub && sub.name;
return false;
}
});
return false;
}
});
_.forEach(checked, ck => {
if (ck.itemType === 'price') {
priceName = ck.name;
} else if (ck.itemType === 'brand') {
brandName = ck.name;
}
});
let bmpcm = brandName + misortName + priceName + channelName + msortName,
bm = brandName + misortName,
bmp = brandName + misortName + priceName,
cmbm = channelName + msortName + brandName + misortName,
cm = channelName + msortName;
let title = (bmpcm ? bmpcm + '|' : '') + 'YOHO!BUY 有货 100%正品保证',
keywords = (bm ? bm + ',' : '') + (bmp ? bmp + ',' : '') + cmbm,
description = bmp + '正品网购。' + (cm ? cm + ',' : '') + bm + '官方授权!YOHO! 有货中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。';
return {
title: title,
keywords: keywords,
description: description
};
};
/**
* 新品到页 seo
* @param channel
* @param dlist
* @returns {{title: string, keywords: string, description: string}}
*/
exports.getNewSeo = (channel, dlist) => {
let channelName = getChannelName(channel),
nlabel = '';
_.forEach(dlist, d => {
if (d && d.active) {
nlabel = d.name || '';
return false;
}
});
let newTitle = channelName + nlabel;
return {
title: newTitle + '新品上架-YOHO!BUY 有货 100%正品保证',
keywords: newTitle + '新品上架',
description: newTitle + '新品上架,正品网购,官方授权!YOHO! 有货中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。'
};
};
/**
* 品牌页 seo
* @param channel
* @param brandInfo
* @returns {{title: (string|*|string), keywords: string, description: string}}
*/
exports.getBrandSeo = (channel, brandInfo) => {
let c = getChannelName(channel),
bcn = brandInfo && brandInfo.brandNameCn,
b = brandInfo && brandInfo.brandNameEn,
bb = b + ' ' + bcn,
bc = b + c,
bbc = b + bcn + c,
title = bb || b || '',
keywords = [b ? b + ',' : '',
((b && bcn) ? (bb + ',') : ''),
bc || '',
'品牌'].join(''),
desc = [b + '正品网购。',
bbc || '',
'品牌官方授权!YOHO! 有货中国最大的潮流商品购物网站。100%品牌正品保证,支持货到付款。'].join('');
title += (title ? (title + '|') : '') + c + '品牌|YOHO!BUY 有货 100%正品保证';
return {
title: title,
keywords: keywords,
description: desc
};
};
/**
* 获取商品的前三个skn 提供给第三方推广使用
* @param glist
* @returns {Array}
*/
exports.getCriteo = (glist) => {
let sknArr = [];
if (glist) {
_.forEach(glist, (val) => {
if (sknArr.length < 3) {
if (val && val.product_skn) {
sknArr.push(val.product_skn);
}
} else {
return false;
}
});
}
return sknArr;
};
// handlePagerData
exports.getSearchParams = params => {
let mlimit = 59;
if (params && params.limit) {
let olimit = parseInt(params.limit, 10);
switch (olimit) {
case 100:
mlimit = 99;
break;
case 200:
mlimit = 199;
break;
default:
mlimit = 59;
}
}
/** 处理页面数据 **/
let nparams = {
limit: mlimit
};
if (params && params.price) {
let mp = params.price.split(',');
let nmp = [];
nmp[0] = (mp && mp[0]) || 0;
nmp[1] = (mp && mp[1]) || 99999;
nparams.price = nmp.join(',');
}
return Object.assign({}, params, nparams);
};
exports.handleFilterBrands = (brands, params) => {
return formatterFilterBrands(brands, null, params);
};
... ...
/**
* search
* @Author: sefon 2016-7-12 16:31:56
*/
'use strict';
const utils = '../../../utils';
const api = global.yoho.API;
const searchApi = require('./search-api');
const headerModel = require('../../../doraemon/models/header');
const productProcess = require(`${utils}/product-process`);
const searchHandler = require('./search-handler');
const _ = require('lodash');
/**
* 获取搜索数据
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
exports.getSearchData = (params, channel) => {
let searchParams = searchHandler.getSearchParams(params);
// 调用接口
let apiMethod = [
headerModel.requestHeaderData(channel),
searchApi.getSortList(Object.assign({}, searchParams, {msort: '', misort: ''})),
searchApi.getProductList(searchParams),
searchApi.getShopList(params)
];
// 搜索店铺
/* if (params.query) {
apiMethod.push(searchApi.getBrandShop(params.query));
}*/
return api.all(apiMethod).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true
}),
search: {}
};
// 获取左侧类目数据
if (result[1].code === 200) {
let dps = {};
if (params.query) {
dps.query = params.query;
}
finalResult.search = {
leftContent: searchHandler.handleSortData(result[1].data.sort, dps)
};
}
// 获取商品数据和顶部筛选条件
if (result[2].code === 200) {
Object.assign(finalResult.search,
searchHandler.handlePathNavData({total: result[2].data.total }, params, 'search', channel),
{
filters: searchHandler.handleFilterDataAll(result[2].data, params),
opts: searchHandler.handleOptsData(params, result[2].data.total, result[2].data.filter),
totalCount: result[2].data.total,
footPager: searchHandler.handlePagerData(result[2].data.total, params),
goods: productProcess.processProductList(result[2].data.product_list,
Object.assign({showDiscount: false}, params)),
latestWalk: 6,
hasNextPage: searchHandler.handleNextPage(params, result[2].data.total),
shopEntry: result[3]
});
finalResult.criteo = {skn: searchHandler.getCriteo(_.get(finalResult.search, 'goods'))};
}
return finalResult;
});
};
/**
* 搜索提示
*/
exports.getSuggest = (params) => {
return searchApi.getSuggest(params).then(result => {
let dest = [];
if (result.code === 200) {
dest = searchHandler.handleSuggest(result.data);
}
return dest;
});
};
exports.getBrands4Filter = (params) => {
let nparams = {};
if (params && params.price) {
let mp = params.price.split(',');
let nmp = [];
nmp[0] = (mp && mp[0]) || 0;
nmp[1] = (mp && mp[1]) || 99999;
nparams.price = nmp.join(',');
}
return searchApi.getBrands4Filter(Object.assign({}, params, nparams)).then(result => {
let dest = [];
if (result.code === 200) {
dest = searchHandler.handleFilterBrands(result.data, params);
}
return dest;
});
};
... ...