Authored by yyq

get code

... ... @@ -7,11 +7,19 @@ const zerobuyModel = require('../models/zero-buy');
module.exports = {
list(req, res, next) {
req.ctx(zerobuyModel).getList(req.query.status)
if (req.isAdmin) {
req.query.noCache = true;
}
req.ctx(zerobuyModel).getList(req.query.status, req.query.page, req.query)
.then(res.json).catch(next);
},
content(req, res, next) {
req.ctx(zerobuyModel).getContent(req.query.prizeId)
if (req.isAdmin) {
req.query.noCache = true;
}
req.ctx(zerobuyModel).getContent(req.query.prizeId, req.query)
.then(res.json).catch(next);
},
listMine(req, res, next) {
... ... @@ -28,4 +36,17 @@ module.exports = {
req.ctx(zerobuyModel).getCodeMine(uid, prizeId)
.then(res.json).catch(next);
},
codeGain(req, res, next) {
let params = req.body;
if (!params.uid || !params.actPrizeId || !params.userName || !params.userThumb) {
return res.json({
code: 400,
message: '参数非法'
});
}
req.ctx(zerobuyModel).getPrizeCode(req.query)
.then(res.json).catch(next);
}
};
... ...
... ... @@ -5,11 +5,18 @@
*/
const _ = require('lodash');
const mysqlCli = global.yoho.utils.mysqlCli;
const MemoryCache = require('../../../utils/memory-cache');
const TABLE_ACT_PRIZE_PRODUCT = 'act_prize_product';
const TABLE_ACT_PRIZE_PRODUCT_CONTENT = 'act_prize_product_content';
const TABLE_ACT_PRIZE_PRODUCT_USER = 'act_prize_product_user';
const CACHE_TIMES = 60 * 5; // 缓存时间
const MAX_JOIN_TIMES = 2; // 最大活动参与次数
const userTimesCache = new MemoryCache();
const productCache = new MemoryCache();
function handelResult(result) {
return {
code: 200,
... ... @@ -17,6 +24,20 @@ function handelResult(result) {
};
}
function getActivityStatus(info = {}, num, now) {
let resData = {
isEnd: true
};
now = now || (new Date().getTime() / 1000);
if (!info.status || num >= info.limit || now >= info.end_time) {
return resData;
}
return {};
}
module.exports = class extends global.yoho.BaseModel {
constructor(ctx) {
super(ctx);
... ... @@ -25,30 +46,51 @@ module.exports = class extends global.yoho.BaseModel {
/**
* 0元购活动列表
* @param status
* @param actId
* @param page
* @param extra
* @returns {*}
*/
getList(status, actId) {
getList(status, page, extra = {}) {
const pageSize = 20;
const actId = parseInt(extra.actId, 10) || 0;
status = parseInt(status, 10);
actId = parseInt(actId, 10) || 0;
page = parseInt(page, 10) || 1;
const listCacheKey = `list_${actId}_${status}_${page}_${status}`;
if (!extra.noCache) {
let cacheList = productCache.get(listCacheKey);
if (!_.isUndefined(cacheList)) {
return handelResult(cacheList);
}
}
status = _.isNaN(status) ? '> 0' : `= ${status}`;
let limit = `${(page - 1) * pageSize},${page * pageSize}`;
return mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where act_id = :actId and status ${status}`, {
where act_id = :actId and status ${status} limit ${limit}`, {
actId
}).then(handelResult);
}).then(result => {
productCache.set(listCacheKey, result, CACHE_TIMES);
return handelResult(result);
});
}
/**
* 0元购用户参与列表
* @param uid
* @param actId
* @param extra
* @returns {*}
*/
getListMine(uid, actId) {
getListMine(uid, extra = {}) {
const actId = parseInt(extra.actId, 10) || 0;
uid = parseInt(uid, 10) || 0;
actId = parseInt(actId, 10) || 0;
return mysqlCli.query(`select u.*, p.name, p.price, p.status, p.cover_img from
${TABLE_ACT_PRIZE_PRODUCT_USER} u left join
... ... @@ -65,18 +107,28 @@ module.exports = class extends global.yoho.BaseModel {
* @param actPrizeId
* @returns {*}
*/
getContent(actPrizeId) {
getContent(actPrizeId, extra = {}) {
let resData = {};
if (!actPrizeId) {
return Promise.resolve(handelResult(resData));
}
const contentCacheKey = `content_${actPrizeId}`;
if (!extra.noCache) {
let cacheContent = productCache.get(contentCacheKey);
if (!_.isUndefined(cacheContent)) {
return handelResult(cacheContent);
}
}
return Promise.all([
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1`, {actPrizeId}),
where id = :actPrizeId limit 1;`, {actPrizeId}),
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_CONTENT}
where act_prize_id = :actPrizeId`, {actPrizeId})
where act_prize_id = :actPrizeId;`, {actPrizeId})
]).then(result => {
let [product, content] = result;
... ... @@ -87,6 +139,8 @@ module.exports = class extends global.yoho.BaseModel {
});
}
productCache.set(contentCacheKey, resData, CACHE_TIMES);
return resData;
}).then(handelResult);
}
... ... @@ -98,7 +152,7 @@ module.exports = class extends global.yoho.BaseModel {
getCodeRecent() {
return mysqlCli.query(`select user_name, user_thumb, create_time
from ${TABLE_ACT_PRIZE_PRODUCT_USER}
order by u.create_time desc limit 10`).then(handelResult);
order by u.create_time desc limit 10;`).then(handelResult);
}
/**
... ... @@ -112,9 +166,166 @@ module.exports = class extends global.yoho.BaseModel {
actPrizeId = parseInt(actPrizeId, 10) || 0;
return mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId and uid = :uid`, {
where act_prize_id = :actPrizeId and uid = :uid;`, {
actPrizeId,
uid
}).then(handelResult);
}
/**
* 0元购获取抽奖码
* @param uid
* @param actPrizeId
* @returns {*}
*/
async getPrizeCode(uid, actPrizeId, extra = {}) {
uid = parseInt(uid, 10) || 0;
actPrizeId = parseInt(actPrizeId, 10) || 0;
let info = await Promise.all([
mysqlCli.query(`select * from ${TABLE_ACT_PRIZE_PRODUCT}
where id = :actPrizeId limit 1;`, {actPrizeId}),
mysqlCli.query(`select count(distinct uid) as join_num from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId;`, {actPrizeId})
]);
const errorData = {
code: 400,
message: '活动已结束或已达人数上限'
};
let status = getActivityStatus(_.get(info, '[0][0]'), _.get(info, '[1][0].join_num', 0));
if (status.isEnd) {
return errorData;
}
if (extra.shareUid) {
this.sendPrizeCode(extra.shareUid, actPrizeId, {isShareTake: true});
}
return this.sendPrizeCode(uid, actPrizeId, Object.assign(extra, {isShareTake: false}));
}
/**
* 生成抽奖码
* @param length
* @param isRetry
* @returns {*}
*/
async createPrizeCode(length = 8, isRetry) {
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 prize_code = :prizeCode limit 1`, {prizeCode});
if (info && info.length && !isRetry) {
return this.createPrizeCode(length, true);
} else {
return prizeCode;
}
}
/**
* 向单用户发送抽奖码
* @param uid
* @param actPrizeId
* @param extra
* @returns {*}
*/
async sendPrizeCode(uid, actPrizeId, extra = {}) {
// 查询用户参与次数
const TimesCacheKey = `${actPrizeId}_${uid}`;
let userJoinTimes = userTimesCache.get(TimesCacheKey);
if (_.isUndefined(userJoinTimes)) {
userJoinTimes = await mysqlCli.query(`select count(*) from ${TABLE_ACT_PRIZE_PRODUCT_USER}
where act_prize_id = :actPrizeId and uid = :uid`, {
actPrizeId,
uid
});
if (userJoinTimes >= MAX_JOIN_TIMES) {
userTimesCache.set(TimesCacheKey, userJoinTimes);
}
}
let errorData = {
code: 400,
message: '领取失败,请稍后重试'
};
// 大于最大活动参与次数
if (userJoinTimes >= MAX_JOIN_TIMES) {
errorData.message = '活动参与已达上限';
return errorData;
}
// 2个及以上的抽奖码仅限分享获得
if (userJoinTimes >= 1 && !extra.isShareTake) {
errorData.message = '您已获得抽奖码,分享好友获取更多抽奖码';
return errorData;
}
// 未获取过抽奖码需用户信息参数
if (!userJoinTimes && !extra.userName) {
errorData.message = '缺少必要参数';
return errorData;
}
const prizeCode = await this.createPrizeCode();
if (!prizeCode) {
return errorData;
}
const keyArr = ['act_prize_id', 'uid', 'user_name', 'user_thumb', 'union_id', 'prize_code', 'is_share_take', 'share_uid'];
const isShareTake = extra.isShareTake ? 1 : 0;
const shareUid = extra.shareUid || 0;
let insert;
if (extra.userName) {
insert = await mysqlCli.insert(`insert into ${TABLE_ACT_PRIZE_PRODUCT_USER} (${keyArr.join(',')})
values (:actPrizeId, :uid, :userName, :userThumb, :unionId, :prizeCode, :isShareTake, :shareUid);`, {
actPrizeId,
uid,
userName: extra.userName,
userThumb: extra.userThumb,
unionId: extra.unionId,
prizeCode,
isShareTake,
shareUid
});
} else {
insert = await mysqlCli.insert(`insert into ${TABLE_ACT_PRIZE_PRODUCT_USER} (${keyArr.join(',')})
select :actPrizeId as act_prize_id, :uid as uid, user_name, user_thumb, union_id, :prizeCode as prize_code,
:isShareTake as is_share_take, :shareUid as share_uid from ${TABLE_ACT_PRIZE_PRODUCT_USER} where uid = :uid limit 1;`, {
actPrizeId,
uid,
prizeCode,
isShareTake,
shareUid
});
}
if (_.isNumber(insert)) {
return {
code: 200,
data: {prizeCode},
message: '领取成功'
};
} else {
return errorData;
}
}
};
... ...
... ... @@ -22,5 +22,6 @@ router.get('/zerobuy/list/mine', zeroBuy.listMine); // 0元购用户参与列
router.get('/zerobuy/content', zeroBuy.content); // 0元购详情
router.get('/zerobuy/code/recent', zeroBuy.codeRecent); // 0元购抽奖码最近获取记录
router.get('/zerobuy/code/mine', zeroBuy.codeMine); // 0元购用户单个活动抽奖码
router.post('/zerobuy/code/gain', zeroBuy.codeGain); // 0元购获取抽奖码
module.exports = router;
... ...
... ... @@ -34,5 +34,7 @@ module.exports = (req, res, next) => {
});
}
req.isAdmin = true;
next();
};
... ...
/**
* memoryCache
* @author: yyq<yanqing.yang@yoho.cn>
* @date: 2018/07/23
*/
const _ = require('lodash');
function _clearArray(list, key) {
_.remove(list, (n) => {
return n === key;
});
}
function _clearObject(obj, key) {
delete obj[key];
}
function _getNowTime() {
return new Date().getTime() / 1000;
}
module.exports = function memoryCache(maxLength = 1000) {
let cache = {};
let mapList = [];
// 获取缓存数据
this.get = (key) => {
if (!cache.hasOwnProperty(key)) {
return;
}
let info = cache[key];
// 校验过期时间
if (info.exptime && info.exptime - _getNowTime() < 0) {
this.clear(key);
return;
}
return info.value;
};
// 设置缓存数据
this.set = (key, value, exptime) => {
cache.hasOwnProperty(key) && _clearArray(mapList, key);
mapList.push(key);
cache[key] = {
value,
exptime: exptime ? (exptime + _getNowTime()) : 0 // 过期时间
};
// 清除老旧数据
if (mapList.length > maxLength) {
let len = mapList.length - maxLength;
for (let i = 0; i < len; i++) {
_clearObject(cache, mapList.shift());
}
}
};
// 清除单条缓存数据
this.clear = (key) => {
if (cache.hasOwnProperty(key)) {
_clearArray(mapList, key);
_clearObject(cache, key);
}
};
// 清除所有缓存数据
this.clearAll = () => {
cache = {};
mapList = [];
};
return this;
};
... ...