Authored by htoooth

add newproduct and articles

@@ -6,70 +6,129 @@ @@ -6,70 +6,129 @@
6 6
7 const config = global.yoho.config; 7 const config = global.yoho.config;
8 const redis = global.yoho.redis; 8 const redis = global.yoho.redis;
  9 +const cache = global.yoho.cache;
  10 +const logger = global.yoho.logger;
  11 +
  12 +const SearchServiceModel = require('./search-api');
  13 +const GuangServiceModel = require('../../guang/models/guang-service');
  14 +const productProcess = require('../../../utils/product-process');
  15 +const _ = require('lodash');
  16 +const uuid = require('uuid');
  17 +
  18 +function _cacheGet(key) {
  19 + return cache.get(key).then((data) => {
  20 + if (data) {
  21 + return JSON.parse(data);
  22 + }
  23 +
  24 + return Promise.reject();
  25 + });
  26 +}
  27 +
  28 +// 半个月
  29 +const HALF_MONTH = 60 * 60 * 24 * 15;
  30 +
  31 +function _cacheSave(key, value) {
  32 + return cache.set(key, value, HALF_MONTH)
  33 + .catch(err => logger.error(`save cache data fail:${err.toString()}`));
  34 +}
  35 +
  36 +function _cached(fn, ctx) {
  37 + const pre = (fn.name || 'random:' + uuid.v4()) + ':';
  38 +
  39 + return function() {
  40 + const args = Array.prototype.slice.call(arguments);
  41 + const key = pre + JSON.stringify(args || '[]');
  42 +
  43 + return _cacheGet(key).catch(() => {
  44 + return fn.apply(ctx, args).then(result => {
  45 + _cacheSave(key, result);
  46 + return result;
  47 + });
  48 + });
  49 + };
  50 +}
  51 +
