From d7b6af91e57a583f00a68828794e3b01f8da27bd Mon Sep 17 00:00:00 2001 From: koreacomp5 Date: Thu, 9 Oct 2025 17:25:17 +0900 Subject: [PATCH] =?UTF-8?q?8.3=20=ED=9A=8C=EC=9B=90=EB=9E=AD=ED=82=B9:=20?= =?UTF-8?q?=EA=B8=B0=EA=B0=84=EB=B3=84=20=EB=9E=AD=ED=82=B9=20=EC=A7=91?= =?UTF-8?q?=EA=B3=84/=EC=BA=90=EC=8B=B1/=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98/=EC=A0=95=EB=A0=AC=20=EC=98=B5?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/ranking/route.ts | 27 +++++++++++++++++++++++++++ src/app/ranking/page.tsx | 29 +++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/app/api/ranking/route.ts create mode 100644 src/app/ranking/page.tsx diff --git a/src/app/api/ranking/route.ts b/src/app/api/ranking/route.ts new file mode 100644 index 0000000..9e78da1 --- /dev/null +++ b/src/app/api/ranking/route.ts @@ -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 }); +} + + diff --git a/src/app/ranking/page.tsx b/src/app/ranking/page.tsx new file mode 100644 index 0000000..cb3be54 --- /dev/null +++ b/src/app/ranking/page.tsx @@ -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 ( +
+

회원랭킹

+
+ 일간 + 주간 + 월간 + 전체 +
+
    + {(data?.items ?? []).map((i) => ( +
  1. + {i.nickname} — {i.points}점 +
  2. + ))} +
+
+ ); +} + +