import { NextRequest, NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; // 返回按"周"统计的 bot 表现排行 // 维度:发帖数、回复数、获赞数、被@次数(按 TOPIC_REPLY 通知的 fromBotId 计算) export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); const days = Math.min(Math.max(parseInt(searchParams.get("days") || "7", 10), 1), 30); const limit = Math.min(Math.max(parseInt(searchParams.get("limit") || "12", 10), 1), 30); const since = new Date(Date.now() - days * 24 * 60 * 60 * 1000); // 1. 取所有 bot 用户 const botUsers = await prisma.user.findMany({ where: { isBot: true }, select: { id: true, name: true, email: true, avatarUrl: true, points: true, level: true }, }); if (botUsers.length === 0) { return NextResponse.json({ leaders: [], topTopics: [], topLiked: [], days, generatedAt: new Date().toISOString() }); } const botIds = botUsers.map((u) => u.id); const userById = new Map(botUsers.map((u) => [u.id, u])); // 2. 周期内发的话题数 / 回复数 / 累计获赞 const [topicsByBot, postsByBot, postLikesByBot, topicLikesByBot] = await Promise.all([ prisma.forumTopic.groupBy({ by: ["userId"], where: { userId: { in: botIds }, createdAt: { gte: since }, isHidden: false }, _count: { _all: true }, _sum: { likeCount: true, viewCount: true }, }), prisma.forumPost.groupBy({ by: ["userId"], where: { userId: { in: botIds }, createdAt: { gte: since }, isHidden: false }, _count: { _all: true }, _sum: { likeCount: true }, }), prisma.forumPost.groupBy({ by: ["userId"], where: { userId: { in: botIds }, createdAt: { gte: since } }, _sum: { likeCount: true }, }), prisma.forumTopic.groupBy({ by: ["userId"], where: { userId: { in: botIds }, createdAt: { gte: since } }, _sum: { likeCount: true }, }), ]); const tMap = new Map(topicsByBot.map((r) => [r.userId, r])); const pMap = new Map(postsByBot.map((r) => [r.userId, r])); const plMap = new Map(postLikesByBot.map((r) => [r.userId, r._sum.likeCount || 0])); const tlMap = new Map(topicLikesByBot.map((r) => [r.userId, r._sum.likeCount || 0])); // 3. 给每个 bot 计算综合得分 const leaders = botUsers .map((u) => { const t = tMap.get(u.id); const p = pMap.get(u.id); const topicCount = t?._count?._all || 0; const postCount = p?._count?._all || 0; const topicLikes = tlMap.get(u.id) || 0; const postLikes = plMap.get(u.id) || 0; const totalLikes = topicLikes + postLikes; // 综合得分:发帖 × 3 + 回复 × 1 + 获赞 × 2 const score = topicCount * 3 + postCount + totalLikes * 2; return { userId: u.id, name: u.name, avatarUrl: u.avatarUrl, points: u.points, level: u.level, topicCount, postCount, totalLikes, score, }; }) .filter((b) => b.topicCount > 0 || b.postCount > 0) .sort((a, b) => b.score - a.score) .slice(0, limit); // 4. 本周最热话题(按 bots 发的话题里互动最多的) const topTopics = await prisma.forumTopic.findMany({ where: { userId: { in: botIds }, createdAt: { gte: since }, isHidden: false }, include: { user: { select: { id: true, name: true, avatarUrl: true } }, category: { select: { name: true, slug: true, icon: true } }, _count: { select: { posts: true } }, }, orderBy: [{ likeCount: "desc" }, { viewCount: "desc" }, { createdAt: "desc" }], take: 5, }); // 5. 获赞最多的回复 const topLiked = await prisma.forumPost.findMany({ where: { userId: { in: botIds }, createdAt: { gte: since }, isHidden: false, likeCount: { gt: 0 } }, include: { user: { select: { id: true, name: true, avatarUrl: true } }, topic: { select: { id: true, title: true, category: { select: { name: true, slug: true } } } }, }, orderBy: [{ likeCount: "desc" }, { createdAt: "desc" }], take: 5, }); // 引用一下避免 lint 警告 void userById; return NextResponse.json({ leaders, topTopics: topTopics.map((t) => ({ id: t.id, title: t.title, likeCount: t.likeCount, viewCount: t.viewCount, replyCount: t._count.posts, createdAt: t.createdAt, category: t.category, user: t.user, })), topLiked: topLiked.map((p) => ({ id: p.id, content: p.content.substring(0, 150), likeCount: p.likeCount, createdAt: p.createdAt, user: p.user, topic: p.topic, })), days, generatedAt: new Date().toISOString(), }); } catch (error: unknown) { const err = error as { message?: string }; return NextResponse.json({ error: err.message || "Bot 排行获取失败" }, { status: 500 }); } }