Authored by 周少峰

Merge branch 'feature/tdk'

... ... @@ -3,4 +3,4 @@
"parserOptions": {
"sourceType": "module"
}
}
}
\ No newline at end of file
... ...
... ... @@ -35,6 +35,10 @@ const pkg = require('./package.json');
const app = express();
const helpers = global.yoho.helpers;
// tdk
global.yoho.redis = require('./doraemon/middleware/redis');
const tdk = require('./utils/getTDK');
// NOTE: 这里修改了图片质量的参数
helpers.image = _.flow(helpers.image, fp.replace(/\/quality\/\d*$/, '/quality/90'));
... ... @@ -163,6 +167,24 @@ app.use((req, res, next) => {
next();
});
// redis seo
app.use((req, res, next) => {
if (!req.xhr) {
tdk('url', `${req.hostname}${req.originalUrl}`, req).then(TDKObj =>{
if (TDKObj[0]) {
req.tdk = {
title: TDKObj[1],
keywords: TDKObj[2],
description: TDKObj[3]
};
}
next();
});
} else {
return next();
}
});
const logger = global.yoho.logger;
// dispatcher
... ...
... ... @@ -13,6 +13,7 @@ const headerModel = require('../../../doraemon/models/header');
const ghelper = require('../models/guang-helper');
const urlHelper = require('../models/url-helper');
const querystring = require('querystring');
const tdk = require('../../../utils/getTDK');
/**
* 首页文章列表 类型列表
... ... @@ -252,7 +253,8 @@ exports.detail = (req, res, next) => {
reqCtx.getArticleBaseInfo(id, uid, udid),
reqCtx.getArticleRelateBrand(id),
reqCtx.getRecoArticles(gender, 1, 10, channel),
reqCtx.getAds(channel, isAdDegrade)
reqCtx.getAds(channel, isAdDegrade),
tdk('article', id, req)
];
if (info.authorId) {
... ... @@ -275,6 +277,24 @@ exports.detail = (req, res, next) => {
res.set('Cache-Control', 'no-cache');
}
let title, keywords, description;
if (ret[8][0]) {
req.tdk = {
title: ret[8][1],
keywords: ret[8][2],
description: ret[8][3]
};
}
title = `${info.title} | YOHO!BUY有货`;
keywords = info.tag.length > 0 ? `${info.tag}` :
['Yoho! 有货,潮流,时尚,流行,购物,B2C,正品,购物网站,网上购物,货到付款,品牌服饰,男士护肤,',
'黑框眼镜,匡威,板鞋,i.t,izzue,5cm,eastpak,vans,lylescott,g-shock,new balance,lacoste,melissa,',
'casio,卡西欧手表,舒雅,jasonwood,odm,AAAA,香港购物,日本潮流'].join('');
description = info.desc.length > 0 ? `${info.desc}` :
'潮流商品搜索,上衣,衬衫,TEE,卫衣,冲锋衣,风衣,羽绒服,裤子,休闲鞋,板鞋,配饰,复古眼镜';
res.render('guang/detail', Object.assign({
module: 'guang',
page: 'detail',
... ... @@ -305,13 +325,9 @@ exports.detail = (req, res, next) => {
commentFirstPageUrl: '?pageSize=10'
}
}, {
title: `${info.title} | YOHO!BUY有货`,
keywords: info.tag.length > 0 ? `${info.tag}` :
['Yoho! 有货,潮流,时尚,流行,购物,B2C,正品,购物网站,网上购物,货到付款,品牌服饰,男士护肤,',
'黑框眼镜,匡威,板鞋,i.t,izzue,5cm,eastpak,vans,lylescott,g-shock,new balance,lacoste,melissa,',
'casio,卡西欧手表,舒雅,jasonwood,odm,AAAA,香港购物,日本潮流'].join(''),
description: info.desc.length > 0 ? `${info.desc}` :
'潮流商品搜索,上衣,衬衫,TEE,卫衣,冲锋衣,风衣,羽绒服,裤子,休闲鞋,板鞋,配饰,复古眼镜',
title: title,
keywords: keywords,
description: description,
webNavHeader: channel
}));
});
... ...
... ... @@ -31,7 +31,7 @@ const showMain = (req, res, next) => {
saveInCookies: null
};
return service.showMainAsync(Object.assign({
return service.showMainAsync(req, Object.assign({
skn: skn,
channel: channel,
gender: gender
... ...
... ... @@ -9,6 +9,7 @@ const list = require(`${mRoot}/list`);
const listSeoMap = require(`${global.middleware}/seo/listSeoMap`);
const helpers = global.yoho.helpers;
const _ = require('lodash');
const tdk = require('../../../utils/getTDK');
// 搜索相关接口
const searchApi = require(`${mRoot}/search-api`);
... ... @@ -22,58 +23,71 @@ const shop = (shopId, req, res, next, brandInfo) => {
shopId = parseInt(shopId, 10);
Object.assign(params, {shopId: shopId});
list.getShopInfo(shopId, req.user.uid).then(shopInfo => {
let pjax = params._pjax;
// 获取不到店铺信息跳转至首页
if (!shopInfo || _.isEmpty(shopInfo)) {
return res.redirect(helpers.urlFormat('', null, ''));
}
// 比较品牌域名与店铺域名是否一致,不一致跳转至店铺域名
if (!pjax && shopInfo.domain && domain && domain !== _.toLower(shopInfo.domain)) {
res.redirect(helpers.urlFormat('', params, shopInfo.domain));
return;
tdk('shop', shopId, req).then(TDKObj => {
if (TDKObj[0]) {
req.tdk = {
title: TDKObj[1],
keywords: TDKObj[2],
description: TDKObj[3]
};
}
return list.getShopInfo(shopId, req.user.uid).then(shopInfo => {
let pjax = params._pjax;
if (+shopInfo.shopTemplateType === 2) { // 经典模板
// 获取不到店铺信息跳转至首页
if (!shopInfo || _.isEmpty(shopInfo)) {
return res.redirect(helpers.urlFormat('', null, ''));
}
if (pjax) {
list.getShopGoodsData(shopId, req.yoho.channel, params, shopInfo).then(result => {
Object.assign(result, {
shopId: shopId,
layout: false
});
res.render('list/goods-list', result);
}).catch(next);
// 比较品牌域名与店铺域名是否一致,不一致跳转至店铺域名
if (!pjax && shopInfo.domain && domain && domain !== _.toLower(shopInfo.domain)) {
res.redirect(helpers.urlFormat('', params, shopInfo.domain));
return;
}
list.getShopData(shopId, req.yoho.channel, params, shopInfo).then(result => {
Object.assign(result, {
page: 'shop',
shopId: shopId
});
// 店铺装修为空则不cache
if (!result.shopTopBanner) {
res.set('Cache-Control', 'no-cache');
if (+shopInfo.shopTemplateType === 2) { // 经典模板
if (pjax) {
list.getShopGoodsData(shopId, req.yoho.channel, params, shopInfo).then(result => {
Object.assign(result, {
shopId: shopId,
layout: false
});
res.render('list/goods-list', result);
}).catch(next);
return;
}
res.render('list/shop-index', result);
}).catch(next);
} else { // 基础模板
list.getBaseShopData(params, Object.assign({uid: req.user.uid}, brandInfo),
req.yoho.channel, shopId).then(result => {
Object.assign(result, {page: 'list'});
// 基础店铺装修为空则不cache
if (!result.brand || !result.brand.shopBanner) {
list.getShopData(shopId, req.yoho.channel, params, shopInfo).then(result => {
Object.assign(result, {
page: 'shop',
shopId: shopId
});
// 店铺装修为空则不cache
if (!result.shopTopBanner) {
res.set('Cache-Control', 'no-cache');
}
res.render('list/brand', result);
res.render('list/shop-index', result);
}).catch(next);
}
} else { // 基础模板
list.getBaseShopData(params, Object.assign({uid: req.user.uid}, brandInfo),
req.yoho.channel, shopId).then(result => {
Object.assign(result, {page: 'list'});
// 基础店铺装修为空则不cache
if (!result.brand || !result.brand.shopBanner) {
res.set('Cache-Control', 'no-cache');
}
res.render('list/brand', result);
}).catch(next);
}
});
}).catch(next);
};
/**
... ...
... ... @@ -35,6 +35,7 @@ const HeaderModel = require('../../../doraemon/models/header');
const BLANK_STR = ' ';
const BUNDLE_PRODUCE = 2; // 量贩
const BUNDLE_PACKAGE = 1; // 套餐
const tdk = require('../../../utils/getTDK');
// 展览票
const YOHOOD_TICKET = 51335912;
... ... @@ -1514,7 +1515,7 @@ const _removeSalePrice = (productInfo) => {
/**
* 获取某一个商品详情主页面
*/
const showMainAsync = (data) => {
const showMainAsync = (req, data) => {
return co(function * () {
// 获取商品基本信息
let productData = yield productAPI.getProductAsync({skn: data.skn}, data.uid, data.isStudent, data.vipLevel);
... ... @@ -1537,7 +1538,8 @@ const showMainAsync = (data) => {
_getSortNavAsync(smallSortId, data.gender), // 面包屑导航
HeaderModel.requestHeaderData(data.channel), // 通用头部数据
_getProductIntroAsync(productId, productSkn), // 商品详细介绍
curUserProduct(productData) // 商品详细价格
curUserProduct(productData), // 商品详细价格
tdk('skn', data.skn, req) // seo
]);
let smallSortNavigator = requestData[0];
... ... @@ -1545,6 +1547,14 @@ const showMainAsync = (data) => {
let productDescription = requestData[2];
let productInfo = requestData[3];
if (requestData[4][0]) {
req.tdk = {
title: requestData[4][1],
keywords: requestData[4][2],
description: requestData[4][3]
};
}
// 拼装数据
let result = {};
... ...
... ... @@ -17,18 +17,18 @@ module.exports = {
cookieDomain: '.yohobuy.com',
domains: {
// test3
singleApi: 'http://api-test3.yohops.com:9999/',
api: 'http://api-test3.yohops.com:9999/',
service: 'http://service-test3.yohops.com:9999/',
serviceNotify: 'http://service-test3.yohops.com:9999/',
global: 'http://global-test-soa.yohops.com:9999/',
// singleApi: 'http://api-test3.yohops.com:9999/',
// api: 'http://api-test3.yohops.com:9999/',
// service: 'http://service-test3.yohops.com:9999/',
// serviceNotify: 'http://service-test3.yohops.com:9999/',
// global: 'http://global-test-soa.yohops.com:9999/',
// prod
// singleApi: 'http://single.yoho.cn/',
// api: 'http://api.yoho.cn/',
// service: 'http://service.yoho.cn/',
// serviceNotify: 'http://service.yoho.cn/',
// global: 'http://api-global.yohobuy.com/',
singleApi: 'http://single.yoho.cn/',
api: 'http://api.yoho.cn/',
service: 'http://service.yoho.cn/',
serviceNotify: 'http://service.yoho.cn/',
global: 'http://api-global.yohobuy.com/',
// gray
// singleApi: 'http://single.gray.yohops.com/',
... ... @@ -131,7 +131,26 @@ module.exports = {
},
zookeeperServer: '192.168.102.168:2188',
maxQps: 1200,
sessionMemcachedPrefix: 'yohobuy_session:'
sessionMemcachedPrefix: 'yohobuy_session:',
redis: {
connect: {
host: '127.0.0.1',
port: '6379',
retry_strategy(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
console.log('redis连接不成功');
}
if (options.total_retry_time > 1000 * 60 * 60 * 6) {
console.log('redis连接超时');
return;
}
if (options.attempt > 10) {
return 1000 * 60 * 60 * 0.5;
}
return Math.min(options.attempt * 100, 1000);
}
}
}
};
if (isProduction) {
... ... @@ -165,7 +184,26 @@ if (isProduction) {
open: false,
url: 'http://123.206.2.55/strategy'
},
zookeeperServer: 'web.zookeeper.yohoops.org:2181'
zookeeperServer: 'web.zookeeper.yohoops.org:2181',
redis: {
connect: {
host: 'web.redis.yohoops.org'
},
port: '6379',
retry_strategy(options) {
if (options.error && options.error.code === 'ECONNREFUSED') {
console.log('redis连接不成功');
}
if (options.total_retry_time > 1000 * 60 * 60 * 6) {
console.log('redis连接超时');
return;
}
if (options.attempt > 10) {
return 1000 * 60 * 60 * 0.5;
}
return Math.min(options.attempt * 100, 1000);
}
}
});
} else if (isTest) {
Object.assign(module.exports, {
... ...
... ... @@ -74,7 +74,6 @@ module.exports = [
{
type: TYPE.rewrite,
origin: (req) => {
console.log(req.path);
return req.path === '/erp2goods';
},
target: '/common/erp2goods'
... ...
const redis = require('redis');
const bluebird = require('bluebird');
const config = require('../../config/common');
let client;
try {
client = redis.createClient(config.redis.connect);
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);
client.on('error', function() {
global.yoho.redis = '';
});
client.on('connect', function() {
global.yoho.redis = client;
});
} catch (e) {
global.yoho.redis = '';
}
module.exports = client;
... ...
... ... @@ -44,15 +44,15 @@
"passport-sina": "^0.1.0",
"passport-strategy": "1.x.x",
"passport-weixin": "^0.1.0",
"redis": "^2.7.1",
"request": "^2.81.0",
"request-ip": "^1.2.2",
"request-promise": "^3.0.0",
"serve-favicon": "^2.3.0",
"sitemap": "^1.12.0",
"urlencode": "^1.1.0",
"uuid": "^2.0.2",
"yoho-express-session": "^2.0.0",
"yoho-node-lib": "^0.2.23",
"yoho-node-lib": "^0.2.28",
"yoho-zookeeper": "^1.0.8"
},
"devDependencies": {
... ...
const md5 = require('md5');
const redis = global.yoho.redis;
const _ = require('lodash');
module.exports = (type, query, req) => {
query = type === 'url' ? md5(query) : query;
if (redis && _.get(req.app.locals.pc, 'ci.tdk', false)) {
let arr = [];
arr.push(redis.multi([
['exists', `tdk:${type}:${query}`],
['hmget', `tdk:${type}:${query}`, 'key', 'title', 'keywords', 'description']
]).execAsync());
arr.push(new Promise((resolve)=>{
setTimeout(resolve, 500, []);
}));
return Promise.race(arr).then(function(res) {
if (res.length) {
return res[1];
} else {
return [];
}
});
} else {
return Promise.resolve([]);
}
};
... ...