9 52
10 module.exports = class extends global.yoho.BaseModel { 53 module.exports = class extends global.yoho.BaseModel {
11 constructor(ctx) { 54 constructor(ctx) {
12 super(ctx); 55 super(ctx);
  56 +
  57 + this.searchApi = new SearchServiceModel(ctx);
  58 + this.guangService = new GuangServiceModel(ctx);
  59 +
  60 + this.getNewProduct = _cached(this._getNewProduct, this);
  61 + this.getGuangArticles = _cached(this._getGuangArticles, this);
13 } 62 }
14 63
15 /** 64 /**
16 * 商品的 banner 65 * 商品的 banner
17 */ 66 */
18 getProductBannerAsync(pid) { 67 getProductBannerAsync(pid) {
19 - return this.get({data: {  
20 - method: 'web.productBanner.data',  
21 - product_id: pid  
22 - }, params: {  
23 - cache: config.apiCache  
24 - }}); 68 + return this.get({
  69 + data: {
  70 + method: 'web.productBanner.data',
  71 + product_id: pid
  72 + }, params: {
  73 + cache: config.apiCache
  74 + }
  75 + });
25 } 76 }
26 77
27 /** 78 /**
28 * 商品尺寸 79 * 商品尺寸
29 */ 80 */
30 sizeInfoAsync(skn) { 81 sizeInfoAsync(skn) {
31 - return this.get({data: {  
32 - method: 'h5.product.intro',  
33 - productskn: skn  
34 - }, params: {  
35 - cache: config.apiCache  
36 - }}); 82 + return this.get({
  83 + data: {
  84 + method: 'h5.product.intro',
  85 + productskn: skn
  86 + }, params: {
  87 + cache: config.apiCache
  88 + }
  89 + });
37 } 90 }
38 91
39 /** 92 /**
40 * 特殊商品退换货 93 * 特殊商品退换货
41 */ 94 */
42 isSupportReturnedSale(skn) { 95 isSupportReturnedSale(skn) {
43 - return this.get({data: {  
44 - method: 'web.product.refundExchange',  
45 - product_skn: skn  
46 - }, params: {  
47 - cache: config.apiCache  
48 - }}); 96 + return this.get({
  97 + data: {
  98 + method: 'web.product.refundExchange',
  99 + product_skn: skn
  100 + }, params: {
  101 + cache: config.apiCache
  102 + }
  103 + });
49 } 104 }
50 105
51 /** 106 /**
52 * 商品舒适度 107 * 商品舒适度
53 */ 108 */
54 getProductComfortAsync(pid) { 109 getProductComfortAsync(pid) {
55 - return this.get({data: {  
56 - method: 'web.productComfort.data',  
57 - product_id: pid  
58 - }, params: {  
59 - cache: config.apiCache  
60 - }}); 110 + return this.get({
  111 + data: {
  112 + method: 'web.productComfort.data',
  113 + product_id: pid
  114 + }, params: {
  115 + cache: config.apiCache
  116 + }
  117 + });
61 } 118 }
62 119
63 /** 120 /**
64 * 模特试穿 121 * 模特试穿
65 */ 122 */
66 getProductModelTryAsync(skn) { 123 getProductModelTryAsync(skn) {
67 - return this.get({data: {  
68 - method: 'web.productModelTry.data',  
69 - product_skn: skn  
70 - }, params: {  
71 - cache: config.apiCache  
72 - }}); 124 + return this.get({
  125 + data: {
  126 + method: 'web.productModelTry.data',
  127 + product_skn: skn
  128 + }, params: {
  129 + cache: config.apiCache
  130 + }
  131 + });
73 } 132 }
74 133
75 /** 134 /**
@@ -101,9 +160,11 @@ module.exports = class extends global.yoho.BaseModel { @@ -101,9 +160,11 @@ module.exports = class extends global.yoho.BaseModel {
101 data.current_vip_level = vipLevel; 160 data.current_vip_level = vipLevel;
102 } 161 }
103 162
104 - return this.get({data, params: {  
105 - cache: config.apiCache  
106 - }}); 163 + return this.get({
  164 + data, params: {
  165 + cache: config.apiCache
  166 + }
  167 + });
107 } 168 }
108 169
109 /** 170 /**
@@ -115,9 +176,11 @@ module.exports = class extends global.yoho.BaseModel { @@ -115,9 +176,11 @@ module.exports = class extends global.yoho.BaseModel {
115 product_skn: skn 176 product_skn: skn
116 }; 177 };
117 178
118 - return this.get({data, params: {  
119 - cache: config.apiCache  
120 - }}); 179 + return this.get({
  180 + data, params: {
  181 + cache: config.apiCache
  182 + }
  183 + });
121 } 184 }
122 185
123 /** 186 /**
@@ -137,44 +200,52 @@ module.exports = class extends global.yoho.BaseModel { @@ -137,44 +200,52 @@ module.exports = class extends global.yoho.BaseModel {
137 data.product_skn = skn; 200 data.product_skn = skn;
138 } 201 }
139 202
140 - return this.get({data, params: {  
141 - cache: config.apiCache  
142 - }}); 203 + return this.get({
  204 + data, params: {
  205 + cache: config.apiCache
  206 + }
  207 + });
143 } 208 }
144 209
145 /** 210 /**
146 * 店铺推荐 211 * 店铺推荐
147 */ 212 */
148 getShopRecommendAsync(skn, page, limit) { 213 getShopRecommendAsync(skn, page, limit) {
149 - return this.get({data: {  
150 - method: 'web.product.shopRecommend',  
151 - product_skn: skn,  
152 - page: page || 1,  
153 - limit: limit || 20  
154 - }}); 214 + return this.get({
  215 + data: {
  216 + method: 'web.product.shopRecommend',
  217 + product_skn: skn,
  218 + page: page || 1,
  219 + limit: limit || 20
  220 + }
  221 + });
155 } 222 }
156 223
157 /** 224 /**
158 * 套餐和量贩 225 * 套餐和量贩
159 */ 226 */
160 getBundleAsync(skn) { 227 getBundleAsync(skn) {
161 - return this.get({data: {  
162 - method: 'app.query.bundleSkn',  
163 - product_skn: skn  
164 - }}); 228 + return this.get({
  229 + data: {
  230 + method: 'app.query.bundleSkn',
  231 + product_skn: skn
  232 + }
  233 + });
165 } 234 }
166 235
167 /** 236 /**
168 * 找相似 237 * 找相似
169 */ 238 */
170 getLikeAsync(skn, limit) { 239 getLikeAsync(skn, limit) {
171 - return this.get({data: {  
172 - method: 'app.search.findLike',  
173 - limit: limit || 10,  
174 - product_skn: skn  
175 - }, params: {  
176 - cache: 86400  
177 - }}); 240 + return this.get({
  241 + data: {
  242 + method: 'app.search.findLike',
  243 + limit: limit || 10,
  244 + product_skn: skn
  245 + }, params: {
  246 + cache: 86400
  247 + }
  248 + });
178 } 249 }
179 250
180 // 根据small_sort从redis获取分类下的关键词 251 // 根据small_sort从redis获取分类下的关键词
@@ -183,4 +254,37 @@ module.exports = class extends global.yoho.BaseModel { @@ -183,4 +254,37 @@ module.exports = class extends global.yoho.BaseModel {
183 return res[0]; 254 return res[0];
184 }); 255 });
185 } 256 }
  257 +
  258 + _getNewProduct(sort) {
  259 + return this.searchApi.getProductList({
  260 + limit: 10,
  261 + sort: sort,
  262 + order: 's_t_desc'
  263 + }).then(result => {
  264 + if (result.code === 200) {
  265 + return productProcess.processProductList(result.data.product_list);
  266 + }
  267 +
  268 + return [];
  269 + }).then(productList => {
  270 + return productList.map((it) => ({
  271 + url: it.url,
  272 + name: it.product_name
  273 + }));
  274 + });
  275 + }
  276 +
  277 + _getGuangArticles(skn) { //eslint-disable-line
  278 + return this.guangService.getArticleList(
  279 + 3, null, null, null, _.random(1, 700), null, null, 10, null, null
  280 + ).then(result => {
  281 + return _.get(result, 'msgs', []).map((article) => ({
  282 + url: article.url,
  283 + name: article.title
  284 + }));
  285 + });
  286 + }
