task-svr/src/controllers/lottery.controller.ts
2024-01-12 15:07:53 +08:00

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,
}
}
}