|
|
1
|
+/**
|
|
|
2
|
+ * 商品详情models
|
|
|
3
|
+ * @author: xuqi<qi.xu@yoho.cn>
|
|
|
4
|
+ * @date: 2016/5/6
|
|
|
5
|
+ */
|
|
|
6
|
+
|
|
|
7
|
+'use strict';
|
|
|
8
|
+
|
|
|
9
|
+const Promise = require('bluebird');
|
|
|
10
|
+const co = Promise.coroutine;
|
|
|
11
|
+const _ = require('lodash');
|
|
|
12
|
+const helpers = global.yoho.helpers;
|
|
|
13
|
+
|
|
|
14
|
+const productAPI = require('./product-api');
|
|
|
15
|
+
|
|
|
16
|
+const _getProductIntroAsync = (productSkn) => {
|
|
|
17
|
+ return co(function * () {
|
|
|
18
|
+ let result = yield Promise.props({
|
|
|
19
|
+ sizeInfo: productAPI.sizeInfoAsync(productSkn)
|
|
|
20
|
+ });
|
|
|
21
|
+
|
|
|
22
|
+ return result;
|
|
|
23
|
+ })();
|
|
|
24
|
+};
|
|
|
25
|
+
|
|
|
26
|
+/**
|
|
|
27
|
+ * 获得sku商品数据
|
|
|
28
|
+ */
|
|
|
29
|
+const _getSkuDataByProductBaseInfo = (data) => {
|
|
|
30
|
+ let totalStorageNum = 0;
|
|
|
31
|
+ let skuGoods = null;// sku商品
|
|
|
32
|
+ let defaultImage = '';// 默认图
|
|
|
33
|
+ let chooseSkuFlag = false; // 选中状态
|
|
|
34
|
+
|
|
|
35
|
+ if (_.isEmpty(_.get(data, 'goods_list', []))) {
|
|
|
36
|
+ return {
|
|
|
37
|
+ totalStorageNum,
|
|
|
38
|
+ skuGoods,
|
|
|
39
|
+ defaultImage
|
|
|
40
|
+ };
|
|
|
41
|
+ }
|
|
|
42
|
+
|
|
|
43
|
+ skuGoods = _.get(data, 'goods_list', []).reduce((acc, cur, pos)=> {
|
|
|
44
|
+
|
|
|
45
|
+ // 如果status为0,即skc下架时就跳过该商品$value['status'] === 0
|
|
|
46
|
+ let goodsGroup = {};
|
|
|
47
|
+
|
|
|
48
|
+ if (_.isEmpty(cur.color_image)) {
|
|
|
49
|
+ return acc;
|
|
|
50
|
+ }
|
|
|
51
|
+
|
|
|
52
|
+ if (cur.images_list) {
|
|
|
53
|
+ // 商品列表
|
|
|
54
|
+ goodsGroup.productSkc = cur.product_skc;
|
|
|
55
|
+ goodsGroup.src = helpers.image(cur.color_image, 40, 40);
|
|
|
56
|
+ goodsGroup.title = `${_.trim(data.product_name)} ${cur.color_name}`;
|
|
|
57
|
+ goodsGroup.name = cur.color_name;
|
|
|
58
|
+ goodsGroup.focus = false;
|
|
|
59
|
+ goodsGroup.total = 0;
|
|
|
60
|
+ goodsGroup.thumbs = [];
|
|
|
61
|
+ goodsGroup.size = [];
|
|
|
62
|
+ }
|
|
|
63
|
+
|
|
|
64
|
+ _.get(cur, 'images_list', []).forEach((good) => {
|
|
|
65
|
+ if (good.image_url) {
|
|
|
66
|
+ goodsGroup.thumbs.push({
|
|
|
67
|
+ url: '',
|
|
|
68
|
+ shower: helpers.image(good.image_url, 420, 560),
|
|
|
69
|
+ img: helpers.image(good.image_url, 75, 100)
|
|
|
70
|
+ });
|
|
|
71
|
+ }
|
|
|
72
|
+ });
|
|
|
73
|
+
|
|
|
74
|
+ // 缩略图空,不显示
|
|
|
75
|
+ if (_.isEmpty(goodsGroup.thumbs)) {
|
|
|
76
|
+ return acc;
|
|
|
77
|
+ }
|
|
|
78
|
+
|
|
|
79
|
+ // 默认第一张图片
|
|
|
80
|
+ if (pos === 0) {
|
|
|
81
|
+ defaultImage = helpers.image(cur.color_image, 420, 560);
|
|
|
82
|
+ }
|
|
|
83
|
+
|
|
|
84
|
+ // 商品的尺码列表
|
|
|
85
|
+ _.get(cur, 'size_list', []).forEach((size) => {
|
|
|
86
|
+ if (data.attribute === 3) {
|
|
|
87
|
+ // 虚拟商品,门票默认最大为4,
|
|
|
88
|
+ size.storage_number = size.storage_number > 4 ? 4 : size.storage_number;
|
|
|
89
|
+ }
|
|
|
90
|
+
|
|
|
91
|
+ // 如果status为0,即skc下架时就跳过该商品
|
|
|
92
|
+ if (cur.status === 0) {
|
|
|
93
|
+ size.storage_number = 0;
|
|
|
94
|
+ }
|
|
|
95
|
+
|
|
|
96
|
+ goodsGroup.size.push({
|
|
|
97
|
+ name: size.size_name,
|
|
|
98
|
+ sku: size.product_sku,
|
|
|
99
|
+ num: _.parseInt(size.storage_number),
|
|
|
100
|
+ goodsId: size.size_id
|
|
|
101
|
+ });
|
|
|
102
|
+
|
|
|
103
|
+ // 单个sku商品的总数
|
|
|
104
|
+ goodsGroup.total += _.parseInt(size.storage_number);
|
|
|
105
|
+
|
|
|
106
|
+ if (goodsGroup.total > 0 && !chooseSkuFlag) { // 默认选中该sku商品
|
|
|
107
|
+ goodsGroup.focus = true;
|
|
|
108
|
+ chooseSkuFlag = true;// 选中sku商品
|
|
|
109
|
+ }
|
|
|
110
|
+
|
|
|
111
|
+ totalStorageNum += _.parseInt(size.storage_number);
|
|
|
112
|
+
|
|
|
113
|
+ });
|
|
|
114
|
+
|
|
|
115
|
+ acc.push(goodsGroup);
|
|
|
116
|
+ return acc;
|
|
|
117
|
+ }, []);
|
|
|
118
|
+
|
|
|
119
|
+ if (!_.isEmpty(skuGoods) && !chooseSkuFlag) { // 没有选中一个sku商品,默认选中第一个sku商品
|
|
|
120
|
+ _.head(skuGoods).focus = true;
|
|
|
121
|
+ }
|
|
|
122
|
+
|
|
|
123
|
+ return {
|
|
|
124
|
+ defaultImage: defaultImage,
|
|
|
125
|
+ skuGoods: skuGoods,
|
|
|
126
|
+ totalStorageNum: totalStorageNum
|
|
|
127
|
+ };
|
|
|
128
|
+};
|
|
|
129
|
+
|
|
|
130
|
+/**
|
|
|
131
|
+ * 使sizeBoList id以 sizeAttributeBos id顺序一样
|
|
|
132
|
+ * @param sizeInfoBo
|
|
|
133
|
+ */
|
|
|
134
|
+const _sizeInfoBoSort = (sizeInfoBo) => {
|
|
|
135
|
+ if (!sizeInfoBo.sizeBoList || !sizeInfoBo.sizeAttributeBos) {
|
|
|
136
|
+ return {};
|
|
|
137
|
+ }
|
|
|
138
|
+
|
|
|
139
|
+ _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
|
|
|
140
|
+ let sortAttr = {};
|
|
|
141
|
+
|
|
|
142
|
+ sizeBoList.sortAttributes.forEach(sortAttributes => {
|
|
|
143
|
+ sortAttr[sortAttributes.id] = sortAttributes;
|
|
|
144
|
+ });
|
|
|
145
|
+
|
|
|
146
|
+ sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
|
|
|
147
|
+ });
|
|
|
148
|
+
|
|
|
149
|
+ _.get(sizeInfoBo, 'sizeBoList', []).forEach((sizeBoList, sizek)=> {
|
|
|
150
|
+ let sortAttr = [];
|
|
|
151
|
+
|
|
|
152
|
+ sizeInfoBo.sizeAttributeBos.forEach(val => {
|
|
|
153
|
+ if (sizeBoList.sortAttributes[val.id]) {
|
|
|
154
|
+ sortAttr.push(sizeBoList.sortAttributes[val.id]);
|
|
|
155
|
+ }
|
|
|
156
|
+ });
|
|
|
157
|
+
|
|
|
158
|
+ sizeInfoBo.sizeBoList[sizek].sortAttributes = sortAttr;
|
|
|
159
|
+
|
|
|
160
|
+ });
|
|
|
161
|
+
|
|
|
162
|
+ return sizeInfoBo;
|
|
|
163
|
+};
|
|
|
164
|
+
|
|
|
165
|
+/**
|
|
|
166
|
+ * 获取尺寸信息
|
|
|
167
|
+ * @param sizeInfo
|
|
|
168
|
+ * @returns {{}}
|
|
|
169
|
+ */
|
|
|
170
|
+const _getSizeData = (sizeInfo) => {
|
|
|
171
|
+
|
|
|
172
|
+ // 尺码信息
|
|
|
173
|
+ if (!_.has(sizeInfo, 'sizeInfoBo')) {
|
|
|
174
|
+ return {};
|
|
|
175
|
+ }
|
|
|
176
|
+
|
|
|
177
|
+ sizeInfo.sizeInfoBo = _sizeInfoBoSort(sizeInfo.sizeInfoBo);
|
|
|
178
|
+
|
|
|
179
|
+ let boyReference = _.get(sizeInfo, 'productExtra.boyReference', false);
|
|
|
180
|
+ let girlReference = _.get(sizeInfo, 'productExtra.girlReference', false);
|
|
|
181
|
+ let gender = _.get(sizeInfo, 'productDescBo.gender', 3);
|
|
|
182
|
+ let referenceName = (function() {
|
|
|
183
|
+ if (gender === 3 && boyReference) {
|
|
|
184
|
+ return '参考尺码(男)';
|
|
|
185
|
+ } else if (gender === 3 && girlReference) {
|
|
|
186
|
+ return '参考尺码(女)';
|
|
|
187
|
+ } else {
|
|
|
188
|
+ return '参考尺码';
|
|
|
189
|
+ }
|
|
|
190
|
+ }());
|
|
|
191
|
+
|
|
|
192
|
+ // 判断是否显示参考尺码
|
|
|
193
|
+ let showReference = (boyReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].boyReferSize', false)) ||
|
|
|
194
|
+ (girlReference && _.get(sizeInfo, 'sizeInfoBo.sizeBoList[0].girlReferSize', false));
|
|
|
195
|
+
|
|
|
196
|
+ if (!_.has(sizeInfo, 'sizeInfoBo.sizeAttributeBos')) {
|
|
|
197
|
+ return {};
|
|
|
198
|
+ }
|
|
|
199
|
+
|
|
|
200
|
+ // 尺码信息头部
|
|
|
201
|
+ let size = {
|
|
|
202
|
+ thead: [{name: '吊牌尺码', id: ''}],
|
|
|
203
|
+ tbody: []
|
|
|
204
|
+ };
|
|
|
205
|
+
|
|
|
206
|
+ // 显示参考尺码
|
|
|
207
|
+ if (showReference) {
|
|
|
208
|
+ size.thead[1] = {name: referenceName, id: ''};
|
|
|
209
|
+ }
|
|
|
210
|
+
|
|
|
211
|
+ _.get(sizeInfo, 'sizeInfoBo.sizeAttributeBos', []).forEach((value) => {
|
|
|
212
|
+ size.thead.push({
|
|
|
213
|
+ name: value.attributeName || ' ',
|
|
|
214
|
+ id: value.id
|
|
|
215
|
+ });
|
|
|
216
|
+ });
|
|
|
217
|
+
|
|
|
218
|
+ _.get(sizeInfo, 'sizeInfoBo.sizeBoList', []).forEach((value) => {
|
|
|
219
|
+ let sizes = [];
|
|
|
220
|
+
|
|
|
221
|
+ // 吊牌尺码
|
|
|
222
|
+ sizes.push(value.sizeName);
|
|
|
223
|
+
|
|
|
224
|
+ // 判断是否显示参考尺码
|
|
|
225
|
+ if (boyReference && (gender === 1 || gender === 3) && showReference) {
|
|
|
226
|
+ sizes.push(_.get(value, 'boyReferSize.referenceName', ' '));
|
|
|
227
|
+ } else if (girlReference && (gender === 2 || gender === 3) && showReference) {
|
|
|
228
|
+ sizes.push(_.get(value, 'girlReferSize.referenceName', ' '));
|
|
|
229
|
+ } else {
|
|
|
230
|
+ if (size.thead[1] && showReference) {
|
|
|
231
|
+ size.thead[1] = {};
|
|
|
232
|
+ }
|
|
|
233
|
+ }
|
|
|
234
|
+
|
|
|
235
|
+ // 其他尺码信息
|
|
|
236
|
+ _.get(value, 'sortAttributes', []).forEach(attr => {
|
|
|
237
|
+ sizes.push(_.get(attr, 'sizeValue', ' '));
|
|
|
238
|
+ });
|
|
|
239
|
+
|
|
|
240
|
+ // 尺码信息
|
|
|
241
|
+ size.tbody.push(sizes);
|
|
|
242
|
+ });
|
|
|
243
|
+
|
|
|
244
|
+ // 参考尺码为空
|
|
|
245
|
+ if (_.isEmpty(size.thead[1]) && showReference) {
|
|
|
246
|
+ // 移除这个值
|
|
|
247
|
+ size.thead.splice(1, 1);
|
|
|
248
|
+ }
|
|
|
249
|
+
|
|
|
250
|
+ // 测量方式
|
|
|
251
|
+ if (sizeInfo.sizeImage) {
|
|
|
252
|
+ size.sizeImg = sizeInfo.sizeImage;
|
|
|
253
|
+ }
|
|
|
254
|
+
|
|
|
255
|
+ return size;
|
|
|
256
|
+};
|
|
|
257
|
+
|
|
|
258
|
+/**
|
|
|
259
|
+ * 商品尺码信息
|
|
|
260
|
+ *
|
|
|
261
|
+ * @param productSkn
|
|
|
262
|
+ * @param maxSortId
|
|
|
263
|
+ * @return object
|
|
|
264
|
+ */
|
|
|
265
|
+const _getIntroInfo = (productSkn, additionalData)=> {
|
|
|
266
|
+ if (!productSkn) {
|
|
|
267
|
+ return {};
|
|
|
268
|
+ }
|
|
|
269
|
+
|
|
|
270
|
+ let sizeInfo = additionalData.sizeInfo;
|
|
|
271
|
+
|
|
|
272
|
+ if (_.isEmpty(sizeInfo)) {
|
|
|
273
|
+ return {};
|
|
|
274
|
+ }
|
|
|
275
|
+
|
|
|
276
|
+ let result = {};
|
|
|
277
|
+
|
|
|
278
|
+ // 尺寸数据
|
|
|
279
|
+ result.size = _getSizeData(sizeInfo);
|
|
|
280
|
+
|
|
|
281
|
+ return result;
|
|
|
282
|
+};
|
|
|
283
|
+
|
|
|
284
|
+/**
|
|
|
285
|
+ * 详情页数据格式化
|
|
|
286
|
+ * @param origin Object 原始数据
|
|
|
287
|
+ * @return result Object 格式化数据
|
|
|
288
|
+ */
|
|
|
289
|
+const _detailDataPkg = (origin) => {
|
|
|
290
|
+ return co(function*() {
|
|
|
291
|
+ if (_.isEmpty(origin) || _.isEmpty(origin)) {
|
|
|
292
|
+ return {};
|
|
|
293
|
+ }
|
|
|
294
|
+
|
|
|
295
|
+ let result = {};
|
|
|
296
|
+
|
|
|
297
|
+ let propOrigin = _.partial(_.get, origin);
|
|
|
298
|
+
|
|
|
299
|
+ // 商品名称
|
|
|
300
|
+ if (!propOrigin('product_name')) {
|
|
|
301
|
+ return result;
|
|
|
302
|
+ }
|
|
|
303
|
+
|
|
|
304
|
+ result.name = propOrigin('product_name');
|
|
|
305
|
+ result.skn = propOrigin('product_skn');
|
|
|
306
|
+ result.productId = propOrigin('product_id');
|
|
|
307
|
+
|
|
|
308
|
+ // 商品价格
|
|
|
309
|
+ result.marketPrice = propOrigin('format_market_price');
|
|
|
310
|
+ result.salePrice = propOrigin('format_sales_price');
|
|
|
311
|
+ result.hasOtherPrice = true;
|
|
|
312
|
+
|
|
|
313
|
+ if (result.salePrice === '0') {
|
|
|
314
|
+ delete result.salePrice;
|
|
|
315
|
+ result.hasOtherPrice = false;
|
|
|
316
|
+ }
|
|
|
317
|
+
|
|
|
318
|
+ // 上市期
|
|
|
319
|
+ if (propOrigin('expect_arrival_time')) {
|
|
|
320
|
+ result.arrivalDate = `${propOrigin('expect_arrival_time')}月`;
|
|
|
321
|
+ result.presalePrice = propOrigin('format_sales_price');
|
|
|
322
|
+ delete result.salePrice;
|
|
|
323
|
+ result.hasOtherPrice = false;
|
|
|
324
|
+ }
|
|
|
325
|
+
|
|
|
326
|
+ // sku商品信息
|
|
|
327
|
+ let skuData = _getSkuDataByProductBaseInfo(origin);
|
|
|
328
|
+
|
|
|
329
|
+ // 商品购买状态
|
|
|
330
|
+ let soldOut = !!(propOrigin('status') === 0 || skuData.totalStorageNum === 0);
|
|
|
331
|
+ let notForSale = propOrigin('attribute') === 2; // 非卖品
|
|
|
332
|
+ let virtualGoods = propOrigin('attribute') === 3; // 虚拟商品
|
|
|
333
|
+
|
|
|
334
|
+ if (!soldOut && !notForSale && !virtualGoods) {
|
|
|
335
|
+ result.addToCart = 1;
|
|
|
336
|
+ }
|
|
|
337
|
+
|
|
|
338
|
+ result.colors = skuData.skuGoods;
|
|
|
339
|
+
|
|
|
340
|
+ return result;
|
|
|
341
|
+ })();
|
|
|
342
|
+};
|
|
|
343
|
+
|
|
|
344
|
+/**
|
|
|
345
|
+ * 获取某一个商品详情主页面
|
|
|
346
|
+ */
|
|
|
347
|
+const getProductInfoAsync = (pid) => {
|
|
|
348
|
+ return co(function * () {
|
|
|
349
|
+ if (!pid) {
|
|
|
350
|
+ return {};
|
|
|
351
|
+ }
|
|
|
352
|
+
|
|
|
353
|
+ // 获取商品基本信息
|
|
|
354
|
+ let productData = yield productAPI.getProductAsync(pid);
|
|
|
355
|
+
|
|
|
356
|
+ if (_.isEmpty(productData.data)) {
|
|
|
357
|
+ return Promise.reject({
|
|
|
358
|
+ code: 404,
|
|
|
359
|
+ message: 'app.product.data api wrong'
|
|
|
360
|
+ });
|
|
|
361
|
+ }
|
|
|
362
|
+
|
|
|
363
|
+ let productSkn = _.get(productData, 'data.product_skn');
|
|
|
364
|
+
|
|
|
365
|
+ let requestData = yield Promise.all([
|
|
|
366
|
+ _getProductIntroAsync(productSkn), // 商品详细介绍
|
|
|
367
|
+ _detailDataPkg(productData.data) // 商品详细价格
|
|
|
368
|
+ ]);
|
|
|
369
|
+
|
|
|
370
|
+ let productDescription = requestData[0];
|
|
|
371
|
+ let productInfo = requestData[1];
|
|
|
372
|
+
|
|
|
373
|
+ let intro = _getIntroInfo(productSkn, productDescription);
|
|
|
374
|
+
|
|
|
375
|
+ return Object.assign(productInfo, intro);
|
|
|
376
|
+ })();
|
|
|
377
|
+};
|
|
|
378
|
+
|
|
|
379
|
+module.exports = {
|
|
|
380
|
+ getProductInfoAsync // 获取某一个商品详情主页面
|
|
|
381
|
+}; |