全项目扫描修复: Docker数据卷修复+安全requireAdmin+12页SEO+API白名单+脚本超时+常量提取+假数据删除
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user