2026-05-25: 等级体系升级(20级)+论坛二级分类+会员空间

This commit is contained in:
ZhuiGuangAI Dev
2026-05-24 23:05:38 +08:00
parent 6c78c32157
commit 9bf3e7574a
118 changed files with 11842 additions and 3262 deletions
@@ -0,0 +1,126 @@
import { prisma } from "@/lib/prisma";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";
import { NextResponse } from "next/server";
import { rewardPoints } from "@/lib/points-reward";
export async function GET(req: Request, { params }: { params: { id: string } }) {
const topicId = parseInt(params.id);
const { searchParams } = new URL(req.url);
const page = parseInt(searchParams.get("page") || "1", 10);
const pageSize = parseInt(searchParams.get("pageSize") || "20", 10);
const [posts, totalCount] = await Promise.all([
prisma.forumPost.findMany({
where: { topicId },
include: {
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true } },
},
orderBy: { createdAt: "asc" },
skip: (page - 1) * pageSize,
take: pageSize,
}),
prisma.forumPost.count({ where: { topicId } }),
]);
const formatted = posts.map((p) => ({
id: p.id,
content: p.content,
isAnswer: p.isAnswer,
createdAt: p.createdAt,
updatedAt: p.updatedAt,
user: {
id: p.user.id,
name: p.user.name || p.user.email.split("@")[0],
avatarUrl: p.user.avatarUrl,
level: p.user.level,
points: p.user.points,
},
}));
return NextResponse.json({ posts: formatted, totalCount, page, pageSize });
}
export async function POST(req: Request, { params }: { params: { id: string } }) {
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json({ error: "请先登录" }, { status: 401 });
}
const topicId = parseInt(params.id);
const userId = parseInt(session.user.id);
const body = await req.json();
const { content } = body;
if (!content || !content.trim()) {
return NextResponse.json({ error: "回复内容不能为空" }, { status: 400 });
}
if (content.trim().length > 5000) {
return NextResponse.json({ error: "回复不能超过5000个字符" }, { status: 400 });
}
const topic = await prisma.forumTopic.findUnique({ where: { id: topicId } });
if (!topic) {
return NextResponse.json({ error: "话题不存在" }, { status: 404 });
}
if (topic.isLocked) {
return NextResponse.json({ error: "话题已锁定" }, { status: 400 });
}
const post = await prisma.$transaction(async (tx) => {
const newPost = await tx.forumPost.create({
data: {
topicId,
userId,
content: content.trim(),
},
include: {
user: { select: { id: true, name: true, email: true, avatarUrl: true, level: true, points: true } },
},
});
const now = new Date();
await tx.forumTopic.update({
where: { id: topicId },
data: {
replyCount: { increment: 1 },
lastReplyAt: now,
updatedAt: now,
},
});
if (topic.userId !== userId) {
await tx.notification.create({
data: {
userId: topic.userId,
type: "TOPIC_REPLY",
title: `${session.user.name || "用户"} 回复了你的话题`,
content: content.trim().slice(0, 100),
link: `/community/topic/${topicId}`,
},
});
}
return newPost;
});
await rewardPoints(userId, "forum_post", `回复话题`, "topic", topicId);
return NextResponse.json(
{
id: post.id,
content: post.content,
isAnswer: post.isAnswer,
createdAt: post.createdAt,
updatedAt: post.updatedAt,
user: {
id: post.user.id,
name: post.user.name || post.user.email.split("@")[0],
avatarUrl: post.user.avatarUrl,
level: post.user.level,
points: post.user.points,
},
},
{ status: 201 }
);
}