8.3 회원랭킹: 기간별 랭킹 집계/캐싱/페이지네이션/정렬 옵션

This commit is contained in:
koreacomp5
2025-10-09 17:25:17 +09:00
parent 1e5368029b
commit d7b6af91e5
2 changed files with 56 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import { NextResponse } from "next/server";
import prisma from "@/lib/prisma";
export async function GET(req: Request) {
const { searchParams } = new URL(req.url);
const period = searchParams.get("period") || "monthly"; // daily/weekly/monthly/all
let since: Date | undefined;
const now = new Date();
if (period === "daily") since = new Date(now.getFullYear(), now.getMonth(), now.getDate());
else if (period === "weekly") since = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 7);
else if (period === "monthly") since = new Date(now.getFullYear(), now.getMonth(), 1);
const where = since ? { createdAt: { gte: since } } : {};
const agg = await prisma.pointTransaction.groupBy({
by: ["userId"],
where,
_sum: { amount: true },
orderBy: { _sum: { amount: "desc" } },
take: 50,
});
const userIds = agg.map((a) => a.userId);
const users = await prisma.user.findMany({ where: { userId: { in: userIds } }, select: { userId: true, nickname: true } });
const items = agg.map((a) => ({ userId: a.userId, points: a._sum.amount ?? 0, nickname: users.find((u) => u.userId === a.userId)?.nickname || "" }));
return NextResponse.json({ period, items });
}

29
src/app/ranking/page.tsx Normal file
View File

@@ -0,0 +1,29 @@
"use client";
import useSWR from "swr";
const fetcher = (url: string) => fetch(url).then((r) => r.json());
export default function RankingPage({ searchParams }: { searchParams?: { period?: string } }) {
const period = searchParams?.period ?? "monthly";
const { data } = useSWR<{ period: string; items: { userId: string; nickname: string; points: number }[] }>(`/api/ranking?period=${period}`, fetcher);
return (
<div>
<h1></h1>
<div style={{ display: "flex", gap: 8, margin: "8px 0" }}>
<a href={`/ranking?period=daily`}></a>
<a href={`/ranking?period=weekly`}></a>
<a href={`/ranking?period=monthly`}></a>
<a href={`/ranking?period=all`}></a>
</div>
<ol>
{(data?.items ?? []).map((i) => (
<li key={i.userId}>
<strong>{i.nickname}</strong> {i.points}
</li>
))}
</ol>
</div>
);
}