fix: 盲盒抽取使用事务 + 乐观锁防止并发抽到同一想法

- 将 findMany + update 包裹在 prisma.$transaction 中
- 使用 updateMany({ where: { id, status: "in_pool" } }) 保证原子性
- count=0 时返回 409 提示用户重试
This commit is contained in:
2026-02-26 20:10:04 +08:00
parent 90d3b35069
commit b9a07f84c0
+31 -17
View File
@@ -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,