Authored by 陈峰

Merge branch 'feature/yoluckTip' into 'master'

Feature/yoluck tip



See merge request !89
... ... @@ -149,3 +149,4 @@ nbproject/*
.scannerwork
/scripts/*
*.tar.gz
dist
\ No newline at end of file
... ...
... ... @@ -76,6 +76,11 @@ app.use((req, res, next) => {
app.use(global.yoho.httpCtx());
app.enable('trust proxy');
app.use((req, res, next) => {
req.isApmReport = _.get(req.app.locals, 'wap.open.bughd', false);
next();
});
app.use(global.yoho.hbs({
extname: '.hbs',
defaultLayout: 'layout',
... ...
... ... @@ -139,7 +139,7 @@ module.exports = class extends global.yoho.BaseModel {
let limit = `${(page - 1) * PAGE_SIZE},${PAGE_SIZE}`;
let where = ['act_id = :actId'];
let now = new Date().getTime() / 1000;
let now = moment().startOf('minute').unix();
switch (+extra.type) {
case 1:
... ... @@ -163,7 +163,7 @@ module.exports = class extends global.yoho.BaseModel {
now,
channel: extra.channel
}, {
cache: PRODUCT_CACHE_TIMES
cache: 60
}).then(result => {
return handelResult(handelActivityList(result));
});
... ... @@ -197,7 +197,7 @@ module.exports = class extends global.yoho.BaseModel {
}
return mysqlCli.query(`select u.*, p.name, p.price, p.status, p.cover_img,
p.start_time, p.end_time, p.is_full from
p.start_time, p.end_time, p.is_full, p.lottery_info from
${TABLE_ACT_PRIZE_PRODUCT_USER} u left join
${TABLE_ACT_PRIZE_PRODUCT} p on u.act_prize_id = p.id
where u.act_id = :actId and u.uid = :uid and ${where}
... ... @@ -218,7 +218,9 @@ module.exports = class extends global.yoho.BaseModel {
*/
getListRecommend(actPrizeId, extra = {}) {
const actId = parseInt(extra.actId, 10) || 0;
const time = new Date().getTime() / 1000;
const now = moment();
const minute = parseInt(now.minute() / 5, 10) * 5;
const time = now.startOf('hour').unix() + minute * 60;
let where = 'act_id = :actId and status = 1 and start_time < :time and end_time > :time';
... ... @@ -232,7 +234,7 @@ module.exports = class extends global.yoho.BaseModel {
actId,
actPrizeId
}, {
cache: PRODUCT_CACHE_TIMES
cache: 5 * 60
}).then(result => {
_.remove(result, n => {
return +n.id === +actPrizeId;
... ... @@ -257,11 +259,11 @@ module.exports = class extends global.yoho.BaseModel {
let query = [
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1;`, {actPrizeId}, {
cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
cache: extra.noCache ? 0 : 60
}),
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_CONTENT}
where act_prize_id = :actPrizeId;`, {actPrizeId}, {
cache: extra.noCache ? 0 : PRODUCT_CACHE_TIMES
cache: extra.noCache ? 0 : 5 * 60
}),
this.getUserJoinNum(actPrizeId).then(result => {
return result.data;
... ... @@ -322,7 +324,7 @@ module.exports = class extends global.yoho.BaseModel {
actPrizeId,
actId
}, {
cache: RECENT_CODE_CACHE_TIME
cache: 2 * 60
}).then(result => {
return handelResult(_.uniqBy(result, 'uid'));
});
... ... @@ -352,14 +354,17 @@ module.exports = class extends global.yoho.BaseModel {
* @returns {*}
*/
getUserJoinNum(actPrizeId, noCache) {
return mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId;`, {
actPrizeId
}, {
cache: noCache ? 0 : MINUTE_TIMES / 6
}).then(result => {
return handelResult({join_num: _.get(result, '[0].join_num')});
});
// 前台不需要显示参与人数,且sql影响性能,故直接返回1
return Promise.resolve(handelResult({join_num: 1}));
// return mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
// where act_prize_id = :actPrizeId;`, {
// actPrizeId
// }, {
// cache: noCache ? 0 : MINUTE_TIMES / 6
// }).then(result => {
// return handelResult({join_num: _.get(result, '[0].join_num')});
// });
}
/**
... ... @@ -374,7 +379,7 @@ module.exports = class extends global.yoho.BaseModel {
let info = await Promise.all([
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1;`, {actPrizeId}, {cache: PRODUCT_CACHE_TIMES}),
where id = :actPrizeId limit 1;`, {actPrizeId}, {cache: 60}),
this.getUserJoinNum(actPrizeId, true).then(result => {
return result.data;
})
... ... @@ -388,8 +393,6 @@ module.exports = class extends global.yoho.BaseModel {
let productInfo = _.get(info, '[0][0]');
let joinNum = _.get(info, '[1].join_num', 0);
let status = getActivityStatus(productInfo, joinNum);
if (!status || status >= 3) {
... ... @@ -400,9 +403,7 @@ module.exports = class extends global.yoho.BaseModel {
delete extra.shareUid;
}
if (extra.shareUid) {
this.sendPrizeCode(extra.shareUid, actPrizeId, {isShareTake: true});
}
let shareUid = extra.shareUid;
return new Promise((resolve, reject) => {
this.sendPrizeCode(uid, actPrizeId, Object.assign(extra, {
... ... @@ -411,10 +412,16 @@ module.exports = class extends global.yoho.BaseModel {
actStartTime: productInfo.start_time,
actEndTime: productInfo.end_time
})).then(response => {
// 参与人数满时更新活动状态
if (+response.code === 200 && (+productInfo.is_full === 0) && (joinNum + 1) >= productInfo.limit) {
mysqlCli.update(`update ${TABLE_ACT_PRIZE_PRODUCT} set is_full = 1
where id = :actPrizeId and is_full = 0 limit 1`, {actPrizeId});
if (+response.code === 200) {
if (shareUid) {
this.sendPrizeCode(shareUid, actPrizeId, {isShareTake: true});
}
// 参与人数满时更新活动状态
if ((+productInfo.is_full === 0) && (joinNum + 1) >= productInfo.limit) {
mysqlCli.update(`update ${TABLE_ACT_PRIZE_PRODUCT} set is_full = 1
where id = :actPrizeId and is_full = 0 limit 1`, {actPrizeId});
}
}
resolve(response);
})
... ... @@ -435,24 +442,27 @@ module.exports = class extends global.yoho.BaseModel {
const mapStr = 'QQWERTYUIOPASDFGHJKLZXCVBNM12345678900';
let prizeCode = '';
for (let i = 0; i < length; i++) {
prizeCode += mapStr[parseInt(Math.random() * mapStr.length, 10)];
}
const info = await mysqlCli.query(`select id from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId and prize_code = :prizeCode limit 1`, {actPrizeId, prizeCode});
if (info && info.length) {
if (isRetry) {
prizeCodeCreateCount.fail(); // 记录生成抽奖码失败
return '';
} else {
return this.createPrizeCode(actPrizeId, length, true);
}
} else {
prizeCodeCreateCount.success(); // 记录生成抽奖码成功
return prizeCode;
}
for (let i = 0; i < length; i++) {
prizeCode += mapStr[parseInt(Math.random() * mapStr.length, 10)];
}
return prizeCode; // 不校验重复,直接返回抽奖嘛,如需校验请删除该行使用下面代码
// const info = await mysqlCli.query(`select id from ${TABLE_ACT_PRIZE_PRODUCT_USER}
// where act_prize_id = :actPrizeId and prize_code = :prizeCode limit 1`, {actPrizeId, prizeCode});
// if (info && info.length) {
// if (isRetry) {
// prizeCodeCreateCount.fail(); // 记录生成抽奖码失败
// return '';
// } else {
// return this.createPrizeCode(actPrizeId, length, true);
// }
// } else {
// prizeCodeCreateCount.success(); // 记录生成抽奖码成功
// return prizeCode;
// }
}
/**
... ... @@ -568,32 +578,20 @@ module.exports = class extends global.yoho.BaseModel {
*/
sendWechatMessage(uid, actPrizeId, extra = {}) {
let info = {};
let baseUri = 'page/subPackage/pages/zeroSell/detail';
switch (+extra.miniAppType) {
case 63:
// case 29:
info.miniAppType = extra.miniAppType;
baseUri = 'pages/zeroSell/detail';
break;
default:
break;
}
let baseUri = 'pages/zeroSell/detail';
return this.get({
data: Object.assign({
data: {
method: 'wechat.message.send',
sendScene: 'MINI_ACTIVITY_JOIN',
// miniAppType: extra.miniAppType ? +extra.miniAppType : 29,
miniAppType: info.miniAppType || 0,
miniAppType: +extra.miniAppType === 63 ? extra.miniAppType : 29,
params: JSON.stringify({
activityTitle: extra.actName,
activityTime: `${timeFormat(extra.actStartTime)} - ${timeFormat(extra.actEndTime)}`,
pageUrl: `${baseUri}?actPrizeId=${actPrizeId}`
}),
uidList: [uid]
}, info)
}
}).then(result => {
if (result.code !== 200) {
logger.info(`zerobuy_join_notice send fail uid: ${uid}`);
... ...
... ... @@ -61,8 +61,9 @@ const zeroBuy = {
let ctx = req.ctx(ActivityModel);
let id = req.body.id;
let channel = req.body.channel;
let lotteryInfo = req.body.lotteryInfo;
ctx.editZerobuyStatus(id, 2).then(result => {
ctx.setZeroBuyPublish(id, lotteryInfo).then(result => {
if (result.code === 200) {
ctx.sendWechatMessage(id, channel);
}
... ... @@ -105,6 +106,14 @@ const zeroBuy = {
req.ctx(ActivityModel).saveZerobuyContent(req.body)
.then(res.json).catch(next);
},
zeroBuyNoticeEdit(req, res, next) {
req.ctx(ActivityModel).editZerobuyNotice(req.body)
.then(res.json).catch(next);
},
zeroBuyUserJoinNum(req, res, next) {
req.ctx(ActivityModel).getUserJoinNum(req.body.id)
.then(res.json).catch(next);
}
};
const activity = {
... ...
... ... @@ -23,7 +23,7 @@ const TABLE_ACT_PRIZE_PRODUCT_CONTENT = 'act_prize_product_content';
const TABLE_ACT_PRIZE_PRODUCT_USER = 'act_prize_product_user';
const NOTICE_BATCH_SEND_USER_NUM = 400; // 批量发送模板消息每个请求uid数量
const NOTICE_BATCH_SEND_API_NUM = 10; // 批量发送模板消息每个批次请求数量
const NOTICE_BATCH_SEND_API_NUM = 1; // 批量发送模板消息每个批次请求数量
const NOTICE_BATCH_SEND_INTERVAL = 5000; // 批量发送模板消息批次间隔时间(ms)
const timeFormat = (time) => {
... ... @@ -434,7 +434,9 @@ class AdminModel extends global.yoho.BaseModel {
{type: 'edit', color: 'info', name: '编辑'},
{type: 'switch', color: 'info', name: '开启/关闭'},
{type: 'export', color: 'info', name: '导出'},
{type: 'publish', color: 'danger', name: '开奖'}
{type: 'publish', color: 'danger', name: '开奖'},
{type: 'notice', color: 'info', name: '悬浮内容'},
{type: 'joinnum', color: 'info', name: '查看参与人数'}
];
resData.list = _.forEach(result[0], value => {
... ... @@ -488,12 +490,12 @@ class AdminModel extends global.yoho.BaseModel {
});
}
/**
* 获取0元购活动列表
* @param id 活动id
* @returns {*}
*/
/**
* 设置0元够活动状态
* @param id 活动id
* @param status 活动status
* @returns {*}
*/
editZerobuyStatus(id, status) {
if (!id || !_.inRange(status, 0, 3)) {
return Promise.resolve({
... ... @@ -513,6 +515,29 @@ class AdminModel extends global.yoho.BaseModel {
});
}
/**
* 设置活动开奖
* @param id 活动id
* @param lotteryInfo 活动开奖信息
* @returns {*}
*/
setZeroBuyPublish(id, lotteryInfo) {
return mysqlCli.update(`update ${TABLE_ACT_PRIZE_PRODUCT} set status = 2, lottery_info = :lotteryInfo, lottery_time = ${new Date().getTime() / 1000} where id = :id`, {
id,
lotteryInfo: lotteryInfo
}).then(() => {
return {
code: 200,
message: '操作成功'
};
});
}
/**
* 获取0元购活动导出列表
* @param id 活动id
* @returns {*}
*/
getZerobuyExportList(id) {
return Promise.all([
mysqlCli.query(`select uid, user_name, prize_code, is_share_take, share_uid, create_time
... ... @@ -709,53 +734,78 @@ class AdminModel extends global.yoho.BaseModel {
});
}
editZerobuyNotice(params) {
if (!params.id) {
return Promise.resolve({
code: 400,
message: '缺少参数'
});
}
return mysqlCli.update(`update ${TABLE_ACT_PRIZE_PRODUCT} set
notice = :notice where id = :id`, {
id: params.id,
notice: params.notice
}).then(() => {
return {
code: 200,
message: '操作成功'
};
});
}
getUserJoinNum(actPrizeId) {
return mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId;`, {
actPrizeId
}).then(result => {
return {
code: 200,
data: _.get(result, '[0].join_num', 0)
};
});
}
/**
* 向参加活动用户发送开奖消息
* @param id
* @returns {*}
*/
async sendWechatMessage(id, channel = 0) {
let info = await Promise.all([
mysqlCli.query(`select name from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :id limit 1`, {id}),
mysqlCli.query(`select uid from ${TABLE_ACT_PRIZE_PRODUCT_USER}
mysqlCli.query(`select distinct uid from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :id`, {id})
]);
let productInfo = _.get(info, '[0][0]');
let userList = [];
const miniAppType = ((+channel) === 1 ? 63 : 0);
const baseUrl = miniAppType ? 'pages/zeroSell/detail' : 'page/subPackage/pages/zeroSell/detail';
_.forEach(_.get(info, '[1]', []), value => {
userList.push(value.uid);
});
userList = _.compact(_.uniq(userList));
let msgApi = [];
let msgData = {
method: 'wechat.message.send',
sendScene: 'MINI_WINNING_NOTICE',
// miniAppType: ((+channel) === 1 ? 63 : 29),
miniAppType,
miniAppType: ((+channel) === 1 ? 63 : 29),
params: JSON.stringify({
activityTitle: productInfo.name,
activityTime: moment().format('YYYY-MM-DD HH:mm') + ':00',
pageUrl: `${baseUrl}?actPrizeId=${id}`
// pageUrl: 'pages/zeroSell/detail?actPrizeId=' + id
pageUrl: 'pages/zeroSell/detail?actPrizeId=' + id
})
};
_.forEach(_.chunk(userList, NOTICE_BATCH_SEND_USER_NUM), value => {
value = _.compact(value);
if (value && value.length) {
msgApi.push(this.get({data: Object.assign({uidList: value}, msgData)}).then(result => {
logger.info(`zerobuy_winning_notice send ${result.code === 200 ? 'success' : 'fail'} uids: ${value.join(',')}`);
}));
msgApi.push(() => {
return this.get({data: Object.assign({uidList: value}, msgData)}).then(result => {
logger.info(`zerobuy_winning_notice send ${result.code === 200 ? 'success' : 'fail'} uids: ${value.join(',')}`);
});
});
}
});
... ... @@ -776,7 +826,7 @@ class AdminModel extends global.yoho.BaseModel {
this.batchSend(_.drop(apis, NOTICE_BATCH_SEND_API_NUM));
}, NOTICE_BATCH_SEND_INTERVAL);
return Promise.all(_.take(apis, NOTICE_BATCH_SEND_API_NUM));
return Promise.all(_.take(apis, NOTICE_BATCH_SEND_API_NUM).map(rp => rp()));
}
}
... ...
... ... @@ -36,6 +36,8 @@ router.post('/activity/zerobuy/publish', activity.zeroBuyPublish);
router.get('/activity/zerobuy/export', activity.zeroBuyExport);
router.get('/activity/zerobuy/edit', activity.zeroBuyEdit);
router.post('/activity/zerobuy/save', activity.zeroBuySave);
router.post('/activity/zerobuy/notice', activity.zeroBuyNoticeEdit);
router.post('/activity/zerobuy/joinnum', activity.zeroBuyUserJoinNum);
// 用户管理[page]
router.get('/user/list', user.userListPage);
... ...
... ... @@ -66,7 +66,7 @@
</div>
</div>
</div>
<div class="item form-group">
<div class="item form-group hide">
<label class="control-label col-md-3 col-sm-3 col-xs-12" for="actName">开奖需要人数<span
class="required">*</span>
</label>
... ... @@ -77,7 +77,7 @@
name="actVoteLimit"
type="text"
maxlength="10"
value="{{limit}}">
value="9999999">
</div>
</div>
<div class="item form-group">
... ...
... ... @@ -56,7 +56,7 @@
<td class="text-center">
<image src="{{cover_img}}" class="cover-img">
</td>
<td class="text-center">
<td class="text-center" data-notice="{{notice}}">
{{# btns}}
<button class="btn btn-{{color}} btn-{{type}}" data-id="{{../id}}" data-channel="{{../channel}}">{{name}}</button>
{{/ btns}}
... ... @@ -121,7 +121,79 @@
<h3>确认开奖</h3>
</div>
<div class="modal-body">
<p>确认将此活动设为开奖状态?开奖后无法进行状态变更</p>
<p>请输入中奖提示信息</p>
<form class="form-horizontal" id="publish-form">
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">APP</span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="app" placeholder="请输入抽奖码">
</div>
</div>
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">小程序</span>
<div class="col-md-9 col-sm-6 col-xs-12">
<select class="form-control col-md-4 notice-btn-select" name="miniapptype">
<option value="0">默认</option>
<option value="1">关注</option>
<option value="2">跳转</option>
</select>
</div>
</div>
<div class="input-switch-group">
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniapptip" placeholder="请输入弹窗内容">
</div>
</div>
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniappcopy" placeholder="请输入点击复制内容">
</div>
</div>
<div class="form-group link-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniapplink" placeholder="请输入跳转链接">
</div>
</div>
</div>
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">H5</span>
<div class="col-md-9 col-sm-6 col-xs-12">
<select class="form-control notice-btn-select" name="h5type">
<option value="0">默认</option>
<option value="1">关注</option>
<option value="2">跳转</option>
</select>
</div>
</div>
<div class="input-switch-group">
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5tip" placeholder="请输入弹窗内容">
</div>
</div>
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5copy" placeholder="请输入点击复制内容">
</div>
</div>
<div class="form-group link-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5link" placeholder="请输入跳转链接">
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<a class="btn btn-success" data-dismiss="modal">取消</a>
... ... @@ -130,4 +202,98 @@
</div>
</div>
</div>
<div id="notice-modal" class="modal fade in" style="display: none; ">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<a class="close" data-dismiss="modal">×</a>
<h3>编辑悬浮内容</h3>
</div>
<div class="modal-body">
<form class="form-horizontal" id="notice-form">
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">悬浮内容</span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="content" placeholder="请输入悬浮内容">
</div>
</div>
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">小程序按钮</span>
<div class="col-md-5 col-sm-6 col-xs-12">
<input type="text" class="form-control col-md-4" name="miniappbtn" placeholder="请输入按钮名称">
</div>
<div class="col-md-4 col-sm-6 col-xs-12">
<select class="form-control col-md-4 notice-btn-select" name="miniapptype">
<option value="1">关注</option>
<option value="2">跳转</option>
</select>
</div>
</div>
<div class="input-switch-group">
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniapptip" placeholder="请输入弹窗内容">
</div>
</div>
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniappcopy" placeholder="请输入点击复制内容">
</div>
</div>
<div class="form-group link-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="miniapplink" placeholder="请输入跳转链接">
</div>
</div>
</div>
<div class="form-group">
<span class="control-label col-md-2 col-xs-12">H5按钮</span>
<div class="col-md-5 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5btn" placeholder="请输入按钮名称">
</div>
<div class="col-md-4 col-sm-6 col-xs-12">
<select class="form-control notice-btn-select" name="h5type">
<option value="1">关注</option>
<option value="2">跳转</option>
</select>
</div>
</div>
<div class="input-switch-group">
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5tip" placeholder="请输入弹窗内容">
</div>
</div>
<div class="form-group tip-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5copy" placeholder="请输入点击复制内容">
</div>
</div>
<div class="form-group link-group">
<span class="control-label col-md-2 col-xs-12"></span>
<div class="col-md-9 col-sm-6 col-xs-12">
<input type="text" class="form-control" name="h5link" placeholder="请输入跳转链接">
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<a class="btn btn-success" data-dismiss="modal">取消</a>
<a class="btn btn-success sure-notice-btn">确定</a>
</div>
</div>
</div>
</div>
<!-- /page content -->
... ...
const shelljs = require('shelljs');
const path = require('path');
const distDir = path.join(__dirname, '../dist/node');
shelljs.rm('-rf', distDir);
shelljs.mkdir('-p', distDir);
const cpPaths = [
'favicon.ico',
'config',
'apps',
'*.js',
'db',
'.npmrc',
'doraemon',
'Dockerfile',
'public',
'utils',
'package.json',
'yarn.lock',
'process.json',
'manifest.json'
];
new Promise(resolve => { // 加载manifest.json文件
resolve();
}).then(() => { // 拷贝node代码
cpPaths.forEach(p => {
let dist = distDir;
let file = p;
if (typeof p === 'object') {
dist = path.join(dist, p[1]);
file = p[0];
if (!shelljs.test('-e', dist)) {
shelljs.mkdir('-p', dist);
}
}
shelljs.cp('-R', path.join(__dirname, '../', file), dist);
});
}).then(() => { // 安装依赖和清理node_modules
shelljs.cd(distDir);
if (shelljs.exec('yarn --production=true').code !== 0) {
throw 'yarn install faild';
}
}).catch(error => {
console.error(`error:${error}`);
return process.exit(1); //eslint-disable-line
});
... ...
... ... @@ -104,10 +104,10 @@ module.exports = {
},
mysql: {
connect: {
host: 'localhost',
host: '192.168.102.219',
port: '3306',
user: 'root',
password: '',
user: 'yh_test',
password: 'yh_test',
charset: 'utf8mb4',
timezone: '+08:00'
},
... ... @@ -183,6 +183,12 @@ if (isProduction) {
}
}
},
report: {
host: 'badjs.yohoops.org',
port: 80,
db: 'web-apm',
immediate: true
},
// 数据上报
monitorReport: {
... ...
... ... @@ -4,4 +4,5 @@ cd $wordir
rm -rf yoho-activity-platform.tar.gz
yarn --production=false
yarn build
yarn build:node
tar -czf yoho-activity-platform.tar.gz *
... ...
... ... @@ -3,7 +3,10 @@
* @author: leo<qi.li@yoho.cn>
* @date: 2017/06/23
*/
const _ = require('lodash');
const logger = global.yoho.logger;
const sender = global.yoho.apmSender;
const hostname = require('os').hostname();
const serverError = (err, req, res, next) => { // eslint-disable-line
logger.error(err);
... ... @@ -13,6 +16,30 @@ const serverError = (err, req, res, next) => { // eslint-disable-line
code = err.code;
msg = err.message;
}
if (req.isApmReport && !err.apiError) {
try {
sender.addMessage({
measurement: 'error-report',
tags: {
app: global.yoho.config.appName, // 应用名称
hostname,
type: 'server',
route: `[${req.method}]${req.route && req.route.path}`, // 请求路由
reqID: req.reqID || '',
code: err.code || 500,
url: encodeURIComponent(req.originalUrl),
ip: _.get(req, 'yoho.clientIp', '')
},
fields: {
message: err.message,
stack: err.stack,
useragent: req && req.get('user-agent')
}
});
} catch(e) {} // eslint-disable-line
}
return res.status(code).json({
code: code,
message: msg
... ...
/*
0元购开奖信息
@author: yyq <kingcoon@163.com>
@date: 2018-12-24 11:43:47
*/
#注意:GO;分割执行块
ALTER TABLE act_prize_product ADD (
`notice` VARCHAR(1000) DEFAULT '' comment '公告',
`lottery_info` VARCHAR(1000) DEFAULT '' comment '开奖信息',
`lottery_time` int(10) NOT NULL DEFAULT 0 comment '开奖时间'
);
GO;
... ...
module.exports = ['_migration', '20170913102356', '20170927092926', '20171025145423', '20171102104558', '20171115144710', '20180131104311', '20180402134610', '20180413105509', '20180719134813', '20180806151252', '20180810104355', '20180816142744', '20180920153207', '20181108134201', '20181114190801', '20181119101042'];
module.exports = ['_migration', '20170913102356', '20170927092926', '20171025145423', '20171102104558', '20171115144710', '20180131104311', '20180402134610', '20180413105509', '20180719134813', '20180806151252', '20180810104355', '20180816142744', '20180920153207', '20181108134201', '20181114190801', '20181119101042', '20181224114347'];
... ...
... ... @@ -12,6 +12,7 @@
"dev": "nodemon -e js,hbs -i public/ -i build/ app.js",
"static": "webpack-dev-server --config ./build/webpack.dev.config.js",
"build": "webpack --config ./build/webpack.prod.config.js",
"build:node": "node ./build/node-build.js",
"debug": "DEBUG=\"express:*\" nodemon -e js,hbs -i public/ app.js",
"migration-add": "node ./build/migration/add.js",
"migration-script": "node ./build/migration/update.js --script",
... ...
... ... @@ -5,9 +5,17 @@ const _ = require('lodash');
const $alert = $('#alert-modal');
const $confirm = $('#confirm-modal');
$alert.showMsg = function(msg) {
this.find('.modal-text').text(msg);
this.modal('show');
};
function bindListPageEvent() {
const $searchKey = $('#search-key');
const $statusSwitch = $('#status-switch');
const $publishForm = $('#publish-form');
const $noticeModal = $('#notice-modal');
const $noticeForm = $noticeModal.find('#notice-form');
const searchFn = function() {
let val = $searchKey.val();
... ... @@ -62,12 +70,45 @@ function bindListPageEvent() {
};
const publishFn = function() {
let t = $publishForm.serializeArray();
let tips = {};
let tipsVal = {};
$.each(t, function() {
tips[this.name] = this.value;
});
tipsVal.app = tips.app;
tipsVal.miniappType = +tips.miniapptype;
if (tipsVal.miniappType) {
if (tipsVal.miniappType === 1) {
tipsVal.miniappTip = tips.miniapptip;
tipsVal.miniappCopy = tips.miniappcopy;
} else {
tipsVal.miniappLink = tips.miniapplink;
}
}
tipsVal.h5Type = +tips.h5type;
if (tipsVal.h5Type) {
if (tipsVal.h5Type === 1) {
tipsVal.h5Tip = tips.h5tip;
tipsVal.h5Copy = tips.h5copy;
} else {
tipsVal.h5Link = tips.h5link;
}
}
$.ajax({
method: 'post',
url: '/admin/activity/zerobuy/publish',
data: {
id: $confirm.data('id'),
channel: $confirm.data('channel')
channel: $confirm.data('channel'),
lotteryInfo: JSON.stringify(tipsVal)
}
}).then(res => {
if (res.code === 200) {
... ... @@ -79,6 +120,82 @@ function bindListPageEvent() {
});
};
const noticeFn = function() {
let n = $noticeForm.serializeArray();
let notice = {};
let noticeVal = {};
$.each(n, function() {
notice[this.name] = this.value;
});
noticeVal.content = notice.content;
noticeVal.miniappBtnName = notice.miniappbtn;
noticeVal.miniappBtnType = notice.miniapptype;
if (+notice.miniapptype === 1) {
noticeVal.miniappTip = notice.miniapptip;
noticeVal.miniappCopy = notice.miniappcopy;
} else {
noticeVal.miniappLink = notice.miniapplink;
}
noticeVal.h5BtnName = notice.h5btn;
noticeVal.h5BtnType = notice.h5type;
if (+notice.h5type === 1) {
noticeVal.h5Tip = notice.h5tip;
noticeVal.h5Copy = notice.h5copy;
} else {
noticeVal.h5Link = notice.h5link;
}
$.ajax({
method: 'post',
url: '/admin/activity/zerobuy/notice',
data: {
id: $noticeModal.data('id'),
notice: JSON.stringify(noticeVal)
}
}).then(res => {
if (res.code === 200) {
location.reload();
} else {
alert(res.message);
}
});
};
const joinnumFn = function() {
$.ajax({
method: 'post',
url: '/admin/activity/zerobuy/joinnum',
data: {
id: $(this).data('id')
}
}).then(res => {
if (res.code === 200) {
$alert.find('.modal-text').text(`该活动当前参与人数为 ${res.data} 人`);
$alert.modal('show');
} else {
alert(res.message);
}
});
};
const noticeBtnTypeChangeFn = function() {
let $this = $(this);
let $dom = $this.closest('.form-group').next('.input-switch-group');
let val = +$this.val();
if (val === 0) {
$dom.removeClass('type-link type-tip');
} else if (val === 1) {
$dom.removeClass('type-link').addClass('type-tip');
} else {
$dom.removeClass('type-tip').addClass('type-link');
}
};
$('#search-btn').on('click', searchFn);
$('.status-switch').on('click', statusFn);
... ... @@ -87,11 +204,37 @@ function bindListPageEvent() {
$('.btn-switch-close').on('click', switchFn);
$('.btn-export').on('click', exportFn);
$('.btn-publish').on('click', function() {
$('input', $confirm).val('');
$('select', $confirm).val(0).change();
$confirm.data('id', $(this).data('id'));
$confirm.data('channel', $(this).data('channel'));
$confirm.modal('show');
});
$('.btn-notice').on('click', function() {
let notice = $(this).parent().data('notice') || {};
$('input[name="content"]', $noticeForm).val(notice.content || '');
$('input[name="miniappbtn"]', $noticeForm).val(notice.miniappBtnName || '');
$('select[name="miniapptype"]', $noticeForm).val(notice.miniappBtnType || 1).change();
$('input[name="miniapptip"]', $noticeForm).val(notice.miniappTip || '');
$('input[name="miniappcopy"]', $noticeForm).val(notice.miniappCopy || '');
$('input[name="miniapplink"]', $noticeForm).val(notice.miniappLink || '');
$('input[name="h5btn"]', $noticeForm).val(notice.h5BtnName || '');
$('select[name="h5type"]', $noticeForm).val(notice.h5BtnType || 1).change();
$('input[name="h5tip"]', $noticeForm).val(notice.h5Tip || '');
$('input[name="h5copy"]', $noticeForm).val(notice.h5Copy || '');
$('input[name="h5link"]', $noticeForm).val(notice.h5Link || '');
$noticeModal.data('id', $(this).data('id'));
$noticeModal.modal('show');
});
$('.btn-joinnum').on('click', joinnumFn);
$('.sure-publish-btn').on('click', publishFn);
$('.sure-notice-btn').on('click', noticeFn);
$('.notice-btn-select').change(noticeBtnTypeChangeFn);
}
let uploadedFn;
... ...
... ... @@ -80,3 +80,16 @@
.floor-item:hover .del-item-btn {
display: block;
}
.input-switch-group .tip-group,
.input-switch-group .link-group{
display: none;
}
.input-switch-group.type-tip .tip-group{
display: block;
}
.input-switch-group.type-link .link-group{
display: block;
}
... ...
... ... @@ -2,12 +2,14 @@ const mysql = require('mysql');
const _ = require('lodash');
const md5 = require('yoho-md5');
const qs = require('querystring');
const monitorType = 'sql';
class SqlHelper {
constructor(database) {
this.config = global.yoho.config.mysql;
this.logger = global.yoho.logger;
this.cache = global.yoho.cache;
this.monitor = global.yoho.monitorSender;
database = database || 'mysql';
this.createPool(database);
}
... ... @@ -95,10 +97,12 @@ class SqlHelper {
// 写入缓存
cacheTime && this.cache.set(cacheKey, result, cacheTime);
resolve(result);
this.monitor && this.monitor.tallySuccess(monitorType);
}
});
}).catch(e => {
reject(e);
reject(e);
this.monitor && this.monitor.tallyFail(monitorType, e);
});
});
}
... ...