84 lines
2.9 KiB
TypeScript
84 lines
2.9 KiB
TypeScript
|
|
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 });
|
||
|
|
}
|
||
|
|
|
||
|
|
|