优化房间点赞同步为增量更新策略
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { diffRoomLikes } from "@/lib/roomRepository";
|
||||
|
||||
describe("diffRoomLikes", () => {
|
||||
it("returns incremental create/delete changes", () => {
|
||||
const current = {
|
||||
r1: ["u1", "u2"],
|
||||
r2: ["u3"],
|
||||
};
|
||||
const updated = {
|
||||
r1: ["u1", "u3"],
|
||||
r3: ["u2"],
|
||||
};
|
||||
|
||||
const result = diffRoomLikes(current, updated);
|
||||
|
||||
expect(result.toDelete).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ userId: "u2", restaurantId: "r1" },
|
||||
{ userId: "u3", restaurantId: "r2" },
|
||||
]),
|
||||
);
|
||||
expect(result.toCreate).toEqual(
|
||||
expect.arrayContaining([
|
||||
{ userId: "u3", restaurantId: "r1" },
|
||||
{ userId: "u2", restaurantId: "r3" },
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it("deduplicates repeated user likes", () => {
|
||||
const current = {
|
||||
r1: ["u1", "u1", "u1"],
|
||||
};
|
||||
const updated = {
|
||||
r1: ["u1"],
|
||||
};
|
||||
|
||||
const result = diffRoomLikes(current, updated);
|
||||
|
||||
expect(result.toCreate).toEqual([]);
|
||||
expect(result.toDelete).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns empty arrays when likes are unchanged", () => {
|
||||
const current = {
|
||||
r1: ["u1", "u2"],
|
||||
};
|
||||
const updated = {
|
||||
r1: ["u1", "u2"],
|
||||
};
|
||||
|
||||
const result = diffRoomLikes(current, updated);
|
||||
|
||||
expect(result.toCreate).toEqual([]);
|
||||
expect(result.toDelete).toEqual([]);
|
||||
});
|
||||
});
|
||||
+69
-12
@@ -16,8 +16,56 @@ export interface RoomData {
|
||||
scene: SceneType;
|
||||
}
|
||||
|
||||
interface RoomLikeRow {
|
||||
userId: string;
|
||||
restaurantId: string;
|
||||
}
|
||||
|
||||
const ROOM_ID_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
||||
|
||||
function normalizeLikeRows(likes: Record<string, string[]>): RoomLikeRow[] {
|
||||
const rows: RoomLikeRow[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
for (const [restaurantId, userIds] of Object.entries(likes)) {
|
||||
for (const userId of userIds) {
|
||||
const key = `${userId}::${restaurantId}`;
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
rows.push({ userId, restaurantId });
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
export function diffRoomLikes(
|
||||
currentLikes: Record<string, string[]>,
|
||||
updatedLikes: Record<string, string[]>,
|
||||
): { toCreate: RoomLikeRow[]; toDelete: RoomLikeRow[] } {
|
||||
const currentRows = normalizeLikeRows(currentLikes);
|
||||
const updatedRows = normalizeLikeRows(updatedLikes);
|
||||
|
||||
const currentMap = new Map(
|
||||
currentRows.map((row) => [`${row.userId}::${row.restaurantId}`, row]),
|
||||
);
|
||||
const updatedMap = new Map(
|
||||
updatedRows.map((row) => [`${row.userId}::${row.restaurantId}`, row]),
|
||||
);
|
||||
|
||||
const toCreate: RoomLikeRow[] = [];
|
||||
const toDelete: RoomLikeRow[] = [];
|
||||
|
||||
for (const [key, row] of updatedMap) {
|
||||
if (!currentMap.has(key)) toCreate.push(row);
|
||||
}
|
||||
for (const [key, row] of currentMap) {
|
||||
if (!updatedMap.has(key)) toDelete.push(row);
|
||||
}
|
||||
|
||||
return { toCreate, toDelete };
|
||||
}
|
||||
|
||||
function generateRoomId(): string {
|
||||
let id = "";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
@@ -181,18 +229,27 @@ export async function atomicUpdateRoom(
|
||||
});
|
||||
}
|
||||
|
||||
// Sync likes: rebuild from updated data
|
||||
if (JSON.stringify(current.likes) !== JSON.stringify(updated.likes)) {
|
||||
await tx.roomLike.deleteMany({ where: { roomId } });
|
||||
const likeRows: { roomId: string; userId: string; restaurantId: string }[] = [];
|
||||
for (const [restaurantId, userIds] of Object.entries(updated.likes)) {
|
||||
for (const userId of userIds) {
|
||||
likeRows.push({ roomId, userId, restaurantId });
|
||||
}
|
||||
}
|
||||
if (likeRows.length > 0) {
|
||||
await tx.roomLike.createMany({ data: likeRows });
|
||||
}
|
||||
// Sync likes incrementally to avoid full delete + rebuild on every change.
|
||||
const { toCreate, toDelete } = diffRoomLikes(current.likes, updated.likes);
|
||||
if (toDelete.length > 0) {
|
||||
await tx.roomLike.deleteMany({
|
||||
where: {
|
||||
roomId,
|
||||
OR: toDelete.map((row) => ({
|
||||
userId: row.userId,
|
||||
restaurantId: row.restaurantId,
|
||||
})),
|
||||
},
|
||||
});
|
||||
}
|
||||
if (toCreate.length > 0) {
|
||||
await tx.roomLike.createMany({
|
||||
data: toCreate.map((row) => ({
|
||||
roomId,
|
||||
userId: row.userId,
|
||||
restaurantId: row.restaurantId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
// Sync swipes
|
||||
|
||||
Reference in New Issue
Block a user