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