diff --git a/app.js b/app.js index c914d1d..ec96bdb 100644 --- a/app.js +++ b/app.js @@ -7,25 +7,33 @@ const config = require('./config/common'); +global.Promise = require('bluebird'); + +const yohoLib = require('yoho-node-lib'); + +// 全局注册library +yohoLib.global(config); + +const EventEmitter = require('events'); + +EventEmitter.defaultMaxListeners = 100; + const express = require('express'); +const compression = require('compression'); const path = require('path'); const bodyParser = require('body-parser'); const cookieParser = require('cookie-parser'); const favicon = require('serve-favicon'); -const session = require('yoho-express-session'); +const _ = require('lodash'); +const fp = require('lodash/fp'); -const memcached = require('connect-memcached'); -const hbs = require('express-handlebars'); +const session = require('cookie-session'); const pkg = require('./package.json'); - -const yohoLib = require('yoho-node-lib'); - const app = express(); +const helpers = global.yoho.helpers; -const MemcachedStore = memcached(session); - -// 全局注册library -yohoLib.global(config); +// NOTE: 这里修改了图片质量的参数 +helpers.image = _.flow(helpers.image, fp.replace(/\/quality\/\d*$/, '/quality/90')); global.middleware = path.resolve('./doraemon/middleware'); global.utils = path.resolve('./utils'); @@ -35,45 +43,34 @@ global.appRoot = path.resolve(__dirname); app.locals.devEnv = app.get('env') === 'development'; app.locals.version = pkg.version; +// zookeeper +if (config.zookeeperServer) { + require('yoho-zookeeper')(config.zookeeperServer, 'pc', app.locals.pc = {}); +} + app.set('subdomain offset', 2); -app.set('view engine', '.hbs'); -app.set('views', './doraemon/views'); -app.set('view cache', true); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', - layoutsDir: './doraemon/views', - partialsDir: './doraemon/views/partial', + layoutsDir: path.join(__dirname, 'doraemon/views'), + partialsDir: path.join(__dirname, 'doraemon/views/partial'), + views: path.join(__dirname, 'doraemon/views'), helpers: global.yoho.helpers })); +app.use(global.yoho.middleware()); + app.use(favicon(path.join(__dirname, '/public/favicon.ico'))); app.use(express.static(path.join(__dirname, 'public'))); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(cookieParser()); +app.use(compression()); app.use(session({ - proxy: true, - resave: false, - saveUninitialized: true, - unset: 'destroy', + name: 'yohobuy_session_cookie', secret: '82dd7e724f2c6870472c89dfa43cf48d', - name: 'yohobuy_session', - cookie: { - domain: 'yohobuy.com', - httpOnly: false - }, - - store: new MemcachedStore({ - hosts: config.memcache.session, - prefix: 'yohobuy_session:', - poolSize: 25, - reconnect: 5000, - timeout: 1000, - retries: 1, - retry: 3000 - }) + domain: config.cookieDomain })); app.use((req, res, next) => { @@ -88,6 +85,7 @@ const logger = global.yoho.logger; // dispatcher try { const subDomain = require('./doraemon/middleware/sub-domain'); + const mobileRefer = require('./doraemon/middleware/mobile-refer'); const itemNameHandler = require('./doraemon/middleware/item-name-handler'); const mobileCheck = require('./doraemon/middleware/mobile-check'); const user = require('./doraemon/middleware/user'); @@ -95,9 +93,11 @@ try { const setYohoData = require('./doraemon/middleware/set-yoho-data'); const errorHanlder = require('./doraemon/middleware/error-handler'); const setPageInfo = require('./doraemon/middleware/set-pageinfo'); + const pageCache = require('./doraemon/middleware/page-cache'); // YOHO 前置中间件 app.use(subDomain()); + app.use(mobileRefer()); app.use(itemNameHandler); app.use(mobileCheck()); app.use(setYohoData()); @@ -105,6 +105,7 @@ try { app.use(seo()); app.use(setPageInfo()); + app.use(pageCache()); require('./dispatch')(app); app.all('*', errorHanlder.notFound()); // 404 diff --git a/apps/activity/index.js b/apps/activity/index.js index f6d8ceb..8520cd0 100644 --- a/apps/activity/index.js +++ b/apps/activity/index.js @@ -5,8 +5,7 @@ */ var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); @@ -17,12 +16,12 @@ app.on('mount', function(parent) { delete parent.locals.settings; // 不继承父 App 的设置 Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/brands/controllers/brands.js b/apps/brands/controllers/brands.js index ccddc9a..48b749d 100644 --- a/apps/brands/controllers/brands.js +++ b/apps/brands/controllers/brands.js @@ -20,7 +20,10 @@ exports.index = (req, res, next) => { let channel = req.query.channel || req.cookies._Channel || 'boys'; brandsService.getBrandViewList(channel, req).then(result => { - + // 返回null,不cashe + if (result.noCashe) { + res.set('Cashe-Control', 'no-cashe'); + } res.render('brands/brands', result); }).catch(next); @@ -50,6 +53,10 @@ exports.brandInfo = (req, res, next) => { let brandId = req.query.brandId || 0; brandsService.brandInfo(brandId, req.user.uid).then(result => { + // 返回null,不cashe + if (result.noCashe) { + res.set('Cashe-Control', 'no-cashe'); + } res.json(result); }).catch(next); }; @@ -61,6 +68,10 @@ exports.plusstarList = (req, res, next) => { let channel = req.query.channel || req.cookies._Channel || 'boys'; brandsService.plusstarList(channel, req).then(result => { + // 返回null,不cashe + if (result.noCashe) { + res.set('Cashe-Control', 'no-cashe'); + } res.render('brands/plusstar', result); }).catch(next); }; diff --git a/apps/brands/index.js b/apps/brands/index.js index 90093ef..c28208e 100644 --- a/apps/brands/index.js +++ b/apps/brands/index.js @@ -5,8 +5,7 @@ */ var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); @@ -18,12 +17,12 @@ app.on('mount', function(parent) { Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/brands/models/brands-api.js b/apps/brands/models/brands-api.js index e666740..95c013f 100644 --- a/apps/brands/models/brands-api.js +++ b/apps/brands/models/brands-api.js @@ -6,6 +6,7 @@ 'use strict'; const api = global.yoho.API; const serviceAPI = global.yoho.ServiceAPI; +const config = global.yoho.config; /** * 分开取数,品牌一览 顶部的轮翻广告及热门品牌数据-PC @@ -15,7 +16,7 @@ const serviceAPI = global.yoho.ServiceAPI; const getBrandTopData = (contentCode) => { return serviceAPI.get('operations/api/v5/resource/get', { content_code: contentCode - }, {cache: 3600}); + }, config.apiCache); }; /** @@ -28,7 +29,7 @@ const getBrandListData = channel => { if (!isNaN(channel)) { params.yh_channel = channel; } - return api.get('', params); + return api.get('', params, config.apiCache); }; /** @@ -48,7 +49,7 @@ const getBrandIntro = (brandId, uid) => { method: 'app.brand.getBrandIntro', brand_id: brandId, uid: uid - }, param); + }, param, config.apiCache); }; /** @@ -61,7 +62,7 @@ const getProductByBrand = (brandId, limit) => { method: 'web.search.search', brand: brandId, limit: limit - }); + }, config.apiCache); }; /** @@ -74,7 +75,7 @@ const getBrandInfoByIds = (ids) => { return api.get('', { method: 'web.brand.info', ids: ids instanceof Array ? ids.join(',') : parseInt(ids, 10) - }, {cache: 3600}); + }, config.apiCache); }; /** @@ -89,7 +90,7 @@ const getPlusstarList = (brandType, gender) => { return serviceAPI.get('guang/api/v3/plustar/getlist', { gender: gender, brand_type: brandType - }, {cache: 3600}); + }, config.apiCache); }; module.exports = { diff --git a/apps/brands/models/brands-service.js b/apps/brands/models/brands-service.js index 8a3f6f5..744fcd7 100644 --- a/apps/brands/models/brands-service.js +++ b/apps/brands/models/brands-service.js @@ -101,13 +101,14 @@ exports.getBrandViewList = (channel) => { let apiMethod = [ headerModel.requestHeaderData(channel), brandsModel.getBrandViewTop(channel), - brandsModel.getBrandViewList(channel, 1, 5) // 分屏加载 + brandsModel.getBrandViewList(channel) // 分屏加载 ]; return api.all(apiMethod).then(result => { let responseData = { module: 'brands', - page: 'brands' + page: 'brands', + noCashe: false }; // 头部数据 @@ -135,6 +136,10 @@ exports.getBrandViewList = (channel) => { // SEO Object.assign(responseData, seoMap[channel]); + // 数据出错不cashe + if (_.isEmpty(result[0].headerData) || _.isEmpty(result[1]) || _.isEmpty(result[2])) { + responseData.noCashe = true; + } return responseData; }); }; @@ -168,11 +173,17 @@ exports.brandInfo = (brandId, uid) => { ]; return api.all(apiMethod).then(result => { - - return { + let responseData = { code: _.isEmpty(result[0]) ? 400 : 200, - brand: _.isEmpty(result[0]) ? '' : result[0] + brand: _.isEmpty(result[0]) ? '' : result[0], + noCashe: false }; + + // 数据出错不cashe + if (_.isEmpty(result[0])) { + responseData.noCashe = true; + } + return responseData; }); }; @@ -189,7 +200,8 @@ exports.plusstarList = (channel, req) => { let responseData = { module: 'brands', page: 'brands', - brandsHomePage: true + brandsHomePage: true, + noCashe: false }; let id = req.query.id || '', @@ -208,6 +220,10 @@ exports.plusstarList = (channel, req) => { let list = plustarList.data; + // 数据出错不cashe + if (_.isEmpty(headerData.headerData) || _.isEmpty(items) || _.isEmpty(list)) { + responseData.noCashe = true; + } let brandIds = [], brands = [], pageList = {}; @@ -232,6 +248,11 @@ exports.plusstarList = (channel, req) => { } brands.push(list[brandId]); }); + + // 数据出错不cashe + if (_.isEmpty(brandsInfo)) { + responseData.noCashe = true; + } } let data = { brandsHomePage: true, diff --git a/apps/brands/views/action/brands/brands.hbs b/apps/brands/views/action/brands/brands.hbs index 1beaac5..5fa2c92 100644 --- a/apps/brands/views/action/brands/brands.hbs +++ b/apps/brands/views/action/brands/brands.hbs @@ -48,7 +48,7 @@ {{/each}} </div> </div> - <div class="brands-list" > + <div class="brands-list" data-ishover="{{@root.pc.brands.disBrandNameHover}}"> {{> brand-list}} </div> {{/ brands}} diff --git a/apps/cart/index.js b/apps/cart/index.js index 25d93fd..c6dbe6d 100644 --- a/apps/cart/index.js +++ b/apps/cart/index.js @@ -7,25 +7,24 @@ 'use strict'; var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); // set view engin -var doraemon = path.join(__dirname, '../../doraemon/views'); //parent view root +var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root app.on('mount', function(parent) { delete parent.locals.settings; // 不继承父 App 的设置 Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/channel/controllers/index.js b/apps/channel/controllers/index.js index 36ee641..067f077 100644 --- a/apps/channel/controllers/index.js +++ b/apps/channel/controllers/index.js @@ -17,6 +17,11 @@ exports.index = (req, res, next) => { channelType === 'woman' ? channelType = 'girls' : null; channelModel.getContent(channelType).then(data => { + + // channel为空不缓存 + if (_.isEmpty(data.channel)) { + res.set('Cache-Control', 'no-cache'); + } res.render('channel', data); }).catch(next); }; diff --git a/apps/channel/index.js b/apps/channel/index.js index 4e48e31..3b7f544 100644 --- a/apps/channel/index.js +++ b/apps/channel/index.js @@ -5,14 +5,12 @@ */ var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); // set view engin var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root -var partials = path.join(__dirname, './views'); // parent view root app.on('mount', function(parent) { @@ -20,16 +18,15 @@ app.on('mount', function(parent) { Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [`${partials}/partial`, `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); - // router app.use(require('./router')); diff --git a/apps/channel/models/index.js b/apps/channel/models/index.js index 194f7d3..af6a49a 100644 --- a/apps/channel/models/index.js +++ b/apps/channel/models/index.js @@ -10,6 +10,7 @@ const _ = require('lodash'); const dataMap = require('../../../config/data-map'); const helpers = global.yoho.helpers; +const config = global.yoho.config; const processProduct = require(`${global.utils}/product-process`).processProduct; const searchApi = require('../../product/models/search-api'); @@ -335,7 +336,7 @@ const _getGirlsSingleHot = (args, type) => { page: 1, gender: channelMap[type].gender, limit: 60 - }); + }, config.apiCache); }; @@ -592,18 +593,18 @@ const _processFloorData = (rawData, type) => { let queryParam = ''; if (data.template_name === 'recommend_content_three' || - (data.template_intro === '焦点图' && index === 0)) { // 处理banner + (data.template_intro === '焦点图' && index === 0)) { // 处理banner floorData = floorMap.slide.call(null, data.data); } else if (data.template_intro === '热门品类') { // 处理热门品类 floorData = floorMap.hot.call(null, rawData.slice(index, index + hotCategoryLength), type); } else if (data.data && data.data.text) { // 处理一般楼层 let text = _getText(data.data.text); let lastIndex = index + bigFloorLength < rawData.length ? - index + bigFloorLength : index + (rawData.length - index - 1); + index + bigFloorLength : index + (rawData.length - index - 1); floorData = floorMap[text] && - floorMap[text].call(null, rawData.slice(index, lastIndex), type); + floorMap[text].call(null, rawData.slice(index, lastIndex), type); if (needQuery[text]) { queryParam = getQuery(rawData.slice(index, lastIndex)); @@ -657,9 +658,7 @@ const _processFloorData = (rawData, type) => { }; const _formatResourceParams = (channel, code) => { - return serviceApi.get('operations/api/v5/resource/get', {content_code: code}, { - cache: true - }).then(data => { + return serviceApi.get('operations/api/v5/resource/get', {content_code: code}, config.apiCache).then(data => { let result = data && data.data[0] && data.data[0].data[0]; if (result) { @@ -834,8 +833,6 @@ const getNewArrival = channel => { }; - - /** * 获取频道页数据 * @param {String} type 传入频道页类型,值可以是: boys, girls, kids, lifestyle @@ -956,9 +953,7 @@ const getIndexGuideData = () => { private_key: '0ed29744ed318fd28d2c07985d3ba633' }; - return serviceApi.get('operations/api/v6/category/getCategory', params, { - cache: true - }); + return serviceApi.get('operations/api/v6/category/getCategory', params, config.apiCache); }; module.exports = { diff --git a/apps/common/index.js b/apps/common/index.js index 84aaeb5..561895d 100644 --- a/apps/common/index.js +++ b/apps/common/index.js @@ -4,28 +4,15 @@ * @date: 2016/10/11 */ -var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); +var express = require('express'); var app = express(); -// set view engin -var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root - app.on('mount', function(parent) { delete parent.locals.settings; // 不继承父 App 的设置 Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ - extname: '.hbs', - defaultLayout: 'layout', - layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], - helpers: global.yoho.helpers -})); // router app.use(require('./router')); diff --git a/apps/guang/controllers/index.js b/apps/guang/controllers/index.js index caf5109..ff99a4c 100644 --- a/apps/guang/controllers/index.js +++ b/apps/guang/controllers/index.js @@ -26,16 +26,23 @@ exports.index = (req, res, next) => { let uid = req.user.uid; let udid = ghelper.getUdid(req, res); let gender = ghelper.getGenderByCookie(req); + let isHotDegrade = _.get(req.app.locals.pc, 'guang.removeHotTag', false); + let isAdDegrade = _.get(req.app.locals.pc, 'guang.removeAd', false); Promise.all([ guangModel.getBanner(channel), guangModel.getCategory(type, channel), guangModel.getArticleList(gender, type, uid, udid, page, '', '', pageSize, channel, true), - guangModel.getHotTags(page, pageSize, channel), - guangModel.getAds(channel), + guangModel.getHotTags(page, pageSize, channel, isHotDegrade), + guangModel.getAds(channel, isAdDegrade), guangModel.getRecoArticles(gender, 1, 10, channel), headerModel.requestHeaderData(channel) ]).then(ret => { + + if (_.isEmpty(ret[2]) || _.isEmpty(ret[2].msgs)) { + res.set('Cache-Control', 'no-cache'); + } + res.render('guang/index', { title: '逛' + (res.locals.title || ''), guang: { @@ -77,17 +84,23 @@ exports.tags = (req, res, next) => { let gender = ghelper.getGenderByCookie(req); let channel = req.yoho.channel; let pathNav = guangModel.getPathNav(channel, query); + let isHotDegrade = _.get(req.app.locals.pc, 'guang.removeHotTag', false); + let isAdDegrade = _.get(req.app.locals.pc, 'guang.removeAd', false); Promise.all([ guangModel.getBanner(channel), guangModel.getArticleList(gender, 0, uid, udid, page, query, '', pageSize, channel, true), - guangModel.getHotTags(1, 20, channel), - guangModel.getAds(channel), + guangModel.getHotTags(1, 20, channel, isHotDegrade), + guangModel.getAds(channel, isAdDegrade), guangModel.getRecoArticles(gender, 1, 10, channel), headerModel.requestHeaderData(channel) ]).then(ret => { + if (_.isEmpty(ret[1]) || _.isEmpty(ret[1].msgs)) { + res.set('Cache-Control', 'no-cache'); + } + res.render('guang/tag', { title: query + (res.locals.title || ''), guang: { @@ -121,6 +134,8 @@ exports.editor = (req, res, next) => { let authorId = req.query.author_id; let channel = req.yoho.channel; let gender = ghelper.getGenderByCookie(req); + let isHotDegrade = _.get(req.app.locals.pc, 'guang.removeHotTag', false); + let isAdDegrade = _.get(req.app.locals.pc, 'guang.removeAd', false); let uid = req.user.uid; let udid = ghelper.getUdid(req, res); @@ -133,11 +148,16 @@ exports.editor = (req, res, next) => { Promise.all([ guangModel.getAuthor(authorId), guangModel.getArticleList(gender, null, uid, udid, page, '', authorId, pageSize, channel, true), - guangModel.getHotTags(1, 20, channel), - guangModel.getAds(channel), + guangModel.getHotTags(1, 20, channel, isHotDegrade), + guangModel.getAds(channel, isAdDegrade), guangModel.getRecoArticles(gender, 1, 10, channel), headerModel.requestHeaderData(channel) ]).then(ret => { + + if (_.isEmpty(ret[1]) || _.isEmpty(ret[1].msgs)) { + res.set('Cache-Control', 'no-cache'); + } + res.render('guang/editor', { title: _.get(ret[0], 'name', '') + (res.locals.title || ''), guang: { @@ -175,9 +195,11 @@ exports.detail = (req, res, next) => { let udid = ghelper.getUdid(req, res); let gender = ghelper.getGenderByCookie(req); let channel = req.yoho.channel; + let isHotDegrade = _.get(req.app.locals.pc, 'guang.removeHotTag', false); + let isAdDegrade = _.get(req.app.locals.pc, 'guang.removeAd', false); if (!_.isNumber(id)) { - id = parseInt(id); + id = _.parseInt(id); } if (pjax) { @@ -212,12 +234,12 @@ exports.detail = (req, res, next) => { let promises = [ headerModel.requestHeaderData(channel), guangModel.getArticleContent(id), - guangModel.getHotTags(1, 20, channel), + guangModel.getHotTags(1, 20, channel, isHotDegrade), guangModel.getArticleComments(id, page, pageSize), guangModel.getArticleBaseInfo(id, uid, udid), guangModel.getArticleRelateBrand(id), guangModel.getRecoArticles(gender, 1, 10, channel), - guangModel.getAds(channel) + guangModel.getAds(channel, isAdDegrade) ]; if (info.authorId) { @@ -236,6 +258,10 @@ exports.detail = (req, res, next) => { Promise.all(promises).then(ret => { + if (_.isEmpty(ret[1])) { + res.set('Cache-Control', 'no-cache'); + } + res.render('guang/detail', Object.assign({ module: 'guang', page: 'detail', diff --git a/apps/guang/helpers/pager.js b/apps/guang/helpers/pager.js index 2de780e..cce5a11 100644 --- a/apps/guang/helpers/pager.js +++ b/apps/guang/helpers/pager.js @@ -44,7 +44,7 @@ exports.pager = function() { base = base.replace(clearSizeReg, ''); } - base += (base.indexOf('?') < 0 ? '?' : (base.charAt(base.length - 1) !== '?' ? '&' : '')) + + base += (base.indexOf('?') < 0 ? '?' : (/(\?|&)$/.test(base) ? '' : '&')) + (options.hash.pageSize ? (pageSizeVar + '=' + pageSize + '&') : '') + pageVar + '='; diff --git a/apps/guang/index.js b/apps/guang/index.js index 82d5844..5311f91 100644 --- a/apps/guang/index.js +++ b/apps/guang/index.js @@ -6,27 +6,24 @@ 'use strict'; var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); // set view engin var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root - var partials = path.join(__dirname, './views'); // parent view root - app.on('mount', function(parent) { delete parent.locals.settings; // 不继承父 App 的设置 Object.assign(app.locals, parent.locals); }); - app.set('views', path.join(__dirname, 'views/action')); - app.engine('.hbs', hbs({ + app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [`${partials}/partial`, `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: Object.assign(require('./helpers/pager'), global.yoho.helpers) })); diff --git a/apps/guang/models/index.js b/apps/guang/models/index.js index e056b7e..9af394a 100644 --- a/apps/guang/models/index.js +++ b/apps/guang/models/index.js @@ -121,7 +121,8 @@ const _formatArticle = (articleData, showTag, showAuthor, channel) => { let result = { id: articleData.id, classification: articleData.category_name, - isReco: articleData.is_recommended && Number(articleData.is_recommended) === 1 ? true : false, + + // isReco: articleData.is_recommended && Number(articleData.is_recommended) === 1 ? true : false, url: ghelper.getArticleUrl(articleData.url, articleData.id, channel), img: helpers.image(articleData.src, width, height, 1), isSquareImg: isSquareImage, @@ -133,6 +134,26 @@ const _formatArticle = (articleData, showTag, showAuthor, channel) => { comment: articleData.comment_num }; + if (showTag && articleData.category_id) { + switch (String(articleData.category_id)) { + case '1': // 话题 + result.isTopic = true; + break; + case '2': // 搭配 + result.isCollocation = true; + break; + case '3': // 潮人 + result.isFashionMan = true; + break; + case '4': // 潮品 + result.isFashionGood = true; + break; + case '19': // 专题 + result.isSpecialTopic = true; + break; + } + } + if (!articleData.author) { articleData.author = { name: '', @@ -305,7 +326,7 @@ const getArticleList = (gender, sortId, uid, udid, page, tag, authorId, limit, c * @param {String} type 传入频道页类型,值可以是: boys, girls, kids, lifestyle * @return {Object} */ -const getHotTags = (page, limit, channel) => { +const getHotTags = (page, limit, channel, isDegrade) => { let data = { client_type: 'web', @@ -313,6 +334,10 @@ const getHotTags = (page, limit, channel) => { limit: limit || 10 }; + if (isDegrade) { + return null; + } + return serviceApi.get('guang/api/v2/article/getTagTop', data, { cache: true }).then(res => { @@ -338,10 +363,14 @@ const getHotTags = (page, limit, channel) => { * @param {String} channelType 传入频道页类型,值可以是: boys, girls, kids, lifestyle * @return {Object} */ -const getAds = channelType => { +const getAds = (channelType, isDegrade) => { let contentCode = ADS_CODE[channelType] || ADS_CODE.boys; + if (isDegrade) { + return null; + } + return serviceApi.get(URL_OPERATIONS_RESOURCE_GET, { content_code: contentCode }, {cache: true}).then(res => { diff --git a/apps/guang/views/action/guang/detail.hbs b/apps/guang/views/action/guang/detail.hbs index 5004eec..e9d398e 100644 --- a/apps/guang/views/action/guang/detail.hbs +++ b/apps/guang/views/action/guang/detail.hbs @@ -130,18 +130,20 @@ </div> {{/if}} - <div id="comment-area" class="comment-area"> - <div class="comment-textarea"> - <textarea id="comment-info" placeholder="我有话要说。。。">{{commentInfo}}</textarea> - </div> - <div class="comment-publish clearfix"> - <span id="word-count-tip" class="word-count-tip"></span> - <a id="comment-btn" class="publish-btn disable">评论</a> - </div> - <div id="pjax-container" class="comments-wrap"> - {{> comment}} + {{#unless @root.pc.guang.removeItemComment}} + <div id="comment-area" class="comment-area"> + <div class="comment-textarea"> + <textarea id="comment-info" placeholder="我有话要说。。。">{{commentInfo}}</textarea> + </div> + <div class="comment-publish clearfix"> + <span id="word-count-tip" class="word-count-tip"></span> + <a id="comment-btn" class="publish-btn disable">评论</a> + </div> + <div id="pjax-container" class="comments-wrap"> + {{> comment}} + </div> </div> - </div> + {{/unless}} </div> <div class="right-side detail-side"> {{> right-side}} diff --git a/apps/guang/views/partial/msg.hbs b/apps/guang/views/partial/msg.hbs index 11fb04d..5e1d1b9 100644 --- a/apps/guang/views/partial/msg.hbs +++ b/apps/guang/views/partial/msg.hbs @@ -3,9 +3,21 @@ <div class="classification"> {{classification}} </div> - {{#if isReco}} - <div class="reco"></div> - {{/if}} + {{# isCollocation}} + <div class="type-icon collocation"></div> + {{/ isCollocation}} + {{# isFashionMan}} + <div class="type-icon fashion-man"></div> + {{/ isFashionMan}} + {{# isFashionGood}} + <div class="type-icon fashion-good"></div> + {{/ isFashionGood}} + {{# isTopic}} + <div class="type-icon topic"></div> + {{/ isTopic}} + {{# isSpecialTopic}} + <div class="type-icon special-topic"></div> + {{/ isSpecialTopic}} <a href="{{url}}" target="_blank"> <img class="lazy{{#if isSquareImg}} square{{/if}}" data-original="{{img}}"> </a> @@ -46,4 +58,4 @@ </div> </div> </div> -</div> \ No newline at end of file +</div> diff --git a/apps/guang/views/partial/right-side.hbs b/apps/guang/views/partial/right-side.hbs index fff0cbe..8063196 100644 --- a/apps/guang/views/partial/right-side.hbs +++ b/apps/guang/views/partial/right-side.hbs @@ -14,16 +14,18 @@ </div> </div> -<div class="hot"> - <h1 class="hot-title">热门标签</h1> - <div class="hot-tag-list"> - {{#hotTags}} - <a class="hot-tag" href="{{url}}" target="_blank"> - {{tagName}} - </a> - {{/hotTags}} +{{#unless @root.pc.guang.removeHotTag}} + <div class="hot"> + <h1 class="hot-title">热门标签</h1> + <div class="hot-tag-list"> + {{#hotTags}} + <a class="hot-tag" href="{{url}}" target="_blank"> + {{tagName}} + </a> + {{/hotTags}} + </div> </div> -</div> +{{/unless}} <div class="ads"> {{# ads}} @@ -31,4 +33,4 @@ <img class="lazy" data-original="{{img}}"> </a> {{/ ads}} -</div> \ No newline at end of file +</div> diff --git a/apps/home/index.js b/apps/home/index.js index 7dcd7b7..e112dc9 100644 --- a/apps/home/index.js +++ b/apps/home/index.js @@ -5,8 +5,7 @@ */ var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); @@ -17,12 +16,13 @@ app.on('mount', function(parent) { delete parent.locals.settings; // 不继承父 App 的设置 Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ + +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/passport/auth.js b/apps/passport/auth.js index 88a95ff..c092451 100644 --- a/apps/passport/auth.js +++ b/apps/passport/auth.js @@ -15,7 +15,7 @@ const DoubanStrategy = require('passport-douban').Strategy; const RenrenStrategy = require('passport-renren').Strategy; const AlipayStrategy = require('./models/passport-alipay').Strategy; -const AuthHelper = require('./models/auth-helper'); +const LoginApi = require('./models/login-service'); const config = global.yoho.config; const helpers = global.yoho.helpers; @@ -26,6 +26,16 @@ const cache = global.yoho.cache; let siteUrl = config.siteUrl.indexOf('//') === 0 ? 'http:' + config.siteUrl : config.siteUrl; +function getLoginStat(account) { + let errorLoginKey = 'account_errorlogin_' + account; + let accountKey = 'account_signin_' + account; + let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey)]; + + return Promise.all(cacheGet).catch(() => { + return [0, 0]; + }); +} + // 本地登录 passport.use('local', new LocalStrategy({ usernameField: 'account', @@ -54,25 +64,23 @@ passport.use('local', new LocalStrategy({ let errorLoginKey = 'account_errorlogin_' + account; let accountKey = 'account_signin_' + account; - let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey)]; - - Promise.all(cacheGet).then(times => { + getLoginStat(account).then(times => { let errLoginTimes = _.parseInt(times[0]) || 0; let accountTimes = _.parseInt(times[1]) || 0; if (accountTimes >= 10) { done({message: '您的账号已被暂时锁定,请稍后再试'}, null); } else { - return AuthHelper.signin(type, area, username, password, shoppingKey).then((result) => { + return LoginApi.signin(type, area, username, password, shoppingKey).then((result) => { if (result.code && result.code === 200 && result.data.uid) { - cache.del(errorLoginKey); + cache.del(errorLoginKey).catch(() => {}); done(null, result.data); } else { errLoginTimes = errLoginTimes + 1; accountTimes = accountTimes + 1; - cache.set(errorLoginKey, errLoginTimes); - cache.set(accountKey, accountTimes, 1800); + cache.set(errorLoginKey, errLoginTimes).catch(() => {}); + cache.set(accountKey, accountTimes, 1800).catch(() => {}); // 再次校验 if (accountTimes >= 10) { @@ -116,8 +124,8 @@ passport.use('wechat', new WeixinStrategy({ // sina 登录 passport.use('sina', new SinaStrategy({ - clientID: '3739328910', - clientSecret: '9d44cded26d048e23089e5e975c93df1', + clientID: config.thirdLogin.sina.appID, + clientSecret: config.thirdLogin.sina.appSecret, callbackURL: `${siteUrl}/passport/login/sina/callback`, requireState: false }, (accessToken, refreshToken, profile, done) => { @@ -126,8 +134,8 @@ passport.use('sina', new SinaStrategy({ // qq 登录 passport.use('qq', new QQStrategy({ - clientID: '100229394', - clientSecret: 'c0af9c29e0900813028c2ccb42021792', + clientID: config.thirdLogin.qq.appID, + clientSecret: config.thirdLogin.qq.appSecret, callbackURL: `${siteUrl}/passport/login/qq/callback`, requireState: false }, (accessToken, refreshToken, profile, done) => { @@ -136,8 +144,8 @@ passport.use('qq', new QQStrategy({ // alipay 登录 passport.use('alipay', new AlipayStrategy({ - partner: '2088701661478015', - key: 'kcxawi9bb07mzh0aq2wcirsf9znusobw', + partner: config.thirdLogin.alipay.appID, + key: config.thirdLogin.alipay.appSecret, return_url: `${siteUrl}/passport/login/alipay/callback` }, (profile, done) => { done(null, profile); @@ -145,8 +153,8 @@ passport.use('alipay', new AlipayStrategy({ // douban 登录 passport.use('douban', new DoubanStrategy({ - clientID: '03b4e36bf13dc75a0b1eaa43d3b9560e', - clientSecret: 'f16d5913e8610672', + clientID: config.thirdLogin.douban.appID, + clientSecret: config.thirdLogin.douban.appSecret, callbackURL: `${siteUrl}/passport/autosign/doubanback` }, (accessToken, refreshToken, profile, done) => { done(null, profile); @@ -154,8 +162,8 @@ passport.use('douban', new DoubanStrategy({ // renren 登录 passport.use('renren', new RenrenStrategy({ - clientID: '783130c654c94a77ace97054ae266019', - clientSecret: '05e430de8c1e40d3a1f39ca8d3f8252c', + clientID: config.thirdLogin.renren.appID, + clientSecret: config.thirdLogin.renren.appSecret, callbackURL: `${siteUrl}/passport/login/renren/callback` }, (accessToken, refreshToken, profile, done) => { done(null, profile); diff --git a/apps/passport/controllers/back.js b/apps/passport/controllers/back.js index 9c33d6a..5ebe3a9 100644 --- a/apps/passport/controllers/back.js +++ b/apps/passport/controllers/back.js @@ -6,6 +6,7 @@ 'use strict'; const helpers = global.yoho.helpers; +const config = global.yoho.config; const service = require('../models/back-service'); const passportHelper = require('../models/passport-helper'); const simpleHeaderModel = require('../../../doraemon/models/simple-header'); @@ -17,12 +18,15 @@ const _ = require('lodash'); const index = (req, res, next) => { // 清除cookie res.clearCookie('_UID', { - domain: 'yohobuy.com' + domain: config.cookieDomain }); res.clearCookie('_TOKEN', { - domain: 'yohobuy.com' + domain: config.cookieDomain }); + // 清除 session + req.session = null; + service.indexPageDataAsync() .then(result => { res.render('back/index', Object.assign({ @@ -102,21 +106,18 @@ const sendCodePage = (req, res, next) => { const saveInSession = (req, res) => { switch (req.inputInfo.type) { - case 'email': { + case 'email': req.session.email = req.inputInfo.phone; res.redirect(helpers.urlFormat('/passport/back/sendEmail')); break; - } - case 'mobile': { + case 'mobile': req.session.mobile = req.inputInfo.phone; req.session.area = req.inputInfo.area; req.session.verifyCode = req.session.captcha; res.redirect(helpers.urlFormat('/passport/back/verification')); break; - } - default: { + default: res.redirect(helpers.urlFormat('/passport/back/index')); - } } }; @@ -133,10 +134,9 @@ const sendBackMobileAPI = (req, res, next) => { const validateMobileAPI = (req, res, next) => { let mobile = req.body.mobile || ''; - let area = req.body.area || '86'; const ERR = {code: 400, message: '验证失败'}; - if (!passportHelper.validator.isAreaMobile(passportHelper.makeAreaMobile(area, mobile))) { + if (!passportHelper.validator.isMobile(mobile)) { return res.json(ERR); } diff --git a/apps/passport/controllers/bind.js b/apps/passport/controllers/bind.js index c894829..b6d925c 100644 --- a/apps/passport/controllers/bind.js +++ b/apps/passport/controllers/bind.js @@ -8,9 +8,10 @@ const _ = require('lodash'); const helpers = global.yoho.helpers; -const PassportHelper = require('../models/passport-helper'); -const BindService = require('../models/bind-service'); -const AuthHelper = require('../models/auth-helper'); +const passportHelper = require('../models/passport-helper'); +const bindService = require('../models/bind-service'); +const loginService = require('../models/login-service'); +const userService = require('../models/user-service'); // const UserService = require('../models/user-service'); const simpleHeaderModel = require('../../../doraemon/models/simple-header'); @@ -33,7 +34,7 @@ const bind = { thirdLogin: true, openId: openId, sourceType: sourceType, - region: PassportHelper.getCountry(), + region: passportHelper.getCountry(), serviceUrl: helpers.urlFormat('/help', { category_id: 9 }), @@ -80,7 +81,7 @@ const bind = { // } // }) - PassportHelper.getUserInfo(area, mobile).then(user => { + userService.getUserInfo(area, mobile).then(user => { let data = _.assign(user, { phoneNum: mobile, areaCode: area, @@ -121,49 +122,59 @@ const bind = { let sourceType = req.body.sourceType; if (mobile && openId && area && sourceType) { - BindService.bindCheck(mobile, openId, sourceType, area).then(result => { + + /** + * 接口绑定返回值: + * code:200,is_register=0 // 绑定流程:未注册,可绑定 + * code:200,is_register=1 // 绑定流程:已注册绑定过其他第三方 + * code:200:is_register=3 // 关联流程 + * code:505 // 手机号码注册过,而且该第三方也已经绑定过手机号 + * code:506 // 手机号码注册过,而且该手机号码也已经绑定过该类型第三方 + */ + + bindService.bindCheck(mobile, openId, sourceType, area).then(result => { if (!result || !result.code) { - return { code: 400, message: '', data: '' }; + return {code: 400, message: '', data: ''}; } else if (result.code === 200 && result.data.is_register === 0) { let nextUrl = helpers.urlFormat('/passport/thirdlogin/noregist'); // 绑定流程:code=200 未注册,可绑定 - return { code: 200, message: result.message, data: { next: nextUrl } }; + return {code: 200, message: result.message, data: {next: nextUrl}}; } else if (result.code === 200 && result.data.is_register === 1) { - return PassportHelper.getUserInfo(area, mobile).then(user => { + return userService.getUserInfo(area, mobile).then(user => { // 绑定流程:code=201 已注册 绑定过其他第三方 - return {code: 201, message: result.message, data: { user: user } }; + return {code: 201, message: result.message, data: {user: user}}; }); } else if (result.code === 200 && result.data.is_register === 3) { let nextUrl = helpers.urlFormat('/passport/thirdlogin/relate'); // 关联流程 - return { code: 203, message: result.message, data: { next: nextUrl } }; + return {code: 203, message: result.message, data: {next: nextUrl}}; } else if (result.code === 506 || result.code === 505) { - return PassportHelper.getUserInfo(area, mobile).then(user => { + return userService.getUserInfo(area, mobile).then(user => { // 绑定流程:code=506 手机号码注册过,而且该手机号码也已经绑定过该类型第三方 // code=505 手机号码注册过,而且该第三方也已经绑定过手机号 - return { code: 205, message: result.message, data: { user: user } }; + return {code: 205, message: result.message, data: {user: user}}; }); } else { - return { code: result.code, message: result.message, data: result.data ? result.data : '' }; + return {code: result.code, message: result.message, data: result.data ? result.data : ''}; } }).then(result => { - res.json(result); + return res.json(result); }).catch(next); } else { - res.json({ code: 400, message: '', data: '' }); + return res.json({code: 400, message: '', data: ''}); } }, sendBindMsg: (req, res, next) => { let mobile = req.body.mobile; let area = req.body.area; - BindService.sendBindMsg(area, mobile).then(result => { + bindService.sendBindMsg(area, mobile).then(result => { if (result && result.code) { - res.json(result); + return res.json(result); } else { - res.json({ code: 400, message: '', data: '' }); + return res.json({code: 400, message: '', data: ''}); } }).catch(next); }, @@ -172,11 +183,11 @@ const bind = { let area = req.body.area; let code = req.body.code; - BindService.checkBindCode(area, mobile, code).then(result => { + bindService.checkBindCode(area, mobile, code).then(result => { if (result && result.code) { - res.json(result); + return res.json(result); } else { - res.json({ code: 400, message: '', data: '' }); + return res.json({code: 400, message: '', data: ''}); } }).catch(next); }, @@ -188,11 +199,11 @@ const bind = { let code = _.trim(req.body.code); let password = _.trim(req.body.password) || ''; - BindService.checkBindCode(area, mobile, code).then(result => { + bindService.checkBindCode(area, mobile, code).then(result => { if (result && result.code !== 200) { - return { code: 402, message: '短信验证码不正确', data: '' }; + return {code: 402, message: '短信验证码不正确', data: ''}; } else { - return BindService.bindMobile(openId, sourceType, mobile, area, password); + return bindService.bindMobile(openId, sourceType, mobile, area, password); } }).then(result => { if (result && result.code) { @@ -201,17 +212,17 @@ const bind = { sourceType: sourceType + '_bind' }); - return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => { - return { code: 200, message: result.message, data: {refer: refer }}; + return loginService.syncUserSession(result.data.uid, req, res).then(() => { + return {code: 200, message: result.message, data: {refer: refer}}; }); } else { - return { code: result.code, message: result.message, data: { refer: ''} }; + return {code: result.code, message: result.message, data: {refer: ''}}; } } else { return {code: 400, message: '', data: ''}; } }).then(result => { - res.json(result); + return res.json(result); }).catch(next); }, relateMobile: (req, res, next) => { @@ -221,12 +232,12 @@ const bind = { let sourceType = req.body.sourceType; let code = req.body.code; - if (_.isNumber(parseInt(mobile, 0)) && openId && areaCode && sourceType && code) { - BindService.checkBindCode(areaCode, mobile, code).then(result => { + if (_.toNumber(mobile) && openId && areaCode && sourceType && code) { + bindService.checkBindCode(areaCode, mobile, code).then(result => { if (result && result.code && result.code === 200) { - return BindService.relateMobile(openId, sourceType, mobile, areaCode); + return bindService.relateMobile(openId, sourceType, mobile, areaCode); } else { - return { code: 402, message: '短信验证码错误', data: '' }; + return {code: 402, message: '短信验证码错误', data: ''}; } }).then(result => { if (result && result.code) { @@ -235,20 +246,20 @@ const bind = { sourceType: sourceType + '_relate' }); - return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => { - return { code: 200, message: result.message, data: {refer: refer }}; + return loginService.syncUserSession(result.data.uid, req, res).then(() => { + return {code: 200, message: result.message, data: {refer: refer}}; }); } else { - return { code: result.code, message: result.message, data: { refer: ''} }; + return {code: result.code, message: result.message, data: {refer: ''}}; } } else { return {code: 400, message: '', data: ''}; } }).then(result => { - res.json(result); + return res.json(result); }).catch(next); } else { - res.json({ code: 400, message: '', data: '' }); + return res.json({code: 400, message: '', data: ''}); } } }; diff --git a/apps/passport/controllers/login.js b/apps/passport/controllers/login.js index 0905150..6e38169 100644 --- a/apps/passport/controllers/login.js +++ b/apps/passport/controllers/login.js @@ -6,7 +6,7 @@ 'use strict'; const _ = require('lodash'); -const Fp = require('lodash/fp'); +const Fn = require('lodash/fp'); const Promise = require('bluebird'); const qs = require('querystring'); @@ -17,7 +17,7 @@ const helpers = global.yoho.helpers; const log = global.yoho.logger; const config = global.yoho.config; const cache = global.yoho.cache; -const AuthHelper = require('../models/auth-helper'); +const loginService = require('../models/login-service'); const PassportHelper = require('../models/passport-helper'); const simpleHeaderModel = require('../../../doraemon/models/simple-header'); const loginPage = `${config.siteUrl}/signin.html`; @@ -40,8 +40,9 @@ function doPassportCallback(req, res, user) { user.openId = user.unionId; } - return AuthHelper.signinByOpenID(user.nickname, user.openId, user.sourceType, shoppingKey) + return loginService.signinByOpenID(user.nickname, user.openId, user.sourceType, shoppingKey) .then((result) => { + console.log(result); if (result.code !== 200) { return res.redirect(config.siteUrl); } @@ -51,8 +52,8 @@ function doPassportCallback(req, res, user) { sourceType: user.sourceType, refer: refer }); - } else if (result.code === 200 && result.data.uid) { - return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => { + } else if (result.data['is_bind'] === 'Y' && result.data.uid) {//eslint-disable-line + return loginService.syncUserSession(result.data.uid, req, res, result.data.session_key).then(() => { return refer; }); } @@ -70,7 +71,7 @@ const common = { let refer = req.query.refer || req.get('Referer'); refer && res.cookie('refer', encodeURI(refer), { - domain: '.yohobuy.com' + domain: config.cookieDomain }); next(); }, @@ -86,10 +87,10 @@ const common = { if (!isNaN(errloginTimes) && errloginTimes >= 3) { result.data = {needCaptcha: true}; } - res.json(result); + return res.json(result); }).catch(next); } else { - res.json(result); + return res.json(result); } } }; @@ -98,12 +99,14 @@ const local = { loginPage: (req, res) => { // 清除cookie res.clearCookie('_UID', { - domain: 'yohobuy.com' + domain: config.cookieDomain }); res.clearCookie('_TOKEN', { - domain: 'yohobuy.com' + domain: config.cookieDomain }); + req.session = null; + let bindMobile = _.trim(req.query.bindMobile || ''); let bindArea = '+' + _.trim(req.query.bindArea || '86'); let areaArr = PassportHelper.getCountry(); @@ -148,52 +151,52 @@ const local = { login: (req, res, next) => { passport.authenticate('local', (err, user) => { if (err) { - res.json({ + return res.json({ code: 400, message: err.message, data: { needCaptcha: err.needCaptcha } }); - } else { - if (_.get(req, 'body.isRemember', false)) { - AuthHelper.rememberAccount({ - area: req.body.areaCode || '86', - account: req.body.account, - password: req.body.password - }, req, res); + } + + if (_.get(req.body, 'isRemember', 'false') === 'true') { + loginService.rememberAccount({ + area: req.body.areaCode || '86', + account: req.body.account, + password: req.body.password + }, req, res); + } + + let refer = (function() { + if (/sign|login|reg|passport/.test(_.get(req.cookies, 'refer', ''))) { + return `${config.siteUrl}/home`; + } else if (_.get(req.cookies, 'refer')) { + return decodeURI(req.cookies.refer); + } else { + return `${config.siteUrl}/home`; } + }()); - let refer = (function() { - if (/sign|login|reg|passport/.test(_.get(req, 'cookies.refer', ''))) { - return `${config.siteUrl}/home`; - } else if (_.get(req, 'cookies.refer')) { - return decodeURI(req.cookies.refer); - } else { - return `${config.siteUrl}/home`; + loginService.syncUserSession(user.uid, req, res, user.session_key).then(() => { + return res.json({ + code: 200, + data: { + session: refer, + href: refer } - }()); - - AuthHelper.syncUserSession(user.uid, req, res).then(() => { - res.json({ - code: 200, - data: { - session: refer, - href: refer - } - }); }); - } + }); })(req, res, next); }, logout: (req, res) => { req.session = null; res.clearCookie('_UID', { - domain: 'yohobuy.com' + domain: config.cookieDomain }); - res.clearCookie('_TOKEN', { - domain: 'yohobuy.com' + res.clearCookie('TOKEN_', { + domain: config.cookieDomain }); res.clearCookie('_SPK'); res.clearCookie('_g'); @@ -201,7 +204,7 @@ const local = { res.clearCookie('remem'); let refer = req.get('Referer') || config.siteUrl; - res.redirect(refer); + return res.redirect(refer); }, sms: { send: (req, res, next) => { @@ -215,7 +218,7 @@ const local = { }); } - AuthHelper.sendPasswordBySMS(area, mobile).then((result) => { + loginService.sendPasswordBySMS(area, mobile).then((result) => { return res.json(result); }).catch(next); }, @@ -231,7 +234,7 @@ const local = { }); } - AuthHelper.verifyPasswordBySMS(area, mobile, code).then((result) => { + loginService.verifyPasswordBySMS(area, mobile, code).then((result) => { if (_.get(result, 'code', 200) !== 200) { return Promise.reject('error'); } @@ -261,18 +264,18 @@ const local = { }); } - AuthHelper.checkUserExitBySMS(area, mobile).then((result) => { + loginService.checkUserExitBySMS(area, mobile).then((result) => { if (_.get(result, 'code') !== 200) { return Promise.reject('check user by sms error'); } if (_.get(result, 'data.is_register', 'N') === 'Y') { - res.json({ + return res.json({ code: 200, message: '用户已注册' }); } else { - res.json({ + return res.json({ code: 402, message: '手机号尚未注册,请核对后重新输入或<a href="/reg.html?' + qs.stringify({mobile: mobile, area: (area ? '+' + area : '+86') || '+86'}) + @@ -284,7 +287,7 @@ const local = { }, qrcode: { refresh: (req, res, next) => { - AuthHelper.fetchByQrCode().then((result) => { + loginService.fetchByQrCode().then((result) => { if (result.code === 200) { return res.json({ code: 200, @@ -319,7 +322,7 @@ const local = { let token = qrcode.substring(qrcode.indexOf('=') + 1); - AuthHelper.checkByQrCode(token).then((result) => { + loginService.checkByQrCode(token).then((result) => { if (_.isEmpty(result)) { return res.json({ code: 400, @@ -366,7 +369,6 @@ const local = { const wechat = { login: (req, res, next) => { - req.session = req.session || {}; req.session.authState = uuid.v4(); return passport.authenticate('wechat', { state: req.session.authState @@ -389,14 +391,13 @@ const wechat = { } })(req, res, next); } else { - return next(new Error('Auth State Mismatch')); + return next(new Error('Wechat Auth State Mismatch')); } } }; const sina = { login: (req, res, next) => { - req.session = req.session || {}; req.session.authState = uuid.v4(); return passport.authenticate('sina', { state: req.session.authState @@ -419,15 +420,13 @@ const sina = { }).catch(next); })(req, res, next); } else { - return next(new Error('Auth State Mismatch')); + return next(new Error('Sina Auth State Mismatch')); } } }; const qq = { login: (req, res, next) => { - req.session = req.session || {}; - let type = req.query.type || 'yohobuy'; let authState = req.session.authState = (req.query.state || uuid.v4()) + '::' + type; @@ -436,8 +435,8 @@ const qq = { })(req, res, next); }, callback: (req, res, next) => { - let auth = Fp.compose(Fp.head, Fp.split('::'))(req.query.state); - let type = Fp.compose(Fp.nth(1), Fp.split('::'))(req.query.state); + let auth = Fn.compose(Fn.head, Fn.split('::'))(req.query.state); + let type = Fn.compose(Fn.nth(1), Fn.split('::'))(req.query.state); if (req.session && req.session.authState && req.session.authState === req.query.state) { passport.authenticate('qq', (err, user) => { @@ -460,7 +459,7 @@ const qq = { } })(req, res, next); } else { - return next(new Error('Auth State Mismatch')); + return next(new Error('QQ Auth State Mismatch')); } } }; @@ -489,7 +488,6 @@ const alipay = { const douban = { login: (req, res, next) => { - req.session = req.session || {}; req.session.authState = uuid.v4(); return passport.authenticate('douban', { state: req.session.authState @@ -516,7 +514,6 @@ const douban = { const renren = { login: (req, res, next) => { - req.session = req.session || {}; req.session.authState = uuid.v4(); return passport.authenticate('renren', { state: req.session.authState diff --git a/apps/passport/controllers/reg.js b/apps/passport/controllers/reg.js index 92dbef3..dc2f8e5 100644 --- a/apps/passport/controllers/reg.js +++ b/apps/passport/controllers/reg.js @@ -7,7 +7,7 @@ const Promise = require('bluebird'); const passportHelper = require('../models/passport-helper'); const regService = require('../models/reg-service'); const userService = require('../models/user-service'); -const authHelper = require('../models/auth-helper'); +const loginService = require('../models/login-service'); const config = require('../../../config/common'); const simpleHeaderModel = require('../../../doraemon/models/simple-header'); let helpers = global.yoho.helpers; @@ -33,10 +33,10 @@ let checkCode = (req, res, next) => { * 检查手机格式 */ let checkMobileMiddleware = (req, res, next) => { - let mobile = +req.body.mobile; - let area = +req.body.area; + let mobile = req.body.mobile; + let area = req.body.area; - if (!_.isNumber(mobile) || !_.isNumber(area)) { + if (!(_.toNumber(mobile) && _.toNumber(area))) { return res.json({ code: 400, message: '手机号码格式不正确' @@ -66,7 +66,7 @@ let checkPassword = (req, res, next) => { */ let index = (req, res, next) => { // 设置注册有效时间30分钟, 防机器刷 - req.session._REG_EXPIRE = Date.now() + 1800000; + req.session.REG_EXPIRE_ = Date.now() + 1800000; let refer = req.query.refer; let mobile = req.query.mobile; @@ -106,11 +106,11 @@ let checkMobile = (req, res, next) => { let data = { code: 400 }; - let mobile = +req.body.mobile; - let area = +req.body.area; + let mobile = req.body.mobile; + let area = req.body.area || '86'; // 判断手机号是否检查超过指定次数 - let regCheckKey = 'regCheckMobileNum_' + passportHelper.makeAreaMobile(area, mobile); + let regCheckKey = `regCheckMobileNum_${area}-${mobile}`; cache.get(regCheckKey).then((checkNum) => { checkNum = +(checkNum || 0); @@ -168,8 +168,8 @@ let sendBindMsg = (req, res, next) => { data: '' }; - let mobile = +req.body.mobile; - let area = +req.body.area; + let mobile = req.body.mobile; + let area = req.body.area; // 校验是否发送过多 let sendCodeKey = `send_code_${area}_${mobile}`; @@ -204,9 +204,9 @@ let msgCaptcha = (req, res, next) => { message: '', data: '' }; - let area = +req.body.area; - let mobile = +req.body.mobile; - let code = +req.body.code; // 短信验证码 + let area = req.body.area; + let mobile = req.body.mobile; + let code = req.body.code; // 短信验证码 regService.validMobileCode(area, mobile, code).then((result) => { if (result.code) { @@ -230,7 +230,7 @@ let mobileRegister = (req, res, next) => { }; /* 判断是否是有效的注册方式,防注册机刷 */ - let regExpireTime = req.session._REG_EXPIRE; + let regExpireTime = req.session.REG_EXPIRE_; if (!regExpireTime || regExpireTime < Date.now()) { data.message = '注册超时'; @@ -254,9 +254,9 @@ let mobileRegister = (req, res, next) => { yield cache.set(ipKey, ipTimes + 1, 3600); - let area = +req.body.area; - let mobile = +req.body.mobile; - let code = +req.body.code; // 短信验证码 + let area = req.body.area; + let mobile = req.body.mobile; + let code = req.body.code; // 短信验证码 let password = req.body.password; let result = yield regService.validMobileCode(area, mobile, code); // 验证注册的标识码是否有效 @@ -273,7 +273,7 @@ let mobileRegister = (req, res, next) => { return res.json(data); } - return authHelper.syncUserSession(regResult.data.uid, req, res).then(() => { + return loginService.syncUserSession(regResult.data.uid, req, res).then(() => { return res.json({ code: 200, message: '注册成功', diff --git a/apps/passport/index.js b/apps/passport/index.js index 3d6258a..0172f45 100644 --- a/apps/passport/index.js +++ b/apps/passport/index.js @@ -5,8 +5,7 @@ */ 'use strict'; var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var passport = require('passport'); @@ -20,12 +19,12 @@ app.on('mount', function(parent) { Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/passport/models/back-service.js b/apps/passport/models/back-service.js index e23a7b3..0f04cac 100644 --- a/apps/passport/models/back-service.js +++ b/apps/passport/models/back-service.js @@ -24,13 +24,13 @@ const validateEmailOrMobileAsync = (userInput, areaCode) => { return new Promise(function(resolve, rejected) { let result = {type: 'email', area: '', phone: ''}; - if (passportHelper.validator.verifyEmail(userInput)) { + if (passportHelper.validator.isEmail(userInput)) { result.type = 'email'; result.area = ''; result.phone = userInput; resolve(result); - } else if (passportHelper.validator.isAreaMobile(passportHelper.makeAreaMobile(areaCode, userInput))) { + } else if (passportHelper.validator.isMobile(userInput)) { result.type = 'mobile'; result.area = areaCode; result.phone = userInput; @@ -57,7 +57,7 @@ const findUserAsync = (type, phone, area) => { const findBy = { email: userService.findByEmailAsync, - mobile: (phone1, area1) => userService.findByMobileAsync(area1, phone1) // 交换参数 + mobile: _.rearg(userService.findByMobileAsync, [1, 0]) // 交换参数 }; const OK = {code: 200, message: MESSAGE.ok}; diff --git a/apps/passport/models/bind-api.js b/apps/passport/models/bind-api.js new file mode 100644 index 0000000..c6623af --- /dev/null +++ b/apps/passport/models/bind-api.js @@ -0,0 +1,99 @@ +/** + * 注册数据接口 + * + * @author JiangFeng<jeff.jiang@yoho.cn> + * @date 2016/06/21 + */ + +'use strict'; + +const api = global.yoho.API; + +const bindCheck = (mobile, openId, sourceType, area) => { + let params = { + method: 'app.passport.signCheck', + area: area, + mobile: mobile, + open_id: openId, + source_type: sourceType + }; + + return api.get('', params); +}; + +const sendBindMsg = (area, mobile) => { + let params = { + method: 'app.passport.smsbind', + mobile: mobile, + area: area + }; + + return api.get('', params); +}; + +const checkBindCode = (area, mobile, code) => { + return api.get('', { + method: 'app.register.validRegCode', + mobile: mobile, + area: area, + code: code + }); +}; + +const bindMobile = (openId, sourceType, mobile, area, password, nickname) => { + let params = { + method: 'app.passport.bind', + mobile: mobile, + open_id: openId, + source_type: sourceType, + area: area + }; + + if (password) { + params.password = password; + } + + if (nickname) { + params.nickname = nickname; + } + + return api.get('', params); +}; + +const relateMobile = (openId, sourceType, mobile, area) => { + return api.get('', { + method: 'app.passport.relatedMobile', + mobile: mobile, + openId: openId, + source_type: sourceType, + area: area + }); +}; + +const changeCheck = (mobile, area) => { + return api.get('', { + method: 'app.passport.changeCheck', + mobile: mobile, + area: area + }); +}; + +const changeMobile = (uid, mobile, area, code) => { + return api.get('', { + method: 'app.passport.changeMobile', + mobile: mobile, + uid: uid, + code: code, + area: area + }); +}; + +module.exports = { + bindCheck, + sendBindMsg, + checkBindCode, + bindMobile, + relateMobile, + changeCheck, + changeMobile +}; diff --git a/apps/passport/models/bind-service.js b/apps/passport/models/bind-service.js index 73eee51..ae61f4c 100644 --- a/apps/passport/models/bind-service.js +++ b/apps/passport/models/bind-service.js @@ -7,81 +7,6 @@ 'use strict'; -const api = global.yoho.API; +const api = require('./bind-api'); -const BindService = { - bindCheck(mobile, openId, sourceType, area) { - let params = { - method: 'app.passport.signCheck', - area: area, - mobile: mobile, - open_id: openId, - source_type: sourceType - }; - - return api.get('', params); - }, - sendBindMsg(area, mobile) { - let params = { - method: 'app.passport.smsbind', - mobile: mobile, - area: area - }; - - return api.get('', params); - }, - checkBindCode(area, mobile, code) { - return api.get('', { - method: 'app.register.validRegCode', - mobile: mobile, - area: area, - code: code - }); - }, - bindMobile(openId, sourceType, mobile, area, password, nickname) { - let params = { - method: 'app.passport.bind', - mobile: mobile, - open_id: openId, - source_type: sourceType, - area: area - }; - - if (password) { - params.password = password; - } - - if (nickname) { - params.nickname = nickname; - } - - return api.get('', params); - }, - relateMobile(openId, sourceType, mobile, area) { - return api.get('', { - method: 'app.passport.relatedMobile', - mobile: mobile, - openId: openId, - source_type: sourceType, - area: area - }); - }, - changeCheck(mobile, area) { - return api.get('', { - method: 'app.passport.changeCheck', - mobile: mobile, - area: area - }); - }, - changeMobile(uid, mobile, area, code) { - return api.get('', { - method: 'app.passport.changeMobile', - mobile: mobile, - uid: uid, - code: code, - area: area - }); - } -}; - -module.exports = BindService; +module.exports = api; diff --git a/apps/passport/models/index-api.js b/apps/passport/models/index-api.js index ccb90ca..bf99462 100644 --- a/apps/passport/models/index-api.js +++ b/apps/passport/models/index-api.js @@ -6,13 +6,15 @@ const serviceAPI = global.yoho.ServiceAPI; +const config = global.yoho.config; + /** * 资源码找资源 */ const getResourceAsync = resourceCode => { return serviceAPI.get('/operations/api/v5/resource/get', { content_code: resourceCode - }); + }, config.apiCache); }; module.exports = { diff --git a/apps/passport/models/auth-helper.js b/apps/passport/models/login-api.js index 923807f..05f1da2 100644 --- a/apps/passport/models/auth-helper.js +++ b/apps/passport/models/login-api.js @@ -1,193 +1,124 @@ -'use strict'; +/** + * Created by TaoHuang on 2016/10/21. + */ -const md5 = require('md5'); +'use strict'; const aes = require('./aes-pwd'); -const cache = global.yoho.cache; -const sign = global.yoho.sign; const api = global.yoho.API; -const cookie = global.yoho.cookie; - -const Promise = require('bluebird'); - -const cartService = require('./cart-service'); - -const Auth = { - signin(type, area, profile, password, shoppingKey) { - let _that = this; - let loginBy = { - password: _that.signinByPasswordWithAes, - sms: _that.signinBySMS, - qrcode: _that.signinByQrCode - }; - - return loginBy[type](area, profile, password, shoppingKey); - }, - signinByPasswordWithAes(area, profile, password, shoppingKey) { - let param = { - method: 'app.passport.signinAES', - area: area, - profile: profile, - password: aes.aesPwd(password) - }; - - if (shoppingKey) { - param.shopping_key = shoppingKey; - } - - return api.post('', param); - }, - signinBySMS(area, mobile, token, shoppingKey) { - let param = { - method: 'app.passport.autoSignin', - area: area, - profile: mobile, - code: token - }; - - if (shoppingKey) { - param.shopping_key = shoppingKey; - } - - 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', - area: area, - mobile: mobile, - type: 1 // 手机快捷登录短信验证码 - }; - - return api.get('', param); - }, - checkUserExitBySMS(area, mobile) { - return api.get('', { - method: 'app.passport.checkUserExist', - area: area, - mobile: mobile - }); - }, - verifyPasswordBySMS(area, mobile, code) { - return api.get('', { - method: 'app.message.verifySmsCode', - area: area, - mobile: mobile, - code: code, - type: 1 // 手机快捷登录短信验证码 - }); - }, - signinByOpenID(nickname, openId, sourceType, shoppingKey) { - let param = { - nickname: nickname, - openId: openId, - source_type: sourceType, - method: 'app.passport.signinByOpenID' - }; - - if (shoppingKey) { - param.shopping_key = shoppingKey; - } - - return api.get('', param); - }, - signinByWechat(nickname, openId, unionId, sourceType, shoppingKey) { - let param = { - nickname: nickname, - openId: openId, - unionId: unionId, - source_type: sourceType, - method: 'app.passport.signinByWechat' - }; - - if (shoppingKey) { - param.shopping_key = shoppingKey; - } - - return api.get('', param); - }, - profile(uid) { - let param = { - uid: uid, - method: 'app.passport.profile' - }; - - return api.get('', param); - }, - syncUserSession(uid, req, res) { - return Promise.all([Auth.profile(uid), cartService.goodsCount(uid)]).spread((userInfo, count) => { - let token = sign.makeToken(uid); - let data = userInfo.data; - let encryptionUid = aes.encryptionUid(data.uid); - - if (data) { - let uidCookie = `${data.profile_name}::${encryptionUid}::${data.vip_info.title}::${token}`; - let isStudent = data.vip_info.is_student || 0; - - req.session._TOKEN = token; - req.session._LOGIN_UID = uid; - - res.cookie('_UID', uidCookie, { - domain: 'yohobuy.com' - }); - res.cookie('isStudent', isStudent, { - domain: 'yohobuy.com' - }); - - // 购物车中商品的数量 - res.cookie('_g', JSON.stringify({ - _k: cookie.getShoppingKey(req), - _nac: count, - _ac: 0, - _c: 1 - }), { - domain: 'yohobuy.com' - }); - } - req.session._TOKEN = token; // esline-disable-line - req.session._LOGIN_UID = uid; // esline-disable-line - res.cookie('_TOKEN', token, { - domain: 'yohobuy.com' - }); // esline-disable-line - }).catch(console.log); - }, - rememberAccount(accountInfo, req, res) { - let aWeek = (new Date()).getTime() / 1000 + 504000; // 504000-一周 - let rememKey = md5(md5(accountInfo.account + accountInfo.password + accountInfo.area)); - - res.cookie('isRemember', true, aWeek); - res.cookie('remem', rememKey, aWeek); - if (!cache.get(rememKey)) { - cache.set(rememKey, accountInfo, aWeek); - } + +const signinByPasswordWithAes = (area, profile, password, shoppingKey) => { + let param = { + method: 'app.passport.signinAES', + area: area, + profile: profile, + password: aes.aesPwd(password) + }; + + if (shoppingKey) { + param.shopping_key = shoppingKey; + } + + return api.post('', param); +}; + +const signinBySMS = (area, mobile, token, shoppingKey) => { + let param = { + method: 'app.passport.autoSignin', + area: area, + profile: mobile, + code: token + }; + + if (shoppingKey) { + param.shopping_key = shoppingKey; + } + + return api.post('', param); +}; + +const 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); +}; + +const fetchByQrCode = () => { + let param = { + method: 'app.twoDimen.getCode' + }; + + return api.post('', param); +}; + +const checkByQrCode = (code) => { + let param = { + method: 'app.twoDimen.loginCheck', + code: code + }; + + return api.post('', param); +}; + +const sendPasswordBySMS = (area, mobile) => { + let param = { + method: 'app.message.sendSms', + area: area, + mobile: mobile, + type: 1 // 手机快捷登录短信验证码 + }; + + return api.get('', param); +}; + +const checkUserExitBySMS = (area, mobile) => { + return api.get('', { + method: 'app.passport.checkUserExist', + area: area, + mobile: mobile + }); +}; + +const verifyPasswordBySMS = (area, mobile, code) => { + return api.get('', { + method: 'app.message.verifySmsCode', + area: area, + mobile: mobile, + code: code, + type: 1 // 手机快捷登录短信验证码 + }); +}; + +const signinByOpenID = (nickname, openId, sourceType, shoppingKey) => { + let param = { + nickname: nickname, + openId: openId, + source_type: sourceType, + method: 'app.passport.signinByOpenID' + }; + + if (shoppingKey) { + param.shopping_key = shoppingKey; } + + return api.get('', param); }; -module.exports = Auth; +module.exports = { + signinByPasswordWithAes, + signinBySMS, + signinByQrCode, + fetchByQrCode, + checkByQrCode, + sendPasswordBySMS, + checkUserExitBySMS, + verifyPasswordBySMS, + signinByOpenID +}; diff --git a/apps/passport/models/login-service.js b/apps/passport/models/login-service.js new file mode 100644 index 0000000..9e889b7 --- /dev/null +++ b/apps/passport/models/login-service.js @@ -0,0 +1,99 @@ +'use strict'; + +const md5 = require('md5'); + +const aes = require('./aes-pwd'); +const cache = global.yoho.cache; +const sign = global.yoho.sign; +const cookie = global.yoho.cookie; +const config = global.yoho.config; + +const Promise = require('bluebird'); + +const cartService = require('./cart-service'); +const userService = require('./user-service'); +const api = require('./login-api'); + +const signin = (type, area, profile, password, shoppingKey) => { + let loginBy = { + password: api.signinByPasswordWithAes, + sms: api.signinBySMS, + qrcode: api.signinByQrCode + }; + + return loginBy[type](area, profile, password, shoppingKey); +}; + +const syncUserSession = (uid, req, res, sessionKey) => { + if (sessionKey) { + cache.set(`java_session_key:${uid}`, sessionKey).catch(() => { + global.yoho.logger.error('write session key fail'); + }); + } + + return Promise.all([userService.profile(uid), cartService.goodsCount(uid)]).spread((userInfo, count) => { + let token = sign.makeToken(uid); + let data = userInfo.data; + let encryptionUid = aes.encryptionUid(data.uid); + + if (data) { + let uidCookie = `${data.profile_name}::${encryptionUid}::${data.vip_info.title}::${token}`; + let isStudent = data.vip_info.is_student || 0; + + res.cookie('_UID', uidCookie, { + domain: config.cookieDomain + }); + res.cookie('isStudent', isStudent, { + domain: config.cookieDomain + }); + + // 购物车中商品的数量 + res.cookie('_g', JSON.stringify({ + _k: cookie.getShoppingKey(req), + _nac: count, + _ac: 0, + _c: 1 + }), { + domain: config.cookieDomain + }); + } + + req.session.TOKEN_ = token; + req.session.LOGIN_UID_ = uid; + + res.cookie('_TOKEN', token, { + domain: config.cookieDomain + }); + + }).catch(console.log); +}; + +const rememberAccount = (accountInfo, req, res) => { + let aWeek = (new Date()).getTime() / 1000 + 504000; // 504000-一周 + let rememKey = md5(md5(accountInfo.account + accountInfo.password + accountInfo.area)); + + res.cookie('isRemember', true, { + maxAge: aWeek, + domain: config.cookieDomain + }); + res.cookie('remem', rememKey, { + maxAge: aWeek, + domain: config.cookieDomain + }); + + // if (!cache.get(rememKey)) { + // cache.set(rememKey, accountInfo, aWeek); + // } +}; + +module.exports = { + signin, + syncUserSession, + rememberAccount, + fetchByQrCode: api.fetchByQrCode, + checkByQrCode: api.checkByQrCode, + sendPasswordBySMS: api.sendPasswordBySMS, + checkUserExitBySMS: api.checkUserExitBySMS, + verifyPasswordBySMS: api.verifyPasswordBySMS, + signinByOpenID: api.signinByOpenID +}; diff --git a/apps/passport/models/passport-helper.js b/apps/passport/models/passport-helper.js index db64da9..64ba154 100644 --- a/apps/passport/models/passport-helper.js +++ b/apps/passport/models/passport-helper.js @@ -9,9 +9,6 @@ const _ = require('lodash'); const helpers = global.yoho.helpers; const indexService = require('./index-service'); -const UserService = require('./user-service'); - -const DEFAULT_HEAD_IMG_ICO = 'http://img10.static.yhbimg.com/headimg/2013/11/28/09/01cae078abe5fe320c88cdf4c220212688.gif?imageView/2/w/100/h/100'; /** * 获得图片 @@ -109,125 +106,14 @@ const getCountry = () => { }; /** - * 各国手机号规则 - */ -const _areaMobileVerify = (phone, area) => { - area = area || '86'; - phone = phone.trim(); - - let verify = { - 86: { - name: '中国', - match: /^1[3|4|5|8|7][0-9]{9}$/.test(phone) - }, - 852: { - name: '中国香港', - match: /^[9|6|5][0-9]{7}$/.test(phone) - }, - 853: { - name: '中国澳门', - match: /^[0-9]{8}$/.test(phone) - }, - 886: { - name: '中国台湾', - match: /^[0-9]{10}$/.test(phone) - }, - 65: { - name: '新加坡', - match: /^[9|8][0-9]{7}$/.test(phone) - }, - 60: { - name: '马来西亚', - match: /^1[1|2|3|4|6|7|9][0-9]{8}$/.test(phone) - }, - 1: { - name: '加拿大&美国', - match: /^[0-9]{10}$/.test(phone) - }, - 82: { - name: '韩国', - match: /^01[0-9]{9}$/.test(phone) - }, - 44: { - name: '英国', - match: /^7[7|8|9][0-9]{8}$/.test(phone) - }, - 81: { - name: '日本', - match: /^0[9|8|7][0-9]{9}$/.test(phone) - }, - 61: { - name: '澳大利亚', - match: /^[0-9]{11}$/.test(phone) - } - }; - - if (verify[area]) { - return verify[area].match; - } else { - return false; - } -}; - -/** * 验证国际手机号是否合法 */ -const isAreaMobile = areaMobile => { - if (!areaMobile) { - return false; - } - - let mobile = { - area: '86', - phone: '' - }; - - let splitMobile = areaMobile.split('-'); - - if (splitMobile.length === 2) { - mobile.area = splitMobile[0]; - mobile.phone = splitMobile[1]; - } else { - mobile.phone = splitMobile[0]; - } - - return _areaMobileVerify(mobile.phone, mobile.area); -}; +const isMobile = _.toNumber; /** * 验证邮箱是否合法 */ -const verifyEmail = email => { - if (!email) { - return false; - } - - const emailRegExp = /^[\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/; - - return emailRegExp.test(email); -}; - -/** - * 验证手机是否合法 - */ -const verifyMobile = phone => { - if (!phone) { - return false; - } - - return /^1[3|4|5|8|7][0-9]{9}$/.test(phone); -}; - -/** - * 生成带区号的手机号码 - */ -const makeAreaMobile = (area, mobile) => { - if (!area || area === '86') { - return mobile; - } - - return `${area}-${mobile}`; -}; +const isEmail = email => _.includes(email, '@'); /** * 密码是否合法 @@ -242,35 +128,13 @@ const isPassword = pwd => { return pwdRegexp.test(_.trim(pwd)); }; -/** - * 第三方登录 根据手机号获取用户相关信息 - */ -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)) { - profile = profile.substring(0, 3) + '****' + profile.substring(7, 11); - } - - return { - username: profile, - headImg: user.head_ico ? helpers.image(user.head_ico, 100, 100, 2) : DEFAULT_HEAD_IMG_ICO, - bindLogin: helpers.urlFormat('/signin.html', { bindMobile: mobile, bindArea: area }) - }; - }); -}; module.exports = { validator: { - verifyMobile, - isAreaMobile, - verifyEmail, + isMobile, + isEmail, isPassword }, - makeAreaMobile, getCountry, - getLeftBannerAsync, - getUserInfo + getLeftBannerAsync }; diff --git a/apps/passport/models/reg-api.js b/apps/passport/models/reg-api.js new file mode 100644 index 0000000..3013e9d --- /dev/null +++ b/apps/passport/models/reg-api.js @@ -0,0 +1,68 @@ +/** + * Created by TaoHuang on 2016/10/21. + */ + +/** + * 注册 model + */ +'use strict'; +const aes = require('./aes-pwd'); +const api = global.yoho.API; + +let sendCodeToMobile = (area, mobile) => { + let params = { + method: 'app.register.sendRegCodeToMobile', + area: area, + mobile: mobile + }; + + return api.post('', params); +}; + +let validMobileCode = (area, mobile, code) => { + let params = { + method: 'app.register.validRegCode', + area: area, + mobile: mobile, + code: code + }; + + return api.post('', params); +}; + +let regMobile = (area, mobile, password, shoppingKey)=> { + let params = { + method: 'app.passport.register', + area: area, + profile: mobile, + password: password + }; + + if (shoppingKey) { + params.shopping_key = shoppingKey; + } + + return api.post('', params); +}; + +let regMobileAes = (area, mobile, password, shoppingKey)=> { + let params = { + method: 'app.passport.registerAES', + area: area, + profile: mobile, + password: aes.aesPwd(password) + }; + + if (shoppingKey) { + params.shopping_key = shoppingKey; + } + + return api.post('', params); +}; + +module.exports = { + sendCodeToMobile, + validMobileCode, + regMobile, + regMobileAes +}; diff --git a/apps/passport/models/reg-service.js b/apps/passport/models/reg-service.js index 2f0bf70..bb36eba 100644 --- a/apps/passport/models/reg-service.js +++ b/apps/passport/models/reg-service.js @@ -3,69 +3,14 @@ */ 'use strict'; const passportHelper = require('./passport-helper'); -const aes = require('./aes-pwd'); +const api = require('./reg-api'); + const REGISTER_LEFT_BANNER_CODE = 'c479ec90120cae7f96e52922b4917064'; // 注册左边的banner -const api = global.yoho.API; let getRegData = () => { return passportHelper.getLeftBannerAsync(REGISTER_LEFT_BANNER_CODE); }; -let sendCodeToMobile = (area, mobile) => { - let params = { - method: 'app.register.sendRegCodeToMobile', - area: area, - mobile: mobile - }; - - return api.post('', params); -}; - -let validMobileCode = (area, mobile, code) => { - let params = { - method: 'app.register.validRegCode', - area: area, - mobile: mobile, - code: code - }; - - return api.post('', params); -}; - -let regMobile = (area, mobile, password, shoppingKey)=> { - let params = { - method: 'app.passport.register', - area: area, - profile: mobile, - password: password - }; - - if (shoppingKey) { - params.shopping_key = shoppingKey; - } - - return api.post('', params); -}; - -let regMobileAes = (area, mobile, password, shoppingKey)=> { - let params = { - method: 'app.passport.registerAES', - area: area, - profile: mobile, - password: aes.aesPwd(password) - }; - - if (shoppingKey) { - params.shopping_key = shoppingKey; - } - - return api.post('', params); -}; - -module.exports = { - getRegData, - sendCodeToMobile, - validMobileCode, - regMobile, - regMobileAes -}; +module.exports = Object.assign(api, { + getRegData +}); diff --git a/apps/passport/models/user-api.js b/apps/passport/models/user-api.js index fb1df1a..8d9f456 100644 --- a/apps/passport/models/user-api.js +++ b/apps/passport/models/user-api.js @@ -34,21 +34,30 @@ const findByEmailAsync = (email) => { return api.get('', { email: email, method: 'app.passport.getProfileByEmail' - }) - .then(result => { - if (!result.code || result.code !== 200 || !result.data || _.isEmpty(result.data)) { - return EMPTY; - } + }).then(result => { + if (!result.code || result.code !== 200 || !result.data || _.isEmpty(result.data)) { + return EMPTY; + } - return result.data; + return result.data; - }) - .catch(() => { - return EMPTY; - }); + }).catch(() => { + return EMPTY; + }); +}; + +const profile = (uid) => { + let param = { + uid: uid, + method: 'app.passport.profile' + }; + + return api.get('', param); }; + module.exports = { findByMobileAsync, - findByEmailAsync + findByEmailAsync, + profile }; diff --git a/apps/passport/models/user-service.js b/apps/passport/models/user-service.js index f6ec65f..0bbf0fc 100644 --- a/apps/passport/models/user-service.js +++ b/apps/passport/models/user-service.js @@ -5,4 +5,30 @@ const api = require('./user-api'); -module.exports = api; +const helpers = global.yoho.helpers; + +const DEFAULT_HEAD_IMG_ICO = 'http://img10.static.yhbimg.com/headimg/2013/11/28/09/01cae078abe5fe320c88cdf4c220212688.gif?imageView/2/w/100/h/100'; + +/** + * 第三方登录 根据手机号获取用户相关信息 + */ +const getUserInfo = (area, mobile) => { + return api.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)) { + profile = profile.substring(0, 3) + '****' + profile.substring(7, 11); + } + + return { + username: profile, + headImg: user.head_ico ? helpers.image(user.head_ico, 100, 100, 2) : DEFAULT_HEAD_IMG_ICO, + bindLogin: helpers.urlFormat('/signin.html', {bindMobile: mobile, bindArea: area}) + }; + }); +}; + +module.exports = Object.assign(api, { + getUserInfo +}); diff --git a/apps/passport/views/action/login/index.hbs b/apps/passport/views/action/login/index.hbs index 0e1b0c7..a8eb139 100644 --- a/apps/passport/views/action/login/index.hbs +++ b/apps/passport/views/action/login/index.hbs @@ -92,7 +92,7 @@ <span id="login-btn" class="login-btn btn">登录</span> </li> <li class="other-opts"> - <span class="remember-me"> + <span class="remember-me checked"> <i class="iconfont"></i> 记住登录状态 </span> diff --git a/apps/product/controllers/list.js b/apps/product/controllers/list.js index 4054cfc..3e75ee1 100644 --- a/apps/product/controllers/list.js +++ b/apps/product/controllers/list.js @@ -7,6 +7,7 @@ const mRoot = '../models'; const list = require(`${mRoot}/list`); const helpers = global.yoho.helpers; +const _ = require('lodash'); // 搜索相关接口 const searchApi = require(`${mRoot}/search-api`); @@ -32,12 +33,22 @@ const shop = (shopId, req, res, next, brandInfo) => { page: 'shop', shopId: shopId }); + + // 店铺装修为空则不cache + if (!result.shopTopBanner) { + res.set('Cache-Control', 'no-cache'); + } 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'}); + + // 基础店铺装修为空则不cache + if (!result.shopTopBannerBase) { + res.set('Cache-Control', 'no-cache'); + } res.render('list/brand', result); }).catch(next); } @@ -55,6 +66,11 @@ exports.index = (req, res, next) => { list.getListData(req.query, req.yoho.channel).then(result => { Object.assign(resData, result); + + // 查询结果为空则不cache + if (_.isEmpty(_.get(resData, 'list.goods', []))) { + res.set('Cache-Control', 'no-cache'); + } res.render('list/index', resData); }).catch(next); @@ -73,6 +89,12 @@ exports.new = (req, res, next) => { Object.assign(resData, result, { hideInfo: {from: 'newProduct'} }); + + // 查询结果为空则不cache + if (_.isEmpty(_.get(resData, 'list.goods', []))) { + res.set('Cache-Control', 'no-cache'); + } + res.render('list/index', resData); }).catch(next); @@ -168,6 +190,11 @@ exports.shopList = (req, res, next) => { if (req.query.query) { result.shopKey = req.query.query; } + + // 店铺装修为空则不cache + if (!result.shopTopBanner) { + res.set('Cache-Control', 'no-cache'); + } res.render('list/shop-list', result); }).catch(next); }; diff --git a/apps/product/index.js b/apps/product/index.js index 2f3d33f..e26e9a3 100644 --- a/apps/product/index.js +++ b/apps/product/index.js @@ -5,8 +5,7 @@ */ var express = require('express'), - path = require('path'), - hbs = require('express-handlebars'); + path = require('path'); var app = express(); @@ -18,12 +17,12 @@ app.on('mount', function(parent) { Object.assign(app.locals, parent.locals); }); -app.set('views', path.join(__dirname, 'views/action')); -app.engine('.hbs', hbs({ +app.use(global.yoho.hbs({ extname: '.hbs', defaultLayout: 'layout', layoutsDir: doraemon, - partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`], + partialsDir: [path.join(__dirname, 'views/partial')], + views: path.join(__dirname, 'views/action'), helpers: global.yoho.helpers })); diff --git a/apps/product/models/brand-api.js b/apps/product/models/brand-api.js index 2c3152e..9fff972 100644 --- a/apps/product/models/brand-api.js +++ b/apps/product/models/brand-api.js @@ -5,12 +5,13 @@ 'use strict'; const api = global.yoho.API; +const config = global.yoho.config; const getBannerInfoAsync = bid => { return api.get('', { method: 'web.brand.banner', brand_id: bid - }); + }, config.apiCache); }; @@ -18,7 +19,7 @@ const getBrandLogoByDomainAsync = domain => { return api.get('', { domain: domain, method: 'web.brand.byDomain' - }); + }, config.apiCache); }; module.exports = { diff --git a/apps/product/models/detail-comment-api.js b/apps/product/models/detail-comment-api.js index ee391eb..ca06a43 100644 --- a/apps/product/models/detail-comment-api.js +++ b/apps/product/models/detail-comment-api.js @@ -7,18 +7,6 @@ const api = global.yoho.API; /** - * 获取评论 - */ -const indexAsync = (pid, page, size) => { - return api.get('', { - method: 'app.comment.li', - product_id: pid, - page: page, - limit: size - }); -}; - -/** * 商品详情页的评论列表 * * @param pid 商品productId @@ -39,7 +27,6 @@ const getShareOrderListAsync = (pid, page, size) => { }; module.exports = { - indexAsync, getShareOrderListAsync }; diff --git a/apps/product/models/detail-comment-service.js b/apps/product/models/detail-comment-service.js index 09df24a..33790d8 100644 --- a/apps/product/models/detail-comment-service.js +++ b/apps/product/models/detail-comment-service.js @@ -4,45 +4,7 @@ 'use strict'; -const Promise = require('bluebird'); -const co = Promise.coroutine; -const _ = require('lodash'); -const helpers = global.yoho.helpers; const api = require('./detail-comment-api'); -const detailHelper = require('./detail-helper'); - - -const indexAsync = (pid, page, size) => { - return co(function *() { - let commentList = yield api.indexAsync(pid, page, size); - - if (!(commentList.code && commentList.code === 200)) { - return []; - } - - return commentList.data.map(value => { - let item = {}; - - let avatar = detailHelper.DEFAULT_AVATAR_ICO; - - if (value.head_ico) { - avatar = `${_.last(value.head_ico.split('headimg'))}`; - avatar = helpers.image(avatar, 30, 30); - } - - item.avatar = avatar; - item.userName = value.nickname; - item.color = value.color_name; - item.size = value.size_name; - item.comment = value.content || ''; - item.date = value.create_time; - item.total = value.total; - - return item; - }); - - })(); -}; /** * 获取订单评论 @@ -51,11 +13,8 @@ const indexAsync = (pid, page, size) => { * @param page * @param size */ -const getShareOrderListAsync = (pid, page, size) => { - return api.getShareOrderListAsync(pid, page, size); -}; +const getShareOrderListAsync = api.getShareOrderListAsync; module.exports = { - indexAsync, getShareOrderListAsync }; diff --git a/apps/product/models/detail-hotarea-api.js b/apps/product/models/detail-hotarea-api.js index d4b36b4..ae1bbfb 100644 --- a/apps/product/models/detail-hotarea-api.js +++ b/apps/product/models/detail-hotarea-api.js @@ -5,6 +5,7 @@ 'use strict'; const api = global.yoho.API; +const config = global.yoho.config; /** * 获取商品的热区 @@ -13,7 +14,7 @@ const indexAsync = pid => { return api.get('', { method: 'web.productCollocation.list', product_id: pid - }); + }, config.apiCache); }; module.exports = { diff --git a/apps/product/models/detail-product-api.js b/apps/product/models/detail-product-api.js index e2b068a..645ae7b 100644 --- a/apps/product/models/detail-product-api.js +++ b/apps/product/models/detail-product-api.js @@ -5,12 +5,13 @@ 'use strict'; const api = global.yoho.API; +const config = global.yoho.config; const getProductBannerAsync = (pid) => { return api.get('', { method: 'web.productBanner.data', product_id: pid - }); + }, config.apiCache); }; @@ -18,7 +19,7 @@ const sizeInfoAsync = skn => { return api.get('', { method: 'h5.product.intro', productskn: skn - }); + }, config.apiCache); }; @@ -27,14 +28,14 @@ const isSupportReturnedSale = skn => { return api.get('', { method: 'app.product.refundExchange', product_skn: skn - }); + }, config.apiCache); }; const getProductComfortAsync = pid => { return api.get('', { method: 'web.productComfort.data', product_id: pid - }); + }, config.apiCache); }; @@ -42,14 +43,14 @@ const getProductModelCardAsync = pid => { return api.get('', { method: 'web.productModelcard.list', product_id: pid - }); + }, config.apiCache); }; const getProductModelTryAsync = skn => { return api.get('', { method: 'web.productModelTry.data', product_skn: skn - }); + }, config.apiCache); }; /** @@ -74,7 +75,7 @@ const getProductAsync = (pid, uid, isStudents, vipLevel) => { if (vipLevel) { params.current_vip_level = vipLevel; } - return api.get('', params); + return api.get('', params, config.apiCache); }; const getPromotionAsync = (skn) => { @@ -83,7 +84,7 @@ const getPromotionAsync = (skn) => { product_skn: skn }; - return api.get('', params); + return api.get('', params, config.apiCache); }; const getLimitedProductStatusAsync = (code, uid, skn) => { @@ -100,7 +101,7 @@ const getLimitedProductStatusAsync = (code, uid, skn) => { params.product_skn = skn; } - return api.get('', params); + return api.get('', params, config.apiCache); }; module.exports = { diff --git a/apps/product/models/detail-service.js b/apps/product/models/detail-service.js index 7914228..bc244d6 100644 --- a/apps/product/models/detail-service.js +++ b/apps/product/models/detail-service.js @@ -19,7 +19,6 @@ const consultService = require('./detail-consult-service'); const commentService = require('./detail-comment-service'); const hotAreaService = require('./detail-hotarea-service'); const brandService = require('./brand-service'); -const favoriteBrandService = require('./favorite-brand-service'); const favoriteProductService = require('./favorite-product-service'); const shopService = require('./shop-service'); @@ -1406,7 +1405,6 @@ const showMainAsync = (data) => { }; module.exports = { - indexCommentAsync: commentService.indexAsync, // 获取评论列表 getShareOrderListAsync: commentService.getShareOrderListAsync, // 获取评论列表 indexConsultAsync: consultService.indexAsync, // 获取咨询列表 createConsultAsync: consultService.createAsync, // 添加咨询 diff --git a/apps/product/models/outlets-api.js b/apps/product/models/outlets-api.js index c7d7378..5cad5c7 100644 --- a/apps/product/models/outlets-api.js +++ b/apps/product/models/outlets-api.js @@ -10,6 +10,7 @@ const serviceApi = global.yoho.ServiceAPI; const api = global.yoho.API; const _ = require('lodash'); +const config = global.yoho.config; const yhChannel = { boys: { @@ -49,7 +50,7 @@ exports.getOutletsActivityOrigin = (params) => { size: params.size || 0, yh_channel: yhChannel[tempChannel].channel, type: params.type || 0 - }); + }, config.apiCache); }; /** @@ -58,7 +59,7 @@ exports.getOutletsActivityOrigin = (params) => { * @return {[type]} */ exports.getChannelResouceData = (params) => { - return serviceApi.get('operations/api/v5/resource/home', params); + return serviceApi.get('operations/api/v5/resource/home', params, config.apiCache); }; /** @@ -77,7 +78,7 @@ exports.getOutletsTrendData = (params) => { stocknumber: 1, // 过滤出库存 > 1的商品 limit: params.limit || 5, outlets: params.outlets || 1 // 默认取奥莱商品 - }); + }, config.apiCache); }; @@ -110,5 +111,5 @@ exports.getOutletsGoodsList = (params) => { tempParams[paramsName] = params[paramsName]; } }); - return api.get('', tempParams); + return api.get('', tempParams, config.apiCache); }; diff --git a/apps/product/models/sale-api.js b/apps/product/models/sale-api.js index a785f9f..4818bed 100644 --- a/apps/product/models/sale-api.js +++ b/apps/product/models/sale-api.js @@ -10,6 +10,7 @@ const serviceAPI = global.yoho.ServiceAPI; const api = global.yoho.API; const _ = require('lodash'); +const config = global.yoho.config; const yhChannel = { boys: { @@ -42,7 +43,7 @@ exports.getSaleGoodsList = (params) => { finalParams.yh_channel = yhChannel[params.channel].channel; } - return api.get('', Object.assign(finalParams, params)); + return api.get('', Object.assign(finalParams, params), config.apiCache); }; /** @@ -55,7 +56,7 @@ exports.getSalebreakingYardsSortList = (params) => { return api.get('', { method: 'app.sale.getBreakingSort', yh_channel: yhChannel[tempChannel].channel - }); + }, config.apiCache); }; /** @@ -71,7 +72,7 @@ exports.getSaleActivityList = (params, channel) => { sort: '2', plateform: '1', yh_channel: tempChannel.channel - }); + }, config.apiCache); }; /** @@ -81,7 +82,7 @@ exports.getSaleActivityList = (params, channel) => { exports.getSaleBannerList = (cCode) => { return serviceAPI.get('operations/api/v5/resource/get', { content_code: cCode - }); + }, config.apiCache); }; /** @@ -91,7 +92,7 @@ exports.getSaleBannerList = (cCode) => { exports.getLeftContentList = () => { return api.get('', { method: 'app.sort.get' - }); + }, config.apiCache); }; /** diff --git a/apps/product/models/search-api.js b/apps/product/models/search-api.js index 6f52c26..eac7a1c 100644 --- a/apps/product/models/search-api.js +++ b/apps/product/models/search-api.js @@ -7,17 +7,17 @@ const md5 = require('md5'); const api = global.yoho.API; const serviceApi = global.yoho.ServiceAPI; +const config = global.yoho.config; const helpers = global.yoho.helpers; const cache = global.yoho.cache; const logger = global.yoho.logger; -const config = require('../../../config/common'); const images = require('../../../utils/images.js'); const getSortByConditionAsync = (condition) => { return api.get('', Object.assign({ method: 'web.regular.groupsort' - }, condition)); + }, condition), config.apiCache); }; // 判断用户是否收藏品牌 @@ -111,7 +111,7 @@ const getProductList = (params, from) => { } }; -const getSortListOrig = (finalParams) => api.get('', finalParams); +const getSortListOrig = (finalParams) => api.get('', finalParams, config.apiCache); /** * 获取分类列表 @@ -167,7 +167,7 @@ const getSortIntro = (params) => { }; Object.assign(finalParams, params); - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -180,7 +180,7 @@ const getSortAds = (params) => { }; Object.assign(finalParams, params); - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -195,7 +195,7 @@ const getBrandSeries = (params) => { status: params.status || 1 }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -210,7 +210,7 @@ const getBrandFolder = (params) => { status: params.status || 1 }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -224,7 +224,7 @@ const getNodeContent = (params) => { node: params.node || '' }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -238,7 +238,7 @@ const getWeekNew = (params) => { }; Object.assign(finalParams, params); - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -441,7 +441,7 @@ const getSuggest = (params) => { keyword: params.keyword || '' }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; @@ -455,7 +455,7 @@ const getBrandData = (params) => { domain: params.domain || '' }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -466,7 +466,7 @@ const isFavoriteBrand = (uid, brandId) => { return serviceApi.get(isFavoriteBrandUrl, { uid: uid, brandId: brandId - }); + }, config.apiCache); }; /** diff --git a/apps/product/models/shop-api.js b/apps/product/models/shop-api.js index 8f8f56a..2a7633e 100644 --- a/apps/product/models/shop-api.js +++ b/apps/product/models/shop-api.js @@ -4,6 +4,7 @@ 'use strict'; const api = global.yoho.API; +const config = global.yoho.config; /** * 获取店铺装修的所有资源接口 @@ -12,5 +13,5 @@ exports.shopsDecoratorListAsync = shopId => { return api.get('', { method: 'app.shopsdecorator.getList', shop_id: shopId - }); + }, config.apiCache); }; diff --git a/apps/product/models/students-api.js b/apps/product/models/students-api.js index e6ae0d7..8540c07 100644 --- a/apps/product/models/students-api.js +++ b/apps/product/models/students-api.js @@ -6,7 +6,7 @@ 'use strict'; const api = global.yoho.API; -const config = require('../../../config/common'); +const config = global.yoho.config; const searchApi = require('./search-api'); const cache = global.yoho.cache; const logger = global.yoho.logger; @@ -17,7 +17,7 @@ const logger = global.yoho.logger; */ exports.getVerifiedTotal = () => { - return api.get('', {method: 'app.student.verifiedStudentTotal'}); + return api.get('', {method: 'app.student.verifiedStudentTotal'}, config.apiCache); }; /** @@ -26,7 +26,7 @@ exports.getVerifiedTotal = () => { */ exports.getEduLevelList = () => { - return api.get('', {method: 'app.studentMarket.getEducationLevelList'}); + return api.get('', {method: 'app.studentMarket.getEducationLevelList'}, config.apiCache); }; /** @@ -38,7 +38,7 @@ exports.getArea = () => { method: 'app.studentMarket.getAddressList' }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -51,7 +51,7 @@ exports.getSchool = (areaCode) => { areaCode: areaCode }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -66,7 +66,7 @@ exports.getStuProducts = (params) => { }; Object.assign(finalParams, params); - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -82,7 +82,7 @@ exports.verifyIdentity = (uid, certNo, name, pageUrl) => { page_url: pageUrl }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -99,7 +99,7 @@ exports.verifyStudent = (uid, collegeName, educationDegree, enrollmentYear) => { enrollment_year: enrollmentYear }; - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; /** @@ -117,7 +117,7 @@ exports.userAcquireStatus = (uid, couponIds) => { finalParams.uid = uid; } - return api.get('', finalParams); + return api.get('', finalParams, config.apiCache); }; diff --git a/apps/product/models/students.js b/apps/product/models/students.js index 42eff78..747c4cd 100644 --- a/apps/product/models/students.js +++ b/apps/product/models/students.js @@ -149,7 +149,7 @@ exports.verifyIdentity = (uid, params) => { educationDegree: params.educationDegree, enrollmentYear: params.enrollmentYear }) + '&'; - + return studentsApi.verifyIdentity(uid, params.certNo, params.name, pageUrl); }; @@ -238,7 +238,7 @@ exports.getStudentsList = (params, channel) => { // 获取商品数据和顶部筛选条件 if (result[2].code === 200) { - //删掉student_price,不让页面显示 + // 删掉student_price,不让页面显示 _.forEach(result[2].data.product_list, goods => { delete goods.student_price; }); diff --git a/apps/product/views/action/product/detail.hbs b/apps/product/views/action/product/detail.hbs index 58d5da8..19b8dd4 100644 --- a/apps/product/views/action/product/detail.hbs +++ b/apps/product/views/action/product/detail.hbs @@ -660,16 +660,18 @@ <div class="service"></div> - {{#if latestWalk}} - <div class="info-block latest-walk"> - <input id="latest-walk-count" type="hidden" value="{{latestWalk}}"> - <p class="block-title"> - <span class="title cur">最近浏览 RECENT REVIEW</span> - </p> - <div id="latest-walk-goods" class="goods clearfix"></div> - {{> product/latest-walk-tpl}} - </div> - {{/if}} + {{#unless @root.pc.product.removeRecentView}} + {{#if latestWalk}} + <div class="info-block latest-walk"> + <input id="latest-walk-count" type="hidden" value="{{latestWalk}}"> + <p class="block-title"> + <span class="title cur">最近浏览 RECENT REVIEW</span> + </p> + <div id="latest-walk-goods" class="goods clearfix"></div> + {{> product/latest-walk-tpl}} + </div> + {{/if}} + {{/unless}} </div> </div> {{/ detail}} diff --git a/config/cache.js b/config/cache.js new file mode 100644 index 0000000..15b423c --- /dev/null +++ b/config/cache.js @@ -0,0 +1,61 @@ + +'use strict'; + +const SECOND = 1; +const MINUTE = 60 * SECOND; + +const cachePage = { + + // 频道页 + '/': 30 * SECOND, + '/woman': 30 * SECOND, + '/kids': 30 * SECOND, + '/lifestyle': 30 * SECOND, + + // 商品详情页 + '/product/\\/pro_([\\d]+)_([\\d]+)\\/(.*)/': 30 * MINUTE, + + // 逛 + '/guang/': 1 * MINUTE, + '/guang/info/index': 10 * MINUTE, + '/guang/detail/:id': 10 * MINUTE, + '/guang/index/editor': 1 * MINUTE, + '/guang/tags/index': 1 * MINUTE, + + // 领券中心 + '/coupon/index': 5 * MINUTE, + + // 商品列表 + '/product/list/index': 5 * MINUTE, + '/product/index/index': 5 * MINUTE, + + // 秒杀列表 + '/product/seckill': 30 * SECOND, + + // 秒杀详情 + + // sale + '/product/sale': 5 * MINUTE, + '/product/sale/vip': 5 * MINUTE, + '/product/sale/breakingYards': 5 * MINUTE, + '/product/sale/newSale': 5 * MINUTE, + '/product/sale/discount/detail': 5 * MINUTE, + + '/product/outlet': 30 * SECOND, + + '/product/index/brand': 2 * MINUTE, + '/product/index/about': 10 * MINUTE, + '/product/shoplist': 2 * MINUTE, + + '/product/list/new': 30 * SECOND, + + // 品牌一览 + '/brands/': 5 * MINUTE, + '/brands/plusstar': 5 * MINUTE, + + '/special/(\\d+)_(.*)\\.html': 5 * MINUTE + + +}; + +module.exports = cachePage; diff --git a/config/common.js b/config/common.js index 24d6199..d359926 100644 --- a/config/common.js +++ b/config/common.js @@ -14,6 +14,7 @@ module.exports = { appVersion: '4.9.0', // 调用api的版本 port: 6002, siteUrl: 'http://www.yohobuy.com', + cookieDomain: '.yohobuy.com', domains: { favApi: 'http://192.168.102.31:8092/brower', api: 'http://api.yoho.cn/', @@ -44,6 +45,7 @@ module.exports = { }, loggers: { infoFile: { + close: true, name: 'info', level: 'info', filename: 'logs/info.log', @@ -62,7 +64,7 @@ module.exports = { port: '4444' // influxdb port }, console: { - level: 'debug', + level: 'error', colorize: 'all', prettyPrint: true } @@ -71,8 +73,32 @@ module.exports = { wechat: { appID: 'wx3ae21dcbb82ad672', appSecret: 'e78afb2321e6a19085767e1a0f0d52c1' + }, + sina: { + appID: '3739328910', + appSecret: '9d44cded26d048e23089e5e975c93df1' + }, + qq: { + appID: '100229394', + appSecret: 'c0af9c29e0900813028c2ccb42021792' + }, + alipay: { + appID: '2088701661478015', + appSecret: 'kcxawi9bb07mzh0aq2wcirsf9znusobw' + }, + douban: { + appID: '03b4e36bf13dc75a0b1eaa43d3b9560e', + appSecret: 'f16d5913e8610672' + }, + renren: { + appID: '783130c654c94a77ace97054ae266019', + appSecret: '05e430de8c1e40d3a1f39ca8d3f8252c' } - } + }, + apiCache: { + cache: true + }, + zookeeperServer: '192.168.102.168:2188' }; if (isProduction) { @@ -98,7 +124,8 @@ if (isProduction) { interfaceShunt: { open: false, url: 'http://123.206.2.55/strategy' - } + }, + zookeeperServer: '10.66.1.97:2181' }); } else if (isTest) { Object.assign(module.exports, { diff --git a/doraemon/middleware/mobile-refer.js b/doraemon/middleware/mobile-refer.js new file mode 100644 index 0000000..099fd83 --- /dev/null +++ b/doraemon/middleware/mobile-refer.js @@ -0,0 +1,72 @@ +/** + * page cache 前端js跳转url中间件 + */ +'use strict'; + +const _ = require('lodash'); +const querystring = require('querystring'); + +const ptm = { + // 首页 + '/': '', + '/woman': '/girls', + '/kids': '/kids', + '/lifestyle': '/lifestyle', + + // 登录注册 + '/signin.html': '/signin.htm', + '/reg.html': '/passport/reg/index', + '/passport/back/index': '/passport/back/mobile', + + // 列表 + '/product/list/new': '/product/new', + + // 品牌列表 + '/brands': '/brands', + + // 领券中心 + '/coupon/index': '/coupon/floor?title=领券中心&code=b78b32ed81b18dde8ac84fd33602b88b' + +}; + +module.exports = () => { + return (req, res, next) => { + let domain = 'm.yohobuy.com'; + let proReg = /^\/product\/pro/, + guangReg = /^\/guang/, + guangDetailReg = /.html$/, + qsReg = /\?/; + + if (!req.xhr) { + let url = _.head(_.split(req.url, '?')); + let data = {}; + + if (ptm.hasOwnProperty(url)) { + data.mobileRefer = `//${domain}${ptm[url]}`; + } else if (url === '/product/list/index') { + data.mobileRefer = `//list.${domain}`; + } else if (url === '/product/index/brand') { + data.mobileRefer = `//${req.query.domain}.${domain}`; + } else if (url === '/product/search/index') { + data.mobileRefer = `//search.${domain}`; + } else if (guangReg.test(url)) { + data.mobileRefer = `//guang.${domain}`; + + if (guangDetailReg.test(url)) { + data.mobileRefer += url.replace(/\/guang\/detail\/([\d]+).html/, '/info/index?id=$1'); + } else if (url === '/guang/Index/editor') { + data.mobileRefer += `/author/index?id=${req.query.author_id}`; + } + } else if (proReg.test(url)) { + data.mobileRefer = `//${domain}${url}`; + } + + if (!_.isEmpty(req.query) && !qsReg.test(data.mobileRefer)) { + data.mobileRefer += `?${querystring.stringify(req.query)}`; + } + + Object.assign(res.locals, data); + } + next(); + }; +}; diff --git a/doraemon/middleware/page-cache.js b/doraemon/middleware/page-cache.js new file mode 100644 index 0000000..32dc892 --- /dev/null +++ b/doraemon/middleware/page-cache.js @@ -0,0 +1,57 @@ + +'use strict'; + +const cachePage = require('../../config/cache'); +const logger = global.yoho.logger; +const _ = require('lodash'); + +function urlJoin(a, b) { + if (_.endsWith(a, '/') && _.startsWith(b, '/')) { + return a + b.substring(1, b.length); + } else if (!_.endsWith(a, '/') && !_.startsWith(b, '/')) { + return a + '/' + b; + } else { + return a + b; + } +} + +module.exports = () => { + return (req, res, next) => { + + function onRender() { + let route = req.route ? req.route.path : ''; + let appPath = req.app.mountpath; + + if (_.isArray(route) && route.length > 0) { + route = route[0]; + } + + let key = urlJoin(appPath, route.toString()); // route may be a regexp + + req.app.set('etag', false); + + logger.debug(`route: ${key} cache = ${cachePage[key]}`); + + // 如果存在cache配置,并且业务代码中没有设置 + if (cachePage[key] && res.get('Cache-Control') !== 'no-cache') { + res.set({ + 'Cache-Control': 'max-age=' + cachePage[key] + }); + res.removeHeader('Pragma'); + res.removeHeader('Expires'); + } else if (req.get('X-Requested-With') === 'XMLHttpRequest') { + res.set('Cache-Control', 'no-cache'); + } else { + res.set({ + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + Expires: new Date(1900, 0, 1, 0, 0, 0, 0) + }); + } + + } + + res.on('render', onRender); + next(); + }; +}; diff --git a/doraemon/middleware/sub-domain.js b/doraemon/middleware/sub-domain.js index 989c59d..2e3b1d9 100644 --- a/doraemon/middleware/sub-domain.js +++ b/doraemon/middleware/sub-domain.js @@ -22,7 +22,15 @@ module.exports = () => { case 'guang': // 逛 case 'cdnsrcguang': // 逛CDN回源解析 if (guangDetailReg.test(req.path)) { - req.url = '/guang/detail' + req.url; + // req.url = '/guang/detail' + req.url; + /\/([\d]*).html(\?)?(.*)/.exec(req.url); + req.url = '/guang/info/index?id=' + RegExp.$1; + if (RegExp.$3) { + req.url += '&' + RegExp.$3; + } + req.query.id = RegExp.$1; + + console.log(req.url); } else if (!guangReg.test(req.path)) { req.url = '/guang' + req.url; } diff --git a/doraemon/middleware/user.js b/doraemon/middleware/user.js index 68cf55c..21d48d8 100644 --- a/doraemon/middleware/user.js +++ b/doraemon/middleware/user.js @@ -1,28 +1,54 @@ 'use strict'; -const Fp = require('lodash/fp'); +const Fn = require('lodash/fp'); +const _ = require('lodash'); +const cache = global.yoho.cache; const cookie = global.yoho.cookie; +const loginService = require('../../apps/passport/models/login-service'); module.exports = () => { return (req, res, next) => { - // session 没有读取到的时候,从 cookie 读取 UID + // 获得原始请求 url + req.fullUrl = () => req.protocol + '://' + req.get('host') + req.originalUrl; + + // 从 cookie 读取 UID if (req.cookies._UID) { - req.user.uid = cookie.getUid(req); - let getVip = Fp.pipe(Fp.split('::'), Fp.nth(2)); + let uidCookie = req.cookies._UID.split('::'); - req.user.vip = getVip(req.cookies._UID); - } + let getName = Fn.nth(0); + let getVip = Fn.nth(2); + let getToken = Fn.nth(3); - // 用户是否学生 - if (req.user.uid && req.cookies.isStudent) { + req.user.name = getName(uidCookie); // 0 + req.user.uid = cookie.getUid(req); // 1 + req.user.vip = getVip(uidCookie); // 2 + req.user.token = getToken(uidCookie); // 3 req.user.isStudent = req.cookies.isStudent || 0; } - // 从 SESSION 中获取到当前登录用户的 UID - // if (req.session && _.isNumber(req.session._LOGIN_UID)) { - // req.user.uid = req.session._LOGIN_UID; + // 记住我 + // if (_.isEmpty(req.user) && req.cookies.isRemember === 'true' && req.cookies.remem) { + // return cache.get(req.cookies.remem).then((result) => { + // let data = JSON.parse(result || '{}'); + + // let area = data.area; + // let account = data.account; + // let password = data.password; + + // return loginService.signin('password', area, account, password); + // }).then((result) => { + // if (result.code !== 200) { + // return Promise.reject(); + // } + + // return loginService.syncUserSession(result.data.uid, req, res); + // }).then(() => { + // return res.redirect(req.fullUrl()); + // }).catch(next); + // } else { + // return next(); // } next(); diff --git a/doraemon/views/layout.hbs b/doraemon/views/layout.hbs index 003cae5..79a713d 100644 --- a/doraemon/views/layout.hbs +++ b/doraemon/views/layout.hbs @@ -1,41 +1,45 @@ -<!DOCTYPE html> -<html> - <head> - <meta charset="utf-8"> - <title>{{title}}</title> - <meta name="keywords" content="{{keywords}}"> - <meta name="description" content="{{description}}"> - <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> - <meta http-equiv="cleartype" content="on"> - <meta name="apple-mobile-web-app-status-bar-style" content="black" /> - <meta content="telephone=no" name="format-detection" /> - <meta content="email=no" name="format-detection" /> - <link rel="dns-prefetch" href="//cdn.yoho.cn"> - <link rel="dns-prefetch" href="//static.yohobuy.com"> - <link rel="dns-prefetch" href="//img12.static.yhbimg.com"> - <link rel="dns-prefetch" href="//img13.static.yhbimg.com"> - {{#if devEnv}} - <link rel="stylesheet" href="//localhost:5002/css/index.css"> - {{^}} - <link rel="stylesheet" href="//cdn.yoho.cn/yohobuy-node/{{version}}/index.css"> - {{/if}} - </head> - <body> - {{> header}} - {{> common/simple-header}} - {{#if pageErr}} - {{> 404}} - {{^}} - {{{body}}} - {{/if}} - {{> footer}} - {{#if devEnv}} - <script src="//localhost:5002/libs.js"></script> - <script src="//localhost:5002/{{module}}.{{page}}.js"></script> - {{^}} - <script src="//cdn.yoho.cn/yohobuy-node/{{version}}/libs.js"></script> - <script src="//cdn.yoho.cn/yohobuy-node/{{version}}/{{module}}.{{page}}.js"></script> - {{> analysis}} - {{/if}} - </body> -</html> +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>{{title}}</title> + <meta name="keywords" content="{{keywords}}"> + <meta name="description" content="{{description}}"> + <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> + <meta http-equiv="cleartype" content="on"> + <meta name="apple-mobile-web-app-status-bar-style" content="black" /> + <meta content="telephone=no" name="format-detection" /> + <meta content="email=no" name="format-detection" /> + <link rel="dns-prefetch" href="//cdn.yoho.cn"> + <link rel="dns-prefetch" href="//static.yohobuy.com"> + <link rel="dns-prefetch" href="//img12.static.yhbimg.com"> + <link rel="dns-prefetch" href="//img13.static.yhbimg.com"> + {{#if devEnv}} + <link rel="stylesheet" href="//localhost:5002/css/index.css"> + {{^}} + <link rel="stylesheet" href="//cdn.yoho.cn/yohobuy-node/{{version}}/index.css"> + {{/if}} + </head> + <body> + {{#if mobileRefer}} + <input type="hidden" id="m-refer" value="{{mobileRefer}}"> + {{/if}} + + {{> header}} + {{> common/simple-header}} + {{#if pageErr}} + {{> 404}} + {{^}} + {{{body}}} + {{/if}} + {{> footer}} + {{#if devEnv}} + <script src="//localhost:5002/libs.js"></script> + <script src="//localhost:5002/{{module}}.{{page}}.js"></script> + {{^}} + <script src="//cdn.yoho.cn/yohobuy-node/{{version}}/libs.js"></script> + <script src="//cdn.yoho.cn/yohobuy-node/{{version}}/{{module}}.{{page}}.js"></script> + {{> analysis}} + {{/if}} + </body> +</html> diff --git a/doraemon/views/partial/header.hbs b/doraemon/views/partial/header.hbs index cb1a6ef..78260c1 100644 --- a/doraemon/views/partial/header.hbs +++ b/doraemon/views/partial/header.hbs @@ -26,7 +26,9 @@ <span class="tag-seprate"></span> <a href="//www.yohobuy.com/home?t=1453340799.4986">MY有货</a> <span class="icon-bottomarrow"></span> - <div class="simple-user-center"></div> + {{#unless @root.pc.common.disMyYohoHover}} + <div class="simple-user-center"></div> + {{/unless}} </li> <li class="myorder"> <span class="tag-seprate"></span> @@ -66,7 +68,8 @@ <div class="main-logo"><a href="//www.yohobuy.com/" class="main-link"></a></div> <ul class="main-nav-list"> {{# navbars}} - <li class="{{type}}"{{#if ico}} style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}> + <li class="{{type}}"{{#if ico}} + style="background: url({{image ico 54 32}}) no-repeat center center"{{/if}}> {{#if ico}} <a href="{{link}}"{{#if isNewPage}} target="_blank"{{/if}} class="menu-ico"></a> {{^}} @@ -81,7 +84,9 @@ {{/ navbars}} </ul> <div class="func-area hide"> - <ul class="search-suggest"></ul> + {{#unless @root.pc.common.disSearchAssociation}} + <ul class="search-suggest"></ul> + {{/unless}} <div class="search-2016"> <form action="//search.yohobuy.com" method="get" id="search-form"> <input type="hidden" id="defaultsearch" value="{{defaultSearch}}"> @@ -92,16 +97,20 @@ <div class="go-cart"> <a href="//www.yohobuy.com/shopping/cart"> <span class="iconfont "></span> - <span class="goods-num-tip">0</span> + {{#unless @root.pc.common.removeCartCount}} + <span class="goods-num-tip">0</span> + {{/unless}} </a> - <div class="mini-cart-wrapper"> - <div class="loading-cart"> - <h3>加载中,请稍后</h3> - </div> - <div class="empty-cart"> - <h3>您的购物车暂无商品</h3> + {{#unless @root.pc.common.disCartHover}} + <div class="mini-cart-wrapper"> + <div class="loading-cart"> + <h3>加载中,请稍后</h3> + </div> + <div class="empty-cart"> + <h3>您的购物车暂无商品</h3> + </div> </div> - </div> + {{/unless}} </div> </div> </div> @@ -109,39 +118,40 @@ <div class="nav-wrapper clearfix"> <div class="center-content"> {{# subNavGroup}} - <ul class="sub-nav-list {{subType}}"> - {{# subNav}} - <li {{#if thirdNav}}class="contain-third"{{/if}}> - <a href="{{link}}">{{name}} - {{#if isNew}} - <span class="newlogo"></span> - {{/if}} - </a> - {{#if thirdNav}} - <div class="third-nav-wrapper"> - <div class="center-content"> - <dl class="hide-list hide"> - {{# thirdNav}} - <dt> - <h1 class=""><a href="{{link}}">{{title}}</a> </h1> - </dt> - {{#brandItems}} - <dd> - <a href="{{link}}"{{#if hot}} class="hot"{{/if}}>{{brandName}}</a> - </dd> - {{/brandItems}} - {{/thirdNav}} - </dl> - <div class="show-detail" data-code="{{imgCode}}"> - <a><img src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="></a> - <a class="title">热门小物优选</a> + <ul class="sub-nav-list {{subType}}"> + {{# subNav}} + <li {{#if thirdNav}}class="contain-third"{{/if}}> + <a href="{{link}}">{{name}} + {{#if isNew}} + <span class="newlogo"></span> + {{/if}} + </a> + {{#if thirdNav}} + <div class="third-nav-wrapper"> + <div class="center-content"> + <dl class="hide-list hide"> + {{# thirdNav}} + <dt> + <h1 class=""><a href="{{link}}">{{title}}</a></h1> + </dt> + {{#brandItems}} + <dd> + <a href="{{link}}"{{#if hot}} + class="hot"{{/if}}>{{brandName}}</a> + </dd> + {{/brandItems}} + {{/thirdNav}} + </dl> + <div class="show-detail" data-code="{{imgCode}}"> + <a><img src="data:image/gif;base64,R0lGODlhAQABAJEAAAAAAP///93d3f///yH5BAEAAAMALAAAAAABAAEAAAICVAEAOw=="></a> + <a class="title">热门小物优选</a> + </div> </div> </div> - </div> - {{/if}} - </li> - {{/ subNav}} - </ul> + {{/if}} + </li> + {{/ subNav}} + </ul> {{/ subNavGroup}} </div> </div> @@ -153,7 +163,7 @@ </script> <script type="text/html" id="simple-account-info-tpl"> <div class="account-info-header"> - <div class="user-img" > + <div class="user-img"> <img src="\{{headIco}}"> </div> <div class="user-name"> @@ -163,15 +173,15 @@ VIP: <span>\{{curTitle}}</span> </h3> \{{#unless vip3}} - <div class="level-detail"> - <div class="level-view-bar"> - <div class="text-span"> - \{{curYearCost}}/\{{nextVipNeedCost}} - </div> - <p class="\{{#if curYearCostPer}}integrate\{{/if}}" style="width: \{{curYearCostPer}}%;"></p> + <div class="level-detail"> + <div class="level-view-bar"> + <div class="text-span"> + \{{curYearCost}}/\{{nextVipNeedCost}} </div> - <span>\{{nextVipTitle}}</span> + <p class="\{{#if curYearCostPer}}integrate\{{/if}}" style="width: \{{curYearCostPer}}%;"></p> </div> + <span>\{{nextVipTitle}}</span> + </div> \{{/unless}} </div> <ul class="account-info-content"> @@ -197,79 +207,82 @@ </li> </ul> <div class="account-info-footer"> - <a href="//www.yohobuy.com/home/user?t=\{{timestamp}}">完善资料 <span>(学生认证)</span></a> + <a href="//www.yohobuy.com/home/user?t=\{{timestamp}}">完善资料 <span>(学生认证)</span></a> </div> </script> <script type="text/html" id="mini-cart-tpl"> - \{{#carData}} - <div class="rich-cart"> - <div class="goods-list"> - \{{# totalGoods}} - \{{#notzero buy_number}} - <div class="goods-item"> - <div class="goods-img"> - <a href="\{{product_url}}"> - <img src="\{{default_img}}"/> - </a> - </div> - <div class="goods-info"> - <p class="title"> - <a href="\{{product_url}}">\{{product_name}}</a> - </p> - <p> - 颜色:\{{color_name}} - 尺码:\{{size_name}} - </p> - </div> - <div class="goods-price"> - <p>\{{show_price}} x \{{buy_number}}</p> - <p> - <a href="javascript:void(0)" class="cart-goods-del" data-id="\{{goods_incart_id}}" data-cheapest="\{{is_cheapest_free}}" data-sku="\{{product_sku}}" data-proid="\{{promotion_id}}" data-num="\{{buy_number}}">删除</a> - </p> - </div> - </div> - \{{/notzero}} - \{{/ totalGoods}} - \{{#if has_promotion}} - <div class="activity-item"> - <label class="activity-name">活动</label> - <h3 class="activity-content"> - \{{#if fit_outlet_promotion }} - 购outlet商品,满¥1999再享9折 - \{{/if}} - \{{#if has_other_promotion}} - <span class="mycart_i_down" title="更多" ></span> - \{{/if}} - \{{#if has_first_promotion}} - \{{first_promotions.promotion_title}} - \{{/if}} - </h3> - </div> - \{{/if}} - \{{#if fit_free_shipping }} - <div class="activity-item"> - <label class="activity-name">免运费</label> - <h3 class="activity-content">全场满 ¥\{{fit_free_shipping}}免运费</h3> - </div> - \{{/if}} - </div> - <div class="go-full-cart"> - <div> - <a href="//www.yohobuy.com/shopping/cart">去购物车结算</a> + \{{#carData}} + <div class="rich-cart"> + <div class="goods-list"> + \{{# totalGoods}} + \{{#notzero buy_number}} + <div class="goods-item"> + <div class="goods-img"> + <a href="\{{product_url}}"> + <img src="\{{default_img}}"/> + </a> + </div> + <div class="goods-info"> + <p class="title"> + <a href="\{{product_url}}">\{{product_name}}</a> + </p> + <p> + 颜色:\{{color_name}} + 尺码:\{{size_name}} + </p> </div> + <div class="goods-price"> + <p>\{{show_price}} x \{{buy_number}}</p> + <p> + <a href="javascript:void(0)" class="cart-goods-del" data-id="\{{goods_incart_id}}" + data-cheapest="\{{is_cheapest_free}}" data-sku="\{{product_sku}}" + data-proid="\{{promotion_id}}" data-num="\{{buy_number}}">删除</a> + </p> + </div> + </div> + \{{/notzero}} + \{{/ totalGoods}} + \{{#if has_promotion}} + <div class="activity-item"> + <label class="activity-name">活动</label> + <h3 class="activity-content"> + \{{#if fit_outlet_promotion }} + 购outlet商品,满¥1999再享9折 + \{{/if}} + \{{#if has_other_promotion}} + <span class="mycart_i_down" title="更多"></span> + \{{/if}} + \{{#if has_first_promotion}} + \{{first_promotions.promotion_title}} + \{{/if}} + </h3> + </div> + \{{/if}} + \{{#if fit_free_shipping }} + <div class="activity-item"> + <label class="activity-name">免运费</label> + <h3 class="activity-content">全场满 ¥\{{fit_free_shipping}}免运费</h3> + </div> + \{{/if}} + </div> + <div class="go-full-cart"> + <div> + <a href="//www.yohobuy.com/shopping/cart">去购物车结算</a> </div> </div> - \{{/carData}} + </div> + \{{/carData}} </script> <script type="text/html" id="search-suggest-tml"> - \{{#data}} - <li> - <a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}" act="\{{href}}"> - <span class="searchvalue" >\{{keyword}}</span> - <span class="valuenum">约\{{count}}个商品</span> - </a> - </li> - \{{/data}} + \{{#data}} + <li> + <a style="display: block;" href="\{{href}}" class="clearfix clear search-item" title="\{{keyword}}" + act="\{{href}}"> + <span class="searchvalue">\{{keyword}}</span> + <span class="valuenum">约\{{count}}个商品</span> + </a> + </li> + \{{/data}} </script> </div> diff --git a/doraemon/views/partial/product/latest-walk.hbs b/doraemon/views/partial/product/latest-walk.hbs index 53fcf36..9b393ad 100644 --- a/doraemon/views/partial/product/latest-walk.hbs +++ b/doraemon/views/partial/product/latest-walk.hbs @@ -1,9 +1,11 @@ -{{#if latestWalk}} - <input id="latest-walk-count" type="hidden" value="{{latestWalk}}"> - <div class="latest-walk"> - <h2>最近浏览的商品</h2> - <div id="latest-walk-goods" class="goods clearfix"></div> - </div> +{{#unless @root.pc.product.removeRecentView}} + {{#if latestWalk}} + <input id="latest-walk-count" type="hidden" value="{{latestWalk}}"> + <div class="latest-walk"> + <h2>最近浏览的商品</h2> + <div id="latest-walk-goods" class="goods clearfix"></div> + </div> - {{> product/latest-walk-tpl}} -{{/if}} + {{> product/latest-walk-tpl}} + {{/if}} +{{/unless}} diff --git a/package.json b/package.json index f10ca9b..6eaa18a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yohobuy-node", - "version": "5.1.1", + "version": "5.1.2", "private": true, "description": "A New Yohobuy Project With Express", "repository": { @@ -34,19 +34,14 @@ "bluebird": "^3.4.0", "body-parser": "^1.15.0", "captchapng": "0.0.1", - "connect-memcached": "^0.2.0", + "compression": "^1.6.2", "cookie-parser": "^1.4.3", + "cookie-session": "^1.2.0", "express": "^4.13.1", - "handlebars": "^4.0.5", - "express-handlebars": "^3.0.0", - "express-session": "^1.13.0", - "influxdb-winston": "^1.0.1", "lodash": "^4.13.1", "md5": "^2.1.0", - "memcached": "^2.2.2", "moment": "^2.14.1", "morgan": "^1.7.0", - "oneapm": "^1.2.20", "passport": "^0.3.2", "passport-douban": "0.0.1", "passport-local": "^1.0.0", @@ -56,13 +51,10 @@ "passport-strategy": "1.x.x", "passport-weixin": "^0.1.0", "request-ip": "^1.2.2", - "request-promise": "^3.0.0", "serve-favicon": "^2.3.0", "uuid": "^2.0.2", - "winston": "^2.2.0", - "winston-daily-rotate-file": "^1.1.4", - "yoho-express-session": "^1.14.1", - "yoho-node-lib": "0.1.28" + "yoho-node-lib": "0.1.28", + "yoho-zookeeper": "^1.0.3" }, "devDependencies": { "autoprefixer": "^6.3.6", diff --git a/public/img/guang/collocation.png b/public/img/guang/collocation.png new file mode 100644 index 0000000..8e5f46f Binary files /dev/null and b/public/img/guang/collocation.png differ diff --git a/public/img/guang/fashion-good.png b/public/img/guang/fashion-good.png new file mode 100644 index 0000000..508d7b4 Binary files /dev/null and b/public/img/guang/fashion-good.png differ diff --git a/public/img/guang/fashion-man.png b/public/img/guang/fashion-man.png new file mode 100644 index 0000000..05a6275 Binary files /dev/null and b/public/img/guang/fashion-man.png differ diff --git a/public/img/guang/topic.png b/public/img/guang/topic.png new file mode 100644 index 0000000..e832ae4 Binary files /dev/null and b/public/img/guang/topic.png differ diff --git a/public/img/sprite.home.png b/public/img/sprite.home.png index afc26e6..b53b663 100644 Binary files a/public/img/sprite.home.png and b/public/img/sprite.home.png differ diff --git a/public/js/brands/brands.page.js b/public/js/brands/brands.page.js index 1d6c1bb..5c29160 100644 --- a/public/js/brands/brands.page.js +++ b/public/js/brands/brands.page.js @@ -13,7 +13,6 @@ var $tabs = $('.brands-tabs'); var $list = $('.brands-list'); var $gory = $('.brands-category'); var $news = $('.news-txt ul'); -var $clearfix = $list.find('dl.clearfix'); var $brand = $list.find('li>a'); var $category = $gory.find('a'); var $tab = $tabs.find('li>a'); @@ -175,6 +174,9 @@ function bindTemplete($select, data, tmp) { // 鼠标悬浮品牌,请求数据,并且展示 function bindHoverEvent() { + if (String($list.data('ishover')) === 'true') { + return; + } $brand.unbind('mouseenter').unbind('mouseleave').hover(function() { var $this = $(this); var key = $this.attr('data-key'); @@ -210,21 +212,4 @@ function bindHoverEvent() { }); } -if ($clearfix.length < 26) { - $.ajax({ - url: '/brands/brandList', - type: 'POST', - data: { - start: $clearfix.length ? ($clearfix.length + 1) : 1 - }, - success: function(_data) { - if (_data) { - $list.append(_data); - $brand = $list.find('li>a'); - bindHoverEvent(); - } - } - }); -} - bindHoverEvent(); diff --git a/public/js/common.js b/public/js/common.js index c571b17..58182eb 100644 --- a/public/js/common.js +++ b/public/js/common.js @@ -94,6 +94,15 @@ function getShoppingKey() { return JSON.parse(c).k; } +// page cache改造-前端移动端检测 +(function() { + var mrefer = $('#m-refer').val(); + + if (mrefer && window.navigator.userAgent.match(/(nokia|iphone|android|ipad|motorola|^mot\-|softbank|foma|docomo|kddi|up\.browser|up\.link|htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|jig\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\s*mobi|opera\*mini|320x320|240x320|176x220)/i)) { // eslint-disable-line + window.location = mrefer; + } +}()); + // YAS统计代码 (function(w, d, s, j, f) { var a = d.createElement(s); diff --git a/public/js/guang/detail.page.js b/public/js/guang/detail.page.js index c2dde86..26f1b77 100644 --- a/public/js/guang/detail.page.js +++ b/public/js/guang/detail.page.js @@ -109,10 +109,15 @@ $('#collect-btn').click(function() { case 401: // 防止从已有col的页面再次进行跳转后出错的情况 - if (/\?col=(1|0)/.test(location.href)) { - hrefUrl = location.href.replace(/\?col=(1|0)/, '?col=' + col); + if (/(\?|&)col=(1|0)/.test(location.href)) { + // hrefUrl = location.href.replace(/\?col=(1|0)/, '?col=' + col); + hrefUrl = location.href.replace(/(\?|&)col=(1|0)/, '$1col=x'); } else { - hrefUrl = location.href + '?col=' + col; + if (location.href.indexOf('?') >= 0) { + hrefUrl = location.href + '&col=' + col; + } else { + hrefUrl = location.href + '?col=' + col; + } } location.href = '//www.yohobuy.com/signin.html?refer=' + encodeURI(hrefUrl); break; @@ -121,10 +126,10 @@ $('#collect-btn').click(function() { // alert(data.message); break; case 200: - if (/\?col=(1|0)/.test(location.href)) { + if (/(\?|&)col=(1|0)/.test(location.href)) { // 如果页面url中含有col,为了防止页面刷新时收藏或者取消收藏会根据col来的问题,进行页面跳转拿掉参数 - location.href = location.href.replace(/\?col=(1|0)/, ''); + location.href = location.href.replace(/(\?|&)col=(1|0)/, ''); } else { $this.toggleClass('collected'); } diff --git a/public/js/header.js b/public/js/header.js index 8474dcc..4f30ba2 100644 --- a/public/js/header.js +++ b/public/js/header.js @@ -39,6 +39,8 @@ var logoAngle = 0, var dataLayer = []; +var cartTimer; + // banner和地址的映射 var bannerMap = { listboys: '4f78b0f418fc42314d8b6e791cfb7fa8', @@ -76,11 +78,8 @@ var bannerMap = { }, cookieMap = {}; -var $signinBtn = $('#signin-url'); -var $regBtn = $('#reg-url'); - -$signinBtn.attr('href', '//www.yohobuy.com/signin.html?refer=' + window.location.href); -$regBtn.attr('href', '//www.yohobuy.com/reg.html?refer=' + window.location.href); +$('#signin-url').attr('href', '//www.yohobuy.com/signin.html?refer=' + window.location.href); +$('#reg-url').attr('href', '//www.yohobuy.com/reg.html?refer=' + window.location.href); // handlebars模板 centerFn = handlebars.compile($('#simple-account-info-tpl').html() || ''); @@ -321,6 +320,11 @@ function syncCratInfo(strG) { domain: '.yohobuy.com' }); } + + if (!$goodsNum || !$goodsNum.length) { + return cartTimer ? clearInterval(cartTimer) : false; + } + if (window.cookie('_g')) { info = $.parseJSON(window.cookie('_g')); total = parseInt(info._nac, 10) + parseInt(info._ac, 10); @@ -538,7 +542,8 @@ if (isSupportCss3Animation()) { syncPageChannel(); getBannerAndNotice(); // 获取头部banner formatThirdMenu(); // 格式化三级菜单 -setInterval(syncCratInfo, 2000); // 定时同步购物车数量 + +cartTimer = setInterval(syncCratInfo, 2000); // 定时同步购物车数量 // 获取头部登陆信息 (function() { @@ -558,7 +563,12 @@ setInterval(syncCratInfo, 2000); // 定时同步购物车数量 $loginBox.show(); }()); -fetchUserInfoEvent.add(syncLoginInfo); +fetchUserInfoEvent.add(function() { + if ($('.simple-user-center').length === 0) { + return; + } + syncLoginInfo(); +}); $myYohoBox.hover(function() { var uid = getUid(); // eslint-disable-line @@ -612,10 +622,10 @@ $searchKey.keyup(function(e) { } else if (e.which === 13) { submitSearch(); } else { - val = val.replace(new RegExp('\'', 'gm'), ''); // 去掉特殊字符 - - $(this).val(val); - searchSuggest(val); + if ($searchSug && $searchSug.length) { + val = val.replace(new RegExp('\'', 'gm'), ''); // 去掉特殊字符 + searchSuggest(val); + } } }).focus(function() { var val = $.trim($(this).val()); @@ -636,39 +646,41 @@ $searchKey.keyup(function(e) { }, 200); }); -$goCart.hover(function() { - var data, _html = ''; +if ($miniCart && $miniCart.length) { + $goCart.hover(function() { + var data, _html = ''; - if ($goCart.hasClass('on-hover')) { - return; - } - - data = $goCart.data(); - if (data && data.num * 1) { - _html = '<div class="loading-cart"><h3>加载中,请稍后</h3></div>'; - loadCartDetail(data.key); - } else { - _html = '<div class="empty-cart"><h3>您的购物车暂无商品</h3></div>'; - } - $miniCart.html(_html); - $goCart.addClass('on-hover'); -}, function() { - $goCart.removeClass('on-hover'); -}); + if ($goCart.hasClass('on-hover')) { + return; + } -$goCart.on('click', '.cart-goods-del', function() { - var $dom = $(this), - data = $dom.data(), - callback; + data = $goCart.data(); + if (data && data.num * 1) { + _html = '<div class="loading-cart"><h3>加载中,请稍后</h3></div>'; + loadCartDetail(data.key); + } else { + _html = '<div class="empty-cart"><h3>您的购物车暂无商品</h3></div>'; + } + $miniCart.html(_html); + $goCart.addClass('on-hover'); + }, function() { + $goCart.removeClass('on-hover'); + }); - if (data) { - callback = function() { - $dom.closest('.goods-item').remove(); - }; - data.key = $goCart.data().key; - delCartGoods(data, callback); - } -}); + $goCart.on('click', '.cart-goods-del', function() { + var $dom = $(this), + data = $dom.data(), + callback; + + if (data) { + callback = function() { + $dom.closest('.goods-item').remove(); + }; + data.key = $goCart.data().key; + delCartGoods(data, callback); + } + }); +} $subNav.on({ mouseenter: function() { diff --git a/public/scss/guang/_index.css b/public/scss/guang/_index.css index 8eb32c8..859d2a8 100644 --- a/public/scss/guang/_index.css +++ b/public/scss/guang/_index.css @@ -167,15 +167,36 @@ z-index: 99; } - .reco { + .type-icon{ + position: absolute; top: 0; left: 66px; height: 32px; width: 32px; - background: resolve("guang/msg-reco.png"); + background: resolve("guang/msg-reco.png") no-repeat; background-size: 100% 100%; z-index: 100; + + &.collocation { + background: resolve('guang/collocation.png') no-repeat; + } + + &.fashion-good { + background: resolve('guang/fashion-good.png') no-repeat; + } + + &.fashion-man { + background: resolve('guang/fashion-man.png') no-repeat; + } + + &.reco { + background: resolve('guang/msg-reco.png') no-repeat; + } + + &.topic { + background: resolve('guang/topic.png') no-repeat; + } } .msg-info { diff --git a/utils/product-process.js b/utils/product-process.js index 1fb45e1..e694717 100644 --- a/utils/product-process.js +++ b/utils/product-process.js @@ -1,6 +1,5 @@ 'use strict'; const _ = require('lodash'); -const fp = require('lodash/fp'); const camelCase = global.yoho.camelCase; const helpers = global.yoho.helpers; @@ -16,9 +15,6 @@ const itemFromBase = { saleSpecial: {domain: 'sale', module: 's'}// sale.yohobuy.com }; -// NOTE: 这里修改了图片质量的参数 -helpers.image = _.flow(helpers.image, fp.replace(/\/quality\/\d*$/, '/quality/90')); - /** * 根据性别来决定 默认图片获取字段 如果是 2、3 *