Authored by 姜敏

Merge branch 'release/1.0' of http://git.yoho.cn/fe/yoho-blk into release/1.0

# Conflicts:
#	apps/me/controllers/setting.js
#	apps/me/models/setting.js
#	apps/me/views/partial/setting/content.hbs
Showing 87 changed files with 1620 additions and 1258 deletions

Too many changes to show.

To preserve performance only 87 of 87+ files are displayed.

... ... @@ -51,7 +51,7 @@ app.engine('.hbs', hbs({
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(bodyParser.urlencoded({extended: true}));
app.use(cookieParser());
app.use(multer());
... ...
... ... @@ -4,6 +4,12 @@
* @date: 2016/7/8
*/
/**
* 品牌一览
* @author: zxr
* @date: 2016/07/13
*/
'use strict';
const brand = require('../models/brand');
... ... @@ -12,7 +18,7 @@ const brand = require('../models/brand');
* @param req
* @param res
*/
const index = (req, res) => {
const index = (req, res, next) => {
let channel = req.query.brand || 1;
let contentCode = 'd0149783b8dd2adaf083fd10556c39a9';
... ... @@ -36,18 +42,11 @@ const index = (req, res) => {
],
tabs: result.tabs,
category: result.category
},
helpers: {
// import component, path depends on your project
pagination: require('../../../doraemon/components/pagination/pagination').createPagination
}
});
});
}).catch(next);
};
module.exports = {
index // 组件demo页
index // 品牌一览
};
... ...
/**
* 品牌一览
* @author: zxr
* @date: 2016/07/13
*/
'use strict';
const api = global.yoho.API;
... ... @@ -79,7 +85,7 @@ const _getResources = (contentCode) => {
if (result && result.code === 200) {
return _processTabData(result.data);
} else {
logger.error('品牌资源数据返回 code 不是 200');
logger.error('The data of brand resources return code is not 200');
return {};
}
});
... ... @@ -98,7 +104,7 @@ const _getBreakingSort = (channel) => {
if (result && result.code === 200) {
return _processListData(result);
} else {
logger.error('品牌页数据返回 code 不是 200');
logger.error('The data of brand resources return code is not 200');
return {};
}
});
... ...
... ... @@ -18,10 +18,11 @@ const index = (req, res, next) => {
let pageNum = req.query.page || 1;
let limit = req.query.limit || 20;
let type = req.query.type || 0;
let appType = 1;
// let gender = req.query.brand || 1;
editorialModel.getIndexData(type, pageNum, limit).then((result) => {
editorialModel.getIndexData(type, pageNum, limit, appType).then((result) => {
res.display('index', {
module: 'editorial',
... ... @@ -36,15 +37,15 @@ const index = (req, res, next) => {
},
{
pathTitle: '资讯',
name: '资讯'
name: 'Editorial资讯'
}
],
msgTypes: result.msgTypes,
msg: result.msg.tabs,
paginationOpts: {
page: pageNum, // current page: http://host/?page=2
limit: 20, // per_page records' number
total: result.msg.total, // total page number
page: pageNum,
limit: 20,
total: result.msg.total,
queryParams: req.query
}
},
... ... @@ -68,8 +69,6 @@ const list = (req, res) => {
let tag = req.query.query;
let authorId = req.query.authorId;
// let curChannel = getChannelData(req.channel, req.query.query);
editorialModel.getListData(pageNum, limit, tag, authorId).then((result) => {
res.display('list', {
... ... @@ -194,7 +193,7 @@ let cancelCollect = (req, res, next) => {
let uid = req.user.uid;
let id = req.body.id;
editorialModel.setCollect(id, uid).then((result) => {
editorialModel.cancelCollect(id, uid).then((result) => {
res.json(result);
}).catch(next);
};
... ... @@ -246,7 +245,7 @@ let addComment = (req, res, next) => {
};
module.exports = {
index, // 资讯页
index,
list,
detail,
setCollect,
... ...
/**
* 资讯
* @author: zxr<xiaoru.zhang@yoho.cn>
* @date: 2016/7/4
*/
'use strict';
const serviceAPI = global.yoho.ServiceAPI;
... ... @@ -6,6 +11,7 @@ const camelCase = global.yoho.camelCase;
const _ = require('lodash');
const Promise = require('bluebird');
const co = Promise.coroutine;
const config = global.yoho.config;
// const moment = require('moment');
const logger = global.yoho.logger;
... ... @@ -75,12 +81,14 @@ const _processListData = (list) => {
* 资讯首页tab
* @param type
*/
const _getResources = (type) => {
return serviceAPI.get('guang/api/v1/category/get', {}).then((result) => {
const _getResources = (type, appType) => {
return serviceAPI.get('guang/api/v1/category/get', {
app_type: appType
}).then((result) => {
if (result && result.code === 200) {
return _processNavData(result.data, type);
} else {
logger.error('资讯首页导航数据返回 code 不是 200');
logger.error('Information page navigation data return code is not 200');
return {};
}
});
... ... @@ -93,16 +101,17 @@ const _getResources = (type) => {
* @param limit
* @returns {*}
*/
const _getBreakingSort = (type, pageNum, limit) => {
const _getBreakingSort = (type, pageNum, limit, appType) => {
return serviceAPI.get('guang/api/*/article/getList', {
sort_id: type,
page: pageNum,
limit: limit
limit: limit,
app_type: appType
}).then((result) => {
if (result && result.code === 200) {
return _processListData(result.data);
} else {
logger.error('资讯首页列表数据返回 code 不是 200');
logger.error('Information page list data return code is not 200');
return {};
}
});
... ... @@ -115,8 +124,8 @@ const _getBreakingSort = (type, pageNum, limit) => {
* @param limit
* @returns {*}
*/
const getIndexData = (type, pageNum, limit) => {
return Promise.all([_getResources(type), _getBreakingSort(type, pageNum, limit)])
const getIndexData = (type, pageNum, limit, appType) => {
return Promise.all([_getResources(type, appType), _getBreakingSort(type, pageNum, limit, appType)])
.then((result) => {
return {
msgTypes: result[0],
... ... @@ -151,7 +160,7 @@ const getListData = (pageNum, limit, tag, authorId) => {
if (result && result.code === 200) {
return _processListData(result.data);
} else {
logger.error('资讯列表数据返回 code 不是 200');
logger.error('Information list data return code is not 200');
return {};
}
});
... ... @@ -170,7 +179,7 @@ const _getAuthorData = (id) => {
if (result && result.code === 200) {
return result.data;
} else {
logger.error('作者信息返回 code 不是 200');
logger.error('The author information return code is not 200');
return {};
}
});
... ... @@ -304,7 +313,7 @@ const _getHeadData = (id) => {
if (result && result.code === 200) {
return result.data;
} else {
logger.error('资讯详情head数据返回 code 不是 200');
logger.error('Information details head data return code is not 200');
return {};
}
});
... ... @@ -322,7 +331,7 @@ const _getContentData = (id) => {
if (result && result.code === 200) {
return _processContentData(result.data);
} else {
logger.error('资讯详情内容数据返回 code 不是 200');
logger.error('Information content data return code is not 200');
return {};
}
});
... ... @@ -340,7 +349,7 @@ const _getArticleData = (id) => {
if (result && result.code === 200) {
return result.data;
} else {
logger.error('资讯详情内容数据返回 code 不是 200');
logger.error('Information content data return code is not 200');
return {};
}
});
... ... @@ -358,7 +367,7 @@ const _getCommentsData = (id) => {
if (result && result.code === 200) {
return camelCase(result.data);
} else {
logger.error('评论内容数据返回 code 不是 200');
logger.error('Review data return code is not 200');
return {};
}
});
... ... @@ -374,9 +383,17 @@ const _getRelateBrand = (id) => {
article_id: id
}).then((result) => {
if (result && result.code === 200) {
_.forEach(result.data, function(data) {
let domain = data.url;
domain = domain.substring(domain.indexOf('/') + 2, domain.indexOf('.'));
console.log(domain);
data.url = `${config.siteUrl}/product/shop/${domain}`;
});
return camelCase(result.data);
} else {
logger.error('相关品牌返回 code 不是 200');
logger.error('Related brand return code is not 200');
return {};
}
});
... ...
... ... @@ -11,44 +11,47 @@ const accountModel = require('../models/account');
* 修改密码
* @param req
* @param res
* @param next
*/
const changePwd = (req, res) => {
const changePwd = (req, res, next) => {
let uid = req.user.uid;
let pwd = req.body.password;
accountModel.changePwd(uid, pwd).then(result=> {
res.send(result);
});
}).catch(next);
};
/**
* 验证手机号码
* @param req
* @param res
* @param next
*/
const checkVerifyMobile = (req, res)=> {
const checkVerifyMobile = (req, res, next)=> {
let uid = req.user.uid;
let mobile = req.body.mobile;
let area = req.body.area;
accountModel.checkVerifyMobile(uid, mobile, area).then(result=> {
res.send(result);
});
}).catch(next);
};
/**
* 发送短信
* @param req
* @param res
* @param next
*/
const sendMobileMsg = (req, res)=> {
const sendMobileMsg = (req, res, next)=> {
let uid = req.user.uid;
let mobile = req.body.mobile;
let area = req.body.area || '+86';
accountModel.sendMobileMsg(uid, mobile, area).then(result=> {
res.send(result);
});
}).catch(next);
};
... ... @@ -56,36 +59,38 @@ const sendMobileMsg = (req, res)=> {
* 验证短信
* @param req
* @param res
* @param next
*/
const checkVerifyMsg = (req, res)=> {
const checkVerifyMsg = (req, res, next)=> {
let code = req.body.code;
let mobile = req.body.mobile;
let area = req.body.area || '+86';
accountModel.checkVerifyMsg(code, mobile, area).then(result=> {
res.send(result);
});
}).catch(next);
};
/**
* 发送验证邮件
* @param req
* @param res
* @param next
*/
const sendVerifyEmail = (req, res)=> {
const sendVerifyEmail = (req, res, next)=> {
let uid = req.user.uid;
let email = req.body.email;
accountModel.sendVerifyEmail(uid, email).then(result=> {
res.send(result);
});
}).catch(next);
};
module.exports = {
changePwd: changePwd,
sendMobileMsg: sendMobileMsg,
checkVerifyMsg: checkVerifyMsg,
sendVerifyEmail: sendVerifyEmail,
checkVerifyMobile: checkVerifyMobile
changePwd,
sendMobileMsg,
checkVerifyMsg,
sendVerifyEmail,
checkVerifyMobile
};
... ...
... ... @@ -26,10 +26,10 @@ const index = (req, res, next) => {
addressModel.getAddressDataAsync(uid, 20).then(result => {
let resultData = result.data ? result.data : result;
let length = resultData.length ? resultData.length : 0;
let reg = /(\d{3})\d{4}(\d{4})/;
for (let i = 0; i < length; i++) {
resultData[i].mobile = resultData[i].mobile.substring(0, 3) + '****' +
resultData[i].mobile.substring(7, 11);
resultData[i].mobile = resultData[i].mobile.replace(reg, "$1****$2");
}
resultData.leftLength = 7 - length;
resultData.length = length;
... ... @@ -64,7 +64,7 @@ const getAddressList = (req, res, next) => {
/**
* 添加地址
*/
const addAddressData = (req, res) => {
const addAddressData = (req, res, next) => {
let uid = req.user.uid;
let address = req.body.address;
let areaCode = req.body.area_code;
... ... @@ -76,13 +76,13 @@ const addAddressData = (req, res) => {
addressModel.addAddressData(uid, address, areaCode, consignee, mobile, phone, isInit).then(result => {
res.send(result);
});
}).catch(next);
};
/**
* 修改地址
*/
const updateAddressData = (req, res) => {
const updateAddressData = (req, res, next) => {
let id = req.body.id;
let uid = req.user.uid;
let address = req.body.address;
... ... @@ -93,19 +93,19 @@ const updateAddressData = (req, res) => {
addressModel.updateAddressData(id, uid, address, areaCode, consignee, mobile, phone).then(result => {
res.send(result);
});
}).catch(next);
};
/**
* 删除地址
*/
const delAddressData = (req, res) => {
const delAddressData = (req, res, next) => {
let id = req.body.id;
let uid = req.user.uid;
addressModel.delAddressData(id, uid).then(result => {
res.send(result);
});
}).catch(next);
};
/**
... ...
... ... @@ -17,18 +17,18 @@ const convertUnitTime = (src) => {
/**
* yoho币页面加载
*/
const index = (req, res) => {
var uid = req.user.uid;
var page = parseInt(req.query.page, 10) || 1;
var queryType = parseInt(req.query.queryType, 10) || 0;
var beginTime = req.query.beginTime || convertUnitTime(new Date() / 1000 - 3600 * 24 * 90);
var date1 = new Date().getTime() / 1000;
var date2 = new Date(beginTime).getTime() / 1000;
var selectIndex = parseInt((date1 - date2) / (3600 * 24 * 30 * 6), 10);
const index = (req, res, next) => {
let uid = req.user.uid;
let page = parseInt(req.query.page, 10) || 1;
let queryType = parseInt(req.query.queryType, 10) || 0;
let beginTime = req.query.beginTime || convertUnitTime(new Date() / 1000 - 3600 * 24 * 90);
let date1 = new Date().getTime() / 1000;
let date2 = new Date(beginTime).getTime() / 1000;
let selectIndex = parseInt((date1 - date2) / (3600 * 24 * 30 * 6), 10);
currencyModel.getIndexData(uid, page, queryType, beginTime).then(result=> {
result.list.tabs[queryType].isActive = true;
result.list.coinList.forEach(function(x) {
result.list.coinList.forEach(function (x) {
x.date = x.date.replace(/\-/g, '.');
});
result.list.selects[selectIndex].isSelected = 'selected';
... ... @@ -58,7 +58,7 @@ const index = (req, res) => {
total: result.list.total ? result.list.total : 0
}
});
});
}).catch(next);
};
... ...
... ... @@ -58,9 +58,7 @@ const getOrderList = (req, res) => {
page: 'order',
isMe: true,
orderList: result.order.orderList,
paginationOpts: result.order.paginationOpts,
emptyMsg: result.order.emptyMsg,
showEmptyEn: result.order.showEmptyEn
orderData: result.order
});
});
};
... ...
... ... @@ -13,7 +13,7 @@ const _ = require('lodash');
* 退换货列表页
*/
const index = (req, res, next) => {
const uid = global.yoho.uid;
const uid = req.user.uid;
const page = req.query.page;
returns.getUserReturn(uid, page).then(result => {
... ... @@ -95,7 +95,7 @@ const refundDetail = (req, res, next) => {
*/
const exchange = (req, res, next) => {
const code = req.params.orderCode;
const uid = req.user.uid || 8050882;
const uid = req.user.uid;
returns.getChangeGoodsList(code, uid).then(result => {
res.display('index', {
... ... @@ -205,6 +205,17 @@ const setEepress = (req, res, next) => {
}).catch(next);
};
/**
* 获取银行列表
* @function setEepress
* @return { Object } 银行列表
*/
const getUnion = (req, res, next) => {
returns.getUnionData().then(result => {
res.json(result);
}).catch(next);
};
module.exports = {
index,
refund,
... ... @@ -215,5 +226,6 @@ module.exports = {
exchangeDeatail,
exchangeSubmit,
cancelApply,
setEepress
setEepress,
getUnion
};
... ...
... ... @@ -16,6 +16,8 @@ var fs = require('fs');
var path = require('path');
const uuid = require('uuid');
const os = require('os');
const regMobile = /(\d{3})\d{4}(\d{4})/;//正则匹配替换手机号码中间4位
const captchaUrl = helpers.urlFormat('/passport/images', {t: Date.now()});
// 根据type获取标题
... ... @@ -79,8 +81,7 @@ const index = (req, res, next) => {
result.info.gender ? result.genders[result.info.gender - 1].checked = true :
result.genders[2].checked = true;
result.info.head_ico = result.info.head_ico ? helpers.image(result.info.head_ico, 400, 300, 2) : '';
result.info.mobile = result.info.mobile ?
result.info.mobile.substring(0, 3) + '****' + result.info.mobile.substring(7, 11) : '';
result.info.mobile = result.info.mobile ? result.info.mobile.replace(regMobile, "$1****$2") : '';
result.stepUrl = '/me/setting/step1';
res.display('index', {
... ... @@ -103,19 +104,20 @@ const index = (req, res, next) => {
* 编辑信息
* @param req
* @param res
* @param next
*/
const editUserInfo = (req, res)=> {
const editUserInfo = (req, res, next)=> {
let uid = req.user.uid;
let query = req.body;
settingModel.editUserInfo(uid, query).then(result=> {
res.send(result);
});
}).catch(next);
};
/*
* step1
* step1 绑定的手机号码身份验证
* */
const bindMobile = (req, res, next) => {
let uid = req.user.uid;
... ... @@ -125,7 +127,7 @@ const bindMobile = (req, res, next) => {
if (result.info.verify_mobile !== '') {
let info = result.info;
info.ellipsisMobile = info.verify_mobile.substring(0, 3) + '****' + info.verify_mobile.substring(7, 11);
info.ellipsisMobile = info.verify_mobile.replace(regMobile, "$1****$2");
info.checkCode = settingModel.cipheriv(info.uid + '.completeverify');
res.display('index', {
... ... @@ -151,6 +153,9 @@ const bindMobile = (req, res, next) => {
});
};
/**
* step1 绑定的邮箱进行身份验证
*/
const bindEmail = (req, res, next) => {
let uid = req.user.uid;
let type = req.params.type;
... ... @@ -182,6 +187,9 @@ const bindEmail = (req, res, next) => {
});
};
/**
*step1 登录密码进行身份验证
*/
const modifyPassword = (req, res) => {
let type = req.params.type;
let checkCode = settingModel.cipheriv(req.user.uid + '.completeverify');
... ... @@ -205,7 +213,7 @@ const modifyPassword = (req, res) => {
};
/*
* step2 渲染页面
* step2 操作界面-渲染页面
* */
const edit = (req, res)=> {
let type = req.params.type;
... ... @@ -244,7 +252,7 @@ const edit = (req, res)=> {
};
/*
* step3
* step3 操作成功-渲染界面
* */
const success = (req, res)=> {
let type = req.params.type;
... ... @@ -281,7 +289,7 @@ const success = (req, res)=> {
};
/*
* post1
* post1 第一步的post请求
* */
const validate1 = (req, res)=> {
co(function *() {
... ... @@ -306,7 +314,7 @@ const validate1 = (req, res)=> {
};
/*
* post2
* post2 第二步的post请求
* */
const validate2 = (req, res)=> {
co(function *() {
... ...
... ... @@ -108,11 +108,11 @@ const modifyVerifyMobile = (uid, area, newMobile)=> {
};
module.exports = {
verifyPwd: verifyPwd,
changePwd: changePwd,
sendMobileMsg: sendMobileMsg,
checkVerifyMsg: checkVerifyMsg,
sendVerifyEmail: sendVerifyEmail,
checkVerifyMobile: checkVerifyMobile,
modifyVerifyMobile: modifyVerifyMobile
verifyPwd,
changePwd,
sendMobileMsg,
checkVerifyMsg,
sendVerifyEmail,
checkVerifyMobile,
modifyVerifyMobile
};
... ...
... ... @@ -120,5 +120,5 @@ const getIndexData = (uid, page, queryType, beginTime) => {
};
module.exports = {
getIndexData: getIndexData
getIndexData
};
... ...
... ... @@ -230,6 +230,12 @@ const _getUserOrder = (uid, type, page) => {
const ot = parseInt(item.orderType, 10);
const st = parseInt(item.status, 10);
item.orderGoods.forEach(good => {
let cnAlphabet = good.cnAlphabet ? good.cnAlphabet : '';
good.goodUrl = helpers.urlFormat(`/product/pro_${good.productId}_${good.goodsId}/${cnAlphabet}.html`);
});
// 转换订单创建时间
item.createTime = _convertUnixTime(item.createTime);
... ... @@ -325,22 +331,28 @@ const getOrderData = (uid, type, page) => {
typeStr: 'paying'
},
{
text: '待货',
text: '待货',
typeStr: 'delivering'
}
]
};
const emptyMsg = {
const emptyObject = {
1: {
text: '您暂时还没有订单',
showEmptyEn: true
emptyMsg: '暂时还没有订单哦~',
showEmptyEn: true,
btnText: '去首页看看',
url: '/'
},
2: {
text: '您暂时还没有待付款的订单'
emptyMsg: '没有待付款的订单哦~',
btnText: '查看全部订单',
url: '/me/'
},
3: {
text: '您暂时还没有待收货的订单'
emptyMsg: '没有待收货的订单哦~',
btnText: '查看全部订单',
url: '/me/'
}
};
... ... @@ -358,10 +370,7 @@ const getOrderData = (uid, type, page) => {
type: type
};
const empty = {
showEmptyEn: emptyMsg[type].showEmptyEn,
emptyMsg: emptyMsg[type].text
};
const empty = emptyObject[type];
navBar.tabs[typeActiveIndexMap[type]].isActive = true;
... ...
/**
* 商品基本信息
* 退换货API
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2016/7/19
*/
... ... @@ -7,12 +7,25 @@
const api = global.yoho.API;
/**
* 获取快递公司列表API
* @function getExpressCompanyAsync
* @return { Object } 快递公司列表
*/
const getExpressCompanyAsync = () => {
return api.get('', {
method: 'app.express.getExpressCompany'
}, {code: 200});
};
/**
* 获取订单信息API
* @function getOrderInfoAsync
* @param { number } orderCode 订单编号
* @param { number } uid 用户uid
* @param { string } sessionKey session
* @return { Object } 订单信息
*/
const getOrderInfoAsync = (orderCode, uid, sessionKey) => {
return api.get('', {
method: 'app.SpaceOrders.info',
... ... @@ -22,6 +35,13 @@ const getOrderInfoAsync = (orderCode, uid, sessionKey) => {
}, {code: 200});
};
/**
* 获取订单退货信息API
* @function getRefundGoodsAsync
* @param { number } orderCode 订单编号
* @param { number } uid 用户uid
* @return { Object } 订单退货信息
*/
const getRefundGoodsAsync = (orderCode, uid) => {
return api.get('', {
method: 'app.refund.goodsList',
... ... @@ -30,6 +50,13 @@ const getRefundGoodsAsync = (orderCode, uid) => {
}, {code: 200});
};
/**
* 获取退货详情信息API
* @function getRefundDetailAsync
* @param { number } applyId 退货申请id
* @param { number } uid 用户uid
* @return { Object } 退货详情信息
*/
const getRefundDetailAsync = (applyId, uid) => {
return api.get('', {
method: 'app.refund.detail',
... ... @@ -38,6 +65,15 @@ const getRefundDetailAsync = (applyId, uid) => {
}, {code: 200});
};
/**
* 退货申请提交API
* @function refundSubmitAsync
* @param { number } orderCode 订单编号
* @param { number } uid 用户uid
* @param { Object } goods 退货商品信息
* @param { Object } payment 退款信息
* @return { Object } 退货申请结果
*/
const refundSubmitAsync = (orderCode, uid, goods, payment) => {
return api.get('', {
method: 'app.refund.submit',
... ... @@ -48,6 +84,13 @@ const refundSubmitAsync = (orderCode, uid, goods, payment) => {
}, {code: 200});
};
/**
* 获取换货商品信息API
* @function getChangeGoodsListAsync
* @param { number } orderCode 订单编号
* @param { number } uid 用户uid
* @return { Object } 换货商品信息
*/
const getChangeGoodsListAsync = (orderCode, uid) => {
return api.get('', {
method: 'app.change.goodsList',
... ... @@ -56,6 +99,13 @@ const getChangeGoodsListAsync = (orderCode, uid) => {
});
};
/**
* 获取商品信息API
* @function getProductInfoAsync
* @param { number } productId 商品id
* @param { number } productSkn 商品skn
* @return { Object } 商品信息
*/
const getProductInfoAsync = (productId, productSkn) => {
return api.get('', {
method: 'app.product.data',
... ... @@ -64,6 +114,13 @@ const getProductInfoAsync = (productId, productSkn) => {
});
};
/**
* 获取换货详情API
* @function getExchangeDetail
* @param { number } id 收藏id
* @param { number } uid 用户uid
* @return { Object } 换货详情
*/
const getExchangeDetail = (id, uid) => {
return api.get('', {
method: 'app.change.detail',
... ... @@ -72,6 +129,13 @@ const getExchangeDetail = (id, uid) => {
});
};
/**
* 换货申请提交API
* @function changeSubmitAsync
* @param { number } data 换货信息
* @param { number } uid 用户uid
* @return { Object } 换货申请结果
*/
const changeSubmitAsync = (data, uid) => {
const requestData = Object.assign(data, {
method: 'app.change.submit',
... ... @@ -82,6 +146,14 @@ const changeSubmitAsync = (data, uid) => {
return api.get('', requestData);
};
/**
* 设置寄回快递信息API
* @function setExpressNumberAsync
* @param { number } uid 用户uid
* @param { number } param 寄回快递信息
* @param { boolean } isChange 是否换货 true--换货 false--退货
* @return { Object } 设置快递结果
*/
const setExpressNumberAsync = (uid, param, isChange) => {
return api.post('', {
method: isChange ? 'app.change.setexpress' : 'app.refund.setexpress',
... ... @@ -93,6 +165,14 @@ const setExpressNumberAsync = (uid, param, isChange) => {
});
};
/**
* 取消退换货API
* @function cancelReturnAsync
* @param { number } id 收藏id
* @param { number } uid 用户uid
* @param { boolean } isChange 是否换货 true--换货 false--退货
* @return { Object } 取消结果
*/
const cancelReturnAsync = (id, uid, isChange) => {
return api.post('', {
method: isChange ? 'app.change.cancel' : 'app.refund.cancel',
... ... @@ -101,6 +181,17 @@ const cancelReturnAsync = (id, uid, isChange) => {
});
};
/**
* 获取退货银行列表API
* @function getRefundBank
* @return { Object } 银行列表信息
*/
const getRefundBank = () => {
return api.get('', {
method: 'app.refund.refundBank'
});
};
module.exports = {
getExpressCompanyAsync,
getOrderInfoAsync,
... ... @@ -112,6 +203,7 @@ module.exports = {
getExchangeDetail,
changeSubmitAsync,
setExpressNumberAsync,
cancelReturnAsync
cancelReturnAsync,
getRefundBank
};
... ...
... ... @@ -84,8 +84,11 @@ const getUserReturn = (uid, page) => {
item.showStatus = true;
item.refundStr = refundStr[item.refundType];
item.orderGoods.forEach(it => {
it.hidePrice = true;
item.orderGoods.forEach(good => {
let cnAlphabet = good.cnAlphabet ? good.cnAlphabet : '';
good.hidePrice = true;
good.goodUrl = helpers.urlFormat(`/product/pro_${good.productId}_${good.goodsId}/${cnAlphabet}.html`); // eslint-disable-line
});
});
}
... ... @@ -218,7 +221,10 @@ const _setReturnStatus = (list, half) => {
* @return { Object } 订单退货信息
*/
const _setRefundGoodList = (data) => {
let resData = {};
let resData = {
specialNotice: data.special_notice,
speclialReason: data.special_return_reason
};
if (data.goods_list) {
let goods = [];
... ... @@ -229,7 +235,11 @@ const _setRefundGoodList = (data) => {
goods.push({
href: helpers.urlFormat(`/product/pro_${value.product_id}_${value.goods_id}/${cnAlphabet}.html`),
img: value.goods_image,
name: value.product_name,
name: _.truncate(value.product_name, {
length: 34,
omission: '...'
}),
title: value.product_name,
size: value.size_name,
color: value.color_name,
num: 1, // 接口目前不支持
... ... @@ -239,6 +249,7 @@ const _setRefundGoodList = (data) => {
sku: value.product_sku,
type: value.goods_type,
typeId: value.goods_type_id,
limitReturn: value.is_limit_skn === 'Y',
reasonList: data.return_reason
});
... ... @@ -248,7 +259,43 @@ const _setRefundGoodList = (data) => {
});
resData.goods = goods;
}
resData.speclialReason = data.special_return_reason;
let returnMode = [];
_.forEach(data.return_amount_mode, value => {
let mode = {
title: value.name,
id: value.id,
cur: value.is_default === 'Y'
};
// 退货类型根据id匹配
switch (mode.id) {
case 1:
Object.assign(mode, {
name: value.name,
tip: '退款会返到您购买时支付的账户中'
});
break;
case 2:
Object.assign(mode, {
class: 'ali-item',
alipay: true
});
break;
case 3:
Object.assign(mode, {
class: 'union-item',
unionpay: true
});
break;
default:
mode.name = value.name;
break;
}
returnMode.push(mode);
});
resData.returnMode = returnMode;
return resData;
};
... ... @@ -268,9 +315,11 @@ const _setRefundDetailData = (data) => {
switch (data.status) {
case 10:
resData.orderReview = {pass: true};
resData.refundExpress = true;
resData.refundAddress = returnAddress;
Object.assign(resData, {
orderReview: {pass: true},
refundExpress: true,
refundAddress: returnAddress
});
break;
case 20:
resData.refundExpress = true;
... ... @@ -372,17 +421,23 @@ const _setExchangeDetailData = (data) => {
switch (data.status) {
case 0:
list.audit = true;
list.reminder = true;
Object.assign(list, {
audit: true,
reminder: true
});
break;
case 10:
list.through = true;
list.reminder = true;
Object.assign(list, {
through: true,
reminder: true
});
if (data.deliveryTpyeName === '寄回换货') {
list.logistics = true;
list.sendBack = true;
list.exchangeAddress = returnAddress;
Object.assign(list, {
logistics: true,
sendBack: true,
exchangeAddress: returnAddress
});
} else {
list.inDoor = true;
}
... ... @@ -394,13 +449,17 @@ const _setExchangeDetailData = (data) => {
}
break;
case 50:
list.send = true;
list.reminder = true;
list.auditSuccess = true;
Object.assign(list, {
send: true,
reminder: true,
auditSuccess: true
});
break;
case 40:
list.finish = true;
list.auditSuccess = true;
Object.assign(list, {
finish: true,
auditSuccess: true
});
break;
case 91:
list.abolish = true;
... ... @@ -431,9 +490,11 @@ const getRefundGoodsData = (orderCode, uid) => {
};
if (result.data) {
Object.assign(resData.returns.refund, _setRefundGoodList(result.data), {
orderCode: orderCode
});
Object.assign(
resData.returns.refund,
_setRefundGoodList(result.data),
{orderCode: orderCode}
);
}
return resData;
... ... @@ -468,12 +529,11 @@ const getRefundDetailData = (applyId, uid) => {
data.expressList = _setExpressData(result[1].data);
}
// 设置状态进度
Object.assign(resData.returns.refundDetail, _setReturnStatus(data.statusList, !data.status));
// 设置退货详情信息
Object.assign(resData.returns.refundDetail, _setRefundDetailData(data));
Object.assign(
resData.returns.refundDetail,
_setReturnStatus(data.statusList, !data.status), // 设置状态进度
_setRefundDetailData(data) // 设置退货详情信息
);
}
return resData;
... ... @@ -545,16 +605,12 @@ const getChangeGoodsList = (orderCode, uid) => {
if (result && result.data) {
data = camelCase(result.data);
// data.goodsList = _reduceArrByProductSku(data.goodsList);
data.hidePrice = true;
data.orderCode = orderCode;
data.goodsList.forEach(good => {
good.showCheckbox = true;
good.hidePrice = true;
// good.buyNumber = good.num;
good.buyNumber = 1;
});
}
... ... @@ -588,19 +644,18 @@ const getExchangeDetailData = (id, uid) => {
};
if (result[0] && result[0].data) {
let data = result[0].data;
if (result[1] && result[1].data) {
data.expressList = _setExpressData(result[1].data);
}
Object.assign(exchangeData.exchangeDetail, _setReturnStatus(data.statusList));// 头部
Object.assign(exchangeData.exchangeDetail, _setExchangeDetailData(data));
Object.assign(exchangeData.exchangeDetail, camelCase(data));
Object.assign(
exchangeData.exchangeDetail,
_setReturnStatus(data.statusList),
_setExchangeDetailData(data),
camelCase(data)
);
}
return exchangeData;
... ... @@ -646,6 +701,22 @@ const setBackEepress = (uid, param) => {
});
};
/**
* 获取银行列表
* @function getUnionData
* @return { Object } 银行列表数据
*/
const getUnionData = () => {
return returnsAPI.getRefundBank().then(result => {
let resData = result || {
code: 400,
message: '获取银行列表失败'
};
return resData;
});
};
module.exports = {
getUserReturn,
getRefundGoodsData,
... ... @@ -656,5 +727,6 @@ module.exports = {
getExchangeDetailData,
submitChange,
cancelReturnApply,
setBackEepress
setBackEepress,
getUnionData
};
... ...
... ... @@ -198,9 +198,10 @@ const modifyHead = (uid, bucket, fileData)=> {
module.exports = {
getUserInfo: getUserInfo,
editUserInfo: editUserInfo,
cipheriv: cipheriv,
decipheriv: decipheriv,
modifyHead: modifyHead
getUserInfo,
editUserInfo,
cipheriv,
decipheriv,
modifyHead
};
... ...
... ... @@ -44,6 +44,7 @@ router.post('/return/cancel', returns.cancelApply);
router.post('/return/setEepress', returns.setEepress);
router.get('/return/getProductInfo', returns.getProductInfo);
router.get('/return/submitExchange', returns.exchangeSubmit);
router.get('/return/unionInfo', returns.getUnion);
// 个人中心首页/收货地址
router.get('/address', auth, address.index);
... ...
<div class="good-info {{#if @last}}last{{/if}}">
<img src="{{image goodsImage 65 90}}">
<a href="{{goodUrl}}">
<img src="{{image goodsImage 65 90}}">
</a>
<div class="detail">
<p class="with-bottom-space">{{productName}}</p>
<a href="{{goodUrl}}">
<p class="with-bottom-space">{{productName}}</p>
</a>
<span class="with-space">颜色:{{colorName}}</span>
<span>尺码:
<span class="bold">{{sizeName}}</span>
... ...
... ... @@ -56,12 +56,16 @@
{{/if}}
{{^}}
<div class="bg"></div>
{{#orderData}}
<div class="msg">
<p class="msg-zh bold">{{emptyMsg}}</p>
{{#if showEmptyEn}}
<p class="msg-en">You do not have an order for the time being</p>
{{/if}}
<span class="btn">去购物</span>
<a href="{{url}}">
<span class="btn">{{btnText}}</span>
</a>
</div>
{{/orderData}}
{{/if}}
</div>
... ...
... ... @@ -118,10 +118,12 @@
{{# goods}}
<div class="goods-item clearfix">
<div class="img">
<img class="lazy" data-original="{{image img 70 90}}">
<a href="{{href}}" title="{{name}}" target="_blank">
<img class="lazy" data-original="{{image img 70 90}}">
</a>
</div>
<div class="info">
<p>{{name}}</p>
<p><a href="{{href}}" class="title" title="{{name}}" target="_blank">{{name}}</a></p>
<p>颜色:{{color}}&nbsp;尺码:{{size}}</p>
<p>×{{num}}</p>
</div>
... ...
... ... @@ -16,10 +16,12 @@
<div class="goods-item clearfix">
<div class="check" data-skn="{{skn}}" data-skc="{{skc}}" data-sku="{{sku}}" data-price="{{price}}" data-type="{{typeId}}">{{> icon/checkbox}}</div>
<div class="img">
<img class="lazy" data-original="{{image img 70 90}}">
<a href="{{href}}" title="{{title}}" target="_blank">
<img class="lazy" data-original="{{image img 70 90}}">
</a>
</div>
<div class="info">
<p>{{name}}</p>
<p><a href="{{href}}" class="title" title="{{title}}" target="_blank">{{name}}</a></p>
<p>颜色:{{color}}&nbsp;尺码:{{size}}</p>
<p>×{{num}}</p>
</div>
... ... @@ -37,6 +39,19 @@
<span class="iconfont">&#xe61f;</span>
</div>
<div class="price">¥{{round price 2}}</div>
{{#if limitReturn}}
<div class="limit-return">
<span class="limit-return-tip blue">
该商品不支持7天无理由退换
<i class="iconfont">&#xe612;</i>
<div class="limit-return-wrap">
<div class="code-horn"></div>
<p>考虑到个人卫生,例如内衣、内裤、袜子等贴身塑身类商品,不支持无理由退换货</p>
<p>香水、香薰、化妆品等特殊商品,无质量问题,不支持无理由退换货</p>
</div>
</span>
</div>
{{/if}}
{{> returns/special-reason}}
</div>
{{/ goods}}
... ... @@ -46,28 +61,35 @@
<div class="refund-type">
<div>
<label class="type-item ali-item cur" title="支付宝"></label>
<label class="type-item union-item" title="银行卡"></label>
{{# returnMode}}
<label class="type-item {{class}}{{#if cur}} cur{{/if}}" title="{{title}}" data-id="{{id}}">{{name}}</label>
{{#if tip}}
<span class="type-tip">{{tip}}</span>
{{/if}}
{{/ returnMode}}
</div>
<dl class="alipay">
<dd>账号:<input type="text" class="name" placeholder="收款人支付宝账号"></dd>
<dd>姓名:<input type="text" class="account" placeholder="姓名"></dd>
</dl>
<dl class="unionpay hide">
<dd>
银行:
<select class="bank">
<option>中国银行</option>
<option>中国农业银行</option>
<option>中国工商银行</option>
<option>中国建设银行</option>
</select>
<input type="text" placeholder="开户支行">
<span class="blue" class="area">例:江苏省南京市奥体支行</span>
</dd>
<dd>账号:<input type="text" class="account" placeholder="收款人银行卡号"></dd>
<dd>姓名:<input type="text" class="name" placeholder="姓名"></dd>
</dl>
{{# returnMode}}
{{#if alipay}}
<dl class="alipay mode{{id}} hide">
<dd>账号:<input type="text" class="name" placeholder="收款人支付宝账号"></dd>
<dd>姓名:<input type="text" class="account" placeholder="姓名"></dd>
</dl>
{{/if}}
{{#if unionpay}}
<dl class="unionpay mode{{id}} hide">
<dd>
银行:
<select class="bank">
<option>请选择银行</option>
</select>
<input type="text" placeholder="开户支行" class="open-bank">
<span class="blue" class="area">例:江苏省南京市奥体支行</span>
</dd>
<dd>账号:<input type="text" class="account" placeholder="收款人银行卡号"></dd>
<dd>姓名:<input type="text" class="name" placeholder="姓名"></dd>
</dl>
{{/if}}
{{/ returnMode}}
</div>
<div class="opt-btn">
<span class="apply-tip blue hide">请填写完整</span>
... ...
... ... @@ -31,7 +31,7 @@
</div>
</div>
</div>
<div class="form">
<div class="form hide">
<div class="group color">
<span class="title">
<span class="asterisk">*</span>
... ... @@ -51,37 +51,40 @@
{{/goodsList}}
</div>
</div>
{{/returnsChange}}
<div class="change-type">
<span class="box-title">换货方式</span>
<span class="type active" data-type="10">寄回换货</span>
<span class="type" data-type="11">上门取货</span>
</div>
{{#address}}
<div class="express-info-box">
<span class="box-title">收货信息</span>
<div class="application-form">
<div class="input-group">
<span class="asterisk">*</span>
<label for="city" class="change-area">所在区域:</label>
<span id="city" class="cascading-address"></span>
<span id="city" class="cascading-address" data-code="{{areaCode}}"></span>
</div>
<div class="input-group require">
<span class="asterisk">*</span>
<label for="person">收货人:</label>
<input id="user" class="input" type="text" name="person">
<input id="user" class="input" type="text" name="person" value="{{consignee}}">
</div>
<div class="input-group require">
<span class="asterisk">*</span>
<label for="address">详细地址:</label>
<input id="addr" class="input" type="text" name="address">
<input id="addr" class="input" type="text" name="address" value="{{address}}">
</div>
<div class="input-group require">
<span class="asterisk">*</span>
<label for="mobile">手机号码:</label>
<input id="mob" class="input" type="text" name="mobile">
<input id="mob" class="input" type="text" name="mobile" value="{{mobile}}">
</div>
</div>
</div>
{{/address}}
{{/returnsChange}}
<span class="btn confirm">确定</span>
<span class="btn confirm right">提交申请</span>
</div>
... ...
... ... @@ -5,6 +5,7 @@
</div>
<div class="left right-content">
<textarea class="mark-text"></textarea>
<span class="word-limit">限制100字</span>
</div>
</div>
<div class="clearfix">
... ... @@ -15,7 +16,11 @@
<div class="img-upload">
<span class="iconfont">&#xe61f;</span>
</div>
<span class="img-up-tip">1/4</span>
<span class="img-up-tip">0/4</span>
<span class="warn hide">
<span class="iconfont blue">&#xe60d;</span>
<span class="blue">最多上传4张照片</span>
</span>
</div>
</div>
</div>
... ...
... ... @@ -40,14 +40,14 @@
<a class="blue operation" href="{{stepUrl}}/bindMobile">绑定</a>
{{/if}}
</div>
<div class="form-group">
<div class="form-group">
<label class="label-name">邮箱:</label>
{{#if info.verify_email}}
<input class="input no-edit" value="{{info.mobile}}">
<!--<a class="blue operation" href="{{stepUrl}}/modifyEmail">修改</a>-->
{{else}}
<input class="input" type="text" placeholder="请绑定邮箱" disabled>
<!--<a class="blue operation" href="{{stepUrl}}/bindEmail">绑定</a>-->
<!--<a class="blue operation" href="{{stepUrl}}/bindEmail">绑定</a>-->
{{/if}}
</div>
<div class="form-group">
... ... @@ -61,7 +61,7 @@
<span class="blue error-tips">{{> icon/error-round}}请选择</span>
</div>
<div class="form-group">
<label class="label-name">详细地址:</label>
<label class="label-name" style="visibility: hidden">详细地址:</label>
<input id="full_address" class="input big-input" type="text" placeholder="请填写详细地址"
maxlength="20" value="{{concat.full_address}}">
</div>
... ...
... ... @@ -10,9 +10,6 @@ const service = require('../models/back-service');
const passportHelper = require('../models/passport-helper');
const _ = require('lodash');
/**
* 找回密码主页面
*/
const index = (req, res, next) => {
service.indexPageDataAsync()
.then(result => {
... ... @@ -26,9 +23,6 @@ const index = (req, res, next) => {
.catch(next);
};
/**
* 校验用户输入信息,是否是已经注册的用户
*/
const validateInputAPI = (req, res, next) => {
let userInput = req.body.phoneNum || '';
let areaCode = (req.body.area || '86').replace('+', '');
... ... @@ -46,9 +40,6 @@ const validateInputAPI = (req, res, next) => {
});
};
/**
* 校验用户输入信息,是否是已经注册的用户
*/
const validateInputPage = (req, res, next) => {
let userInput = req.body.phoneNum || '';
let areaCode = (req.body.area || '86').replace('+', '');
... ...
... ... @@ -10,280 +10,304 @@ 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 LoginService = require('../models/login-service');
const Sources = {
qq: 'QQ',
sina: '微博',
alipay: '支付宝',
wechat: '微信',
renren: '人人',
douban: '豆瓣'
wechat: '微信'
};
const DEFAULT_URL = 'http://img10.static.yhbimg.com/headimg/2013/11/28/09/01cae078abe5fe320c88cdf4c220212688.gif?imageView/2/w/100/h/100';
const bind = {
indexPage: (req, res) => {
let openId = req.query.openId;
let sourceType = req.query.sourceType;
res.display('bind/index', Object.assign({
module: 'passport',
page: 'bind',
title: '联合登录补全信息',
defaultHeader: false
}, {
openId: openId,
sourceType: sourceType,
country: {list: PassportHelper.getCountry()},
local: '+86',
countryName: {text: '中国'},
imgCaptcha: helpers.urlFormat('/passport/images', {t: Date.now()})
}));
},
bindSetPwdPage: (req, res) => {
let mobile = req.query.thirdPart.mobile;
let sourceType = req.query.thirdPart.sourceType;
let openId = req.query.thirdPart.openId;
let area = req.query.thirdPart.area;
res.display('bind/bind-set-pwd', Object.assign({
module: 'passport',
page: 'bind-set-pwd',
title: '登录绑定',
defaultHeader: false
}, {
mobile: mobile,
sourceType: sourceType,
openId: openId,
area: area
}));
},
bindConfirmPage: (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/bind-confirm', Object.assign({
module: 'passport',
page: 'bind-confirm',
title: '绑定确认',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
bindSuccess: helpers.urlFormat('/thirdlogin/bindSuccess', {
sourceType: thirdPart.sourceType
}),
bindUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
sourceType: thirdPart.sourceType
}),
loginUrl: helpers.urlFormat('/passport/login'),
mobile: thirdPart.mobile,
const indexPage = (req, res) => {
let openId = req.query.openId;
let sourceType = req.query.sourceType;
res.display('bind/index', Object.assign({
module: 'passport',
page: 'bind',
title: '联合登录补全信息',
defaultHeader: false
}, {
openId: openId,
sourceType: sourceType,
country: {list: PassportHelper.getCountry()},
local: '+86',
countryName: {text: '中国'},
imgCaptcha: helpers.urlFormat('/passport/images', {t: Date.now()})
}));
};
const bindSetPwdPage = (req, res) => {
let mobile = req.query.thirdPart.mobile;
let sourceType = req.query.thirdPart.sourceType;
let openId = req.query.thirdPart.openId;
let area = req.query.thirdPart.area;
res.display('bind/bind-set-pwd', Object.assign({
module: 'passport',
page: 'bind-set-pwd',
title: '登录绑定',
defaultHeader: false
}, {
mobile: mobile,
sourceType: sourceType,
openId: openId,
area: area
}));
};
const bindConfirmPage = (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/bind-confirm', Object.assign({
module: 'passport',
page: 'bind-confirm',
title: '绑定确认',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
bindSuccess: helpers.urlFormat('/thirdlogin/bindSuccess', {
sourceType: thirdPart.sourceType
}),
bindUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
area: thirdPart.area,
code: thirdPart.code,
sourceType: thirdPart.sourceType
}));
},
bindSuccessPage: (req, res) => {
let sourceType = _.trim(req.query.sourceType);
let sourceInfo = sourceType.split('_');
let sourceName = Sources[sourceInfo[0]];
res.display('bind/bind-success', Object.assign({
module: 'passport',
page: 'bind-success',
title: '绑定手机号',
defaultHeader: false
}, {
goShopping: helpers.urlFormat('/'),
sourceName: sourceName
}));
},
bindedPage: (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/bind-done', Object.assign({
module: 'passport',
page: 'bind-success',
title: '绑定手机号',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
loginUrl: helpers.urlFormat('/passport/login'),
bindUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
sourceType: thirdPart.sourceType
})
}));
},
relateSuccessPage: (req, res) => {
let sourceType = _.trim(req.query.sourceType);
let sourceInfo = sourceType.split('_');
let sourceName = Sources[sourceInfo[0]];
res.display('bind/relate-success', Object.assign({
module: 'passport',
page: 'relate-success',
title: '关联手机号',
defaultHeader: false
}, {
goShopping: helpers.urlFormat('/'),
sourceName: sourceName
}));
},
relateConfirmPage: (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/relate-confirm', Object.assign({
module: 'passport',
page: 'relate-confirm',
title: '关联确认',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
relateUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
sourceType: thirdPart.sourceType
}),
signinUrl: helpers.urlFormat('/passport/login'),
mobile: thirdPart.mobile,
area: thirdPart.area,
sourceType: thirdPart.sourceType,
openId: thirdPart.openId
}));
},
bindCheck: (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let area = req.body.area || '86';
let sourceType = req.body.sourceType;
if (mobile && openId && area && sourceType) {
BindService.bindCheck(mobile, openId, sourceType, area).then(result => {
if (!result || !result.code) {
return {code: 400, message: '', data: ''};
} else if (result.code === 200 && result.data.is_register === 0) {
let nextUrl = helpers.urlFormat('/passport/thirdlogin/bindSetPwd');
// 绑定流程:code=200 未注册,可绑定
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 => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return {code: 201, message: result.message, data: {user: user}};
});
} else if (result.code === 200 && result.data.is_register === 3) {
// 关联流程
return PassportHelper.getUserInfo(area, mobile).then(user => {
return {code: 203, message: result.message, data: {user: user}};
});
} else if (result.code === 506 || result.code === 505) {
return PassportHelper.getUserInfo(area, mobile).then(user => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return {code: 205, message: result.message, data: {user: user}};
});
} else {
return {code: result.code, message: result.message, data: result.data ? result.data : ''};
}
}).then(result => {
res.json(result);
}).catch(next);
} else {
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 => {
if (result && result.code) {
res.json(result);
} else {
res.json({code: 400, message: '', data: ''});
}
}).catch(next);
},
checkBindMsg: (req, res, next) => {
let mobile = req.body.mobile;
let area = req.body.area;
let code = req.body.code;
BindService.checkBindCode(area, mobile, code).then(result => {
if (result && result.code) {
res.json(result);
} else {
res.json({code: 400, message: '', data: ''});
}
}).catch(next);
},
bindMobile: (req, res, next) => {
let mobile = _.trim(req.body.mobile);
let area = _.trim(req.body.area) || '86';
let openId = _.trim(req.body.openId);
let sourceType = _.trim(req.body.sourceType);
let password = _.trim(req.body.password) || '';
BindService.bindMobile(openId, sourceType, mobile, area, password).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/bindsuccess', {
sourceType: sourceType + '_bind'
});
return AuthHelper.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: ''}};
}
} else {
}),
loginUrl: helpers.urlFormat('/passport/login'),
mobile: thirdPart.mobile,
openId: thirdPart.openId,
area: thirdPart.area,
code: thirdPart.code,
sourceType: thirdPart.sourceType
}));
};
const bindSuccessPage = (req, res) => {
let sourceType = _.trim(req.query.sourceType);
let sourceInfo = sourceType.split('_');
let sourceName = Sources[sourceInfo[0]];
res.display('bind/bind-success', Object.assign({
module: 'passport',
page: 'bind-success',
title: '绑定手机号',
defaultHeader: false
}, {
goShopping: helpers.urlFormat('/'),
sourceName: sourceName
}));
};
const bindedPage = (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/bind-done', Object.assign({
module: 'passport',
page: 'bind-success',
title: '绑定手机号',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
loginUrl: helpers.urlFormat('/passport/login'),
bindUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
sourceType: thirdPart.sourceType
})
}));
};
const relateSuccessPage = (req, res) => {
let sourceType = _.trim(req.query.sourceType);
let sourceInfo = sourceType.split('_');
let sourceName = Sources[sourceInfo[0]];
res.display('bind/relate-success', Object.assign({
module: 'passport',
page: 'relate-success',
title: '关联手机号',
defaultHeader: false
}, {
goShopping: helpers.urlFormat('/'),
sourceName: sourceName
}));
};
const relateConfirmPage = (req, res) => {
let thirdPart = req.query.thirdPart;
let user = req.query.user;
let avatar = user.headImg || DEFAULT_URL;
res.display('bind/relate-confirm', Object.assign({
module: 'passport',
page: 'relate-confirm',
title: '关联确认',
defaultHeader: false
}, {
avatar: avatar,
name: user.username || '咸鸭蛋',
relateUrl: helpers.urlFormat('/passport/thirdlogin/index', {
openId: thirdPart.openId,
sourceType: thirdPart.sourceType
}),
signinUrl: helpers.urlFormat('/passport/login'),
mobile: thirdPart.mobile,
area: thirdPart.area,
sourceType: thirdPart.sourceType,
openId: thirdPart.openId
}));
};
const bindCheck = (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let area = req.body.area || '86';
let sourceType = req.body.sourceType;
if (mobile && openId && area && sourceType) {
/**
* 接口绑定返回值:
* code:200,is_register=0 // 绑定流程:未注册,可绑定
* code:200,is_register=1 // 绑定流程:已注册绑定过其他第三方
* code:200:is_register=3 // 关联流程
* code:505 // 手机号码注册过,而且该第三方也已经绑定过手机号
* code:506 // 手机号码注册过,而且该手机号码也已经绑定过该类型第三方
*/
BindService.bindCheckAsync(mobile, openId, sourceType, area).then(result => {
if (!result || !result.code) {
return {code: 400, message: '', data: ''};
}
}).then(result => {
res.json(result);
}).catch(next);
},
relateMobile: (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let areaCode = req.body.areaCode || '86';
let sourceType = req.body.sourceType;
return BindService.relateMobile(openId, sourceType, mobile, areaCode).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/relatesuccess', {
sourceType: sourceType + '_bind'
});
return AuthHelper.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: ''}};
}
} else if (result.code === 200 && result.data.is_register === 0) {
let nextUrl = helpers.urlFormat('/passport/thirdlogin/bindSetPwd');
// 绑定流程:code=200 未注册,可绑定
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 => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return {code: 201, message: result.message, data: {user: user}};
});
} else if (result.code === 200 && result.data.is_register === 3) {
// 关联流程
return PassportHelper.getUserInfo(area, mobile).then(user => {
return {code: 203, message: result.message, data: {user: user}};
});
} else if (result.code === 506 || result.code === 505) {
return PassportHelper.getUserInfo(area, mobile).then(user => {
// 绑定流程:code=201 已注册 绑定过其他第三方
return {code: 205, message: result.message, data: {user: user}};
});
} else {
return {code: 400, message: '', data: ''};
return {code: result.code, message: result.message, data: result.data ? result.data : ''};
}
}).then(result => {
res.json(result);
}).catch(next);
} else {
res.json({code: 400, message: '', data: ''});
}
};
module.exports = bind;
const sendBindMsg = (req, res, next) => {
let mobile = req.body.mobile;
let area = req.body.area;
BindService.sendBindMsgAsync(area, mobile).then(result => {
if (result && result.code) {
res.json(result);
} else {
res.json({code: 400, message: '', data: ''});
}
}).catch(next);
};
const checkBindMsg = (req, res, next) => {
let mobile = req.body.mobile;
let area = req.body.area;
let code = req.body.code;
BindService.checkBindCodeAsync(area, mobile, code).then(result => {
if (result && result.code) {
res.json(result);
} else {
res.json({code: 400, message: '', data: ''});
}
}).catch(next);
};
const bindMobile = (req, res, next) => {
let mobile = _.trim(req.body.mobile);
let area = _.trim(req.body.area) || '86';
let openId = _.trim(req.body.openId);
let sourceType = _.trim(req.body.sourceType);
let password = _.trim(req.body.password) || '';
BindService.bindMobileAsync(openId, sourceType, mobile, area, password).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/bindsuccess', {
sourceType: sourceType + '_bind'
});
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: ''}};
}
} else {
return {code: 400, message: '', data: ''};
}
}).then(result => {
res.json(result);
}).catch(next);
};
const relateMobile = (req, res, next) => {
let mobile = req.body.mobile;
let openId = req.body.openId;
let areaCode = req.body.areaCode || '86';
let sourceType = req.body.sourceType;
return BindService.relateMobileAsync(openId, sourceType, mobile, areaCode).then(result => {
if (result && result.code) {
if (result.code === 200 && result.data && result.data.uid) {
let refer = helpers.urlFormat('/passport/thirdlogin/relatesuccess', {
sourceType: sourceType + '_bind'
});
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: ''}};
}
} else {
return {code: 400, message: '', data: ''};
}
}).then(result => {
res.json(result);
}).catch(next);
};
module.exports = {
indexPage, // 首页
bindSetPwdPage, // 设置密码页面
bindConfirmPage, // 绑定确认页面
bindSuccessPage, // 绑定成功页面
bindedPage, // 已绑定相同类型第三方的页面
relateConfirmPage, // 关联确认页面
relateSuccessPage, // 关联成功页面
bindCheck, // 检查第三方和手机号的绑定情况
sendBindMsg, // 发送绑定手机验证码
checkBindMsg, // 检查手机验证码
bindMobile, // 绑定手机号
relateMobile // 关联手机号
};
... ...
... ... @@ -62,8 +62,8 @@ const requiredPost = (req, res) => {
};
module.exports = {
requiredAPI,
requiredPage,
generate,
requiredPost
requiredAPI, // 中间件的验证
requiredPage, // 跳转页面的验证
generate, // 生成验证码
requiredPost // 端点验证码
};
... ...
... ... @@ -9,32 +9,29 @@ const _ = require('lodash');
const passport = require('passport');
const uuid = require('uuid');
const md5 = require('md5');
const Promise = require('bluebird');
const co = Promise.coroutine;
const cookie = global.yoho.cookie;
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 loginPageURL = `${config.siteUrl}/passport/login`;
const blockRedirectFilter = /sign|login|passport/;
// 第三方登录回调
function doPassportCallback(req, res, user) {
const _doPassportCallback = (req, res, user) => {
let shoppingKey = cookie.getShoppingKey(req);
let refer = req.cookies.refer;
let refer = req.cookies.refer || config.siteUrl;
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = config.siteUrl;
}
if (/sign|login|passport/.test(refer)) {
refer = config.siteUrl;
}
refer = !blockRedirectFilter.test(decodeURI(refer)) ? decodeURI(refer) : config.siteUrl;
if (user.openId && user.nickname) {
let signinByOpenID = AuthHelper.signinByOpenID(
let signinByOpenID = LoginService.signinByOpenIDAsync(
user.nickname, user.openId, user.sourceType, shoppingKey, user.unionId);
return signinByOpenID.then((result) => {
... ... @@ -49,7 +46,7 @@ function doPassportCallback(req, res, user) {
refer: refer
});
} else if (result.code === 200 && result.data.uid) {
return AuthHelper.syncUserSession(result.data.uid, req, res).then(() => {
return LoginService.syncUserSession(result.data.uid, req, res).then(() => {
return refer;
});
}
... ... @@ -57,9 +54,9 @@ function doPassportCallback(req, res, user) {
return res.redirect(redirectTo);
});
} else {
res.redirect(loginPageURL);
return Promise.resolve(res.redirect(loginPageURL));
}
}
};
const common = {
beforeLogin: (req, res, next) => {
... ... @@ -96,35 +93,37 @@ const common = {
const local = {
loginPage: (req, res) => {
// 设置登录有效时间30分钟, 防机器刷,cache不稳定,改为cookie
res.cookie('LE' + md5('_LOGIN_EXPIRE'), (new Date()).getTime() / 1000 + 1800);
res.cookie('LE' + md5('_LOGIN_EXPIRE'), (new Date()).getTime() / 1000 + 1800, {
domain: config.cookieDomain
});
let bindMobile = _.trim(req.query.bindMobile || '');
let bindArea = '+' + _.trim(req.query.bindArea || '86');
let areaArr = PassportHelper.getCountry();
let areaName = '';
let loginMobile = _.trim(req.query.bindMobile || '');
let loginCountryCode = '+' + _.trim(req.query.bindArea || '86');
let countries = PassportHelper.getCountry();
let defaultCountryName = '';
if (bindArea) {
let area = areaArr.find((a) => {
return a.areaCode === bindArea;
if (loginCountryCode) {
let area = countries.find((a) => {
return a.areaCode === loginCountryCode;
});
areaName = area ? area.name : '';
defaultCountryName = area ? area.name : '';
}
res.display('login', {
loginPage: true,
defaultHeader: false,
passport: {
countryCode: bindArea,
countryName: {text: areaName},
country: {list: areaArr},
countryCode: loginCountryCode,
countryName: {text: defaultCountryName},
country: {list: countries},
forgetPwd: helpers.urlFormat('/passport/back/index'),
fastReg: helpers.urlFormat('/passport/reg'),
weixinLogin: helpers.urlFormat('/passport/autosign/wechat'),
qqLogin: helpers.urlFormat('/passport/autosign/qq'),
weiboLogin: helpers.urlFormat('/passport/autosign/sina'),
alipayLogin: helpers.urlFormat('/passport/autosign/alipay'),
bindMobile: bindMobile
bindMobile: loginMobile
},
module: 'passport',
page: 'login',
... ... @@ -142,44 +141,47 @@ const local = {
}
});
} else {
let isRemember = req.body.isRemember;
let refer = req.cookies.refer;
if (isRemember) {
AuthHelper.rememberAccount({
area: req.body.areaCode || '86',
account: req.body.account,
password: req.body.password
}, req, res);
}
co(function * () {
let isRemember = req.body.isRemember === 'true';
let refer = req.cookies.refer;
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = `${config.siteUrl}`;
}
if (isRemember) {
yield LoginService.rememberAccount({
area: req.body.areaCode || '86',
account: req.body.account,
password: req.body.password
}, req, res);
}
if (/sign|login|passport/.test(refer)) {
refer = `${config.siteUrl}`;
}
if (refer) {
refer = decodeURI(req.cookies.refer);
} else {
refer = `${config.siteUrl}`;
}
if (blockRedirectFilter.test(refer)) {
refer = `${config.siteUrl}`;
}
AuthHelper.syncUserSession(user.uid, req, res).then(() => {
res.json({
code: 200,
data: {
refer: refer
}
yield LoginService.syncUserSession(user.uid, req, res).then(() => {
res.json({
code: 200,
data: {
refer: refer
}
});
});
});
})();
}
})(req, res, next);
},
logout: (req, res) => {
req.session = null;
req.session.destroy();
res.clearCookie('_UID', {
domain: config.cookieDomain
});
res.clearCookie('_TOKEN', {
domain: config.cookieDomain
});
... ... @@ -188,10 +190,22 @@ const local = {
domain: config.cookieDomain
});
res.clearCookie('_SPK');
res.clearCookie('_g');
res.clearCookie('isRemember');
res.clearCookie('remem');
res.clearCookie('_SPK', {
domain: config.cookieDomain
});
res.clearCookie('_g', {
domain: config.cookieDomain
});
res.clearCookie('isRemember', {
domain: config.cookieDomain
});
res.clearCookie('remem', {
domain: config.cookieDomain
});
res.redirect(config.siteUrl);
}
};
... ... @@ -211,7 +225,7 @@ const wechat = {
log.error(`wechat authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPageURL);
} else {
doPassportCallback(req, res, {
_doPassportCallback(req, res, {
openId: user._json.openid,
unionId: user._json.unionid || user.id,
nickname: user._json.nickname || user.displayName,
... ... @@ -244,7 +258,7 @@ const sina = {
let nickname = user.screen_name;
let openId = user.id;
doPassportCallback(req, res, {
_doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'sina'
... ... @@ -271,10 +285,11 @@ const qq = {
log.error(`qq authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPageURL);
}
let nickname = user.nickname;
let openId = user.id;
doPassportCallback(req, res, {
_doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'qq'
... ... @@ -296,10 +311,11 @@ const alipay = {
log.error(`alipay authenticate error : ${JSON.stringify(err)}`);
return res.redirect(loginPageURL);
}
let nickname = user.realName;
let openId = user.userId;
doPassportCallback(req, res, {
_doPassportCallback(req, res, {
openId: openId,
nickname: nickname,
sourceType: 'alipay'
... ... @@ -309,10 +325,10 @@ const alipay = {
};
module.exports = {
common: common,
local: local,
wechat: wechat,
qq: qq,
sina: sina,
alipay: alipay
common,
local, // 本地系统登录
wechat, // 微信登录
qq, // QQ登录
sina, // 新浪登录
alipay // 支付宝登录
};
... ...
... ... @@ -5,9 +5,9 @@
const _ = require('lodash');
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 RegService = require('../models/reg-service');
const UserService = require('../models/user-service');
const LoginService = require('../models/login-service');
const config = require('../../../config/common');
let helpers = global.yoho.helpers;
... ... @@ -100,7 +100,7 @@ let checkMobile = (req, res, next) => {
}
// 判断用户是否存在
return userService.findByMobileAsync(area, mobile).then((user) => {
return UserService.findByMobileAsync(area, mobile).then((user) => {
if (!_.isEmpty(user)) {
data.message = '手机号码已经存在';
return res.json(data);
... ... @@ -156,7 +156,7 @@ let sendBindMsg = (req, res, next) => {
}
/* 向手机发送注册验证码 */
let result = yield regService.sendCodeToMobile(area, mobile);
let result = yield RegService.sendCodeToMobileAsync(area, mobile);
return cache.set(sendCodeKey, sendCodeTimes + 1, 3600).then(() => {
if (result.code) {
... ... @@ -182,7 +182,7 @@ let msgCaptcha = (req, res, next) => {
let mobile = req.body.mobile;
let code = req.body.code; // 短信验证码
regService.validMobileCode(area, mobile, code).then((result) => {
RegService.validMobileCodeAsync(area, mobile, code).then((result) => {
if (result.code) {
return res.json(result);
} else {
... ... @@ -232,7 +232,7 @@ let mobileRegister = (req, res, next) => {
let mobile = req.body.mobile;
let code = req.body.code; // 短信验证码
let password = req.body.password;
let result = yield regService.validMobileCode(area, mobile, code); // 验证注册的标识码是否有效
let result = yield RegService.validMobileCodeAsync(area, mobile, code); // 验证注册的标识码是否有效
if (!result.code || result.code !== 200) {
data.message = '验证码错误';
... ... @@ -240,14 +240,14 @@ let mobileRegister = (req, res, next) => {
}
/* 手机注册: 调用注册接口*/
let regResult = yield regService.regMobile(area, mobile, password, cookie.getShoppingKey(req));
let regResult = yield RegService.regMobileAsync(area, mobile, password, cookie.getShoppingKey(req));
if (!regResult.code || regResult.code !== 200) {
data.message = '注册失败';
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: '注册成功',
... ... @@ -272,20 +272,20 @@ let success = (req, res) => {
title: '注册成功',
passport: {
goUrl: goUrl,
goShoppong: goShoppingUrl,
goShopping: goShoppingUrl,
mobile: mobile
}
});
};
module.exports = {
checkCode,
checkPassword,
index,
success,
checkMobile,
picCaptcha,
sendBindMsg,
msgCaptcha,
mobileRegister
checkCode, // 检查图形验证码中间件
checkPassword, // 检查密码格式
index, // 首页
success, // 成功页
checkMobile, // 检查手机号码是否注册过
picCaptcha, // 检查图形验证码
sendBindMsg, // 发送注册验证码
msgCaptcha, // 验证图形验证码
mobileRegister // 手机号注册
};
... ...
... ... @@ -30,7 +30,7 @@ app.engine('.hbs', hbs({
}));
require('./auth');
require('./models/login-auth-service');
app.use(passport.initialize());
app.use(passport.session());
... ...
'use strict';
const md5 = require('md5');
const _ = require('lodash');
const cache = global.yoho.cache;
const sign = global.yoho.sign;
const api = global.yoho.API;
const config = global.yoho.config;
const Auth = {
signin(area, profile, password, shoppingKey) {
let param = {
method: 'app.passport.signin',
area: area,
profile: profile,
password: password
};
if (shoppingKey) {
param.shopping_key = shoppingKey;
}
return api.post('', param);
},
signinByOtherOpenID(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, sourceType, shoppingKey, unionId) {
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);
},
signinByOpenID(nickname, openId, sourceType, shoppingKey, unionId) {
let signinFunc = {
other: this.signinByOtherOpenID,
wechat: this.signinByWechat
};
// PC 的微信登录之前使用了 open_id, 所以需要特别的接口处理
let type = sourceType !== 'wechat' ? 'other' : sourceType;
return signinFunc[type](nickname, openId, sourceType, shoppingKey, unionId);
},
profile(uid) {
let param = {
uid: uid,
method: 'app.passport.profile'
};
return api.get('', param);
},
syncUserSession(uid, req, res) {
return Auth.profile(uid).then((userInfo) => {
let token = sign.makeToken(uid);
let user = userInfo.data;
if (!_.isEmpty(user)) {
let uidCookie = `{data.profile_name}::${user.uid}::${user.username}::${token}`;
req.session._TOKEN = token;
req.session._LOGIN_UID = uid;
req.session._USERNAME = user.username;
res.cookie('_UID', uidCookie, {
domain: config.cookieDomain
});
res.cookie('_USERNAME', user.username, {
domain: config.cookieDomain
});
}
req.session._TOKEN = token; // esline-disable-line
req.session._LOGIN_UID = uid; // esline-disable-line
res.cookie('_TOKEN', token, {
domain: config.cookieDomain
}); // esline-disable-line
});
},
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);
}
}
};
module.exports = Auth;
/**
* 注册数据接口
*
* @author JiangFeng<jeff.jiang@yoho.cn>
* @date 2016/06/21
*/
'use strict';
const api = global.yoho.API;
const bindCheckAsync = (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 sendBindMsgAsync = (area, mobile) => {
let params = {
method: 'app.passport.smsbind',
mobile: mobile,
area: area
};
return api.get('', params);
};
const checkBindCodeAsync = (area, mobile, code) => {
return api.get('', {
method: 'app.register.validRegCode',
mobile: mobile,
area: area,
code: code
});
};
const bindMobileAsync = (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 relateMobileAsync = (openId, sourceType, mobile, area) => {
return api.get('', {
method: 'app.passport.relatedMobile',
mobile: mobile,
openId: openId,
source_type: sourceType,
area: area
});
};
const changeCheckAsync = (mobile, area) => {
return api.get('', {
method: 'app.passport.changeCheck',
mobile: mobile,
area: area
});
};
const changeMobileAsync = (uid, mobile, area, code) => {
return api.get('', {
method: 'app.passport.changeMobile',
mobile: mobile,
uid: uid,
code: code,
area: area
});
};
module.exports = {
bindCheckAsync,
sendBindMsgAsync,
checkBindCodeAsync,
bindMobileAsync,
relateMobileAsync,
changeCheckAsync,
changeMobileAsync
};
... ...
/**
* 注册数据接口
*
* @author JiangFeng<jeff.jiang@yoho.cn>
* @date 2016/06/21
* Created by TaoHuang on 2016/7/26.
*/
'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;
... ...
... ... @@ -7,6 +7,9 @@
const _ = require('lodash');
const Captchapng = require('captchapng');
/**
* 生成验证码
*/
exports.generateCaptcha = (width, height, length) => {
let min = Math.pow(10, (length - 1 || 1));
let max = Math.pow(10, (length - 1 || 1)) * 9;
... ...
/**
* Created by TaoHuang on 2016/7/25.
*/
'use strict';
const api = global.yoho.API;
const signinAsync = (area, profile, password, shoppingKey) => {
let param = {
method: 'app.passport.signin',
area: area,
profile: profile,
password: password
};
if (shoppingKey) {
param.shopping_key = shoppingKey;
}
return api.post('', param);
};
const signinByOtherOpenIDAsync = (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);
};
const signinByWechatAsync = (nickname, openId, sourceType, shoppingKey, unionId) => {
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);
};
module.exports = {
signinAsync,
signinByOtherOpenIDAsync,
signinByWechatAsync
};
... ...
... ... @@ -11,11 +11,11 @@ const WeixinStrategy = require('passport-weixin');
const SinaStrategy = require('passport-sina').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const QQStrategy = require('passport-qq').Strategy;
const AlipayStrategy = require('./models/passport-alipay').Strategy;
const AlipayStrategy = require('./passport-alipay').Strategy;
const md5 = require('md5');
const AuthHelper = require('./models/auth-helper');
const LoginService = require('./login-service');
const config = global.yoho.config;
const helpers = global.yoho.helpers;
... ... @@ -63,25 +63,20 @@ passport.use(new LocalStrategy({
let shoppingKey = cookie.getShoppingKey(req);
let account = req.body.account;
let ip = req.ip;
let errorLoginKey = 'account_errorlogin_' + account;
let accountKey = 'account_signin_' + account;
let ipKey = 'ip_signin_' + ip;
let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey), cache.get(ipKey)];
let cacheGet = [cache.get(errorLoginKey), cache.get(accountKey)];
Promise.all(cacheGet).then(times => {
let errLoginTimes = _.parseInt(times[0]) || 0;
let accountTimes = _.parseInt(times[1]) || 0;
let ipTimes = _.parseInt(times[2]) || 0;
if (accountTimes >= 10) {
done({message: '您的账号已被暂时锁定,请稍后再试'}, null);
} else if (ipTimes >= 100) {
done({message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试'}, null);
done({message: '您的账号已被暂时锁定,请30分钟后再试'}, null);
} else {
return AuthHelper.signin(area, username, password, shoppingKey).then((result) => {
return LoginService.signinAsync(area, username, password, shoppingKey).then((result) => {
if (result.code && result.code === 200 && result.data.uid) {
cache.del(errorLoginKey);
... ... @@ -89,15 +84,11 @@ passport.use(new LocalStrategy({
} else {
errLoginTimes = errLoginTimes + 1;
accountTimes = accountTimes + 1;
ipTimes = ipTimes + 1;
cache.set(errorLoginKey, errLoginTimes);
cache.set(accountKey, accountTimes, 1800);
cache.set(ipKey, ipTimes, 3600);
// 再次校验
if (ipTimes >= 100) {
done({message: '您尝试的次数过多,账号已被暂时锁定,请稍后再试'}, null);
} else if (accountTimes >= 10) {
if (accountTimes >= 10) {
done({message: '您的账号已被暂时锁定,请稍后再试'}, null);
} else if (errLoginTimes >= 3) {
done({
... ...
/**
* Created by TaoHuang on 2016/7/25.
*/
'use strict';
const md5 = require('md5');
const _ = require('lodash');
const cache = global.yoho.cache;
const sign = global.yoho.sign;
const config = global.yoho.config;
const api = require('./login-api');
const userService = require('./user-service');
const signinByOpenIDAsync = (nickname, openId, sourceType, shoppingKey, unionId) => {
let signinFunc = {
other: api.signinByOtherOpenIDAsync,
wechat: api.signinByWechatAsync
};
// PC 的微信登录之前使用了 open_id, 所以需要特别的接口处理
let type = sourceType !== 'wechat' ? 'other' : sourceType;
return signinFunc[type](nickname, openId, sourceType, shoppingKey, unionId);
};
const syncUserSession = (uid, req, res) => {
return userService.profile(uid).then((userInfo) => {
let token = sign.makeToken(uid);
let user = userInfo.data;
if (!_.isEmpty(user)) {
let uidCookie = `{data.profile_name}::${user.uid}::${user.username}::${token}`;
req.session._TOKEN = token;
req.session._LOGIN_UID = uid;
req.session._USERNAME = user.username;
res.cookie('_UID', uidCookie, {
domain: config.cookieDomain
});
res.cookie('_USERNAME', user.username, {
domain: config.cookieDomain
});
}
req.session._TOKEN = token; // esline-disable-line
req.session._LOGIN_UID = uid; // esline-disable-line
res.cookie('_TOKEN', token, {
domain: config.cookieDomain
}); // esline-disable-line
});
};
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
});
return cache.set(rememKey, accountInfo);
};
module.exports = {
signinAsync: api.signinAsync,
signinByOpenIDAsync,
syncUserSession,
rememberAccount
};
... ...
... ... @@ -86,7 +86,7 @@ const verifyEmail = email => {
return false;
}
const emailRegExp = /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
const emailRegExp = /^[\.a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
return emailRegExp.test(email);
};
... ...
/**
* 注册 model
*/
'use strict';
const api = global.yoho.API;
let sendCodeToMobileAsync = (area, mobile) => {
let params = {
method: 'app.register.sendRegCodeToMobile',
area: area,
mobile: mobile
};
return api.post('', params);
};
let validMobileCodeAsync = (area, mobile, code) => {
let params = {
method: 'app.register.validRegCode',
area: area,
mobile: mobile,
code: code
};
return api.post('', params);
};
let regMobileAsync = (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);
};
module.exports = {
sendCodeToMobileAsync,
validMobileCodeAsync,
regMobileAsync
};
... ...
/**
* 注册 model
* Created by TaoHuang on 2016/7/26.
*/
'use strict';
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;
}
'use strict';
return api.post('', params);
};
const api = require('./reg-api');
module.exports = {
sendCodeToMobile,
validMobileCode,
regMobile
};
module.exports = api;
... ...
... ... @@ -46,7 +46,17 @@ const findByEmailAsync = (email) => {
});
};
const profile = (uid) => {
let param = {
uid: uid,
method: 'app.passport.profile'
};
return api.get('', param);
};
module.exports = {
findByMobileAsync,
findByEmailAsync
findByEmailAsync,
profile
};
... ...
... ... @@ -67,7 +67,7 @@
<li>
<div>
<input id="reset-pwd-btn" class="reset-pwd-btn disable" type="submit" value="下一步">
<input id="reset-pwd-btn" class="reset-pwd-btn disable" type="submit" value="下一步" disabled>
</div>
</li>
... ...
... ... @@ -42,7 +42,7 @@
</li>
<div class="success-text">
<div class="title">恭喜您设置完成,请妥善保存您的密码!</div>
<div class="small-title">恭喜您设置完成,请妥善保存您的密码!</div>
<div class="time">页面将在&nbsp;<span id="count-down" class="blue">5</span>&nbsp;秒后将跳转至首页</div>
</div>
</ul>
... ... @@ -52,14 +52,12 @@
var count = 5,
countDown = document.getElementById('count-down');
console.log(countDown);
var timer = setInterval(function () {
setInterval(function () {
if (count > 1) {
count--;
countDown.innerHTML = count;
} else {
location.href = '/';
location.href = '/passport/login';
}
}, 1000);
})();
... ...
... ... @@ -48,7 +48,7 @@
<li class="clearfix">
<span class="left login-fail-tip hide">
<!--<span class="'iconfont">&#xe608;</span>-->
<span class="iconfont">&#xe61d;</span>
<em></em>
</span>
... ...
... ... @@ -9,7 +9,7 @@
<div>
尊敬的{{mobile}},恭喜您已经成为YOHO!BLK会员!即刻您可以开启时尚购物之旅!
</div>
<a class="btn btn-fixed-height success-btn" href="{{goShoppong}}" data-url="{{goUrl}}">开始购物</a>
<a class="btn btn-fixed-height success-btn" href="{{goShopping}}" data-url="{{goUrl}}">开始购物</a>
</div>
{{/ passport}}
</div>
... ...
... ... @@ -6,10 +6,14 @@
'use strict';
const _ = require('lodash');
const Item = require('../models/item');
const item = require('../models/item');
/**
* 商品详情页
* @function index
*/
const index = (req, res, next) => {
Item.getProductItemData(req.params, req.url, req.user.uid).then(result => {
item.getProductItemData(req.params, req.url, req.user.uid).then(result => {
if (_.isEmpty(result)) {
return next();
}
... ... @@ -18,6 +22,13 @@ const index = (req, res, next) => {
}).catch(next);
};
/**
* 获取商品信息接口
* @function getProductInfo
* @param { number } productId 商品id
* @param { number } skn 商品skn
* @return { Object } 商品信息
*/
const getProductInfo = (req, res, next) => {
const productId = req.query.productId;
const skn = req.query.skn;
... ... @@ -27,7 +38,7 @@ const getProductInfo = (req, res, next) => {
uid = req.user.uid;
}
Item.getProductInfo(productId, skn, uid).then(result => {
item.getProductInfo(productId, skn, uid).then(result => {
res.json(result);
}).catch(next);
};
... ...
... ... @@ -27,13 +27,16 @@ const list = {
title: '列表'
};
Search.queryProduct(q).then(result => {
Promise.all([Search.queryAllSort(), Search.queryProduct(q)]).then(allResult => {
let allSort = camelCase(allResult[0]);
let result = allResult[1];
if (result && result.code === 200 && result.data) {
let data = camelCase(result.data);
let nav = [DataHelper.getChannelNav()];
if (data.filter) {
data.filter.groupSort = DataHelper.sortConvert(allSort.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
nav = _.concat(nav, retData.filter.nav);
... ... @@ -71,12 +74,13 @@ const list = {
let retData = {
module: 'product',
page: 'list',
title: '列表'
title: '新品'
};
Promise.all([Resouces.newProductBanner(), Search.queryNewProduct(q)]).then(result => {
Promise.all([Resouces.newProductBanner(), Search.queryAllSort(), Search.queryNewProduct(q)]).then(result => {
let banner = result[0];
let listData = result[1];
let sortData = camelCase(result[1]);
let listData = result[2];
let nav = [DataHelper.getChannelNav(), {
name: '新品'
}];
... ... @@ -87,6 +91,7 @@ const list = {
let data = camelCase(listData.data);
if (data.filter) {
data.filter.groupSort = DataHelper.sortConvert(sortData.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
}
... ...
... ... @@ -28,7 +28,11 @@ const Query = {
query: query
};
Search.queryProduct(q).then(result => {
Promise.all([Search.queryAllSort({
query: query
}), Search.queryProduct(q)]).then(allResult => {
let allSort = camelCase(allResult[0]);
let result = allResult[1];
if (result && result.code === 200 && result.data) {
let data = camelCase(result.data);
... ... @@ -39,6 +43,7 @@ const Query = {
});
if (data.filter) {
data.filter.groupSort = DataHelper.sortConvert(allSort.data.sort);
retData.filter = DataHelper.filterHandle(data.filter, q);
retData.filter.showPrice = data.total > 10;
}
... ...
... ... @@ -109,41 +109,51 @@ const shop = {
nav.push({
name: result.name
});
} else {
res.status(404);
return Promise.reject('brand not found');
}
}).then(() => {
return Search.queryProductOfBrand(q).then(result => {
if (result && result.code === 200 && result.data) {
let ret = camelCase(result.data);
if (ret.filter) {
delete q.brand;
data.filter = DataHelper.filterHandle(ret.filter, req.query);
data.filter.showPrice = ret.total > 10;
return result.brandId;
}).then((brandId) => {
if (brandId) {
return Promise.all([Search.queryAllSort({
brand: q.brand,
shop: q.shop_id,
small_sort: 1
}), Search.queryProductOfBrand(q)]).then(allResult => {
let allSort = camelCase(allResult[0]);
let result = allResult[1];
if (result && result.code === 200 && result.data) {
let ret = camelCase(result.data);
if (ret.filter) {
delete q.brand;
ret.filter.groupSort = DataHelper.sortConvert(allSort.data.sort);
data.filter = DataHelper.filterHandle(ret.filter, req.query);
data.filter.showPrice = ret.total > 10;
}
data.paginationData = {
page: q.page,
limit: ret.limit || 45,
total: ret.total,
pageTotal: ret.pageTotal,
queryParams: req.query
};
data.navPath = {
nav: nav
};
res.display('shop-list', _.assign(data, {
products: DataHelper.handleProductList(ret.productList),
order: q.order
}));
} else {
return Promise.reject('query shop index error');
}
data.paginationData = {
page: q.page,
limit: ret.limit || 45,
total: ret.total,
pageTotal: ret.pageTotal,
queryParams: req.query
};
data.navPath = {
nav: nav
};
res.display('shop-list', _.assign(data, {
products: DataHelper.handleProductList(ret.productList),
order: q.order
}));
} else {
return Promise.reject('query shop index error');
}
});
});
} else {
return next();
}
}).catch(next);
},
... ...
... ... @@ -8,6 +8,14 @@
const api = global.yoho.API;
/**
* 收藏API
* @function addFavAsync
* @param { number } uid 用户uid
* @param { number } id 收藏id
* @param { string } type 收藏类型 product--商品 brand--品牌 shop--店铺
* @return { Object } 收藏结果
*/
const addFavAsync = (uid, id, type) => {
return api.get('', {
method: 'app.favorite.add',
... ... @@ -17,6 +25,14 @@ const addFavAsync = (uid, id, type) => {
});
};
/**
* 取消收藏API
* @function cancelFavAsync
* @param { number } uid 用户uid
* @param { number } id 收藏id
* @param { string } type 收藏类型 product--商品 brand--品牌 shop--店铺
* @return { Object } 取消收藏结果
*/
const cancelFavAsync = (uid, id, type) => {
return api.get('', {
method: 'app.favorite.cancel',
... ... @@ -27,6 +43,6 @@ const cancelFavAsync = (uid, id, type) => {
};
module.exports = {
addFavAsync,
cancelFavAsync
addFavAsync, // 收藏
cancelFavAsync // 取消收藏
};
... ...
... ... @@ -6,35 +6,58 @@
'use strict';
const FavAPI = require('./favorite-api');
const favAPI = require('./favorite-api');
/**
* 收藏商品
* @function toggleFavProduct
* @param { number } productId 商品id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavProduct = (productId, uid, isadd) => {
if (isadd) {
return FavAPI.addFavAsync(uid, productId, 'product');
return favAPI.addFavAsync(uid, productId, 'product');
} else {
return FavAPI.cancelFavAsync(uid, productId, 'product');
return favAPI.cancelFavAsync(uid, productId, 'product');
}
};
/**
* 收藏品牌
* @function toggleFavBrand
* @param { number } brandId 品牌id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavBrand = (brandId, uid, isadd) => {
if (isadd) {
return FavAPI.addFavAsync(uid, brandId, 'brand');
return favAPI.addFavAsync(uid, brandId, 'brand');
} else {
return FavAPI.cancelFavAsync(uid, brandId, 'brand');
return favAPI.cancelFavAsync(uid, brandId, 'brand');
}
};
/**
* 收藏店铺
* @function toggleFavShop
* @param { number } shopId 店铺id
* @param { number } uid 用户uid
* @param { string } isadd 是否收藏 true--添加收藏 false--取消收藏
* @return { Object } 收藏结果
*/
const toggleFavShop = (shopId, uid, isadd) => {
if (isadd) {
return FavAPI.addFavAsync(uid, shopId, 'shop');
return favAPI.addFavAsync(uid, shopId, 'shop');
} else {
return FavAPI.cancelFavAsync(uid, shopId, 'shop');
return favAPI.cancelFavAsync(uid, shopId, 'shop');
}
};
module.exports = {
toggleFavProduct,
toggleFavBrand,
toggleFavShop
toggleFavProduct, // 收藏商品
toggleFavBrand, // 收藏品牌
toggleFavShop // 收藏店铺
};
... ...
... ... @@ -96,27 +96,28 @@ const helpers = {
* @param sorts
* @returns {Array}
*/
getSortNav(sort, sorts) {
getSortNav(msort, misort, sorts) {
let nav = [];
let sortQuery = '?';
if (sort && sorts) {
if (msort && sorts) {
sorts.forEach(s => {
if (s.relationParameter.sort === sort) {
if (s.categoryId === msort) {
sortQuery += 'msort=' + msort;
s.checked = true;
nav.push({
link: '#',
link: sortQuery,
pathTitle: '',
name: s.categoryName
});
} else if (s.sub) {
}
if (s.sub && misort) {
s.sub.forEach(m => {
if (m.relationParameter.sort === sort) {
nav.push({
link: '#',
pathTitle: '',
name: s.categoryName
});
if (m.categoryId === misort) {
sortQuery += '&misort=' + misort;
m.checked = true;
nav.push({
link: '#',
link: sortQuery,
pathTitle: '',
name: m.categoryName
});
... ... @@ -143,6 +144,16 @@ const helpers = {
};
},
sortConvert(sorts) {
return _.map(sorts, s => {
return {
categoryId: s.sortId,
categoryName: s.sortName,
sub: helpers.sortConvert(s.sub)
};
});
},
/**
* 筛选器数据处理
* @param filter
... ... @@ -160,6 +171,7 @@ const helpers = {
let filters = [];
let customPriceLow = '';
let customPriceHigh = '';
let showSize = (!!q.sort || !!q.misort);
genders.forEach(g => {
if (g.value === q.gender) {
... ... @@ -222,6 +234,7 @@ const helpers = {
}
if (q.size) {
showSize = false;
sizeInfo.forEach(s => {
if (s.sizeId === parseInt(q.size, 10)) {
s.checked = true;
... ... @@ -270,9 +283,9 @@ const helpers = {
letters: this.brandLetters(),
customPriceLow: customPriceLow,
customPriceHigh: customPriceHigh,
showSize: !!q.sort,
showSize: showSize,
showPrice: true,
nav: this.getSortNav(q.sort, sorts)
nav: this.getSortNav(q.msort, q.misort, sorts)
};
},
... ...
... ... @@ -7,6 +7,14 @@
const api = global.yoho.API;
/**
* 获取商品基础数据API
* @function getProductBaseAsync
* @param { number } productId 商品id
* @param { number } uid 用户uid
* @param { number } skn 商品skn
* @return { Object } 商品信息
*/
const getProductBaseAsync = (productId, uid, skn) => {
let params = {
method: 'h5.product.data',
... ... @@ -24,6 +32,13 @@ const getProductBaseAsync = (productId, uid, skn) => {
return api.get('', params, { cache: true });
};
/**
* 获取用户商品收藏状态API
* @function getUserIsFav
* @param { number } uid 用户uid
* @param { number } productId 商品id
* @return { Object } 收藏状态
*/
const getUserIsFav = (uid, productId) => {
return api.get('', {
method: 'app.favorite.isFavorite',
... ... @@ -33,6 +48,12 @@ const getUserIsFav = (uid, productId) => {
}, { cache: true });
};
/**
* 获取商品尺码信息API
* @function getsizeInfoAsync
* @param { number } skn 商品skn
* @return { Object } 商品尺码信息
*/
const getsizeInfoAsync = (skn) => {
return api.get('', {
method: 'h5.product.intro',
... ... @@ -40,6 +61,12 @@ const getsizeInfoAsync = (skn) => {
}, { cache: true });
};
/**
* 获取商品材料信息API
* @function getComfortAsync
* @param { number } productId 商品id
* @return { Object } 商品材料信息
*/
const getComfortAsync = productId => {
return api.get('', {
method: 'web.productComfort.data',
... ... @@ -50,6 +77,12 @@ const getComfortAsync = productId => {
});
};
/**
* 获取商品模特试穿信息API
* @function getProductBaseAsync
* @param { number } skn 商品skn
* @return { Object } 商品模特试穿信息
*/
const getModelTryAsync = skn => {
return api.get('', {
method: 'web.productModelTry.data',
... ... @@ -61,9 +94,9 @@ const getModelTryAsync = skn => {
};
module.exports = {
getProductBaseAsync,
getUserIsFav,
getsizeInfoAsync,
getComfortAsync,
getModelTryAsync
getProductBaseAsync, // 获取商品基本信息
getUserIsFav, // 获取商品用户收藏信息
getsizeInfoAsync, // 获取商品尺码信息
getComfortAsync, // 获取商品材质信息
getModelTryAsync // 获取商品模特试穿信息
};
... ...
... ... @@ -6,9 +6,9 @@
'use strict';
const _ = require('lodash');
const itemAPI = require('./item-api');
const brandAPI = require('./brand-api');
const itemFUN = require('./item-handler');
const itemApi = require('./item-api');
const brandApi = require('./brand-api');
const itemFun = require('./item-handler');
const search = require('./search-api');
... ... @@ -26,15 +26,15 @@ const _getMultiResourceByBaseInfo = (base) => {
// 获取相关数据
let promiseData = [
itemAPI.getsizeInfoAsync(skn),
itemAPI.getComfortAsync(productId),
itemAPI.getModelTryAsync(skn),
itemApi.getsizeInfoAsync(skn),
itemApi.getComfortAsync(productId),
itemApi.getModelTryAsync(skn),
search.getSortAsync({sort: base.smallSortId})
];
if (base.uid) {
promiseData.push(itemAPI.getUserIsFav(base.uid, productId));
promiseData.push(brandAPI.getBrandInfo(brandId, base.uid));
promiseData.push(itemApi.getUserIsFav(base.uid, productId));
promiseData.push(brandApi.getBrandInfo(brandId, base.uid));
}
return Promise.all(promiseData).then(result => {
... ... @@ -62,7 +62,7 @@ const getProductItemData = (params, url, uid) => {
// let gid = params[1];
return itemAPI.getProductBaseAsync(pid).then(result => {
return itemApi.getProductBaseAsync(pid).then(result => {
let resData = {};
let data = {};
... ... @@ -76,13 +76,14 @@ const getProductItemData = (params, url, uid) => {
result.mainPath = url;
// 商品基本信息
data.goodInfo = itemFUN.setProductData(result);
data.goodInfo = itemFun.setProductData(result);
if (result.brand) {
Object.assign(data, itemFUN.setBrandBanner(result.brand));
// BRAND品牌简介
Object.assign(data, itemFUN.setBrandIntro(result.brand));
Object.assign(
data,
itemFun.setBrandBanner(result.brand), // banner
itemFun.setBrandIntro(result.brand) // BRAND品牌简介
);
}
return _getMultiResourceByBaseInfo(result).then(mulRes => {
... ... @@ -91,25 +92,17 @@ const getProductItemData = (params, url, uid) => {
_.set(data, 'brandBanner.brandFav', mulRes.brandFav);
_.set(data, 'goodInfo.productFav', mulRes.productFav);
// 面包屑导航
Object.assign(data, itemFUN.setPathNav(mulRes.sort, result.productName));
// DESCRIPTION商品描述
Object.assign(data, itemFUN.setDescriptionData(mulRes.sizeInfo, mulRes.comfort));
// MATERIALS材料洗涤
Object.assign(data, itemFUN.setMaterialData(mulRes.sizeInfo));
// SIZEINFO尺码信息
Object.assign(data, itemFUN.setSizeData(mulRes.sizeInfo, mulRes.modelTry));
// DETAILS商品详情
Object.assign(data, itemFUN.setDetailData(mulRes.sizeInfo));
Object.assign(data,
itemFun.setPathNav(mulRes.sort, result.productName), // 面包屑导航
itemFun.setDescriptionData(mulRes.sizeInfo, mulRes.comfort), // DESCRIPTION商品描述
itemFun.setMaterialData(mulRes.sizeInfo), // MATERIALS材料洗涤
itemFun.setSizeData(mulRes.sizeInfo, mulRes.modelTry), // SIZEINFO尺码信息
itemFun.setDetailData(mulRes.sizeInfo) // DETAILS商品详情
);
resData.content = data;
// 商品详情SEO
Object.assign(resData, itemFUN.setSeoInfo(data.goodInfo, data.nav));
resData.content = data;
Object.assign(resData, itemFun.setSeoInfo(data.goodInfo, data.nav));
return resData;
});
... ... @@ -126,8 +119,8 @@ const getProductItemData = (params, url, uid) => {
* @return { Object } 接口返回单个商品的基本信息
*/
const getProductInfo = (productId, uid, skn) => {
return itemAPI.getProductBaseAsync(productId, uid, skn).then(result => {
return itemFUN.setProductData(result);
return itemApi.getProductBaseAsync(productId, uid, skn).then(result => {
return itemFun.setProductData(result);
});
};
... ...
... ... @@ -47,7 +47,7 @@ const Search = {
},
queryProductOfBrand(params) {
let finalParams = {
method: 'app.search.brand',
method: 'app.search.li',
limit: 45,
app_type: 1
};
... ... @@ -67,11 +67,11 @@ const Search = {
return api.get('', finalParams);
},
queryAllSort() {
return api.get('', {
method: 'app.sort.get',
queryAllSort(params) {
return api.get('', _.assign({
method: 'web.regular.groupsort',
app_type: 1
});
}, params), {code: 200});
}
};
... ...
... ... @@ -10,7 +10,7 @@
{{> list/banner-info }}
</div>
<div class="center-content clearfix">
<div class="yoho-product-list-content center-content clearfix">
<div class="left">
{{!-- 筛选区域 --}}
{{#filter}}
... ...
... ... @@ -34,7 +34,7 @@
<div class="title">全部品类</div>
<div class="yoho-ui-accordion no-active">
{{#each sortData}}
<h3>{{categoryName}}</h3>
<h3 {{#if checked}}class="active"{{/if}}>{{categoryName}}</h3>
<div class="body" data-value="{{categoryId}}">
<div class="list-body nano">
<div class="nano-content">
... ... @@ -137,6 +137,7 @@
</div>
{{/if}}
{{#if colors}}
<div class="yoho-ui-accordion">
<h3>颜色</h3>
<div class="body">
... ... @@ -152,3 +153,4 @@
</div>
</div>
</div>
{{/if}}
... ...
... ... @@ -201,3 +201,22 @@ exports.modifyProduct = (req, res, next) => {
}
}).catch(next);
};
// 获取购物车商品信息
exports.getCartData = (req, res, next) => {
const shoppingKey = req.cookies._SPK || null;
const uid = req.user.uid || null;
cartModel.getCartData(shoppingKey, uid).then((result) => {
if (result.code === 200) {
res.json(
_.merge(
cartModel.filterCartData(result, uid),
{code: result.code, message: result.message}
)
);
} else {
res.send(result);
}
}).catch(next);
};
... ...
... ... @@ -13,10 +13,6 @@ const camelCase = global.yoho.camelCase;
const helpers = global.yoho.helpers;
const Payments = {
alipay: 2
};
/**
* 支付页
* @param req
... ... @@ -76,7 +72,7 @@ const toPay = (req, res, next) => {
*/
const callback = (req, res) => {
let type = req.params.type;
let payId = Payments[type];
let payId = PayData.payments[type];
let query = req.query;
PayHelpers.afterPay(query, payId, req.user).then(result => {
... ...
... ... @@ -24,7 +24,7 @@ const Payment = {
};
if (payInfo && payInfo.id === method) {
if (method === 2) {
if (method === PayData.payments.alipay) {
result = Alipay.pay(user, order, payInfo);
}
}
... ... @@ -77,7 +77,7 @@ const Payment = {
let payInfo = yield PayData.getPaymentInfo(payId);
let payResult = {};
if (payId === 2) {
if (payId === PayData.payments.alipay) {
payResult = Alipay.notify(query, payInfo);
payResult.bankName = payResult.bankName || payInfo.payName || '';
... ...
... ... @@ -229,7 +229,10 @@ const filterCartData = (result, uid) => {
let invalidGoods = [];
// 库存不足商品
let soldOutGoods = [];
// 售罄商品当作一般商品处理
// sold_out_goods_list
let advancedSoldOutGoods = [];
let ordinarySoldOutGoods = [];
// 商品总数量
let buyNumber;
... ... @@ -248,7 +251,7 @@ const filterCartData = (result, uid) => {
totalAdvanceMoney = advancedCartData.shopping_cart_data.last_order_amount;
advancedGoods = _.concat(advancedGoods, advancedCartData.goods_list);
invalidGoods = _.concat(invalidGoods, advancedCartData.off_shelves_goods_list);
soldOutGoods = _.concat(soldOutGoods, advancedCartData.sold_out_goods_list);
advancedSoldOutGoods = _.concat(advancedSoldOutGoods, advancedCartData.sold_out_goods_list);
_.forEach(advancedGoods, function(good) {
buyNumber = parseInt(good.buy_number, 10);
... ... @@ -272,7 +275,7 @@ const filterCartData = (result, uid) => {
totalOrdinaryMoney = ordinaryCartData.shopping_cart_data.last_order_amount;
ordinaryGoods = _.concat(ordinaryGoods, ordinaryCartData.goods_list);
invalidGoods = _.concat(invalidGoods, ordinaryCartData.off_shelves_goods_list);
soldOutGoods = _.concat(soldOutGoods, ordinaryCartData.sold_out_goods_list);
ordinarySoldOutGoods = _.concat(ordinarySoldOutGoods, ordinaryCartData.sold_out_goods_list);
_.forEach(ordinaryGoods, function(good) {
buyNumber = parseInt(good.buy_number, 10);
... ... @@ -293,8 +296,10 @@ const filterCartData = (result, uid) => {
return _.merge(resData, {
hasGoods: advancedGoods.length || ordinaryGoods.length || invalidGoods.length,
preSalePros: advancedGoods.length ? _.groupBy(advancedGoods, 'brand_id') : [],
commonPros: ordinaryGoods.length ? _.groupBy(ordinaryGoods, 'brand_id') : [],
preSalePros: (advancedSoldOutGoods.length || advancedGoods.length) ?
_.groupBy(_.concat(advancedGoods, advancedSoldOutGoods), 'brand_id') : [],
commonPros: (ordinarySoldOutGoods.length || ordinaryGoods.length) ?
_.groupBy(_.concat(ordinaryGoods, ordinarySoldOutGoods), 'brand_id') : [],
invalidPros: invalidGoods,
selectedNum: selectedAdvanceNum + selectedOrdinaryNum,
checkAll: totalNum === (selectedAdvanceNum + selectedOrdinaryNum),
... ...
... ... @@ -17,6 +17,10 @@ const logger = global.yoho.logger;
const _ = require('lodash');
const payments = {
alipay: 33
};
/**
* 获取在线支付方式
* @returns {*|Promise.<TResult>}
... ... @@ -28,13 +32,13 @@ const getOnlinePayProvider = () => {
}
let online = _(result.data)
.filter(i => _.includes(['Alipay'], i.payCode))
.filter(i => _.includes([33], i.id))
.map(i => {
return {
id: i.id,
name: i.payName,
img: i.payIcon,
selected: i.payCode === 'Alipay'
selected: i.id === 33
};
})
.value();
... ... @@ -223,6 +227,7 @@ module.exports = {
setOrderPayBank,
updateOrderPayBank,
sendPayConfirm,
procOrderData
procOrderData,
payments
};
... ...
... ... @@ -23,6 +23,7 @@ router.post('/cart/add', cartCtrl.addToCart);
router.post('/cart/toggleSelectGoods', cartCtrl.toggleSelectGoods);
router.get('/cart/checkStorage', cartCtrl.checkStorage);
router.put('/cart/updateProduct', cartCtrl.modifyProduct);
router.get('/cart/data', cartCtrl.getCartData);
// 结算
router.get('/order', auth, order.index);
... ...
... ... @@ -9,6 +9,7 @@
{{> cart/empty-cart}}
{{/if}}
</div>
<div id="removed_products"></div>
</div>
<script id="edit-color-size-tpl" type="text/html">
... ... @@ -117,10 +118,14 @@
<li class="pro-info">
\{{!-- <div class="brand-name">\{{brand_name}}</div> --}}
<div class="pro-name"><a href="/product/pro_\{{product_id}}_\{{goods_id}}/\{{cn_alphabet}}.html" target="_blank">\{{product_name}}</a></div>
<div class="size">
<div class="color-size editable" data-product_id=\{{product_id}} id="edit_\{{product_id}}">
\{{#if color_name}}
<span class="mr20">颜色: \{{color_name}}</span>
\{{/if}}
\{{#if size_name}}
<span>尺寸: \{{size_name}}</span>
\{{/if}}
<span class="iconfont">&#xe63c;</span>
</div>
\{{#expect_arrival_time}}
<div class="published-at">上市期: \{{expect_arrival_time}}</div>
... ... @@ -146,7 +151,7 @@
<li class="total-price-action">
<span class="price item-total-price">¥ \{{round (multiple sales_price buy_number) 2}}</span>
<div class="actions">
<div class="remove-item action" data-product_id=\{{product_id}}><span class="iconfont">&#xe614;</span> &nbsp;删&nbsp;&nbsp;&nbsp;&nbsp;除</div>
<div class="remove-item action" data-product_extra_info='{"goodsId": "\{{goods_id}}", "cnAlphabet": "\{{cn_alphabet}}", "productId": "\{{product_id}}", "salesPrice": "\{{round sales_price 2}}", "productName": "\{{product_name}}", "goodType":"ordinary", "selected": "\{{selected}}"}'><span class="iconfont">&#xe614;</span> &nbsp;删&nbsp;&nbsp;&nbsp;&nbsp;除</div>
<div class="send-to-favorite action" data-product_id=\{{product_id}}>移入收藏夹</div>
</div>
</li>
... ... @@ -213,7 +218,7 @@
<li class="total-price-action">
<span class="price item-total-price">¥ \{{round (multiple sales_price buy_number) 2}}</span>
<div class="actions">
<div class="remove-item action" data-product_id=\{{product_id}}><span class="iconfont">&#xe614;</span> &nbsp;删&nbsp;&nbsp;&nbsp;&nbsp;除</div>
<div class="remove-item action" data-product_extra_info='{"goodsId": "\{{goods_id}}", "cnAlphabet": "\{{cn_alphabet}}", "productId": "\{{product_id}}", "salesPrice": "\{{round sales_price 2}}", "productName": "\{{product_name}}", "goodType":"ordinary", "selected": "\{{selected}}"}'><span class="iconfont">&#xe614;</span> &nbsp;删&nbsp;&nbsp;&nbsp;&nbsp;除</div>
<div class="send-to-favorite action" data-product_id=\{{product_id}}>移入收藏夹</div>
</div>
</li>
... ... @@ -290,7 +295,6 @@
</div>
<div id="remove_selected" class="action hoverable">删除选中的商品</div>
<div id="send_favorite" class="action hoverable">移入收藏夹</div>
<div id="warning_invalid" class="action hoverable">清除实效商品</div>
<div class="selected-num">已选<span>\{{selectedNum}}</span>件</div>
<div class="checkout-total">
<div class="total-title">商品金额:</div>
... ... @@ -319,3 +323,28 @@
</a>
</div>
</script>
<script id="removed-products" type="text/html">
<div class="cart-removed-list">
<div class="info-bar">
<p class="info-text">已删除商品,您可以重新购买或移入收藏:</p>
</div>
<div class="removed-products">
<ul>
\{{#each removedProducts}}
<li class="removed-product">
<div class="product-name">
<a href="/product/pro_\{{productId}}_\{{goodsId}}/\{{cnAlphabet}}.html" target="_blank">\{{productName}}</a>
</div>
<div class="product-price">¥ \{{salesPrice}}</div>
<div class="bought-num">\{{buyNumber}}</div>
<div class="actions">
<span class="buy-again" data-rebuy_info='{"buyNumber": \{{buyNumber}}, "productSku": "\{{productSku}}"}'>重新购买</span>
<span class="send-to-favorite" data-product_info='{"goods_type": "\{{goodType}}", "buy_number": \{{buyNumber}}, "selected": "\{{selected}}", "product_sku": "\{{productSku}}", "promotion_id": 0}'>移入收藏夹</span>
</div>
</li>
\{{/each}}
</ul>
</div>
</div>
</script>
... ...
... ... @@ -203,58 +203,7 @@
{{/ shopping_cart_data}}
</div>
<script id="balance-tpl" type="text/html">
\{{#each promotion_formula_list}}
<li>
<span class="balance-title">\{{promotion}}</span>
<span class="balance-cost">\{{promotion_amount}}</span>
</li>
\{{/each}}
<li class="need-pay">
<span class="balance-title">应付金额\{{#if shipping_cost}}(含运费)\{{/if}}:</span>
<span id="balance-cost" class="balance-cost" data-cost="{{last_order_amount}}">¥<em>\{{last_order_amount}}</em></span>
</li>
<li>
<span id="submit-order" class="btn submit-order">提交订单</span>
</li>
</script>
<script id="address-list-tpl" type="text/html">
\{{#each address}}
<li class="address\{{#isY is_default}} default\{{/isY}}\{{#if focus}} focus\{{/if}}"
data-id="\{{address_id}}" data-name="\{{consignee}}" data-mobile="\{{mobile}}"
data-phone="\{{phone}}" data-areacode="\{{area_code}}" data-area="\{{area}}"" data-address="\{{address}}">
<div class="address-header"></div>
<div class="address-content">
<p class="default-or-not">
<span class="default-tag">默认地址</span>
<span class="set-default">设为默认</span>
</p>
<p class="name-and-phone">
\{{consignee}}
<span class="phone bold">\{{mobile}}</span>
</p>
<p class="address-area bold">\{{area}}</p>
<p class="address-detail">\{{address}}</p>
<p class="address-opration">
<span class="modify">修改</span>
<span class="delete">删除</span>
</p>
</div>
<span class="address-chosed iconfont">&#xe63b;</span>
</li>
\{{/each}}
\{{#if hasNew}}
<li class="new-address-block">
<span class="iconfont">&#xe645;</span>
增加收货地址
</li>
\{{/if}}
</script>
{{!-- 服务端渲染,客户端使用 --}}
<script id="invoice-dialog-tpl" type="text/html">
<p class="dialog-title">发票信息</p>
<ul class="invoice-tab">
... ... @@ -303,101 +252,6 @@
</div>
</div>
</script>
<script id="address-dialog-tpl" type="text/html">
<p class="dialog-title">
\{{#if updateAddress}}
修改地址
\{{^}}
新增地址
\{{/if}}
</p>
<form id="address-form" name="address-form">
<div class="address-page">
<span class="tip">
<em class="blue">
\{{#if updateAddress}}
修改地址
\{{^}}
新增地址
\{{/if}}
</em>
电话为选填项,其他均为必填项
</span>
<div class="add-address-detail" data-id="\{{id}}">
<div class="form-group">
<label class="label-name">
<em class="required-mark">*</em>
收货人:
</label>
<input class="input address-name" type="text" placeholder="请输入您的姓名" maxlength="12" value="\{{name}}">
<span class="blue error-tips">
{{> icon/error-round}}
<em></em>
</span>
</div>
<div class="form-group clearfix">
<label class="label-name">
<em class="required-mark">*</em>
所在区域:
</label>
<div id="address" class="clearfix"></div>
<span class="blue error-tips">
{{> icon/error-round}}
请填写完整的省市区信息
</span>
</div>
<div class="form-group">
<label class="label-name">
<em class="required-mark">*</em>
详细地址:
</label>
<input class="input address-detail" type="text" placeholder="街道名称或小区名称" maxlength="60" value="\{{detail}}">
<span class="blue error-tips">
{{> icon/error-round}}
<em></em>
</span>
</div>
<div class="form-group">
<label class="label-name">
<em class="required-mark">*</em>
手机号码:
</label>
<input class="input address-mobile" type="text" placeholder="请输入手机号码(重要必填)" maxlength="11" value="\{{mobile}}">
<span class="blue error-tips">
{{> icon/error-round}}
<em></em>
</span>
</div>
<div class="form-group">
<label class="label-name">电话号码:</label>
<input class="input address-phone" type="text" placeholder="请输入电话号码(选填)" value="\{{phone}}" maxlength="15">
<span class="blue error-tips">
{{> icon/error-round}}
<em></em>
</span>
</div>
</div>
</div>
</form>
</script>
<script id="checked-invoice-show-tpl" type="text/html">
<div class="invoice-radio">
\{{#if checked}}
<span class="iconfont radio checked">&#xe603;</span>
\{{^}}
<span class="iconfont radio">&#xe604;</span>
\{{/if}}
<label>发票开具</label>
</div>
\{{#if invoiceTitle}}
<p class="invoice-detail" data-title="\{{invoiceTitle}}" data-content="\{{content}}">
<em>\{{invoiceTitle}}</em><em>\{{invoiceContent}}</em>
<span class="modify-invoice btn white">修改</span>
</p>
\{{/if}}
</script>
</div>
{{/ content}}
</div>
\ No newline at end of file
... ...
... ... @@ -24,10 +24,14 @@
<li class="pro-info">
{{!-- <div class="brand-name">{{brand_name}}</div> --}}
<div class="pro-name"><a href="/product/pro_{{product_id}}_{{goods_id}}/{{cn_alphabet}}.html" target="_blank">{{product_name}}</a></div>
<div class="size">
<div class="color-size editable" data-productId={{product_id}} id="edit_{{product_id}}" data-productSkn={{product_skn}}>
{{#if color_name}}
<span class="mr20">颜色: <span class="default-color">{{color_name}}</span></span>
{{/if}}
{{#if size_name}}
<span>尺寸: {{size_name}}</span>
<span>尺寸: <span class="default-size">{{size_name}}</span></span>
{{/if}}
<span class="iconfont">&#xe63c;</span>
</div>
{{#expect_arrival_time}}
<div class="published-at">上市期: {{expect_arrival_time}}</div>
... ... @@ -40,7 +44,7 @@
<li class="total-price-action">
<span class="price item-total-price">¥ {{round (multiple sales_price buy_number) 2}}</span>
<div class="actions">
<div class="remove-item action" data-product_id={{product_id}}><span class="iconfont">&#xe614;</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="remove-item action" data-product_extra_info='{"goodsId": "{{goods_id}}", "cnAlphabet": "{{cn_alphabet}}", "productId": "{{product_id}}", "salesPrice": "{{round sales_price 2}}", "productName": "{{product_name}}", "goodType":"advance", "selected": "{{selected}}"}'><span class="iconfont">&#xe614;</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="send-to-favorite action" data-product_id={{product_id}}>移入收藏夹</div>
</div>
</li>
... ... @@ -94,7 +98,7 @@
<li class="total-price-action">
<span class="price item-total-price">¥ {{round (multiple sales_price buy_number) 2}}</span>
<div class="actions">
<div class="remove-item action" data-product_id={{product_id}}><span class="iconfont">&#xe614;</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="remove-item action" data-product_extra_info='{"goodsId": "{{goods_id}}", "cnAlphabet": "{{cn_alphabet}}", "productId": "{{product_id}}", "salesPrice": "{{round sales_price 2}}", "productName": "{{product_name}}", "goodType":"ordinary", "selected": "{{selected}}"}'><span class="iconfont">&#xe614;</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</div>
<div class="send-to-favorite action" data-product_id={{product_id}}>移入收藏夹</div>
</div>
</li>
... ...
<div class="cart-removed-list">
<div class="info-bar">
<p class="info-text">已删除商品,您可以重新购买或移入收藏:</p>
</div>
<div class="removed-products">
<ul>
<li class="removed-product">
<div class="product-name">TEEBACOO虎头字母短袖T恤</div>
<div class="product-price">¥ 373.00</div>
<div class="bought-num">2</div>
<div class="actions">
<span class="buy-again">重新购买</span>
<span class="send-to-favorite">移入收藏夹</span>
</div>
</li>
<li class="removed-product">
<div class="product-name">NIKIE AIR FORCE1</div>
<div class="product-price">¥ 373.00</div>
<div class="bought-num">2</div>
<div class="actions">
<span class="buy-again">重新购买</span>
<span class="send-to-favorite">移入收藏夹</span>
</div>
</li>
</ul>
</div>
</div>
\ No newline at end of file
... ... @@ -9,7 +9,6 @@
</div>
<div id="remove_selected" class="action hoverable">删除选中的商品</div>
<div id="send_favorite" class="action hoverable">移入收藏夹</div>
<div id="clear_invalid" class="action hoverable">清除失效商品</div>
<div class="selected-num">已选<span>{{selectedNum}}</span></div>
<div class="checkout-total">
<div class="total-title">商品金额:</div>
... ...
... ... @@ -20,7 +20,9 @@ module.exports = {
cookieDomain: 'yohobuy.com',
domains: {
api: 'http://devapi.yoho.cn:58078/', // devapi.yoho.cn:58078 testapi.yoho.cn:28078 devapi.yoho.cn:58078
service: 'http://testservice.yoho.cn:28077/', // testservice.yoho.cn:28077 devservice.yoho.cn:58077
service: 'http://devservice.yoho.cn:58077/', // testservice.yoho.cn:28077 devservice.yoho.cn:58077
// api: 'http://api.yoho.cn/',
// service: 'http://service.yoho.cn/',
search: 'http://192.168.102.216:8080/yohosearch/'
},
useOneapm: false,
... ... @@ -63,7 +65,7 @@ module.exports = {
}
},
pay: {
serviceNotify: 'http://testservice.yoho.cn:28077/'
serviceNotify: 'http://devservice.yoho.cn:58077/'
}
};
... ...
... ... @@ -8,6 +8,7 @@ module.exports = app => {
// 公共服务
app.use('/partial', require('./apps/partial')); // 组件demo
app.use('/api', require('./doraemon/router')); // API
// 业务模块
app.use('/passport', require('./apps/passport'));
... ... @@ -18,5 +19,4 @@ module.exports = app => {
app.use('/editorial', require('./apps/editorial')); // 资讯
app.use('/product', require('./apps/product'));
app.use('/brand', require('./apps/brand')); // 品牌
app.use('/api', require('./doraemon/api')); // API
};
... ...
/**
* router of sub app shopping
* @author: jinhu.dong<jinhu.dong@yoho.cn>
* @date: 2016/07/04
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const uploadApi = require('./upload/upload.js');
router.post('/uploadImg', uploadApi.uploadImg);
module.exports = router;
... ... @@ -6,43 +6,41 @@
'use strict';
var request = require('request');
var fs = require('fs');
const request = require('request');
const fs = require('fs');
const getImgHost = function(url) {
let urlArr = url.split('/'),
domain = 'static.yhbimg.com/goodsimg',
// 获取图片绝对地址
const getImgHost = (url) => {
let domain = 'static.yhbimg.com/goodsimg',
urlArr = url.split('/'),
num = urlArr[urlArr.length - 1].substr(1, 1),
url1 = domain + url;
if (num === 1) {
if (num === '1') {
return 'http://img11.' + url1;
} else {
return 'http://img12.' + url1;
}
};
// 上传图片
const uploadImg = (req, res) => {
let files, fileIndex;
let imgs, datas;
let flag;
let i = 0;
let files;
let imgs, datas;
if (req.user.uid) {
files = [req.files.filename];
// 判断是单张传还是多张传
flag = Object.prototype.toString.call(files) === '[object Array]';
// 如果是单张,则数组化
if (!flag) {
if (Object.prototype.toString.call(files) !== '[object Array]') {
files = [req.files.filename];
}
req.body.files = [];
req.body.fileNames = [];
for (fileIndex = 0; fileIndex < files.length; fileIndex++) {
for (let fileIndex = 0; fileIndex < files.length; fileIndex++) {
req.body.files[fileIndex] = fs.createReadStream(files[fileIndex].path);
req.body.fileNames[fileIndex] = files[fileIndex].name;
}
... ...
... ... @@ -8,12 +8,12 @@
// render pagination ellipse indicator
function _renderEllipse(templateStr) {
return templateStr + '<li><a>...</a></li>';
return templateStr + '<a>...</a>';
}
// render last page link
function _renderLastPage(templateStr, pageCount, queryParams) {
return templateStr + '<li><a href="?page=' + pageCount + queryParams + '">' + pageCount + '</a></li>';
return templateStr + '<a href="?page=' + pageCount + queryParams + '">' + pageCount + '</a>';
}
/*
... ... @@ -32,8 +32,8 @@ exports.createPagination = function(pagination, options) {
n, // page number ?page=n
queryParams = '', // paginate with query parameter
page = parseInt(pagination.page, 10), // current page number
leftText = '<i class="iconfont">&#xe607;</i>', // prev
rightText = '<i class="iconfont">&#xe61e;</i>', // next
leftText = '<i class="iconfont">&#xe62c;</i>', // prev
rightText = '<i class="iconfont">&#xe629;</i>', // next
paginationClass = 'blk-pagination'; // pagination <ul> default class
var pageCount,
... ... @@ -88,12 +88,12 @@ exports.createPagination = function(pagination, options) {
}
}
template = '<ul class="' + paginationClass + '">';
template = '<div class="' + paginationClass + '">';
// ========= Previous Button ===============
if (page - 1) {
n = page - 1;
template = template + '<li><a href="?page=' + n + queryParams + '">' + leftText + '</a></li>';
template = template + '<a href="?page=' + n + queryParams + '">' + leftText + '</a>';
}
// ========= Page Numbers Middle ===============
... ... @@ -117,13 +117,13 @@ exports.createPagination = function(pagination, options) {
n = start;
if (start === page) {
template = template + '<li class="active"><a href="?page=' + n + queryParams + '">' + n + '</a></li>';
template = template + '<a class="active" href="?page=' + n + queryParams + '">' + n + '</a>';
} else {
// generate left style
if (leftCount >= 4) {
if (i === 0) {
// first page
template = template + '<li><a href="?page=1' + queryParams + '">1</a></li>';
template = template + '<a href="?page=1' + queryParams + '">1</a>';
} else if (i === 1 || (i === 7 && start <= pageCount - 2)) {
// left and right ...
template = _renderEllipse(template);
... ... @@ -132,9 +132,9 @@ exports.createPagination = function(pagination, options) {
} else {
// other links is normal
template = template +
'<li><a href="?page=' + n + queryParams + '">' +
'<a href="?page=' + n + queryParams + '">' +
n +
'</a></li>';
'</a>';
}
} else {
if (i === 7 && start <= pageCount - 2) {
... ... @@ -144,9 +144,9 @@ exports.createPagination = function(pagination, options) {
template = _renderLastPage(template, pageCount, queryParams);
} else {
template = template +
'<li><a href="?page=' + n + queryParams + '">' +
'<a href="?page=' + n + queryParams + '">' +
n +
'</a></li>';
'</a>';
}
}
... ... @@ -159,10 +159,10 @@ exports.createPagination = function(pagination, options) {
// ========= Next page ===============
if (pageCount - page) {
n = page + 1;
template = template + '<li><a href="?page=' + n + queryParams + '">' + rightText + '</a></li>';
template = template + '<a href="?page=' + n + queryParams + '">' + rightText + '</a>';
}
template = template + '</ul>';
template = template + '</div>';
// html template
return template;
... ...
/**
* sub app shopping
* @author: jinhu.dong<jinhu.dong@yoho.cn>
* @date: 2016/07/04
* sub app partial
* @author: xuqi<qi.xu@yoho.cn>
* @date: 2016/06/30
*/
var express = require('express');
var app = express();
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
// router
app.use(require('./router'));
... ...
... ... @@ -10,10 +10,6 @@ const _ = require('lodash');
const helpers = global.yoho.helpers;
module.exports = (req, res, next) => {
req.getUrl = function() {
return req.protocol + '://' + req.get('host') + req.originalUrl;
};
let refer = '';
if (req.method === 'GET') {
... ...
... ... @@ -34,7 +34,7 @@ module.exports = () => {
}
res.render(path, data);
} else {
header.requestHeaderData('boys').then(result => {
header.requestHeaderData(req.yoho.channel).then(result => {
Object.assign(data, result);
// 登录状态
... ...
const headerModel = require('../models/header');
const logger = global.yoho.logger;
/**
* 404 错误
* @return {[type]}
*/
const headerModel = require('../models/header');
const logger = global.yoho.logger;
exports.notFound = () => {
return (req, res, next) => {
return (req, res) => {
if (req.xhr) {
return res.status(404).json({
code: 404,
... ... @@ -14,14 +15,11 @@ exports.notFound = () => {
});
}
headerModel.requestHeaderData().then((result) => {
return res.render('error/404', {
module: 'common',
page: 'error',
title: '页面不存在 | Yoho!Buy有货 | 潮流购物逛不停',
headerData: result.headerData
});
}).catch(next);
return res.display('error/404', {
module: 'common',
page: 'error',
title: '页面不存在 | Yoho!Buy有货 | 潮流购物逛不停'
});
};
};
... ... @@ -43,7 +41,7 @@ exports.serverError = () => {
}
const renderErrPage = (result) => {
res.render('error/500', {
res.display('error/500', {
module: 'common',
page: 'error',
err: err,
... ... @@ -52,7 +50,7 @@ exports.serverError = () => {
});
};
return headerModel.requestHeaderData().then(renderErrPage).catch(() => {
return headerModel.requestHeaderData(req.yoho.channel).then(renderErrPage).catch(() => {
renderErrPage();
});
}
... ...
... ... @@ -11,9 +11,15 @@ module.exports = () => {
let yoho = {
pageChannel: {}
};
const channel = req.query.channel || req.cookies._Channel || 'boys';
const channel = req.query.channel || req.cookies._Channel || 'men';
// 用于头部颜色控制
yoho.pageChannel[channel] = true;
// 当前频道设置
yoho.channel = channel;
// 判断请求是否来自app
yoho.isApp = req.query.app_version || req.query.appVersion;
Object.assign(res.locals, yoho);
... ...
'use strict';
const _ = require('lodash');
const cookie = global.yoho.cookie;
const cache = global.yoho.cache;
const loginService = require('../../apps/passport/models/login-service');
/**
* 已登录用户的信息,包括记住我功能。
*/
module.exports = () => {
return (req, res, next) => {
req.getUrl = function() {
return req.protocol + '://' + req.get('host') + req.originalUrl;
};
// 从 SESSION 中获取到当前登录用户的 UID
if (req.session && _.isNumber(req.session._LOGIN_UID)) {
req.user.uid = req.session._LOGIN_UID;
... ... @@ -22,6 +33,26 @@ module.exports = () => {
}
}
next();
// 记住我
if (req.cookies.isRemember && req.cookies.remem && !req.user.uid) {
cache.get(req.cookies.remem).then(result => {
let data = JSON.parse(result);
let account = data.account;
let password = data.password;
let area = data.area;
return loginService.signin(area, account, password);
}).then(result => {
if (result.code !== 200) {
return Promise.reject(new Error(result.message));
}
return loginService.syncUserSession(result.data.uid, req, res);
}).then(()=> {
return res.redirect(req.getUrl());
}).catch(next);
} else {
return next();
}
};
};
... ...
... ... @@ -54,24 +54,18 @@ const getNavBar = (data, type) => {
let navBars = [];
_.forEach(data, item => {
let obj = {},
let obj = {
link: item.sort_url,
cn: item.sort_name,
en: item.sort_name_en,
isNewPage: item.is_new_page === 'Y'
},
lowEn = _.camelCase(item.sort_name_en).toLowerCase();
obj.link = item.sort_url;
obj.cn = item.sort_name;
obj.en = item.sort_name_en;
obj.isNewPage = item.is_new_page === 'Y' ? true : false;
if (type === lowEn) {
obj.cur = true;
}
// 奥莱频道显示图片,特殊处理
if (lowEn === 'outlets') {
obj.ico = item.sort_ico;
}
navBars.push(obj);
});
... ... @@ -95,7 +89,6 @@ const getBrandItems = (data) => {
});
});
return brandItems;
};
... ... @@ -139,17 +132,19 @@ const getSubNav = (data, type) => {
_.forEach(data, it => {
if (type === _.camelCase(it.sort_name_en).toLowerCase()) {
_.forEach(it.sub, item => {
let obj = {};
obj.link = item.sort_url;
obj.cn = item.sort_name;
obj.en = item.sort_name_en;
obj.isHot = item.is_hot === 'Y' ? true : false;
obj.isNew = item.is_new === 'Y' ? true : false;
let obj = {
link: item.sort_url,
cn: item.sort_name,
en: item.sort_name_en,
isHot: item.is_hot === 'Y',
isNew: item.is_new === 'Y'
};
if (item.sub) {
obj.thirdNav = getThirdNav(item.sub);
obj.imgCode = item.content_code;
Object.assign(obj, {
thirdNav: getThirdNav(item.sub),
imgCode: item.content_code
});
}
subNav.push(obj);
... ... @@ -160,9 +155,6 @@ const getSubNav = (data, type) => {
return subNav;
};
/**
* 处理接口返回的数据
* @param {object} 接口返回的对象
... ... @@ -179,9 +171,15 @@ const setHeaderData = (resData, type) => (
}
);
/**
* 请求头部菜单数据
* @param {String} 频道类型
* @return {promise}
*/
const requestNavBar = (type) => {
return serviceApi.get('operations/api/v6/category/getCategory', {
client_type: 'web'
client_type: 'web',
parent_id: 1155 // 接口写死blk菜单pid为1155
}, {
cache: true,
code: 200
... ... @@ -190,16 +188,12 @@ const requestNavBar = (type) => {
});
};
/**
* 请求头部数据
* @param {String} 频道类型
* @return {promise}
*/
exports.requestHeaderData = (type) => {
let data = {};
const requestHeaderData = (type) => {
let arr = [
getMenuData()
];
... ... @@ -209,8 +203,12 @@ exports.requestHeaderData = (type) => {
}
return Promise.all(arr).then(result => {
return Object.assign(data, {
return Object.assign({
pageHeader: result[0]
}, result[1]);
});
};
module.exports = {
requestHeaderData // 请求头部数据
};
... ...
... ... @@ -7,11 +7,8 @@
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
const apiCtrl = require(`${cRoot}/api`);
router.post('/uploadImg', apiCtrl.uploadImg);
const uploadApi = require('./api/upload');
router.post('/uploadImg', uploadApi.uploadImg);
module.exports = router;
... ...
<div class="wrapper screen">
<div class="clear wrapper-404">
<div class="left"><img src="http://static.yohobuy.com/images/v3/index/404.png"></div>
<div class="right right-tips">
<p class="text1">很抱歉,您访问的页面不存在!</p>
<p class="text2">
</p>
<p class="text3"><a href="http://www.yohobuy.com/product/new" class="button">浏览新品</a>&nbsp;&nbsp;<a href="http://www.yohobuy.com/" class="button">返回首页</a></p>
</div>
<div class="error">
<div class="err-404-img"></div>
<div class="tips">
<span class="tip">哎呀,您访问的页面走丢了...</span>
<span class="tips-english">404 File Not Found, Sorry</span>
<span>建议您:<a href="javascript:location.reload()">刷新该网页/ </a><a href="javascript:history.go(-1)">返回上一页/</a> <a href="">去首页看看</a></span>
</div>
</div>
\ No newline at end of file
... ...
<div class="wrapper screen">
<div class="clear wrapper-404">
<div class="left"><img src="http://static.yohobuy.com/images/v3/index/404.png"></div>
<div class="right right-tips">
<p class="text1">服务器错误!</p>
<p class="text2">
</p>
<p class="text3"><a href="http://www.yohobuy.com/product/new" class="button">浏览新品</a>&nbsp;&nbsp;<a href="http://www.yohobuy.com/" class="button">返回首页</a></p>
</div>
<div class="error">
<div class="err-500-img"></div>
<div class="tips">
<span class="tip">哎呀,您访问的页面走丢了...</span>
<span class="tips-english">500 HTTP Version Not Supported</span>
<span class="">建议您:<a href="javascript:location.reload()">刷新该网页/ </a> 升级您的Web服务器软件</span>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -47,6 +47,26 @@
购物袋
</a>
<div class="mini-bag-box sub-wrapper">
<div class="bag-goods bag-empty">
<dl class="mini-goods-list">
<dd class="goods-item clearfix">
<div class="thumb">
<img src="//placehold.it/48x64">
</div>
<div class="info">
<a href="">范德萨发生大幅度是</a>
<p>颜色:黄 尺码:F</p>
</div>
<div class="price">
<p>¥199.00 X1</p>
<a href="#">删除</a>
</div>
</dd>
</dl>
<div class="go-bag-btn">
<a href="{{../siteUrl}}/shopping/cart">去购物车结算</a>
</div>
</div>
<div class="bag-content">
购物袋空空的哦,去看看心仪的商品吧~
</div>
... ...
... ... @@ -11,8 +11,8 @@
<i class="iconfont douban" data-type="douban" title="分享到豆瓣">&#xe623;</i>
<div class="weixin-share-box"></div>
</span>
<input id="share-img" type="hidden" value="{{image shareImg 90 90}}">
<input id="share-desc" type="hidden" value="{{shareDesc}}">
<input id="weixin-url" type="hidden" value="{{weixinUrl}}">
<input id="share-img" type="hidden" value="{{image shareImg 90 90}}">
<input id="share-desc" type="hidden" value="{{shareDesc}}">
<input id="weixin-url" type="hidden" value="{{weixinUrl}}">
{{/ share}}
</div>
\ No newline at end of file
... ...
... ... @@ -100,7 +100,7 @@
"webpack": "^1.13.1",
"webpack-dev-server": "^1.14.1",
"webpack-stream": "^3.1.0",
"yoho-eventproxy": "^0.3.5",
"yoho-eventproxy": "^0.3.6",
"yoho-handlebars": "^4.0.5",
"yoho-jquery": "^1.12.4",
"yoho-jquery-accordion": "0.0.2",
... ...