This commit is contained in:
92
src/app/api/posts/popular/route.ts
Normal file
92
src/app/api/posts/popular/route.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import prisma from "@/lib/prisma";
|
||||
|
||||
export async function GET(req: Request) {
|
||||
const { searchParams } = new URL(req.url);
|
||||
const boardId = searchParams.get("boardId");
|
||||
const period = searchParams.get("period") || "daily"; // daily | weekly
|
||||
|
||||
// 날짜 범위 계산
|
||||
const now = new Date();
|
||||
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0);
|
||||
const endOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
|
||||
const startOfWeek = new Date(now);
|
||||
startOfWeek.setDate(now.getDate() - 7);
|
||||
startOfWeek.setHours(0, 0, 0, 0);
|
||||
|
||||
const dateFilter = period === "daily"
|
||||
? {
|
||||
date: {
|
||||
gte: startOfToday,
|
||||
lte: endOfToday,
|
||||
}
|
||||
}
|
||||
: { date: { gte: startOfWeek } };
|
||||
|
||||
// 일일 조회수 테이블에서 조회수 합계 계산
|
||||
const dailyViews = await prisma.dailyPostView.groupBy({
|
||||
by: ["postId"],
|
||||
where: dateFilter,
|
||||
_sum: {
|
||||
viewCount: true,
|
||||
},
|
||||
orderBy: {
|
||||
_sum: {
|
||||
viewCount: "desc",
|
||||
},
|
||||
},
|
||||
take: 20, // 조회수 상위 20개만 가져와서 게시글 정보 조회
|
||||
});
|
||||
|
||||
if (dailyViews.length === 0) {
|
||||
return NextResponse.json({ items: [], period });
|
||||
}
|
||||
|
||||
const postIds = dailyViews.map((dv) => dv.postId);
|
||||
|
||||
// 게시글 정보 조회
|
||||
const posts = await prisma.post.findMany({
|
||||
where: {
|
||||
id: { in: postIds },
|
||||
status: "published",
|
||||
...(boardId ? { boardId } : {}),
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
createdAt: true,
|
||||
boardId: true,
|
||||
board: { select: { id: true, name: true, slug: true } },
|
||||
isPinned: true,
|
||||
status: true,
|
||||
author: { select: { userId: true, nickname: true } },
|
||||
stat: { select: { recommendCount: true, views: true, commentsCount: true } },
|
||||
postTags: { select: { tag: { select: { name: true, slug: true } } } },
|
||||
},
|
||||
});
|
||||
|
||||
// 조회수와 게시글 매핑
|
||||
const viewCountMap = new Map(
|
||||
dailyViews.map((dv) => [dv.postId, dv._sum.viewCount ?? 0])
|
||||
);
|
||||
|
||||
// 조회수 순으로 정렬 (조회수가 0보다 큰 것만)
|
||||
const postsWithViews = posts
|
||||
.map((post) => ({
|
||||
...post,
|
||||
viewCount: viewCountMap.get(post.id) ?? 0,
|
||||
}))
|
||||
.filter((post) => post.viewCount > 0) // 조회수가 0보다 큰 것만
|
||||
.sort((a, b) => {
|
||||
// 고정글 우선
|
||||
if (a.isPinned && !b.isPinned) return -1;
|
||||
if (!a.isPinned && b.isPinned) return 1;
|
||||
// 조회수 순
|
||||
if (b.viewCount !== a.viewCount) return b.viewCount - a.viewCount;
|
||||
// 최신순
|
||||
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
||||
})
|
||||
.slice(0, 5); // 상위 5개만
|
||||
|
||||
return NextResponse.json({ items: postsWithViews, period });
|
||||
}
|
||||
Reference in New Issue
Block a user