diff --git a/PROJECT_AUDIT_2026-03-03.md b/PROJECT_AUDIT_2026-03-03.md index 0e6906e..a46af51 100644 --- a/PROJECT_AUDIT_2026-03-03.md +++ b/PROJECT_AUDIT_2026-03-03.md @@ -203,11 +203,13 @@ ## 可重构/可优化项(非阻塞,但收益高) -### R1 计划查询存在 N+1 查询模式 +### R1 计划查询存在 N+1 查询模式【已完成】 +- 修复状态:✅ 已完成(2026-03-03) +- 修复内容: + - `getPendingPlans`、`getHistoryPlans` 改为在 `weekendPlan.findMany` 中直接 `select room { name, code }`; + - 移除逐条 `findUnique` 查房间的 N+1 查询路径。 - 证据: - - `src/lib/planQueries.ts:30-36`、`63-68`(每条计划单独查房间) -- 建议: - - 使用 `include` / 批量映射一次性拉取房间信息,减少 DB 往返。 + - `src/lib/planQueries.ts` 已由“逐条房间查询”改为“单次查询带房间关系”。 ### R2 `atomicUpdateRoom` 对 likes 的“全删全建”策略成本较高 - 证据: diff --git a/src/lib/planQueries.ts b/src/lib/planQueries.ts index 2f83086..364173a 100644 --- a/src/lib/planQueries.ts +++ b/src/lib/planQueries.ts @@ -1,5 +1,4 @@ import { prisma } from "@/lib/prisma"; -import { ApiError } from "@/lib/api"; export async function getLatestPlan(roomId: string, userId: string) { const plan = await prisma.weekendPlan.findFirst({ @@ -23,28 +22,27 @@ export async function getPendingPlans(userId: string) { endTime: { not: null, lt: new Date() }, }, orderBy: { createdAt: "desc" }, - select: { id: true, planData: true, roomId: true, createdAt: true }, + select: { + id: true, + planData: true, + createdAt: true, + room: { select: { name: true, code: true } }, + }, take: 5, }); - const result = await Promise.all( - plans.map(async (p) => { - const room = await prisma.blindBoxRoom.findUnique({ - where: { id: p.roomId }, - select: { name: true, code: true }, - }); - const parsed = JSON.parse(p.planData); - const days = parsed.days as { date: string; items: { activity: string }[] }[]; - return { - id: p.id, - roomName: room?.name ?? "未知房间", - roomCode: room?.code ?? "", - date: days.map((d) => d.date).join(" + "), - activities: days.flatMap((d) => d.items.map((i) => i.activity)), - createdAt: p.createdAt, - }; - }), - ); + const result = plans.map((p) => { + const parsed = JSON.parse(p.planData); + const days = parsed.days as { date: string; items: { activity: string }[] }[]; + return { + id: p.id, + roomName: p.room?.name ?? "未知房间", + roomCode: p.room?.code ?? "", + date: days.map((d) => d.date).join(" + "), + activities: days.flatMap((d) => d.items.map((i) => i.activity)), + createdAt: p.createdAt, + }; + }); return { pending: result }; } @@ -56,30 +54,30 @@ export async function getHistoryPlans(userId: string) { status: { in: ["completed", "expired"] }, }, orderBy: { createdAt: "desc" }, - select: { id: true, planData: true, status: true, roomId: true, createdAt: true }, + select: { + id: true, + planData: true, + status: true, + createdAt: true, + room: { select: { name: true, code: true } }, + }, take: 50, }); - const result = await Promise.all( - plans.map(async (p) => { - const room = await prisma.blindBoxRoom.findUnique({ - where: { id: p.roomId }, - select: { name: true, code: true }, - }); - const parsed = JSON.parse(p.planData); - const days = parsed.days as { date: string; items: { activity: string }[] }[]; - return { - id: p.id, - status: p.status, - roomName: room?.name ?? "未知房间", - roomCode: room?.code ?? "", - date: days.map((d) => d.date).join(" + "), - dayCount: days.length, - activities: days.flatMap((d) => d.items.map((i) => i.activity)), - createdAt: p.createdAt, - }; - }), - ); + const result = plans.map((p) => { + const parsed = JSON.parse(p.planData); + const days = parsed.days as { date: string; items: { activity: string }[] }[]; + return { + id: p.id, + status: p.status, + roomName: p.room?.name ?? "未知房间", + roomCode: p.room?.code ?? "", + date: days.map((d) => d.date).join(" + "), + dayCount: days.length, + activities: days.flatMap((d) => d.items.map((i) => i.activity)), + createdAt: p.createdAt, + }; + }); return { history: result }; }