Authored by 陈峰

Merge branch 'feature/cart' into 'release/5.4.1'

Feature/cart



See merge request !216
... ... @@ -321,7 +321,7 @@ const modify = (req, res, next) => {
data ? res.json(data) : res.status(400).json({
message: '操作失败'
});
});
}).catch(next);
};
/**
... ... @@ -351,7 +351,7 @@ const modifyPriceGift = (req, res, next) => {
data ? res.json(data) : res.status(400).json({
message: '操作失败'
});
});
}).catch(next);
};
/**
... ... @@ -457,7 +457,7 @@ const giftinfo = (req, res, next) => {
}) : res.status(400).json({
message: '操作失败'
});
});
}).catch(next);
};
module.exports = {
... ...
... ... @@ -7,7 +7,7 @@ const headerModel = require('../../../doraemon/models/header');
const userModel = require('../../serverAPI/user');
const addressModel = require('../../serverAPI/user/address');
const orderModel = require('../models/order');
const crypt = global.yoho.crypt;
const crypto = global.yoho.crypto;
exports.orderEnsure = (req, res, next) => {
let headerData = headerModel.setNav({
... ... @@ -132,7 +132,7 @@ exports.orderCompute = (req, res, next) => {
*/
exports.orderSub = (req, res) => {
let uid = req.user.uid;
let addressId = crypt.decrypt('', req.body.addressId);
let addressId = parseInt(crypto.decrypt('', req.body.addressId), 10);
let cartType = req.body.cartType || 'ordinary';
let deliveryTimeId = req.body.deliveryTimeId || 1;
let deliveryId = req.body.deliveryId || 1;
... ... @@ -210,7 +210,7 @@ exports.orderSub = (req, res) => {
yohoCoin, skuList, unionKey, userAgent);
}
if (unionInfo) {
if (unionInfo && result.data) {
result.data.unionCookie = unionInfo;
}
... ...
... ... @@ -13,10 +13,18 @@ const shoppingAPI = require('../../serverAPI/order/shopping');
* @return float|string 转换之后的价格
*/
const transPrice = (price, isSepcialZero) => {
if (!price) {
price = 0;
}
if (!isSepcialZero) {
isSepcialZero = false;
}
if (price || isSepcialZero) {
price = price.toFixed(2);
}
return price;
};
... ...
... ... @@ -588,11 +588,7 @@ let getProductAsyncData = (data) => {
* [商品信息格式化异步接口]
*/
let _detailDataPkgAsync = (origin, uid, vipLevel, ua) => {
let dest = {}, // 结果输出
thumbImageList = [],
colorGroup = {},
sizeGroup = [],
totalStorageNum = 0;
let dest = {}; // 结果输出
// 用户未登录时
if (!uid) {
... ... @@ -679,141 +675,8 @@ let _detailDataPkgAsync = (origin, uid, vipLevel, ua) => {
}
// 商品信息
if (origin.goods_list.length) {
let goodsGroup = [],
sizeName = '',
colorList = [],
sizeList = {},
allSizeList = {},
colorStorageGroup = {},
colorStorageNum = 0;
_.forEach(origin.goods_list, function(value) {
// 未上架也显示
// if (value.status === 0) {
// return;
// }
colorStorageNum = 0;
// 商品分组
if (value.images_list) {
_.forEach(value.images_list, function(good) {
goodsGroup.push({
goodsId: value.goods_id,
img: good.image_url
});
});
}
// 商品的尺码列表
colorStorageGroup[value.product_skc] = {};
if (value.size_list) {
sizeList[value.product_skc] = [];
_.forEach(value.size_list, function(size) {
sizeList[value.product_skc].push({
id: size.size_id,
skuId: size.product_sku,
goodsId: value.goods_id,
colorId: value.color_id,
name: size.size_name,
sizeNum: size.storage_number,
sizeInfo: size.size_info ? size.size_info : ''
});
sizeName = size.size_name;
// 所有尺码列表,赋值用于前端展示默认尺码的时候
// 判断出没有库存则显示灰色
let build = {
id: size.size_id,
storage: size.storage_number,
sizeInfo: size.size_info ? size.size_info : ''
};
allSizeList[sizeName] = (allSizeList[sizeName] === null ||
typeof allSizeList[sizeName] === 'undefined') ? build : allSizeList[sizeName];
colorStorageNum += parseInt(size.storage_number, 10);
colorStorageGroup[value.product_skc][sizeName] = parseInt(size.storage_number, 10);
});
// 颜色分组
colorList.push({
id: value.color_id,
skcId: value.product_skc,
name: value.factory_goods_name || value.color_name,
colorNum: colorStorageNum
});
}
// 缩略图
thumbImageList.push({
img: helpers.image(value.color_image, 300, 395)
});
// 商品库存总数
totalStorageNum += _.toNumber(colorStorageNum);
});
// 遍历所有尺码,构建颜色显示数据
let i = 1;
sizeGroup[0] = {
size: []
};
_.forEach(allSizeList, (value, key) => {
// 默认尺码
sizeGroup[0].size.push({
name: key,
sizeNum: _.toNumber(value.storage) > 0 ? true : false,
id: value.id,
sizeInfo: value.sizeInfo
});
colorGroup[i] = {
color: []
};
// 各个颜色的尺码, 每行显示一个尺码对应的颜色
_.forEach(colorList, (colorArr) => {
let tempColorArr = _.cloneDeep(colorArr);
if (colorStorageGroup[tempColorArr.skcId] &&
colorStorageGroup[tempColorArr.skcId][key]) {
tempColorArr.colorNum = colorStorageGroup[tempColorArr.skcId][key];
} else {
tempColorArr.colorNum = 0;
}
colorGroup[i].color.push(Object.assign({}, tempColorArr));
});
colorGroup[i].id = value.id;
++i;
});
colorGroup[0] = {
color: []
};
// 遍历所有颜色, 构建尺码显示数据
i = 1;
_.forEach(colorList, function(value) {
// 各个尺码的颜色, 每行显示一个颜色的对应尺码
sizeGroup[i] = {
size: sizeList[value.skcId],
colorId: value.skcId
};
// 默认颜色
colorGroup[0].color.push(value);
++i;
});
}
let soldOut = (origin.storage_sum === 0) || (totalStorageNum === 0); // status
let cartInfo = productProcess.processSizeInfo(origin);
let soldOut = (origin.storage_sum === 0) || (cartInfo.totalStorageNum === 0); // status
let notForSale = origin.attribute === 2;
let preSale = (origin.status === 0 && origin.advance_shelve_time > 0);
... ... @@ -827,16 +690,9 @@ let _detailDataPkgAsync = (origin, uid, vipLevel, ua) => {
// 显示加入购物车链接
if (!soldOut && !notForSale && !preSale || origin.isLimitBuy) {
_.orderBy(colorGroup);
Object.assign(dest.cartInfo, {
productId: origin.product_id,
thumbs: thumbImageList,
name: dest.goodsName || '',
Object.assign(dest.cartInfo, cartInfo, {
price: dest.goodsPrice.previousPrice ? dest.goodsPrice.previousPrice : '',
salePrice: dest.goodsPrice.currentPrice ? dest.goodsPrice.currentPrice : '',
totalNum: totalStorageNum,
colors: _.toArray(colorGroup),
sizes: sizeGroup
});
// 限购商品
... ... @@ -889,33 +745,33 @@ let _detailDataPkgAsync = (origin, uid, vipLevel, ua) => {
dest.cartInfo.preSale = true;
}
return resolve(dest);
}).then(dest => {
}).then(result => {
// 虚拟商品(门票)
if (origin.attribute * 1 === 3) {
dest.tickets = true;
dest.ticketsConfirm = helpers.urlFormat('/cart/index/ticketsConfirm');
result.tickets = true;
result.ticketsConfirm = helpers.urlFormat('/cart/index/ticketsConfirm');
// 展览票
if (origin.product_skn * 1 === SINGLE_TICKETS_SKN) {
dest.single = true;
result.single = true;
} else {
// 套票
dest.package = true;
result.package = true;
}
// 购票限制
dest.cartInfo.limit = 4;
result.cartInfo.limit = 4;
// 清空活动
dest.goodsDiscount = [];
result.goodsDiscount = [];
}
dest.id = origin.product_id;
dest.goodsId = origin.goods_id;
if (dest.isSecKill === 'Y' && dest.cartInfo.totalNum > 0) {
dest.totalNum = 1;
result.id = origin.product_id;
result.goodsId = origin.goods_id;
if (result.isSecKill === 'Y' && result.cartInfo.totalNum > 0) {
result.totalNum = 1;
}
return dest;
})
return result;
});
};
... ...
'use strict';
const md5 = require('md5');
const microtime = function() {
let unixtimeMs = new Date().getTime();
let sec = parseInt(unixtimeMs / 1000, 10);
return (unixtimeMs - (sec * 1000)) / 1000 + ' ' + sec;
};
const getTimestamp = function() {
let unixtimeMs = new Date().getTime();
return parseInt(unixtimeMs / 1000, 10);
};
module.exports = function(str, operation, key, expiry) {
operation = operation ? operation : 'encode';
key = key ? key : '';
expiry = expiry ? expiry : 0;
let tmpstr, tmp;
let ckeyLength = 4;
key = md5(key);
// 密匙a会参与加解密
let keya = md5(key.substr(0, 16));
// 密匙b会用来做数据完整性验证
let keyb = md5(key.substr(16, 16));
// 密匙c用于变化生成的密文
let keyc = operation === 'decode' ?
str.substr(0, ckeyLength) : md5(microtime()).substr(-ckeyLength);
// 参与运算的密匙
let cryptkey = keya + md5(keya + keyc);
let strbuf;
if (operation === 'decode') {
str = str.substr(ckeyLength);
strbuf = new Buffer(str, 'base64');
// string = b.toString();
} else {
expiry = expiry ? expiry + getTimestamp() : 0;
tmpstr = expiry.toString();
if (tmpstr.length >= 10) {
str = tmpstr.substr(0, 10) + md5(str + keyb).substr(0, 16) + str;
} else {
let count = 10 - tmpstr.length;
for (let i = 0; i < count; i++) {
tmpstr = '0' + tmpstr;
}
str = tmpstr + md5(str + keyb).substr(0, 16) + str;
}
strbuf = new Buffer(str);
}
let box = new Array(256);
let rndkey = [];
for (let i = 0; i < 256; i++) {
box[i] = i;
// 产生密匙簿
rndkey[i] = cryptkey.charCodeAt(i % cryptkey.length);
}
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上对并不会增加密文的强度
for (let j = 0, i = 0; i < 256; i++) {
j = (j + box[i] + rndkey[i]) % 256;
tmp = box[i];
box[i] = box[j];
box[j] = tmp;
}
// 核心加解密部分
let s = '';
for (let a = 0, j = 0, i = 0; i < strbuf.length; i++) {
a = (a + 1) % 256;
j = (j + box[a]) % 256;
tmp = box[a];
box[a] = box[j];
box[j] = tmp;
// 从密匙簿得出密匙进行异或,再转成字符
// s += String.fromCharCode(string[i] ^ (box[(box[a] + box[j]) % 256]));
/* jshint -W016*/
strbuf[i] = strbuf[i] ^ (box[(box[a] + box[j]) % 256]);
}
if (operation === 'decode') {
s = strbuf.toString();
if ((s.substr(0, 10) === '0'.repeat(10) ||
s.substr(0, 10) - getTimestamp() > 0) &&
s.substr(10, 16) === md5(s.substr(26) + keyb).substr(0, 16)) {
s = s.substr(26);
} else {
s = '';
}
} else {
s = strbuf.toString('base64');
let regex = new RegExp('=', 'g');
s = s.replace(regex, '');
s = keyc + s;
}
return s;
};
// console.log(module.exports('abcdef123', 'encode', 'key123', 99))
... ...
... ... @@ -6,6 +6,7 @@
const helpers = global.yoho.helpers;
const _ = require('lodash');
const productProcess = require('./product-process');
const regPromoTitle = /^【[^]+】(.*)/;
... ... @@ -209,8 +210,9 @@ const formatPromotion = (promo) => {
*/
const procGoodsDetail = (productData, num) => {
let data = {};
let sizeInfo = productProcess.processSizeInfo(productData);
data.name = productData.product_name;
Object.assign(data, sizeInfo);
data.productSkn = productData.product_skn;
if (_.has(productData, 'special_price')) { // 加价购或者赠品的销售价字段
data.price = productData.format_market_price;
... ... @@ -228,145 +230,8 @@ const procGoodsDetail = (productData, num) => {
if (num) {
data.num = _.parseInt(num);
}
// 商品选择
if (_.has(productData, 'goods_list')) {
let goodsList = productData.goods_list;
let sizeName = '',
colors = [],
colorList = [],
sizes = [],
sizeList = {},
allSizeList = {}, // 所有尺码列表
thumbImageList = [],
colorNum = 0,
totalStorageNum = 0, // 总库存数
colorStorageGroup = {}; // 颜色分组的库存总数集合, 多个之间用/分隔
goodsList.forEach(val => {
// 商品的尺码列表
if (!colorStorageGroup[val.product_skc]) {
colorStorageGroup[val.product_skc] = {};
}
if (_.has(val, 'size_list')) {
// 尺码
if (!sizeList[val.product_skc]) {
sizeList[val.product_skc] = [];
}
val.size_list.forEach(one => {
sizeName = one.size_name;
sizeList[val.product_skc].push({
id: one.size_id,
skuId: one.product_sku,
goodsId: val.goods_id,
colorId: val.color_id,
name: one.size_name,
sizeNum: _.parseInt(one.storage_number, 10),
sizeInfo: one.size_info ? one.size_info : ''
});
// 所有尺码列表,赋值用于前端展示默认尺码的时候 判断出没有库存则显示灰色
allSizeList[sizeName] = !_.has(allSizeList, sizeName) ? {
id: one.size_id,
storage: one.storage_number,
sizeInfo: one.size_info ? one.size_info : ''
} : allSizeList[sizeName];
colorNum += _.parseInt(one.storage_number, 10);
colorStorageGroup[val.product_skc][sizeName] = _.parseInt(one.storage_number, 10);
});
// 颜色分组
colorList.push({
id: val.color_id,
skcId: val.product_skc,
name: val.factory_goods_name || val.color_name,
colorNum: colorNum,
goodsName: productData.product_name
});
}
// 缩略图
thumbImageList.push({
img: helpers.image(val.color_image, 300, 395)
});
// 商品库存总数
totalStorageNum += colorNum;
});
// 遍历所有尺码,构建颜色显示数据
let i = 1;
if (!sizes[0]) {
sizes[0] = {
size: []
};
}
if (!colors[0]) {
colors[0] = {
color: []
};
}
_.forEach(allSizeList, (value, sName) => {
// 默认尺码
sizes[0].size.push({
name: sName,
sizeNum: _.toNumber(value.storage) > 0 ? true : false,
id: value.id,
sizeInfo: value.sizeInfo
});
// 各个颜色的尺码, 每行显示一个尺码对应的颜色
if (!colors[i]) {
colors[i] = {
color: []
};
}
colorList.forEach(colorArr => {
colors[i].color.push({
id: colorArr.id,
skcId: colorArr.skcId,
name: colorArr.name,
colorNum: _.has(colorStorageGroup, `${colorArr.skcId}.${sName}`) ? _.parseInt(colorStorageGroup[colorArr.skcId][sName], 10) : 0,
goodsName: colorArr.goodsName
});
});
colors[i].id = value.id;
++i;
});
// 遍历所有颜色, 构建尺码显示数据
i = 1;
if (!sizes[i]) {
sizes[i] = {
color: []
};
}
colorList.forEach(value => {
// 各个尺码的颜色,每行显示一个颜色的对应尺码
sizes[i] = {
size: sizeList[value.skcId],
colorId: value.skcId
};
// 默认颜色
colors[0].color.push(value);
++i;
});
data.thumbs = thumbImageList;
data.colors = colors;
data.sizes = sizes;
data.totalNum = totalStorageNum;
data.colorName = '颜色';
data.sizeName = '尺码';
}
data.colorName = '颜色';
data.sizeName = '尺码';
return data;
};
... ...
... ... @@ -344,3 +344,158 @@ exports.processFilter = (list, options) => {
return filters;
};
/**
* 解析尺码弹出框数据
*/
exports.processSizeInfo = (origin) => {
let cartInfo = {},
thumbImageList = [],
colorGroup = {},
sizeGroup = [],
totalStorageNum = 0;
if (origin.goods_list.length) {
let goodsGroup = [],
sizeName = '',
colorList = [],
sizeList = {},
allSizeList = {},
colorStorageGroup = {},
colorStorageNum = 0;
_.forEach(origin.goods_list, function(value) {
// 未上架也显示
// if (value.status === 0) {
// return;
// }
colorStorageNum = 0;
// 商品分组
if (value.images_list) {
_.forEach(value.images_list, function(good) {
goodsGroup.push({
goodsId: value.goods_id,
img: good.image_url
});
});
}
// 商品的尺码列表
colorStorageGroup[value.product_skc] = {};
if (value.size_list) {
sizeList[value.product_skc] = [];
_.forEach(value.size_list, function(size) {
sizeList[value.product_skc].push({
id: size.size_id,
skuId: size.product_sku,
goodsId: value.goods_id,
colorId: value.color_id,
name: size.size_name,
sizeNum: size.storage_number,
sizeInfo: size.size_info ? size.size_info : ''
});
sizeName = size.size_name;
// 所有尺码列表,赋值用于前端展示默认尺码的时候
// 判断出没有库存则显示灰色
let build = {
id: size.size_id,
storage: size.storage_number,
sizeInfo: size.size_info ? size.size_info : ''
};
allSizeList[sizeName] = (allSizeList[sizeName] === null ||
typeof allSizeList[sizeName] === 'undefined') ? build : allSizeList[sizeName];
colorStorageNum += parseInt(size.storage_number, 10);
colorStorageGroup[value.product_skc][sizeName] = parseInt(size.storage_number, 10);
});
// 颜色分组
colorList.push({
id: value.color_id,
skcId: value.product_skc,
name: value.factory_goods_name || value.color_name,
colorNum: colorStorageNum
});
}
// 缩略图
thumbImageList.push({
img: helpers.image(value.color_image, 300, 395)
});
// 商品库存总数
totalStorageNum += _.toNumber(colorStorageNum);
});
// 遍历所有尺码,构建颜色显示数据
let i = 1;
sizeGroup[0] = {
size: []
};
_.forEach(allSizeList, (value, key) => {
// 默认尺码
sizeGroup[0].size.push({
name: key,
sizeNum: _.toNumber(value.storage) > 0 ? true : false,
id: value.id,
sizeInfo: value.sizeInfo
});
colorGroup[i] = {
color: []
};
// 各个颜色的尺码, 每行显示一个尺码对应的颜色
_.forEach(colorList, (colorArr) => {
let tempColorArr = _.cloneDeep(colorArr);
if (colorStorageGroup[tempColorArr.skcId] &&
colorStorageGroup[tempColorArr.skcId][key]) {
tempColorArr.colorNum = colorStorageGroup[tempColorArr.skcId][key];
} else {
tempColorArr.colorNum = 0;
}
colorGroup[i].color.push(Object.assign({}, tempColorArr));
});
colorGroup[i].id = value.id;
++i;
});
colorGroup[0] = {
color: []
};
// 遍历所有颜色, 构建尺码显示数据
i = 1;
_.forEach(colorList, function(value) {
// 各个尺码的颜色, 每行显示一个颜色的对应尺码
sizeGroup[i] = {
size: sizeList[value.skcId],
colorId: value.skcId
};
// 默认颜色
colorGroup[0].color.push(value);
++i;
});
_.orderBy(colorGroup);
Object.assign(cartInfo, {
productId: origin.product_id,
thumbs: thumbImageList,
name: origin.product_name || '',
totalNum: totalStorageNum,
colors: _.toArray(colorGroup),
sizes: sizeGroup
});
}
return cartInfo;
};
... ...