93 lines
3.0 KiB
TypeScript
93 lines
3.0 KiB
TypeScript
import { prisma } from "@/lib/prisma";
|
|
import { NextResponse } from "next/server";
|
|
|
|
const DAYS = 90;
|
|
|
|
export async function GET(_req: Request, { params }: { params: { id: string } }) {
|
|
try {
|
|
const userId = parseInt(params.id);
|
|
if (isNaN(userId)) {
|
|
return NextResponse.json({ error: "无效的用户ID" }, { status: 400 });
|
|
}
|
|
|
|
const since = new Date();
|
|
since.setDate(since.getDate() - DAYS);
|
|
since.setHours(0, 0, 0, 0);
|
|
|
|
const [topics, posts, comments, checkIns] = await Promise.all([
|
|
prisma.forumTopic.findMany({
|
|
where: { userId, createdAt: { gte: since } },
|
|
select: { createdAt: true },
|
|
}),
|
|
prisma.forumPost.findMany({
|
|
where: { userId, createdAt: { gte: since } },
|
|
select: { createdAt: true },
|
|
}),
|
|
prisma.comment.findMany({
|
|
where: { userId, createdAt: { gte: since } },
|
|
select: { createdAt: true },
|
|
}),
|
|
prisma.forumCheckIn.findMany({
|
|
where: { userId, checkInDate: { gte: since } },
|
|
select: { checkInDate: true, streak: true, pointsEarned: true },
|
|
}),
|
|
]);
|
|
|
|
const all = new Map<string, { count: number; topics: number; posts: number; comments: number }>();
|
|
const bump = (d: Date, type: "topics" | "posts" | "comments") => {
|
|
const k = d.toISOString().slice(0, 10);
|
|
const cur = all.get(k) || { count: 0, topics: 0, posts: 0, comments: 0 };
|
|
cur.count += 1;
|
|
cur[type] += 1;
|
|
all.set(k, cur);
|
|
};
|
|
|
|
topics.forEach(t => bump(t.createdAt, "topics"));
|
|
posts.forEach(p => bump(p.createdAt, "posts"));
|
|
comments.forEach(c => bump(c.createdAt, "comments"));
|
|
|
|
const heatmap: Array<{ date: string; count: number; topics: number; posts: number; comments: number }> = [];
|
|
for (let i = DAYS - 1; i >= 0; i--) {
|
|
const d = new Date();
|
|
d.setDate(d.getDate() - i);
|
|
const k = d.toISOString().slice(0, 10);
|
|
const v = all.get(k);
|
|
heatmap.push({
|
|
date: k,
|
|
count: v?.count || 0,
|
|
topics: v?.topics || 0,
|
|
posts: v?.posts || 0,
|
|
comments: v?.comments || 0,
|
|
});
|
|
}
|
|
|
|
const checkInList = checkIns
|
|
.map(c => ({
|
|
date: c.checkInDate.toISOString().slice(0, 10),
|
|
streak: c.streak,
|
|
points: c.pointsEarned,
|
|
}))
|
|
.sort((a, b) => a.date.localeCompare(b.date));
|
|
|
|
const totalActiveDays = heatmap.filter(d => d.count > 0).length;
|
|
const totalActions = topics.length + posts.length + comments.length;
|
|
|
|
return NextResponse.json({
|
|
heatmap,
|
|
checkIns: checkInList,
|
|
summary: {
|
|
totalDays: DAYS,
|
|
activeDays: totalActiveDays,
|
|
activeRate: Math.round((totalActiveDays / DAYS) * 100),
|
|
totalActions,
|
|
topicsCount: topics.length,
|
|
postsCount: posts.length,
|
|
commentsCount: comments.length,
|
|
},
|
|
});
|
|
} catch (error: unknown) {
|
|
const err = error as { message?: string };
|
|
return NextResponse.json({ error: err.message || "获取活跃度数据失败" }, { status: 500 });
|
|
}
|
|
}
|