import { NextResponse } from "next/server"; import { prisma } from "@/lib/prisma"; import { requireMembership } from "@/lib/blindbox"; import { apiHandler, ApiError, requireUserId } from "@/lib/api"; import { validateIdeaContent, requireString } from "@/lib/validation"; import { tagIdea } from "@/lib/ai"; const TAG_TIMEOUT_MS = 3000; export const POST = apiHandler(async (req) => { const { roomId, userId, content } = await req.json(); requireUserId(userId); requireString(roomId, "roomId"); const trimmedContent = validateIdeaContent(content); await requireMembership(roomId, userId); const idea = await prisma.blindBoxIdea.create({ data: { roomId, userId, content: trimmedContent }, }); const tags = await Promise.race([ tagIdea(trimmedContent), new Promise((resolve) => setTimeout(() => resolve(null), TAG_TIMEOUT_MS)), ]); if (tags) { await prisma.blindBoxIdea.update({ where: { id: idea.id }, data: { category: tags.category, timeSlot: tags.timeSlot, estimatedMinutes: tags.estimatedMinutes, costLevel: tags.costLevel, intensity: tags.intensity, needsBooking: tags.needsBooking, searchQuery: tags.searchQuery, searchType: tags.searchType, }, }); } return NextResponse.json( { id: idea.id, ...tags && { tags } }, { status: 201 }, ); }); export const GET = apiHandler(async (req) => { const userId = requireUserId(req.nextUrl.searchParams.get("userId")); const roomId = requireString(req.nextUrl.searchParams.get("roomId"), "roomId"); await requireMembership(roomId, userId); const [poolCount, myIdeas, drawn] = await Promise.all([ prisma.blindBoxIdea.count({ where: { roomId, status: "in_pool" }, }), prisma.blindBoxIdea.findMany({ where: { roomId, userId, status: "in_pool" }, orderBy: { createdAt: "desc" }, select: { id: true, content: true, createdAt: true, category: true, timeSlot: true, estimatedMinutes: true, costLevel: true, intensity: true, needsBooking: true, searchQuery: true, searchType: true, }, }), prisma.blindBoxIdea.findMany({ where: { roomId, status: "drawn" }, orderBy: { createdAt: "desc" }, include: { user: { select: { id: true, username: true, avatar: true } }, drawnBy: { select: { id: true, username: true, avatar: true } }, }, }), ]); return NextResponse.json({ poolCount, myIdeas, drawn }); }); export const PUT = apiHandler(async (req) => { const { ideaId, userId, content } = await req.json(); requireUserId(userId); requireString(ideaId, "ideaId"); const trimmedContent = validateIdeaContent(content); const { count } = await prisma.blindBoxIdea.updateMany({ where: { id: ideaId, userId, status: "in_pool" }, data: { content: trimmedContent }, }); if (count === 0) throw new ApiError("想法不存在、已被抽中或无权编辑", 404); const tags = await Promise.race([ tagIdea(trimmedContent), new Promise((resolve) => setTimeout(() => resolve(null), TAG_TIMEOUT_MS)), ]); if (tags) { await prisma.blindBoxIdea.updateMany({ where: { id: ideaId, userId, status: "in_pool" }, data: { category: tags.category, timeSlot: tags.timeSlot, estimatedMinutes: tags.estimatedMinutes, costLevel: tags.costLevel, intensity: tags.intensity, needsBooking: tags.needsBooking, searchQuery: tags.searchQuery, searchType: tags.searchType, }, }); } return NextResponse.json({ id: ideaId, content: trimmedContent, ...tags && { tags } }); }); export const DELETE = apiHandler(async (req) => { const { ideaId, userId } = await req.json(); requireUserId(userId); requireString(ideaId, "ideaId"); const { count } = await prisma.blindBoxIdea.deleteMany({ where: { id: ideaId, userId, status: "in_pool" }, }); if (count === 0) throw new ApiError("想法不存在、已被抽中或无权删除", 404); return NextResponse.json({ deleted: true }); });