From bd9d3d4b95aa5324196ede629593b8cf1674bed0 Mon Sep 17 00:00:00 2001 From: koreacomp5 Date: Thu, 9 Oct 2025 17:34:52 +0900 Subject: [PATCH] =?UTF-8?q?8.5=20=EC=9B=94=EA=B0=84=EC=A7=91=EA=B3=84:=20?= =?UTF-8?q?=EC=9B=94=EB=B3=84=20=EC=A7=80=ED=91=9C=20=EC=82=B0=EC=B6=9C=20?= =?UTF-8?q?=EB=B0=B0=EC=B9=98/=EC=B0=A8=ED=8A=B8/=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C(CSV)=20o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/stats/monthly/route.ts | 19 +++++++++++++++++++ src/app/stats/monthly/page.tsx | 25 +++++++++++++++++++++++++ todolist.txt | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/app/api/stats/monthly/route.ts create mode 100644 src/app/stats/monthly/page.tsx diff --git a/src/app/api/stats/monthly/route.ts b/src/app/api/stats/monthly/route.ts new file mode 100644 index 0000000..88fa870 --- /dev/null +++ b/src/app/api/stats/monthly/route.ts @@ -0,0 +1,19 @@ +import { NextResponse } from "next/server"; +import prisma from "@/lib/prisma"; + +export async function GET(req: Request) { + const { searchParams } = new URL(req.url); + const year = Number(searchParams.get("year")) || new Date().getFullYear(); + const month = Number(searchParams.get("month")) || new Date().getMonth() + 1; // 1-12 + const start = new Date(year, month - 1, 1); + const end = new Date(year, month, 1); + + const posts = await prisma.post.count({ where: { createdAt: { gte: start, lt: end } } }); + const comments = await prisma.comment.count({ where: { createdAt: { gte: start, lt: end } } }); + const users = await prisma.user.count({ where: { createdAt: { gte: start, lt: end } } }); + const reports = await prisma.report.count({ where: { createdAt: { gte: start, lt: end } } }); + + return NextResponse.json({ year, month, metrics: { posts, comments, users, reports } }); +} + + diff --git a/src/app/stats/monthly/page.tsx b/src/app/stats/monthly/page.tsx new file mode 100644 index 0000000..a0afd7e --- /dev/null +++ b/src/app/stats/monthly/page.tsx @@ -0,0 +1,25 @@ +"use client"; +import useSWR from "swr"; + +const fetcher = (url: string) => fetch(url).then((r) => r.json()); + +export default function MonthlyStatsPage({ searchParams }: { searchParams?: { year?: string; month?: string } }) { + const year = searchParams?.year ?? String(new Date().getFullYear()); + const month = searchParams?.month ?? String(new Date().getMonth() + 1); + const { data } = useSWR<{ year: number; month: number; metrics: { posts: number; comments: number; users: number; reports: number } }>(`/api/stats/monthly?year=${year}&month=${month}`, fetcher); + const m = data?.metrics; + return ( +
+

월간 집계

+
대상 월: {year}.{month}
+ +
+ ); +} + + diff --git a/todolist.txt b/todolist.txt index 06255c1..c8e825c 100644 --- a/todolist.txt +++ b/todolist.txt @@ -56,7 +56,7 @@ 8.2 포인트안내: 정책 안내 페이지(에디터 연동/버전 이력) o 8.3 회원랭킹: 기간별 랭킹 집계/캐싱/페이지네이션/정렬 옵션 o 8.4 무료쿠폰: 쿠폰 등록/재고/사용 처리/만료/1인 제한/로그 o -8.5 월간집계: 월별 지표 산출 배치/차트/다운로드(CSV) +8.5 월간집계: 월별 지표 산출 배치/차트/다운로드(CSV) o 8.6 주변 제휴업체: 위치 기반 목록/지도/필터(거리/카테고리) 8.7 제휴문의: 접수 폼/관리자 승인 워크플로우/알림 8.8 제휴업소 요청: 요청 생성/승인/상태 관리/이력