186 }; 287 };
  288 +
  289 +
  290 +
@@ -37,7 +37,7 @@ const tdk = require('../../../utils/getTDK'); @@ -37,7 +37,7 @@ const tdk = require('../../../utils/getTDK');
37 const productProcess = require('../../../utils/product-process'); 37 const productProcess = require('../../../utils/product-process');
38 38
39 function _getProductAdditionInfoAsync(data) { 39 function _getProductAdditionInfoAsync(data) {
40 - return co(function * () { 40 + return co(function* () {
41 let productId = _.get(data, 'product_id', 0); 41 let productId = _.get(data, 'product_id', 0);
42 let brandId = _.get(data, 'brand_info.brand_id', 0); 42 let brandId = _.get(data, 'brand_info.brand_id', 0);
43 43
@@ -54,7 +54,7 @@ function _getProductAdditionInfoAsync(data) { @@ -54,7 +54,7 @@ function _getProductAdditionInfoAsync(data) {
54 } 54 }
55 55
56 function _getProductIntroAsync(productId, productSkn) { 56 function _getProductIntroAsync(productId, productSkn) {
57 - return co(function * () { 57 + return co(function* () {
58 let result = yield Promise.props({ 58 let result = yield Promise.props({
59 sizeInfo: this.productAPI.sizeInfoAsync(productSkn), 59 sizeInfo: this.productAPI.sizeInfoAsync(productSkn),
60 productComfort: this.productAPI.getProductComfortAsync(productId), 60 productComfort: this.productAPI.getProductComfortAsync(productId),
@@ -71,7 +71,7 @@ function _getProductIntroAsync(productId, productSkn) { @@ -71,7 +71,7 @@ function _getProductIntroAsync(productId, productSkn) {
71 * bid : brand id 71 * bid : brand id
72 */ 72 */
73 function _getProductFavoriteDataAsync(uid, pid) { 73 function _getProductFavoriteDataAsync(uid, pid) {
74 - return co(function*() { 74 + return co(function* () {
75 let result = { 75 let result = {
76 product: false, 76 product: false,
77 brand: false 77 brand: false
@@ -241,7 +241,7 @@ function _getSkuDataByProductBaseInfo(data) { @@ -241,7 +241,7 @@ function _getSkuDataByProductBaseInfo(data) {
241 }; 241 };
242 } 242 }
243 243
244 - skuGoods = _.get(data, 'goods_list', []).reduce((goodsDetailList, goods)=> { 244 + skuGoods = _.get(data, 'goods_list', []).reduce((goodsDetailList, goods) => {
245 245
246 // 如果status为0,即skc下架时就跳过该商品$value['status'] === 0 246 // 如果status为0,即skc下架时就跳过该商品$value['status'] === 0
247 let goodsDetail = {}; 247 let goodsDetail = {};
@@ -579,7 +579,7 @@ function _sizeInfoBoSort(sizeInfoBo) { @@ -579,7 +579,7 @@ function _sizeInfoBoSort(sizeInfoBo) {
579 return {}; 579 return {};
580 } 580 }
581 581
582 - _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> { 582 + _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek) => {
583 let sortAttr = {}; 583 let sortAttr = {};
584 584
585 sizeBoList.sortAttributes.forEach(sortAttributes => { 585 sizeBoList.sortAttributes.forEach(sortAttributes => {
@@ -589,7 +589,7 @@ function _sizeInfoBoSort(sizeInfoBo) { @@ -589,7 +589,7 @@ function _sizeInfoBoSort(sizeInfoBo) {
589 sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr; 589 sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
590 }); 590 });
591 591
592 - _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> { 592 + _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek) => {
593 let sortAttr = []; 593 let sortAttr = [];
594 594
595 sizeInfoBo.sizeAttributeBos.forEach(val => { 595 sizeInfoBo.sizeAttributeBos.forEach(val => {
@@ -991,7 +991,7 @@ function _getCoupon(coupons) { @@ -991,7 +991,7 @@ function _getCoupon(coupons) {
991 991
992 let pickProp = Fn.pick(['couponName', 'amount', 'couponId', 'acquireStatus', 'rule4ShortName']); 992 let pickProp = Fn.pick(['couponName', 'amount', 'couponId', 'acquireStatus', 'rule4ShortName']);
993 let encodeId = Fn.update('couponId', (cid) => crypto.encryption(null, cid + '')); 993 let encodeId = Fn.update('couponId', (cid) => crypto.encryption(null, cid + ''));
994 - let replace = Fn.update('rule4ShortName', (r)=> r.replace(/¥/g, '¥')); 994 + let replace = Fn.update('rule4ShortName', (r) => r.replace(/¥/g, '¥'));
995 995
996 return Fn.map(Fn.pipe(pickProp, encodeId, replace))(couponList); 996 return Fn.map(Fn.pipe(pickProp, encodeId, replace))(couponList);
997 } 997 }
@@ -1020,7 +1020,7 @@ function _isOfflineSell(status) { @@ -1020,7 +1020,7 @@ function _isOfflineSell(status) {
1020 * @return result Object 格式化数据 1020 * @return result Object 格式化数据
1021 */ 1021 */
1022 function _detailDataPkg(origin, uid, vipLevel, cookies) { 1022 function _detailDataPkg(origin, uid, vipLevel, cookies) {
1023 - return co(function*() { 1023 + return co(function* () {
1024 if (_.isEmpty(origin) || _.isEmpty(origin.data)) { 1024 if (_.isEmpty(origin) || _.isEmpty(origin.data)) {
1025 return {}; 1025 return {};
1026 } 1026 }
@@ -1101,6 +1101,12 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) { @@ -1101,6 +1101,12 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
1101 // 店铺推荐直出(seo需要) 1101 // 店铺推荐直出(seo需要)
1102 requestApi.shopRecommend = this.productAPI.getShopRecommendAsync(result.skn); 1102 requestApi.shopRecommend = this.productAPI.getShopRecommendAsync(result.skn);
1103 1103
  1104 + // 新品推荐
  1105 + requestApi.newProduct = this.productAPI.getNewProduct(result.smallSortId);
  1106 +
  1107 + // 文章推荐
  1108 + requestApi.articles = this.productAPI.getGuangArticles(result.skn);
  1109 +
1104 let requestData = yield Promise.props(requestApi); 1110 let requestData = yield Promise.props(requestApi);
1105 1111
1106 let additionalData = requestData.addition; 1112 let additionalData = requestData.addition;
@@ -1112,6 +1118,14 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) { @@ -1112,6 +1118,14 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
1112 let recommendKeywords = JSON.parse(requestData.recommendAsync || '[]'); 1118 let recommendKeywords = JSON.parse(requestData.recommendAsync || '[]');
1113 let shopRecommend = requestData.shopRecommend; 1119 let shopRecommend = requestData.shopRecommend;
1114 let alike = requestData.alike; 1120 let alike = requestData.alike;
  1121 + let newProduct = requestData.newProduct;
  1122 + let articles = requestData.articles;
  1123 +
  1124 + // 文章推荐
  1125 + result.recommendArticles = articles;
  1126 +
  1127 + // 推荐最新商品
  1128 + result.recommendNewProducts = newProduct;
1115 1129
1116 // 处理相似商品 1130 // 处理相似商品
1117 result.alike = productProcess.processProductList( 1131 result.alike = productProcess.processProductList(
@@ -1405,8 +1419,8 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) { @@ -1405,8 +1419,8 @@ function _detailDataPkg(origin, uid, vipLevel, cookies) {
1405 statGoodsInfo.productName = result.name.replace('\'', '’'); 1419 statGoodsInfo.productName = result.name.replace('\'', '’');
1406 statGoodsInfo.brandName = (result.brandName || '').replace('\'', '’'); 1420 statGoodsInfo.brandName = (result.brandName || '').replace('\'', '’');
1407 statGoodsInfo.marketPrice = (result.marketPrice ? 1421 statGoodsInfo.marketPrice = (result.marketPrice ?
1408 - result.marketPrice :  
1409 - result.presalePrice).replace('¥', ''); // 数字 1422 + result.marketPrice :
  1423 + result.presalePrice).replace('¥', ''); // 数字
1410 statGoodsInfo.salePrice = (result.salePrice ? 1424 statGoodsInfo.salePrice = (result.salePrice ?
1411 result.salePrice : 1425 result.salePrice :
1412 (result.marketPrice || result.presalePrice)).replace('¥', ''); // 数字 1426 (result.marketPrice || result.presalePrice)).replace('¥', ''); // 数字
@@ -1497,7 +1511,7 @@ function _removeSalePrice(productInfo) { @@ -1497,7 +1511,7 @@ function _removeSalePrice(productInfo) {
1497 * 获取某一个商品详情主页面 1511 * 获取某一个商品详情主页面
1498 */ 1512 */
1499 function showMainAsync(req, data) { 1513 function showMainAsync(req, data) {
1500 - return co(function * () { 1514 + return co(function* () {
1501 // 获取商品基本信息 1515 // 获取商品基本信息
1502 let productData = yield this.productAPI.getProductAsync( 1516 let productData = yield this.productAPI.getProductAsync(
1503 {skn: data.skn}, data.uid, data.isStudent, data.vipLevel 1517 {skn: data.skn}, data.uid, data.isStudent, data.vipLevel
@@ -1519,10 +1533,10 @@ function showMainAsync(req, data) { @@ -1519,10 +1533,10 @@ function showMainAsync(req, data) {
1519 ); 1533 );
1520 1534
1521 let requestData = yield Promise.all([ 1535 let requestData = yield Promise.all([
1522 - HeaderModel.requestHeaderData(data.channel), // 通用头部数据 1536 + HeaderModel.requestHeaderData(data.channel), // 通用头部数据
1523 _getProductIntroAsync.call(this, productId, productSkn), // 商品详细介绍 1537 _getProductIntroAsync.call(this, productId, productSkn), // 商品详细介绍
1524 - curUserProduct.call(this, productData), // 商品详细价格  
1525 - tdk('skn', data.skn, req) // seo 1538 + curUserProduct.call(this, productData), // 商品详细价格
  1539 + tdk('skn', data.skn, req) // seo
1526 ]); 1540 ]);
1527 1541
1528 let navigatorHeader = requestData[0]; 1542 let navigatorHeader = requestData[0];
@@ -1589,7 +1603,7 @@ function showMainAsync(req, data) { @@ -1589,7 +1603,7 @@ function showMainAsync(req, data) {
1589 * 获取某一个商品详情主页面 1603 * 获取某一个商品详情主页面
1590 */ 1604 */
1591 function showMainBackAsync(data) { 1605 function showMainBackAsync(data) {
1592 - return co(function * () { 1606 + return co(function* () {
1593 1607
1594 // 获取商品基本信息 1608 // 获取商品基本信息
1595 let productData = yield this.productAPI.getProductAsync({pid: data.pid}); 1609 let productData = yield this.productAPI.getProductAsync({pid: data.pid});
@@ -1600,7 +1614,7 @@ function showMainBackAsync(data) { @@ -1600,7 +1614,7 @@ function showMainBackAsync(data) {
1600 1614
1601 1615
1602 function recommendAsync(skn, page, limit) { 1616 function recommendAsync(skn, page, limit) {
1603 - return co(function * () { 1617 + return co(function* () {
1604 let recommendData = yield this.productAPI.getShopRecommendAsync(skn, page, limit); 1618 let recommendData = yield this.productAPI.getShopRecommendAsync(skn, page, limit);
1605 1619
1606 if (_.get(recommendData, 'code', 400) !== 200) { 1620 if (_.get(recommendData, 'code', 400) !== 200) {
@@ -1705,7 +1719,6 @@ function getPackage(skn) { @@ -1705,7 +1719,6 @@ function getPackage(skn) {
1705 }).bind(this)(); 1719 }).bind(this)();
1706 } 1720 }
1707 1721
1708 -  
1709 module.exports = class extends global.yoho.BaseModel { 1722 module.exports = class extends global.yoho.BaseModel {
1710 constructor(ctx) { 1723 constructor(ctx) {
1711 super(ctx); 1724 super(ctx);
@@ -8,3 +8,25 @@ @@ -8,3 +8,25 @@
8 </p> 8 </p>
9 </div> 9 </div>
10 {{/if}} 10 {{/if}}
  11 +
  12 +{{#if goodsInfo.recommendNewProducts}}
  13 + <div class="recommend-keywords">
  14 + <h3>新品推荐</h3>
  15 + <p>
  16 + {{# goodsInfo.recommendNewProducts}}
  17 + <a href="{{url}}" title="{{name}}" target="_blank">{{name}}</a>
  18 + {{/ goodsInfo.recommendNewProducts}}
  19 + </p>
  20 + </div>
  21 +{{/if}}
  22 +
  23 +{{#if goodsInfo.recommendArticles}}
  24 + <div class="recommend-keywords">
  25 + <h3>文章推荐</h3>
  26 + <p>
  27 + {{# goodsInfo.recommendArticles}}
  28 + <a href="{{url}}" title="{{name}}" target="_blank">{{name}}</a>
  29 + {{/ goodsInfo.recommendArticles}}
  30 + </p>
  31 + </div>
  32 +{{/if}}