This commit is contained in:
83
src/app/api/attendance/rankings/route.ts
Normal file
83
src/app/api/attendance/rankings/route.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
function toYmdUTC(d: Date): string {
|
||||
const yy = d.getUTCFullYear();
|
||||
const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
|
||||
const dd = String(d.getUTCDate()).padStart(2, "0");
|
||||
return `${yy}-${mm}-${dd}`;
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
// 전체 출석 누적 상위 (Top 10)
|
||||
const overallGroups = await prisma.attendance.groupBy({
|
||||
by: ["userId"],
|
||||
_count: { _all: true },
|
||||
orderBy: { _count: { _all: "desc" } },
|
||||
take: 20,
|
||||
});
|
||||
const overallUserIds = overallGroups.map((g) => g.userId);
|
||||
const overallUsers = await prisma.user.findMany({
|
||||
where: { userId: { in: overallUserIds } },
|
||||
select: { userId: true, nickname: true, profileImage: true, grade: true },
|
||||
});
|
||||
const userMeta = new Map(overallUsers.map((u) => [u.userId, u]));
|
||||
const overall = overallGroups.map((g) => ({
|
||||
userId: g.userId,
|
||||
nickname: userMeta.get(g.userId)?.nickname ?? "회원",
|
||||
count: g._count._all,
|
||||
profileImage: userMeta.get(g.userId)?.profileImage ?? null,
|
||||
grade: userMeta.get(g.userId)?.grade ?? 0,
|
||||
}));
|
||||
|
||||
// 연속 출석 상위 (Top 10, 현재 연속 기준)
|
||||
const now = new Date();
|
||||
const todayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 0, 0, 0, 0));
|
||||
const past = new Date(todayUTC);
|
||||
past.setUTCDate(past.getUTCDate() - 60); // 최근 60일 데이터만으로 streak 계산
|
||||
const recent = await prisma.attendance.findMany({
|
||||
where: { date: { gte: past } },
|
||||
select: { userId: true, date: true },
|
||||
orderBy: { userId: "asc" },
|
||||
});
|
||||
const map = new Map<string, Set<string>>();
|
||||
for (const r of recent) {
|
||||
const set = map.get(r.userId) ?? new Set<string>();
|
||||
set.add(toYmdUTC(new Date(r.date)));
|
||||
map.set(r.userId, set);
|
||||
}
|
||||
const streakArr: { userId: string; streak: number }[] = [];
|
||||
for (const [userId, set] of map.entries()) {
|
||||
let streak = 0;
|
||||
let cursor = new Date(todayUTC);
|
||||
while (streak < 60) {
|
||||
const ymd = toYmdUTC(cursor);
|
||||
if (set.has(ymd)) {
|
||||
streak += 1;
|
||||
cursor.setUTCDate(cursor.getUTCDate() - 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (streak > 0) streakArr.push({ userId, streak });
|
||||
}
|
||||
streakArr.sort((a, b) => b.streak - a.streak);
|
||||
const topStreak = streakArr.slice(0, 20);
|
||||
const streakUserIds = topStreak.map((s) => s.userId);
|
||||
const streakUsers = await prisma.user.findMany({
|
||||
where: { userId: { in: streakUserIds } },
|
||||
select: { userId: true, nickname: true, profileImage: true, grade: true },
|
||||
});
|
||||
const streakMeta = new Map(streakUsers.map((u) => [u.userId, u]));
|
||||
const streak = topStreak.map((s) => ({
|
||||
userId: s.userId,
|
||||
nickname: streakMeta.get(s.userId)?.nickname ?? "회원",
|
||||
streak: s.streak,
|
||||
profileImage: streakMeta.get(s.userId)?.profileImage ?? null,
|
||||
grade: streakMeta.get(s.userId)?.grade ?? 0,
|
||||
}));
|
||||
|
||||
return NextResponse.json({ overall, streak });
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user