From b9a07f84c0c9716851f924b68a447923630c7363 Mon Sep 17 00:00:00 2001 From: kurihada Date: Thu, 26 Feb 2026 20:10:04 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=9B=B2=E7=9B=92=E6=8A=BD=E5=8F=96?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E4=BA=8B=E5=8A=A1=20+=20=E4=B9=90=E8=A7=82?= =?UTF-8?q?=E9=94=81=E9=98=B2=E6=AD=A2=E5=B9=B6=E5=8F=91=E6=8A=BD=E5=88=B0?= =?UTF-8?q?=E5=90=8C=E4=B8=80=E6=83=B3=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 findMany + update 包裹在 prisma.$transaction 中 - 使用 updateMany({ where: { id, status: "in_pool" } }) 保证原子性 - count=0 时返回 409 提示用户重试 --- src/app/api/blindbox/draw/route.ts | 48 +++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/src/app/api/blindbox/draw/route.ts b/src/app/api/blindbox/draw/route.ts index 062de1e..7adc1d7 100644 --- a/src/app/api/blindbox/draw/route.ts +++ b/src/app/api/blindbox/draw/route.ts @@ -11,25 +11,39 @@ export const POST = apiHandler(async (req) => { await requireMembership(roomId, userId); - const pool = await prisma.blindBoxIdea.findMany({ - where: { roomId, status: "in_pool" }, - select: { id: true }, + const userSelect = { id: true, username: true, avatar: true } as const; + + const idea = await prisma.$transaction(async (tx) => { + const pool = await tx.blindBoxIdea.findMany({ + where: { roomId, status: "in_pool" }, + select: { id: true }, + }); + + if (pool.length === 0) { + throw new ApiError("盒子是空的,先往里面塞点想法吧!", 404); + } + + const picked = pool[Math.floor(Math.random() * pool.length)]; + + const { count } = await tx.blindBoxIdea.updateMany({ + where: { id: picked.id, status: "in_pool" }, + data: { status: "drawn", drawnById: userId }, + }); + + if (count === 0) { + throw new ApiError("手慢了,再试一次", 409); + } + + return tx.blindBoxIdea.findUnique({ + where: { id: picked.id }, + include: { + user: { select: userSelect }, + drawnBy: { select: userSelect }, + }, + }); }); - if (pool.length === 0) { - throw new ApiError("盒子是空的,先往里面塞点想法吧!", 404); - } - - const picked = pool[Math.floor(Math.random() * pool.length)]; - - const idea = await prisma.blindBoxIdea.update({ - where: { id: picked.id }, - data: { status: "drawn", drawnById: userId }, - include: { - user: { select: { id: true, username: true, avatar: true } }, - drawnBy: { select: { id: true, username: true, avatar: true } }, - }, - }); + if (!idea) throw new ApiError("抽取失败", 500); return NextResponse.json({ id: idea.id,