Authored by 毕凯

Merge branch 'feature/seo2' into 'gray'

Feature/seo2



See merge request !1095
... ... @@ -5,8 +5,9 @@
*/
'use strict';
const _ = require('lodash');
const helpers = global.yoho.helpers;
const utils = require('../../../utils');
// const helpers = global.yoho.helpers;
// const utils = require('../../../utils');
const genderMap = {
boys: '1,3',
... ... @@ -38,27 +39,34 @@ module.exports = class extends global.yoho.BaseModel {
// 如果有二级菜单,二级菜单跳转,否则一级菜单跳转
if (firstItem.sub && firstItem.sub.length) {
_.map(firstItem.sub, function(secondItem) {
secondItem.url = helpers.urlFormat('/', utils.mapSort({
sort: _.get(secondItem, 'relation_parameter.sort'),
sort_name: secondItem.category_name,
gender: genderMap[key] || ''
}), 'list');
// secondItem.url = helpers.urlFormat('/', utils.mapSort({
// sort: _.get(secondItem, 'relation_parameter.sort'),
// sort_name: secondItem.category_name,
// gender: genderMap[key] || ''
// }), 'list');
secondItem.url =
`//m.yohobuy.com/list/gd${genderMap[key] || ''}-ci${_.get(secondItem, 'category_id')}`;
});
firstItem.sub.unshift({
category_name: `全部${firstItem.category_name}`,
url: helpers.urlFormat('/', utils.mapSort({
sort: _.get(firstItem, 'relation_parameter.sort'),
sort_name: firstItem.category_name,
gender: genderMap[key] || ''
}), 'list')
// url: helpers.urlFormat('/', utils.mapSort({
// sort: _.get(firstItem, 'relation_parameter.sort'),
// sort_name: firstItem.category_name,
// gender: genderMap[key] || ''
// }), 'list'),
url: `//m.yohobuy.com/list/gd${genderMap[key] || ''}-ci${_.get(firstItem, 'category_id')}-sn${firstItem.category_name}` // eslint-disable-line
});
} else {
firstItem.url = helpers.urlFormat('/', utils.mapSort({
sort: _.get(firstItem, 'relation_parameter.sort'),
sort_name: firstItem.category_name,
gender: genderMap[key] || ''
}), 'list');
// firstItem.url = helpers.urlFormat('/', utils.mapSort({
// sort: _.get(firstItem, 'relation_parameter.sort'),
// sort_name: firstItem.category_name,
// gender: genderMap[key] || ''
// }), 'list');
firstItem.url =
`//m.yohobuy.com/list/gd${genderMap[key] || ''}-ci${_.get(firstItem, 'category_id')}`;
}
});
});
... ...
... ... @@ -86,17 +86,20 @@ const _relatedTag = (tags, isApp) => {
*/
const _relatedInfo = (getOtherArticle, isApp) => {
let relatedInfo = [];
let articleUrl;
getOtherArticle.forEach(value => {
articleUrl = helpers.urlFormat('/info/index', {
id: value.id
}, 'guang');
if (isApp) {
value.url = articleUrl + '&openby:yohobuy={"action":"go.h5","params":{"id":"' + value.id + '","shareparam":{"id":"' + value.id + '"},"islogin":"N","type":1,"url":"http://guang.m.yohobuy.com/info/index","param":{"id":"' + value.id + '"}}}';
if (value.url.indexOf('guang.m.yohobuy.com') < 0) {
value.url = `${helpers.https(value.url)}?openby:yohobuy={"url":"${value.url}","islogin":"N"}}`;
} else {
value.url = `//m.yohobuy.com/guang/info/${value.id}.html?openby:yohobuy={"action":"go.h5","params":{"param":{"id":"${value.id}"},"shareparam":{"id":"${value.id}"},"share":"/guang/api/v1/share/guang","id":${value.id},"type":1,"url":"http://m.yohobuy.com/guang/info/${value.id}.html","islogin":"N"}}`;
}
} else {
value.url = articleUrl;
if (value.url.indexOf('guang.m.yohobuy.com') < 0) {
value.url = value.url;
} else {
value.url = `//m.yohobuy.com/guang/info/${value.id}.html`;
}
}
value.thumb = helpers.image(value.thumb, 279, 175);
... ...
... ... @@ -10,12 +10,25 @@ const mRoot = '../models';
const headerModel = require('../../../doraemon/models/header');
const listModel = require(`${mRoot}/list`);
const _ = require('lodash');
const md5 = require('yoho-md5');
const co = Promise.coroutine;
const helpers = global.yoho.helpers;
const productProcess = require(`${utils}/product-process`);
const searchModel = require(`${mRoot}/search`);
const shopModel = require(`${mRoot}/shop`);
const searchProcess = require(`${utils}/search-process`);
const stringProcess = require(`${utils}/string-process`);
const listParamsProcess = require(`${utils}/list-params-process`);
const redis = require(`${utils}/redis`);
/**
* 封面图
* @type {{boys: string, gilrs: string}}
*/
const _coverChannel = {
boys: '1,3',
gilrs: '2,3'
};
/**
* 从 useragent 获取 uid
... ... @@ -212,6 +225,7 @@ const category = (req, res, next) => {
}
let params = Object.assign({}, req.query);
let seoTitle = decodeURIComponent(req.query.title || req.query.sort_name || '商品列表');
/* 勿修改,唤起 APP 使用 */
let appParams = Object.assign({}, req.query, {
... ... @@ -253,7 +267,7 @@ const category = (req, res, next) => {
// currentHref: 'https://www.yohobuy.com/list?' // TODO
// },
pageHeader: headerModel.setNav({
navTitle: req.query.title || req.query.sort_name
navTitle: seoTitle
}),
goodList: params,
firstPageGoods: firstPageGoods || [],
... ... @@ -264,7 +278,139 @@ const category = (req, res, next) => {
localCss: true,
appPath: appPath,
introText: req.query.intro_text
}, searchProcess.getListSeoData(req.query.gender, req.query.title || req.query.sort_name)));
}, searchProcess.getListSeoData(req.query.gender, seoTitle)));
}).catch(next);
};
/**
* 品类落地页 SEO 友好的新路由
*/
const listNew = (req, res, next) => {
req.query = listParamsProcess.getParams(req.url);
if (req.query) {
_.forEach(req.query, (perParam, index) => {
req.query[index] = stringProcess.paramsFilter(perParam);
});
}
let params = Object.assign({}, req.query);
/* 勿修改,唤起 APP 使用 */
let appParams = Object.assign({}, req.query, {
title: req.query.title || req.query.sort_name || '',
productPool: req.query.filter_poolId,
actiontype: req.query.actiontype || '1'
});
delete appParams.filter_poolId;
let appPath = 'yohobuy://yohobuy.com/goapp?openby:yohobuy={"action":"go.list","params":' +
JSON.stringify(appParams) +
'}';
let uid = req.user.uid || 0;
// 获取第一页数据做服务端渲染
let initialData = _.assign({
gender: params.gender,
type: 'default',
order: '0',
page: 1,
limit: 24,
isApp: params.app_version
}, params);
if (uid) {
initialData.uid = uid;
}
co(function* () {
let result = yield req.ctx(searchModel).getCategoryGoods(initialData);
let categoryIntro = '';
let responseResult = {
list: productProcess.processProductList(result.data.product_list || [], {
isApp: params.isApp || (params.appVersion && params.appVersion !== 'false'),
gender: _coverChannel[params.coverChannel],
showSimilar: params.shop_id || params.material === 'true' ? false : true
})
};
let seoTitle = _.get(result, 'data.filter.group_sort[0].sub[0].category_name');
if (!seoTitle || !initialData.category_id) {
seoTitle = '商品列表';
}
if (params.sort_name) {
seoTitle = decodeURIComponent(params.sort_name);
}
categoryIntro = yield redis.hmgetAsync(`category:description:${md5(seoTitle)}`, 'category', 'description');
res.render('search/goods-list', Object.assign({
_noLazy: true, // 首屏不使用lazyload
module: 'product',
page: 'search-list-new',
canonical: {
currentHref: `https://www.yohobuy.com${req.url}`
},
pageHeader: headerModel.setNav({
navTitle: seoTitle
}),
goodList: params,
firstPageGoods: responseResult || [],
showDownloadApp: true,
pageFooter: true,
category: true,
localCss: true,
appPath: appPath,
categoryIntro: categoryIntro
}, searchProcess.getListSeoData(req.query.gender, seoTitle)));
})().catch(next);
};
/**
* 品类落地页异步获取商品数据
*/
const getCategoryGoods = (req, res, next) => {
let allowOrigin = _.get(req, 'headers.origin', null) ?
req.headers.origin : req.protocol + '://' + req.headers.host;
res.setHeader('Access-Control-Allow-Origin', allowOrigin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
let currentUrlParams = listParamsProcess.getParams(req.query.currentUrl);
delete req.query.currentUrl;
let params = _.assign({}, currentUrlParams, req.query);
let uid = req.user.uid || 0;
if (uid) {
params.uid = uid;
}
params.isApp = req.yoho.isApp;
params.limit = 24;
req.ctx(searchModel).getCategoryGoods(params).then((result) => {
if (result.data.product_list && result.data.product_list.length > 0) {
let product_list = productProcess.processProductList(result.data.product_list || [], {
isApp: params.isApp || (params.appVersion && params.appVersion !== 'false'),
gender: _coverChannel[params.coverChannel],
showSimilar: params.shop_id || params.material === 'true' ? false : true
});
res.render('search/page', {
layout: false,
new: product_list,
total: result.data.total,
});
} else {
res.json({});
}
}).catch(next);
};
... ... @@ -547,5 +693,7 @@ module.exports = {
shopFav,
baseShopFav,
shopCategory,
getBrandCouponsList
getBrandCouponsList,
listNew,
getCategoryGoods
};
... ...
... ... @@ -13,6 +13,7 @@ const _ = require('lodash');
const helpers = global.yoho.helpers;
const searchProcess = require(`${utils}/search-process`);
const productProcess = require(`${utils}/product-process`);
const listParamsProcess = require(`${utils}/list-params-process`);
const co = require('bluebird').coroutine;
/**
... ... @@ -254,7 +255,17 @@ exports.filter = (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', allowOrigin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
let params = Object.assign({}, req.query);
let params = {};
let currentUrlParams = {}; // 页面链接伪静态固定参数
if (req.query.currentUrl) {
currentUrlParams = listParamsProcess.getParams(req.query.currentUrl);
delete req.query.currentUrl;
delete req.query.page;
}
_.assign(params, currentUrlParams, req.query);
let uid = req.user.uid || 0;
if (uid) {
... ...
... ... @@ -316,6 +316,20 @@ module.exports = class extends global.yoho.BaseModel {
}
/**
* 获取品类下的商品列表
*/
getCategoryGoods(params) {
let requestData = {
method: 'web.search.search'
};
_.assign(requestData, searchProcess.getSearchParamsWithoutMethod(params));
return this.get({
data: requestData
});
}
/**
* 模糊搜索,获取商品数据
*/
getSearchGoods(params) {
... ... @@ -370,6 +384,8 @@ module.exports = class extends global.yoho.BaseModel {
filterDataResult = yield self.getBrandGoods(params);
} else if (params.isShopList === 'Y') { // 无店铺有店铺 ID 的商品列表
filterDataResult = yield self.getShopGoods(params);
} else if (params.isNewList === 'Y') { // 新的 SEO 友好的品类落地页
filterDataResult = yield self.getCategoryGoods(params);
} else {
filterDataResult = yield self._searchGoods(params);
}
... ...
... ... @@ -158,6 +158,8 @@ router.get('/search/shop/goods', search.searchShopGoods); // 搜索店铺下的
// 品类
router.get('/index/index', rewrite.sortParams, list.category);
router.get('/list/index', rewrite.sortParams, list.category); // 兼容 PC 的链接
router.get(/^\/list/, list.listNew); // 列表新的 URL
router.get('/search/category', list.getCategoryGoods);
// 品牌 | 店铺
router.get('/index/shopAppCookie', list.shopAppCookie);
... ...
/**
* URL 重写(主要用于兼容原来PHP的连接)
*/
const querystring = require('querystring');
const helpers = global.yoho.helpers;
module.exports = () => {
... ... @@ -73,8 +73,8 @@ module.exports = () => {
}
if (/^\/list/.test(req.url)) {
// 列表页路由重写 新路由
req.url = `/product/index/index?${querystring.stringify(req.query)}`;
// 列表页路由重写 跳转到新的列表路由,进行参数的前置处理
req.url = `/product/${req.url}`;
}
next();
... ...
... ... @@ -379,7 +379,7 @@ let getResourceContent = function() {
setTimeout(function() {
getResourceContent();
}, 50);
}, 5);
// logo动画
function tsAnimate() {
... ...
... ... @@ -242,10 +242,9 @@ module.exports = function(likeParameter) {
}
}
// 优惠券页面直接加载你可能喜欢。add by @zhaobiao
if (specificGender || likeParameter && likeParameter.isExecute) {
setTimeout(() => {
search();
}
}, 50);
// srcoll to load more
$(window).scroll(function() {
... ...
... ... @@ -69,26 +69,28 @@ class ProductListWithFilter {
});
this.view.listNav.on('touchend touchcancel', this.listNavTouch.bind(this));
let self = this;
/**
* 滚动加载
*/
$(window).scroll(() => {
if (this.scrollActived) {
setTimeout(() => {
let afterScroll = document.body.scrollTop;
window.onscroll = function() {
if (self.scrollActived) {
setTimeout(function() {
let afterScroll = window.scrollY;
if (afterScroll - this.beforeScroll > 0) {
if (afterScroll - self.beforeScroll > 0) {
window.requestAnimationFrame(() => {
this.scrollHandler();
self.scrollHandler();
});
this.beforeScroll = afterScroll;
self.beforeScroll = afterScroll;
} else {
this.beforeScroll = afterScroll;
self.beforeScroll = afterScroll;
return false;
}
}, 5);
}
});
};
}
/**
... ...
/**
* 搜索商品列表页
* @author: wsl<shuiling.wang@yoho.cn>
* @date: 2016/7/21
*/
const ProductListWithFilter = require('./list/product-list-with-filter');
require('product/search/list.page.css');
require('common/footer');
require('./shop/coupon');
new ProductListWithFilter({
page: 2,
currentUrl: location.pathname,
isNewList: 'Y'
}, 'product/search/category');
... ...
... ... @@ -40,7 +40,7 @@ let $input = $('#search-input').find('input[name="query"]'),
let shopId, sort, brand, outlets, app_type, age_level, msort, misort, coin, app_version, students, couponId, searchFrom;
let size, color, style, standard, gender, price, p_d, oldquery, query, limited, specialoffer, specialsale_id, promotion;
let couponCode;
let couponCode, categoryId;
// 默认筛选条件
let defaultOpt = require('common/query-param');
... ... @@ -243,6 +243,7 @@ students = getQueryString('students');
couponId = getQueryString('coupon_id');
couponCode = getQueryString('coupon_code');
searchFrom = getQueryString('from');
categoryId = getQueryString('categoryId');
if ($defaultgc.children().length > 0) {
let fsgcgoods = [],
... ... @@ -522,6 +523,10 @@ function search(opt) {
params.from = searchFrom;
}
if (categoryId) {
params.categoryId = categoryId;
}
$.extend(setting, defaultOpt, params);
searching = true;
loading.showLoadingMask();
... ...
... ... @@ -79,11 +79,11 @@ const formatArticle = (articleData, showTag, isApp, showAuthor, uid, top) => {
if (!articleData.id) {
return false;
}
let result = {
id: articleData.id,
showTags: showTag,
img: articleData.src ? helpers.image(articleData.src, 640, 640) : '',
url: isApp ? `//m.yohobuy.com/guang/info/${articleData.id}.html?openby:yohobuy={"action":"go.h5","params":{"param":{"id":"${articleData.id}"},"shareparam":{"id":"${articleData.id}"},"share":"/guang/api/v1/share/guang","id":${articleData.id},"type":1,"url":"http://m.yohobuy.com/guang/info/${articleData.id}.html","islogin":"N"}}` : `//m.yohobuy.com/guang/info/${articleData.id}.html`, //eslint-disable-line
title: articleData.title,
text: articleData.intro,
publishTime: articleData.publish_time,
... ... @@ -91,6 +91,20 @@ const formatArticle = (articleData, showTag, isApp, showAuthor, uid, top) => {
pageView: articleData.views_num
};
if (isApp) {
if (articleData.url.indexOf('guang.m.yohobuy.com') < 0) {
result.url = `${helpers.https(articleData.url)}?openby:yohobuy={"url":"${articleData.url}","islogin":"N"}}`;
} else {
result.url = `//m.yohobuy.com/guang/info/${articleData.id}.html?openby:yohobuy={"action":"go.h5","params":{"param":{"id":"${articleData.id}"},"shareparam":{"id":"${articleData.id}"},"share":"/guang/api/v1/share/guang","id":${articleData.id},"type":1,"url":"http://m.yohobuy.com/guang/info/${articleData.id}.html","islogin":"N"}}`;
}
} else {
if (articleData.url.indexOf('guang.m.yohobuy.com') < 0) {
result.url = articleData.url;
} else {
result.url = `//m.yohobuy.com/guang/info/${articleData.id}.html`;
}
}
if (result.url.includes('feature.yoho.cn') ||
result.url.includes('cdn.yoho.cn')) {
result.url = transHttpsUrl(result.url);
... ...
const _ = require('lodash');
/**
* 参数列表
*/
const PARAMMAP = {
ag: 'age_level',
gd: 'gender',
sn: 'sort_name',
pa: 'phrase',
ci: 'category_id',
so: 'sort',
ms: 'msort',
mi: 'misort',
tp: 'type',
sz: 'size',
cl: 'color',
pc: 'price',
bd: 'brand',
qr: 'query',
lt: 'limit',
ld: 'limited',
od: 'order',
nw: 'new',
pg: 'page',
st: 'style',
sd: 'standard',
si: 'specialsale_id',
sf: 'specialoffer'
};
/**
* 获取标准参数
*/
const getParams = (url) => {
let params = [];
let paramStringRaw = _.split(url, '/list/')[1];
if (paramStringRaw) {
let paramsRaw = _.split(paramStringRaw, '-');
_.forEach(paramsRaw, paramRaw => {
let keyRaw = paramRaw.substr(0, 2);
let valueRaw = _.replace(paramRaw, keyRaw, '');
if (PARAMMAP[keyRaw]) {
params[PARAMMAP[keyRaw]] = valueRaw;
}
});
}
return params;
};
/**
* 生成链接
*/
const generateUrl = () => {
return '';
};
module.exports = {
getParams,
generateUrl
};
... ...
... ... @@ -237,7 +237,7 @@ exports.processProductList = (list, options) => {
product.seoTitle = `${product.brand_name}|${seoGender}${product.small_sort_name}|${product.product_name}|YOHO!BUY有货`; // eslint-disable-line
product.imgAlt = _.compact([product.brand_name, (product.gender ? GENDER[product.gender] : false), product.small_sort_name, product.product_name]).join('|');
product.imgAlt = _.compact([product.brand_name, (product.gender ? GENDER[product.gender] : false), product.small_sort_name, product.product_name]).join('|'); // eslint-disable-line
pruductList.push(product);
});
... ...
... ... @@ -102,6 +102,10 @@ const getSearchParamsWithoutMethod = (params) => {
finalParams.yh_channel = params.yh_channel;
}
if (params.brand) {
finalParams.brand = params.brand;
}
if (params.brand_id) {
finalParams.brand = params.brand_id;
}
... ... @@ -153,6 +157,11 @@ const getSearchParamsWithoutMethod = (params) => {
if (params.type) {
finalParams.type = params.type;
}
if (params.category_id) {
finalParams.category_id = params.category_id;
}
return finalParams;
};
... ...