Authored by 陈峰

Merge branch 'feature/yohood' into 'master'

Feature/yohood



See merge request !39
... ... @@ -10,7 +10,8 @@
"rules": {
"camelcase": "off",
"no-trailing-spaces": "off",
"max-len": "off"
"max-len": "off",
"no-alert": "off"
}
}
... ...
{
"extends": "stylelint-config-yoho",
"processors": ["stylelint-processor-html"],
"rules": {
"string-quotes": "double",
"no-empty-source": null
}
"extends": "stylelint-config-yoho",
"processors": [
"stylelint-processor-html"
],
"rules": {
"string-quotes": "double",
"no-empty-source": null
}
}
... ...
/**
* 优惠券管理controller
* @author: leo <y.huang@yoho.cn>
* @date: 08/08/2018
*/
const _ = require('lodash');
const CouponModel = require('../models/coupon');
const moment = require('moment');
const excelExport = require('excel-export');
const INVALID_PARAMS = '参数错误';
const xlsx = require('xlsx');
const DO_SUCCESS = '操作成功';
const GET_SUCCESS = '获取成功';
let loadExcel = function(path) {
let workbook = xlsx.readFile(path);
const sheetNames = workbook.SheetNames;
const worksheet = workbook.Sheets[sheetNames[0]];
return Promise.resolve(xlsx.utils.sheet_to_json(worksheet));
};
const timeFormat = (time) => {
if (_.isNumber(time)) {
time = moment.unix(time);
}
return moment(time).format('YYYY-MM-DD HH:mm:ss');
};
const couponController = {
async loadCouponNoList(req, res, next) {
let file = req.files.up_excel;
let id = req.body.id;
let data = await loadExcel(file.path);
console.log(data);
if (!data.length) {
return res.json({
code: 201,
result: false,
message: '文件内容为空'
});
}
try {
// 通用券只允许导入一条数据
let coupon = await req.ctx(CouponModel).couponById(id);
if (coupon[0].type === 3) {
return res.json({
code: 204,
result: false,
message: '无码券不允许导入数据'
});
}
if (coupon[0].type === 1) {
if (data.length > 1) {
return res.json({
code: 202,
result: false,
message: '通用券只允许导入一条数据'
});
}
let count = await req.ctx(CouponModel).couponNoCid(id);
if (count[0].count) {
return res.json({
code: 203,
result: false,
message: '通用券已有数据'
});
}
}
for (let item of data) {
let param = {couponId: id, couponNo: item.couponNo};
await req.ctx(CouponModel).couponNoAdd(param);
}
return res.json({
code: 200,
result: true,
message: DO_SUCCESS
});
} catch (e) {
next;
}
},
couponListPage(req, res) {
res.render('coupon/list', {
bodyClass: 'nav-md',
module: 'admin',
page: 'coupon'
});
},
async couponOptionPage(req, res) {
let result = await req.ctx(CouponModel).couponById(req.query.id);
if (result instanceof Array) {
result = result[0];
}
res.render('coupon/coupon-option', {
bodyClass: 'nav-md',
module: 'admin',
content: result,
page: 'coupon-option'
});
},
async couponList(req, res) {
const pageNo = req.query.pageNo || 1;
const pageSize = req.query.pageSize || 20;
let totalCount = await req.ctx(CouponModel).allCouponNum();
totalCount = totalCount[0].total;
let list = await req.ctx(CouponModel).couponList({pageNo, pageSize});
res.json({
code: 200,
data: list,
pageNo: +pageNo,
pageSize: +pageSize,
totalCount,
totalPage: Math.ceil(totalCount / pageSize),
message: GET_SUCCESS
});
},
async modifyCoupon(req, res, next) {
try {
await req.ctx(CouponModel).couponModify(req.body);
res.json({
code: 200,
message: DO_SUCCESS
});
} catch (e) {
next;
}
},
downloadNoList(req, res, next) {
const couponId = req.query.id;
if (!couponId) {
return res.json({
code: 400,
message: INVALID_PARAMS
});
}
let conf = {
name: 'mysheet',
cols: [
{
caption: '券ID',
type: 'number'
},
{
caption: '券码',
type: 'string'
},
{
caption: '用户id',
type: 'number'
},
{
caption: '领取时间',
type: 'string'
}
],
rows: []
};
req.ctx(CouponModel).couponNoListJoinUser(couponId)
.then(result => {
let temp = [];
_.each(result, item => {
temp = [];
temp.push(couponId);
temp.push(item.couponNo);
temp.push(item.userId);
if (item.createTime) {
temp.push(timeFormat(item.createTime));
} else {
temp.push('');
}
conf.rows.push(temp);
});
let exportFile = excelExport.execute(conf);
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
res.setHeader('Content-Disposition', 'attachment; filename=couponList.xlsx');
res.end(exportFile, 'binary');
})
.catch(next);
},
deleteCoupon: async function(req, res) {
let id = req.query.id;
// 查询有没有用户表里有没,如有返回不给删
let userData = await req.ctx(CouponModel).couponUserListByCId(id);
if (userData.length) {
return res.json({
code: 201,
result: false,
message: '删除失败,有用户数据'
});
}
// 查询查no表里并且删除
await req.ctx(CouponModel).deleteCouponNoByCId(id);
// 删除主表
await req.ctx(CouponModel).deleteCouponById(id);
return res.json({
code: 200,
result: true,
message: DO_SUCCESS
});
}
};
module.exports = couponController;
... ...
/* eslint-disable array-callback-return */
const mysqlCli = global.yoho.utils.mysqlCli;
const _ = require('lodash');
const TABLE_COUPON = 'act_coupon';
const TABLE_COUPON_NO = 'act_coupon_no';
const TABLE_COUPON_USER = 'act_coupon_user';
const toLine = (str) => {
return str.replace(/([A-Z])/g, '_$1').toLowerCase();
};
class CouponModel extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
}
couponList({pageNo, pageSize}) {
return mysqlCli.query(
`select id, coupon_name couponName, coupon_desc couponDesc, shop_name shopName, shop_logo_url shopLogoUrl, status, type, sort , create_time createTime
from ${TABLE_COUPON}
order by sort desc ,create_time desc
limit :start, :page;`, {
start: (pageNo - 1) * pageSize,
page: _.parseInt(pageSize)
});
}
couponById(id) {
if (!id) {
return new Promise(resolve => {
return resolve({});
});
} else {
return mysqlCli.query(
`select id, coupon_name couponName, coupon_desc couponDesc, shop_name shopName, shop_logo_url shopLogoUrl, status, type, sort , create_time createTime
from ${TABLE_COUPON}
where id = :id`, {
id
});
}
}
couponNoAdd(arr) {
return mysqlCli.insert(`insert into ${TABLE_COUPON_NO} (coupon_id, coupon_no) values (:couponId,:couponNo)`, arr);
}
async couponModify(obj) {
if (obj.id) {
let sql = `UPDATE ${TABLE_COUPON} SET `;
let sqlParam = {};
let keyArr = Object.keys(obj);
keyArr.map((value, index) => {
if (value !== 'id') {
let col = toLine(value);
sql += col + ' = :' + value;
if (index !== (keyArr.length - 1)) {
sql += ',';
}
}
sqlParam[value] = obj[value];
});
sql += ' WHERE id = :id';
return mysqlCli.update(
sql,
sqlParam
);
} else {
let insertId = await mysqlCli.insert(
`insert into ${TABLE_COUPON} (coupon_name, coupon_desc, shop_name, shop_logo_url, status,type,sort)
values (:couponName, :couponDesc, :shopName, :shopLogoUrl, :status, :type, :sort);`,
{
couponName: obj.couponName,
couponDesc: obj.couponDesc,
shopName: obj.shopName,
shopLogoUrl: obj.shopLogoUrl,
status: obj.status,
type: obj.type,
sort: obj.sort
});
console.log(insertId);
if (obj.type === 3) {
await mysqlCli.insert(
`insert into ${TABLE_COUPON_NO} (coupon_id) values (:couponId);`,
{
couponId: insertId,
});
}
return Promise.resolve({code: 200, result: true});
}
}
allCouponNum() {
return mysqlCli.query(
`select count(1) as total from ${TABLE_COUPON};`
);
}
couponNoCid(couponId) {
return mysqlCli.query(`select count(1) as count from ${TABLE_COUPON_NO} where coupon_id = :couponId `, {couponId});
}
couponNoListJoinUser(couponId) {
return mysqlCli.query(
`select cn.coupon_id couponId,cn.coupon_no couponNo,cn.send_flag sendFlag,cu.user_id userId,cu.create_time createTime
from ${TABLE_COUPON_NO} as cn left join ${TABLE_COUPON_USER} as cu
on cu.coupon_id = cn.coupon_id where cn.coupon_id = :couponId`, {couponId});
}
couponUserListByCId(couponId) {
return mysqlCli.query(
`select id, coupon_id couponId,coupon_no_id couponNoId,user_id userId,create_time createTime
from ${TABLE_COUPON_USER}
where coupon_id = :couponId `, {couponId: couponId});
}
deleteCouponNoByCId(couponId) {
return mysqlCli.query(`DELETE FROM ${TABLE_COUPON_NO} WHERE coupon_id = :couponId `, {couponId});
}
deleteCouponById(id) {
return mysqlCli.query(`DELETE FROM ${TABLE_COUPON} WHERE id = :id `, {id});
}
}
module.exports = CouponModel;
... ...
... ... @@ -8,6 +8,9 @@ const router = express.Router(); // eslint-disable-line
const admin = require('./controllers/admin');
const activity = require('./controllers/activity');
const user = require('./controllers/user');
const coupon = require('./controllers/coupon');
const multipart = require('connect-multiparty');
const mutilpartMiddleware = multipart();
// 管理员[page]
router.get('/login', admin.loginPage);
... ... @@ -36,6 +39,10 @@ router.get('/user/prize_users_list', user.prizeUsersListPage);
router.get('/user/login-log', user.userLoginLog);
router.get('/user/exportUserLoginLog', user.exportUserLoginLog);
// 优惠券管理[page]
router.get('/coupon/list', coupon.couponListPage);
router.get('/coupon/option', coupon.couponOptionPage);
router.get('/coupon/update', coupon.couponOptionPage);
// 管理员[ajax]
router.post('/api/login', admin.login);
... ... @@ -54,7 +61,12 @@ router.get('/api/activity/exportArticleList', activity.exportArticleList);
router.post('/api/activity/createArticle', activity.createArticle);
router.post('/api/activity/createY100Article', activity.createY100Article);
// 优惠券管理[ajax]
router.get('/api/coupon/list', coupon.couponList);
router.post('/api/coupon/modify', coupon.modifyCoupon);
router.post('/api/coupon/batchAddNo', mutilpartMiddleware, coupon.loadCouponNoList);
router.get('/api/coupon/downloadNo', coupon.downloadNoList);
router.get('/api/coupon/delete', coupon.deleteCoupon);
// 用户管理[ajax]
router.post('/api/user/delete', user.deleteUser);
... ...
<div class="right_col" role="main">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
{{# content}}
<div class="x_title">
<h2>创建券</h2>
<div class="clearfix"></div>
</div>
<form id="createForm" class="form-horizontal form-label-left">
<input class="form-imput" type="hidden" data-type="id" id="id" value="{{id}}">
<input id="type" type="hidden" value="{{type}}">
<input id="status" type="hidden" value="{{status}}">
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
券名称<span class="required">*</span>
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
<input class="form-control col-md-7 col-xs-12 form-imput"
data-type="couponName"
data-toggle="tooltip"
data-placement="bottom"
title="券名称不能为空"
name="couponName"
type="text"
maxlength="50"
value="{{couponName}}">
</div>
</div>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
商家名称<span class="required">*</span>
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
<input class="form-control col-md-7 col-xs-12 form-imput"
data-type="shopName"
data-toggle="tooltip"
data-placement="bottom"
title="商品名称不能为空"
name="shopName"
type="text"
maxlength="50"
value="{{shopName}}">
</div>
</div>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">
商家logo图片<span class="required">*</span>
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
<img src="{{shopLogoUrl}}" class="col-md-6 product-thumb {{#unless shopLogoUrl}}hide{{/unless}}">
<button type="button" class="btn btn-primary btn-upload-thumb" data-cover="{{shopLogoUrl}}">上传</button>
</div>
</div>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12">
券开启状态
</label>
<div class="col-md-6 col-sm-6 col-xs-12">
<label class="control-label col-md-1">
<input type="radio" name="status" required="required"
class="form-contro col-md-7 col-xs-12" value="0">
</label>
<label class="control-label col-md-1">
<input type="radio" name="status" required="required"
class="form-contro col-md-7 col-xs-12" value="1">
</label>
</div>
</div>
<div class="form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12">
券类型
</label>
<div class="col-md-9 col-sm-9 col-xs-12">
<label class="control-label radi">
<input type="radio" name="type" required="required"
class="form-contro col-md-4 col-xs-12" value="1">
通用码
</label>
<label class="control-label radi">
<input type="radio" name="type" required="required"
class="form-contro col-md-4 col-xs-12" value="2">
一人一码
</label>
<label class="control-label radi">
<input type="radio" name="type" required="required"
class="form-contro col-md-4 col-xs-12" value="3">
不用码
</label>
</div>
</div>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">排序<span
class="required">*</span>
</label>
<div class="col-md-2 col-sm-2 col-xs-12">
<input id="actVoteLimit" class="form-control col-md-2 form-imput"
data-type="sort"
data-toggle="tooltip" data-placement="bottom" title="排序"
name="actVoteLimit"
type="text"
maxlength="10"
value="{{sort}}">
</div>
</div>
<div class="item form-group">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">券说明<span
class="required">*</span>
</label>
<div class="col-md-4 col-sm-4 col-xs-12">
<textarea class="form-control form-imput" data-type="couponDesc" rows="3" style="resize: none;height: 300px">{{couponDesc}}</textarea>
</div>
</div>
<div class="item form-group">
<div class="col-md-2 col-md-offset-5 action-wrap">
<button type="button" class="btn btn-primary btn-save">保存</button>
</div>
</div>
</form>
<a id="upload-btn"></a>
{{/ content}}
</div>
</div>
</div>
... ...
<!-- page content -->
<div class="right_col" role="main">
<div class="">
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="x_title">
<a href="/admin/coupon/option" class="btn btn-primary">创建券</a>
<div class="clearfix"></div>
</div>
<div class="x_content">
<div class="table-responsive">
<table class="table table-bordered">
<thead>
<tr class="headings">
<th class="column-title">ID</th>
<th class="column-title">券名称</th>
<th class="column-title">商铺名称</th>
<th class="column-title">商铺图标</th>
<th class="column-title">上下架状态</th>
<th class="column-title">券类型</th>
<th class="column-title">排序权重</th>
<th class="column-title">创建时间</th>
<th class="column-title">操作</th>
</tr>
</thead>
<tbody class="coupon-list">
</tbody>
</table>
</div>
<div class="table-pagination coupon-pagination pull-right"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /page content -->
... ...
/**
* 优惠券管理controller
* @author: leo <y.huang@yoho.cn>
* @date: 08/08/2018
*/
const CouponModel = require('../models/coupon');
const GET_SUCCESS = '获取成功';
const INVALID_PARAMS = '参数错误';
const couponController = {
async couponList(req, res, next) {
try {
let list = await req.ctx(CouponModel).couponList();
res.json({
code: 200,
data: list,
message: GET_SUCCESS
});
} catch (e) {
next;
}
},
async couponSendFlag(req, res, next) {
if (!req.query.userId) {
return res.json({
code: 200,
message: INVALID_PARAMS
});
}
try {
let list = await req.ctx(CouponModel).couponUserFlag(req.query.userId);
return res.json({
code: 200,
data: list,
message: GET_SUCCESS
});
} catch (e) {
next;
}
},
async couponGet(req, res, next) {
if (!req.body.params.userId) {
return res.json({
code: 200,
result: false,
message: INVALID_PARAMS
});
}
try {
let result = await req.ctx(CouponModel).coouponUserGet(req.body.params);
res.json(result);
} catch (e) {
next;
}
},
async couponUserOwner(req, res, next) {
if (!req.query.userId) {
return res.json({
code: 200,
result: false,
message: INVALID_PARAMS
});
}
try {
let result = await req.ctx(CouponModel).couponUserOwner(req.query.userId);
res.json(result);
} catch (e) {
next;
}
}
};
module.exports = couponController;
... ...
... ... @@ -62,7 +62,7 @@ let calcSigature = function(ticket_res) { // 计算signature
let str = `jsapi_ticket=${ticket}&noncestr=${nonceStr}&timestamp=${timestamp}&url=${url}`;
let signature = sha1(str);
// console.log({appID, ticket, nonceStr, timestamp, url, str, signature});
// console.log('calcSigature:',{appID, ticket, nonceStr, timestamp, url, str, signature});
return {
appId: appID,
nonceStr,
... ... @@ -108,14 +108,14 @@ const wechatShare = {
req.ctx(WechatModel).getAccessKeys()
.then(keys_res => { // 先查询数据库中是否有access_token并且未过期
// console.log('keys_res:', keys_res);
if (keys_res.code === 200) { // code=200说明数据库中查找到数据并且未过期
delete keys_res.code;
return Object.assign({
return Promise.resolve(Object.assign({
appID,
url
}, keys_res);
}, keys_res));
} else if (keys_res.code === 201) {
// code=201说明数据库中没有数据,或者数据已过期,根据返回的type判断是添加还是更新
... ... @@ -149,6 +149,7 @@ const wechatShare = {
}
})
.then(result => {
// console.log('result:', result);
let calc_res = calcSigature(result);
if (req.query.callback) {
... ...
/* eslint-disable array-callback-return */
const mysqlCli = global.yoho.utils.mysqlCli;
const TABLE_COUPON = 'act_coupon';
const TABLE_COUPON_USER = 'act_coupon_user';
const TABLE_COUPON_NO = 'act_coupon_no';
class CouponModel extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
}
couponList() {
return mysqlCli.query(
`select id, coupon_name couponName, coupon_desc couponDesc, shop_logo_url shopLogoUrl, sort ,type
from ${TABLE_COUPON} where status = 1
order by sort desc ,create_time desc`);
}
couponUserFlag(userId) {
return mysqlCli.query(`select cn.send_flag sendFlag, cu.coupon_id couponId
from ${TABLE_COUPON_USER} as cu ,${TABLE_COUPON_NO} as cn where cu.user_id = :userId and cn.id = cu.coupon_no_id `, {userId});
}
couponUserOwner(userId) {
return mysqlCli.query(
`select c.id, c.coupon_name couponName, c.coupon_desc couponDesc, c.shop_logo_url shopLogoUrl,cn.coupon_no couponNo
from ${TABLE_COUPON} c ,${TABLE_COUPON_NO} cn, ${TABLE_COUPON_USER} cu where cu.user_id =:userId and cu.coupon_no_id = cn.id and c.id = cn.coupon_id
order by c.sort desc ,c.create_time desc`, {userId});
}
async coouponUserGet(obj) {
// 判断该用户是否有该券
let num = await mysqlCli.query(`select count(1) as count from ${TABLE_COUPON_USER} where user_id = :userId and coupon_id = :couponId`,
{
userId: obj.userId, couponId: obj.couponId
});
if (num[0].count) {
return Promise.resolve({code: 201, result: false, message: '领取失败,该用户已领取过该券'});
}
// 获取当前可用券码(如果是type=1、3则no表就一条数据send_flag都为0)
let couponNolist = await mysqlCli.query(`select id from ${TABLE_COUPON_NO} where coupon_id = :couponId and send_flag = 0 `, {couponId: obj.couponId});
if (!couponNolist.length) {
return Promise.resolve({code: 202, result: false, message: '领取失败,该券已发放完毕'});
}
let noId = couponNolist[0].id;
// 如果type=2(一人一码) 则修改第一张待用券码的send状态
if (obj.type === 2) {
await mysqlCli.update(`UPDATE ${TABLE_COUPON_NO} SET send_flag = 1 where id = :id`, {id: noId});
}
// 在type=2的情况下判断 该券并发是否被别人领取 如果没有再在user表中插入数据
let insertId = await mysqlCli.insert(`insert into ${TABLE_COUPON_USER} (user_id, coupon_id , coupon_no_id)
select :userId,:couponId,:couponNoId from DUAL where NOT EXISTS(
SELECT cu.id
FROM ${TABLE_COUPON_USER} cu left join ${TABLE_COUPON} c on c.id = cu.coupon_id
WHERE cu.coupon_no_id = :couponNoId and c.type = 2 and c.id = :couponId)`,
{userId: obj.userId, couponId: obj.couponId, couponNoId: noId});
if (!insertId) {
await this.coouponUserGet(obj);
} else {
return Promise.resolve({code: 200, result: true});
}
}
couponById(id) {
if (!id) {
return new Promise(resolve => {
return resolve({});
});
} else {
return mysqlCli.query(
`select id, coupon_name couponName, coupon_desc couponDesc, shop_name shopName, shop_logo_url shopLogoUrl, status, type, sort , create_time createTime
from ${TABLE_COUPON}
where id = :id`, {
id
});
}
}
}
module.exports = CouponModel;
... ...
... ... @@ -10,6 +10,7 @@ const wechat = require('./controllers/wechat');
const prize = require('./controllers/prize');
const shoes = require('./controllers/shoes');
const yohood = require('./controllers/yohood');
const coupon = require('./controllers/coupon');
const excel = require('./controllers/excel');
const multipart = require('connect-multiparty');
... ... @@ -44,4 +45,10 @@ router.get('/yohood/getRecommend', yohood.getRecommend);
router.get('/yohood/getResource', yohood.getYohoBuyResource);
router.get('/yohood/getShopProduct', yohood.getYohoBuyShopProduct);
// coupon
router.get('/coupon/getCouponList', coupon.couponList);
router.get('/coupon/getCouponSendFlags', coupon.couponSendFlag);
router.post('/coupon/couponGet', coupon.couponGet);
router.get('/coupon/couponUserOwner', coupon.couponUserOwner);
module.exports = router;
... ...
... ... @@ -109,12 +109,16 @@ const userController = {
async newWechatUserCallback(req, res) {
let {code = 10005, state} = req.query;
// console.log(req.session);
try {
if (code.toString().length > 5 && state === _.get(req.session, 'user.wechatState', '')) {
let appId = config.wechatLogin.appID;
let appSecret = config.wechatLogin.appSecret;
let WECHAT_GET_TOKEN = 'https://api.weixin.qq.com/sns/oauth2/access_token';
let WECHAT_GET_USERINFO = 'https://api.weixin.qq.com/sns/userinfo';
// console.log(WECHAT_GET_TOKEN + '?appid=' + appId + '&secret=' + appSecret + '&code=' + code + '&grant_type=authorization_code');
let tokenResult = await request({
url: WECHAT_GET_TOKEN,
qs: {
... ... @@ -126,7 +130,7 @@ const userController = {
json: true
});
console.log(tokenResult);
// console.log('newWechatCallback', tokenResult);
if (!tokenResult.errcode) {
let access_token = tokenResult.access_token;
... ... @@ -143,6 +147,8 @@ const userController = {
let userInfoStr = JSON.stringify(userInfo);
// console.log('userInfoStr:', userInfoStr);
let referer = `${siteUrl}/passport/user/wechatCallback`;
let note = referer.indexOf('?') > 0 ? '&' : '?';
let redirectUrl = referer + note + 'wechatUserInfo=' + userInfoStr;
... ... @@ -176,9 +182,12 @@ const userController = {
let WECHAT_LOGIN_URL =
`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${redirect_uri}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`;
if (referer && (referer.indexOf('yoho.cn'))) {
// console.log('WECHAT_LOGIN_URL:', WECHAT_LOGIN_URL);
// console.log('referer', referer, referer.indexOf('yoho.cn'));
if (referer && (referer.indexOf('yoho.cn') >= 0)) {
_.set(req.session, 'user.wechatReferer', referer);
_.set(req.session, 'user.wechatState', state);
console.log(req.session);
res.redirect(WECHAT_LOGIN_URL);
} else {
res.json({
... ... @@ -186,9 +195,6 @@ const userController = {
message: INVALID_REFERER
});
}
console.log(WECHAT_LOGIN_URL);
},
/**
... ... @@ -247,7 +253,7 @@ const userController = {
let redirect_url = `${siteUrl}/passport/user/wechatCallback`;
if (referer && (referer.indexOf('yoho.cn') || referer.indexOf('yhurl.com'))) {
_.set(req.session, 'user.wechatReferer', referer);
_.set(req.session, 'npnp', referer);
res.redirect(WECHAT_LOGIN_URL_YOHOBOYS + encodeURIComponent(redirect_url));
} else {
res.json({
... ... @@ -264,6 +270,8 @@ const userController = {
*/
wechatUserCallback(req, res, next) {
let wechatUserInfo = JSON.parse(req.query.wechatUserInfo);
console.log('wechatUserCallBack:', wechatUserInfo);
let user_name = '',
head_img = '',
union_id = '';
... ... @@ -272,7 +280,7 @@ const userController = {
if (wechatUserInfo) {
user_name = wechatUserInfo.nickname;
head_img = wechatUserInfo.headimgurl;
union_id = wechatUserInfo.unionid;
union_id = wechatUserInfo.unionid || wechatUserInfo.openid;
_.set(req.session, 'user.union_id', union_id);
_.set(req.session, 'user.head_img', head_img);
... ...
... ... @@ -118,7 +118,6 @@ module.exports = {
wechatLogin: {
appID: 'wxb785b12717bc31e9',
appSecret: 'fb0bc348577b1e38576c6fe654da5135',
}
};
... ...
... ... @@ -63,6 +63,11 @@
<li><a href="/admin/user/login-log">用户登录日志</a></li>
</ul>
</li>
<li><a><i class="fa fa-ticket"></i> 券管理 <span class="fa fa-chevron-down"></span></a>
<ul class="nav child_menu">
<li><a href="/admin/coupon/list">券列表</a></li>
</ul>
</li>
</ul>
</div>
</div>
... ...
/*
true
@author: qiuj <jun.qiu@yoho.cn>
@date: 2018-08-16 14:27:44
*/
#注意:GO;分割执行块
#优惠券码表
DROP TABLE IF EXISTS `act_coupon_no`;
GO;
CREATE TABLE `act_coupon_no` (
`id` int(8) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`coupon_id` int(8) NOT NULL,
`coupon_no` varchar(50) DEFAULT NULL,
`send_flag` int(2) NOT NULL DEFAULT '0',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) DEFAULT CHARSET=utf8;
GO;
#用户领券信息表
DROP TABLE IF EXISTS `act_coupon_user`;
GO;
CREATE TABLE `act_coupon_user` (
`id` int(8) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`coupon_id` int(8) NOT NULL,
`coupon_no_id` int(8) NOT NULL,
`user_id` int(8) NOT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) DEFAULT CHARSET=utf8;
GO;
#优惠券表
DROP TABLE IF EXISTS `act_coupon`;
GO;
CREATE TABLE `act_coupon` (
`id` int(8) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`coupon_name` varchar(50) NOT NULL,
`coupon_desc` varchar(400) DEFAULT NULL,
`shop_name` varchar(50) NOT NULL,
`shop_logo_url` varchar(200) NOT NULL,
`status` int(2) NOT NULL DEFAULT '0',
`type` int(2) NOT NULL DEFAULT '0',
`sort` int(8) NOT NULL DEFAULT '0',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) DEFAULT CHARSET=utf8;
GO;
... ...
module.exports = ['_migration', '20170913102356', '20170927092926', '20171025145423', '20171102104558', '20171115144710', '20180131104311', '20180402134610', '20180413105509', '20180719134813', '20180806151252', '20180810104355'];
module.exports = ['_migration', '20170913102356', '20170927092926', '20171025145423', '20171102104558', '20171115144710', '20180131104311', '20180402134610', '20180413105509', '20180719134813', '20180806151252', '20180810104355', '20180816142744'];
... ...
... ... @@ -138,7 +138,7 @@
"precss": "^1.4.0",
"shelljs": "^0.7.6",
"style-loader": "^0.18.1",
"stylelint": "^7.10.1",
"stylelint": "^8.3.0",
"stylelint-config-yoho": "^1.2.8",
"stylelint-formatter-table": "^1.0.2",
"stylelint-processor-html": "^1.0.0",
... ...
require('admin/coupon-option.page.css');
require('bootstrap-daterangepicker');
require('bootpag/lib/jquery.bootpag.min');
let uploadedFn;
let $upload;
function initUpload() {
$upload = $('#upload-btn');
if (!$upload.length) {
return;
}
const imgArr = ['image/jpeg', 'image/jpg', 'image/png'];
// why? reference: https://github.com/qiniu/js-sdk/issues/266
Promise.all([
require('mOxie/bin/js/moxie'),
require('plupload/js/plupload.dev'),
require('qiniu-js')
]).then(([moxie, plupload]) => {
window.moxie = moxie;
window.plupload = plupload;
Qiniu.uploader({ // eslint-disable-line
runtimes: 'html5,flash,html4',
browse_button: 'upload-btn',
uptoken_url: '/api/getToken',
unique_names: true,
multi_selection: false,
filters: {
mime_types: [{
extensions: 'jpg,jpeg,png,mp4'
}]
},
dragdrop: false,
domain: 'http://img01.yohoboys.com/',
max_file_size: '10mb',
max_retries: 3,
chunk_size: '10mb',
auto_start: true,
init: {
BeforeUpload: function(up) {
window.checkup = up;
},
UploadProgress: function() {
console.log('上传中...');
},
FileUploaded: function(up, file, info) {
var domain = up.getOption('domain');
var res = JSON.parse(info.response);
var url = domain + res.key;
uploadedFn && uploadedFn(url, imgArr.indexOf(file.type) >= 0);
},
Error: function(up, err, errTip) {
console.log(err, errTip);
}
}
});
});
}
function bindEditPageEvent() {
initUpload();
function packInfo() {
let resData = {};
let error;
$('.form-imput').each(function() {
let $this = $(this);
let val = $this.val();
let data = $this.data();
if (!val && !error && data.type !== 'id' && data.type !== 'couponNo') {
error = data.empty || `请填写${data.type}`;
}
resData[data.type] = val;
});
if (error) {
alert(error);// eslint-disable line
return;
}
resData.shopLogoUrl = $('.btn-upload-thumb').data('cover');
resData.status = $('input[name=status]:checked').val();
resData.type = $('input[name=type]:checked').val();
if (!resData.shopLogoUrl) {
alert('请上传商家logo图');
return;
}
return resData;
}
function GenUploadedFn($dom) {
return function(url, isImg) {
if (!isImg) {
alert('请上传图片');
}
$dom.prev().attr('src', url).removeClass('hide');
$dom.data('cover', url);
};
}
function radioInit() {
let type = $('#type').val();
let status = $('#status').val();
if (type) {
$($('input[name=type]')[parseInt(type) - 1]).attr('checked', true);
} else {
$($('input[name=type]')[0]).attr('checked', true);
}
if (status) {
$($('input[name=status]')[parseInt(status)]).attr('checked', true);
} else {
$($('input[name=status]')[0]).attr('checked', true);
}
if ($('#id').val()) {
$('input[name=type]').each(function(index, el) {
$(el).attr('disabled', true);
});
}
}
radioInit();
$('.btn-upload-thumb').on('click', function() {
uploadedFn = new GenUploadedFn($(this));
$upload[0].click();
});
$('.btn-save').on('click', function() {
let info = packInfo();
if (!info || window.saveing) {
return;
}
window.saveing = true;
$.ajax({
method: 'post',
url: '/admin/api/coupon/modify',
data: info
}).then(res => {
window.saveing = false;
if (res.code === 200) {
alert('保存成功');
} else {
alert(res.message || '保存失败');
}
});
});
}
(function() {
bindEditPageEvent();
}());
... ...
/* eslint-disable eqeqeq */
require('admin/user.page.css');
require('bootpag/lib/jquery.bootpag.min');
const _ = require('lodash');
function bind_table_pagination() {
const $ul = $('.coupon-list');
const $up = $('.coupon-pagination');
let uploadFn = function() {
let id = $(this).data('id');
let $form = $('#uploadForm_' + id)[0];
let formData = new FormData($form);
formData.append('id', $(this).data('id'));
if ($form) {
$.ajax({
url: '/admin/api/coupon/batchAddNo',
data: formData,
method: 'POST',
cache: false,
processData: false,
contentType: false,
success: function(data) {
if (!data.result) {
alert(data.message);
} else {
alert('导入成功');// eslint-disable line
}
}
});
}
};
const fetchRender = (pageNo, pageSize) => {
$.ajax({
url: '/admin/api/coupon/list',
data: {
pageNo,
pageSize
}
})
.then(result => {
const list = result.data;
const totalPage = result.totalPage;
let html = '';
_.each(list, item => {
if (item.status) {
item.statusText = '已上架';
} else {
item.statusText = '已下架';
}
if (item.type == 1) {
item.typeText = '通用码';
} else if (item.type == 2) {
item.typeText = '一人一码';
} else if (item.type == 3) {
item.typeText = '不用码';
}
html += `
<tr class="even pointer">
<td class="">${item.id}</td>1
<td class="">${item.couponName}</td>
<td class="">${item.shopName}</td>
<td class=""><img src="${item.shopLogoUrl}" width="100" height="50"/></td>
<td class="">${item.statusText}</td>
<td class="">${item.typeText}</td>
<td class="">${item.sort}</td>
<td class="">${item.createTime}</td>
<td class="">
<form id="uploadForm_${item.id}" enctype="multipart/form-data">
<input id="up_excel" name="up_excel" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" style="display: inline-block; width: 80%;" type="file">
</form>
<button type="button" class="btn btn-danger btn-import" style="display: inline-block;" data-id="${item.id}">导入券码</button>
<button class="btn btn-danger btn-export-no" data-id="${item.id}">导出券码
</button>
<a class="btn btn-danger btn-update" href="/admin/coupon/update?id=${item.id}">修改</a>
<a class="btn btn-danger btn-delete" data-id="${item.id}">删除</a>
</td>
</tr>`;
});
$ul.html(html);
if (pageNo === 1) {
$up.bootpag({
total: totalPage,
page: 1,
maxVisible: 10,
}).on('page', function(event, num) {
fetchRender(num, 20);
});
}
$('.btn-export-no').on('click', function() {
let id = $(this).attr('data-id');
window.location = '/admin/api/coupon/downloadNo?id=' + id;
});
$('.btn-import').on('click', uploadFn);
$('.btn-delete').click(function() {
let id = $(this).attr('data-id');
console.log(id);
$.ajax({
url: '/admin/api/coupon/delete?id=' + id,
method: 'get',
cache: false,
processData: false,
contentType: false,
success: function(data) {
if (!data.result) {
alert(data.message);
} else {
window.location.reload();
}
}
});
});
});
};
fetchRender(1, 20);
}
(function() {
bind_table_pagination();
}());
... ...
... ... @@ -366,7 +366,7 @@ function bindEditPageEvent() {
uploadedFn = new GenUploadedFn($this);
$upload[0].click();
break;
default:
default: //eslint-disable-line
let info = floorContent[data.cid];
let content = info.newContent || info.content;
let $dom = $(`<textarea class="edit-text form-control">${content || ''}</textarea>`);
... ...
@import '~bootstrap-daterangepicker';
.radi{
white-space: nowrap;
overflow: hidden;
padding: 0 30px 0 10px;
display: inline-block;
}
... ...
... ... @@ -4349,10 +4349,8 @@ ul.notifications {
/** /Dropzone.js **/
/** list pagination**/
.table-pagination {
ul {
margin: 0;
}
.table-pagination ul {
margin: 0;
}
... ...
... ... @@ -59,6 +59,7 @@ class SqlHelper {
});
}
async execute(sql, params, options = {}) {
console.log(sql);
let cacheTime = parseInt(options.cache, 10) || 0;
let cacheKey;
... ...
... ... @@ -9522,7 +9522,7 @@ stylelint@^7.10.1, stylelint@^7.7.0:
svg-tags "^1.0.0"
table "^4.0.1"
stylelint@^8.4.0:
stylelint@^8.3.0, stylelint@^8.4.0:
version "8.4.0"
resolved "http://npm.yohops.com/stylelint/-/stylelint-8.4.0.tgz#c2dbaeb17236917819f9206e1c0df5fddf6f83c3"
dependencies:
... ...