194 lines
5.3 KiB
TypeScript
194 lines
5.3 KiB
TypeScript
import { EMPTY_REWARD, ITEM_FRAME } from "common/Constants";
|
|
import { LotteryCache } from "common/LotteryCache";
|
|
import { SyncLocker } from "common/SyncLocker";
|
|
import { ZError } from "common/ZError";
|
|
import BaseController from "common/base.controller";
|
|
import { FUSION_CFG } from "configs/fusion";
|
|
import { ALL_ITEMS } from "configs/items";
|
|
import { LOTTERY_CFG } from "configs/lottery";
|
|
import { router } from "decorators/router";
|
|
import { FastifyRequest } from "fastify";
|
|
import { ActivityItem } from "models/ActivityItem";
|
|
import { LotteryRecord } from "models/LotteryRecord";
|
|
import { updateRankScore } from "services/rank.svr";
|
|
import { formatDate } from "utils/date.util";
|
|
|
|
const ROUND = 1000000;
|
|
|
|
// Get random prizes according to the set probability
|
|
const draw = (rewards: {probability: number}[]) => {
|
|
let total = 0;
|
|
let random = Math.floor(Math.random() * ROUND);
|
|
let reward = null;
|
|
for (let r of rewards) {
|
|
total += r.probability;
|
|
if (random < total) {
|
|
reward = r;
|
|
break;
|
|
}
|
|
}
|
|
return {id: reward?.item || EMPTY_REWARD, amount: reward?.amount || 1};
|
|
}
|
|
|
|
export default class LotteryController extends BaseController {
|
|
@router('get /api/lottery/stats')
|
|
async userStats(req) {
|
|
let user = req.user;
|
|
let record = await new LotteryCache().getData(user.id, user.activity);
|
|
let result:any = record.toJson();
|
|
let items = await ActivityItem.find({user: user.id, activity: user.activity});
|
|
result.items = items.map((i) => i.toJson());
|
|
return result;
|
|
}
|
|
|
|
@router('post /api/lottery/history')
|
|
async userLotteryHistory(req) {
|
|
let user = req.user;
|
|
let { day, page, limit } = req.params;
|
|
const query: any = {user: user.id, activity: user.activity};
|
|
if (day !== 'all') {
|
|
query.dateTag = day;
|
|
}
|
|
page = +page || 1
|
|
limit = +limit || 10
|
|
let start = page * limit || 0
|
|
|
|
|
|
let historys = await LotteryRecord.find(query).skip(start).limit(limit);
|
|
let total = await LotteryRecord.countDocuments(query);
|
|
return {
|
|
count: total,
|
|
page: +page,
|
|
limit,
|
|
records: historys.map((h) => h.toJson())
|
|
}
|
|
}
|
|
|
|
@router('get /api/lottery/items')
|
|
async items(req) {
|
|
const items = ALL_ITEMS;
|
|
const cfgs = LOTTERY_CFG;
|
|
const itemMap = new Map();
|
|
for (let item of items) {
|
|
itemMap.set(item.id, item);
|
|
}
|
|
let result = [];
|
|
for (let cfg of cfgs.rewards) {
|
|
result.push({
|
|
id: cfg.item,
|
|
amount: cfg.amount,
|
|
name: itemMap.get(cfg.item).name,
|
|
})
|
|
}
|
|
return {items: result, start: cfgs.start, end: cfgs.end};
|
|
}
|
|
|
|
|
|
@router('get /api/lottery/draw')
|
|
async draw(req) {
|
|
new SyncLocker().checkLock(req);
|
|
let user = req.user;
|
|
const { start, end, rewards } = LOTTERY_CFG;
|
|
const startTime = new Date(start).getTime();
|
|
const endTime = new Date(end).getTime();
|
|
const now = Date.now();
|
|
if (now < startTime) {
|
|
throw new ZError(10, 'lottery not start');
|
|
}
|
|
if (now > endTime) {
|
|
throw new ZError(11, 'lottery end');
|
|
}
|
|
let record = await new LotteryCache().getData(user.id, user.activity);
|
|
if (record.amount <= 0) {
|
|
throw new ZError(12, 'no chance');
|
|
}
|
|
record.amount -= 1;
|
|
record.used += 1;
|
|
let reward = draw(rewards);
|
|
const dateTag = formatDate(new Date());
|
|
let history = new LotteryRecord({
|
|
user: user.id,
|
|
activity: user.activity,
|
|
dateTag,
|
|
reward: reward?.id || EMPTY_REWARD,
|
|
amount: reward.amount || 0,
|
|
});
|
|
await history.save();
|
|
const items = ALL_ITEMS;
|
|
const itemMap = new Map();
|
|
for (let item of items) {
|
|
itemMap.set(item.id, item);
|
|
}
|
|
if (itemMap.get(reward.id)?.score) {
|
|
let score = (reward?.amount || 0) * itemMap.get(reward.id).score;
|
|
if (user.boost > 1 && Date.now() < user.boostExpire) {
|
|
score = Math.floor(score * user.boost)
|
|
}
|
|
await updateRankScore({
|
|
user: user.id,
|
|
score,
|
|
activity: user.activity,
|
|
scoreType: "draw",
|
|
scoreParams: {
|
|
date: dateTag
|
|
}
|
|
})
|
|
}
|
|
await ActivityItem.insertOrUpdate({
|
|
user: user.id,
|
|
activity: user.activity,
|
|
item: reward.id
|
|
},
|
|
{
|
|
$inc: {amount: reward.amount},
|
|
last: Date.now()
|
|
})
|
|
return reward;
|
|
}
|
|
|
|
@router('get /api/lottery/fusion')
|
|
async fusion(req) {
|
|
new SyncLocker().checkLock(req);
|
|
let user = req.user;
|
|
let items = await ActivityItem.find({user: user.id, activity: user.activity});
|
|
let itemCountMap = new Map();
|
|
for (let item of items) {
|
|
itemCountMap.set(item.item, item.amount);
|
|
}
|
|
for (let item of FUSION_CFG.source) {
|
|
if (!itemCountMap.has(item.id)) {
|
|
throw new ZError(13, 'no enough item');
|
|
}
|
|
if (itemCountMap.get(item.id) < item.amount) {
|
|
throw new ZError(14, 'no enough item');
|
|
}
|
|
}
|
|
for (let item of FUSION_CFG.source) {
|
|
await ActivityItem.insertOrUpdate({
|
|
user: user.id,
|
|
activity: user.activity,
|
|
item: item.id
|
|
},
|
|
{
|
|
$inc: {amount: -item.amount},
|
|
last: Date.now()
|
|
})
|
|
}
|
|
|
|
await ActivityItem.insertOrUpdate({
|
|
user: user.id,
|
|
activity: user.activity,
|
|
item: FUSION_CFG.target.id
|
|
},
|
|
{
|
|
$inc: {amount: FUSION_CFG.target.amount},
|
|
last: Date.now()
|
|
})
|
|
|
|
return {
|
|
id: FUSION_CFG.target.id,
|
|
amount: FUSION_CFG.target.amount,
|
|
}
|
|
}
|
|
|
|
} |