Files
ranked-eqao/src/services/leaderboard.js
T

74 lines
2.0 KiB
JavaScript

const db = require('../db');
async function upsertLeaderboardEntry(userId, grade, { elo, bestTime, correctAnswers = 0 } = {}) {
const existing = await db.leaderboardEntry.findUnique({
where: { userId_grade: { userId, grade } },
});
return db.leaderboardEntry.upsert({
where: { userId_grade: { userId, grade } },
create: {
userId,
grade,
elo: elo ?? 800,
bestTime: bestTime ?? null,
gamesPlayed: 1,
totalCorrect: correctAnswers,
},
update: {
...(elo !== undefined && { elo }),
...(bestTime !== undefined && (existing?.bestTime == null || bestTime < existing.bestTime)
? { bestTime }
: {}),
gamesPlayed: { increment: 1 },
totalCorrect: { increment: correctAnswers },
},
});
}
async function getLeaderboard(grade, sortBy = 'elo', limit = 10) {
const orderBy = sortBy === 'bestTime'
? { bestTime: { sort: 'asc', nulls: 'last' } }
: { elo: 'desc' };
const entries = await db.leaderboardEntry.findMany({
where: { grade },
orderBy,
take: limit,
include: {
user: { select: { displayName: true, avatarUrl: true, username: true } },
},
});
return entries.map((entry, index) => ({ ...entry, ...entry.user, rank: index + 1 }));
}
async function getUserRank(userId, grade, sortBy = 'elo') {
const entry = await db.leaderboardEntry.findUnique({
where: { userId_grade: { userId, grade } },
});
if (!entry) return null;
if (sortBy === 'bestTime') {
if (entry.bestTime === null) return { rank: null, entry };
const count = await db.leaderboardEntry.count({
where: {
grade,
AND: [
{ bestTime: { not: null } },
{ bestTime: { lt: entry.bestTime } },
],
},
});
return { rank: count + 1, entry };
}
const count = await db.leaderboardEntry.count({
where: { grade, elo: { gt: entry.elo } },
});
return { rank: count + 1, entry };
}
module.exports = { upsertLeaderboardEntry, getLeaderboard, getUserRank };