优化计划查询避免 N+1 数据库访问

This commit is contained in:
2026-03-03 13:05:29 +08:00
parent 41ac21ea12
commit 325b7b5742
2 changed files with 45 additions and 45 deletions
+6 -4
View File
@@ -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`(每条计划单独查房间) - `src/lib/planQueries.ts` 已由“逐条房间查询”改为“单次查询带房间关系”。
- 建议:
- 使用 `include` / 批量映射一次性拉取房间信息,减少 DB 往返。
### R2 `atomicUpdateRoom` 对 likes 的“全删全建”策略成本较高 ### R2 `atomicUpdateRoom` 对 likes 的“全删全建”策略成本较高
- 证据: - 证据:
+39 -41
View File
@@ -1,5 +1,4 @@
import { prisma } from "@/lib/prisma"; import { prisma } from "@/lib/prisma";
import { ApiError } from "@/lib/api";
export async function getLatestPlan(roomId: string, userId: string) { export async function getLatestPlan(roomId: string, userId: string) {
const plan = await prisma.weekendPlan.findFirst({ const plan = await prisma.weekendPlan.findFirst({
@@ -23,28 +22,27 @@ export async function getPendingPlans(userId: string) {
endTime: { not: null, lt: new Date() }, endTime: { not: null, lt: new Date() },
}, },
orderBy: { createdAt: "desc" }, 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, take: 5,
}); });
const result = await Promise.all( const result = plans.map((p) => {
plans.map(async (p) => { const parsed = JSON.parse(p.planData);
const room = await prisma.blindBoxRoom.findUnique({ const days = parsed.days as { date: string; items: { activity: string }[] }[];
where: { id: p.roomId }, return {
select: { name: true, code: true }, id: p.id,
}); roomName: p.room?.name ?? "未知房间",
const parsed = JSON.parse(p.planData); roomCode: p.room?.code ?? "",
const days = parsed.days as { date: string; items: { activity: string }[] }[]; date: days.map((d) => d.date).join(" + "),
return { activities: days.flatMap((d) => d.items.map((i) => i.activity)),
id: p.id, createdAt: p.createdAt,
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,
};
}),
);
return { pending: result }; return { pending: result };
} }
@@ -56,30 +54,30 @@ export async function getHistoryPlans(userId: string) {
status: { in: ["completed", "expired"] }, status: { in: ["completed", "expired"] },
}, },
orderBy: { createdAt: "desc" }, 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, take: 50,
}); });
const result = await Promise.all( const result = plans.map((p) => {
plans.map(async (p) => { const parsed = JSON.parse(p.planData);
const room = await prisma.blindBoxRoom.findUnique({ const days = parsed.days as { date: string; items: { activity: string }[] }[];
where: { id: p.roomId }, return {
select: { name: true, code: true }, id: p.id,
}); status: p.status,
const parsed = JSON.parse(p.planData); roomName: p.room?.name ?? "未知房间",
const days = parsed.days as { date: string; items: { activity: string }[] }[]; roomCode: p.room?.code ?? "",
return { date: days.map((d) => d.date).join(" + "),
id: p.id, dayCount: days.length,
status: p.status, activities: days.flatMap((d) => d.items.map((i) => i.activity)),
roomName: room?.name ?? "未知房间", createdAt: p.createdAt,
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,
};
}),
);
return { history: result }; return { history: result };
} }