统一盲盒请求契约并移除冗余 userId 参数

This commit is contained in:
2026-03-03 13:11:33 +08:00
parent 4a5ed3b25a
commit 532d8ff7ad
7 changed files with 30 additions and 34 deletions
+7 -5
View File
@@ -220,12 +220,14 @@
- 证据:
- `src/lib/roomRepository.ts` 已移除 likes 全删全建逻辑,改为差量同步。
### R3 请求参数契约不统一(前端仍大量发送已废弃 `userId`)
### R3 请求参数契约不统一(前端仍大量发送已废弃 `userId`)【已完成】
- 修复状态:✅ 已完成(2026-03-03
- 修复内容:
- 盲盒主链路前端请求已去除冗余 `userId``useBlindboxIdeas``useBlindboxDraw``useBlindboxPlan``useBlindboxRoom``blindbox/page`);
- `useBlindboxRooms` 改为基于登录态启用请求,接口统一使用鉴权 cookie,不再拼接 `userId` query
- 保留房间实时 SSE 的 `userId` 参数(用于已修复的成员校验链路),其余盲盒链路契约已统一。
- 证据:
- 例如 `src/hooks/useBlindboxIdeas.ts:57/71/104/...``src/hooks/useBlindboxPlan.ts:50/68/...`
- 建议:
- 明确“鉴权由 cookie/token 提供”,清理冗余 `userId` 参数;
- 用 Zod schema 统一约束(`src/lib/schemas/requests.ts` 目前未形成全链路使用)。
- `rg` 检索显示盲盒前端链路已无 `?userId=``userId: profile.id` 传参。
### R4 统一 API 调用层(减少重复 fetch + 错误处理分散)
- 现状:
+3 -4
View File
@@ -172,7 +172,7 @@ export default function BlindboxLobbyPage() {
const [showAuth, setShowAuth] = useState(false);
const toast = useToast();
const { rooms: swrRooms, isLoading: swrLoading, isUnauthorized, error: swrError, mutate: mutateRooms } = useBlindboxRooms(
loggedIn && profile ? profile.id : undefined,
loggedIn,
);
const rooms = swrRooms;
const loading = !loggedIn ? false : swrLoading;
@@ -233,7 +233,7 @@ export default function BlindboxLobbyPage() {
const res = await fetch("/api/blindbox/room", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id, name: name || undefined }),
body: JSON.stringify({ name: name || undefined }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
@@ -254,7 +254,7 @@ export default function BlindboxLobbyPage() {
const res = await fetch("/api/blindbox/room/join", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id, code }),
body: JSON.stringify({ code }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
@@ -273,7 +273,6 @@ export default function BlindboxLobbyPage() {
const res = await fetch(`/api/blindbox/room/${room.code}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id }),
});
if (!res.ok) {
const data = await res.json();
+1 -1
View File
@@ -81,7 +81,7 @@ export function useBlindboxDraw(
const res = await fetch("/api/blindbox/draw", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ roomId: room.id, userId: profile.id }),
body: JSON.stringify({ roomId: room.id }),
});
if (!res.ok) {
+5 -5
View File
@@ -62,7 +62,7 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
const fetchIdeas = useCallback(async () => {
if (!room || !profile) return;
try {
const res = await fetch(`/api/blindbox?roomId=${room.id}&userId=${profile.id}`);
const res = await fetch(`/api/blindbox?roomId=${room.id}`);
if (res.ok) {
const data = await res.json();
setPoolCount(data.poolCount ?? 0);
@@ -76,7 +76,7 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
if (!room || !profile) return;
setSuggestionsLoading(true);
try {
const res = await fetch(`/api/blindbox/suggest?roomId=${room.id}&userId=${profile.id}`);
const res = await fetch(`/api/blindbox/suggest?roomId=${room.id}`);
if (res.ok) {
const data = await res.json();
if (data.suggestions?.length > 0) {
@@ -109,7 +109,7 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
const res = await fetch("/api/blindbox", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ roomId: room.id, userId: profile.id, content: text }),
body: JSON.stringify({ roomId: room.id, content: text }),
});
if (!res.ok) {
const data = await res.json();
@@ -156,7 +156,7 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
const res = await fetch("/api/blindbox", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ideaId, userId: profile.id, content: trimmed }),
body: JSON.stringify({ ideaId, content: trimmed }),
});
if (!res.ok) {
const data = await res.json();
@@ -188,7 +188,7 @@ export function useBlindboxIdeas(room: RoomInfo | null, profile: UserProfile | n
const res = await fetch("/api/blindbox", {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ ideaId, userId: profile.id }),
body: JSON.stringify({ ideaId }),
});
if (!res.ok) {
const data = await res.json();
+10 -14
View File
@@ -1,7 +1,6 @@
"use client";
import { useState, useCallback, useRef, useEffect } from "react";
import { getCachedProfile } from "@/lib/userId";
import { useToast } from "@/hooks/useToast";
import type { RoomInfo } from "@/hooks/useBlindboxRoom";
import type { WeekendPlanData, UserProfile } from "@/types";
@@ -47,7 +46,7 @@ export function useBlindboxPlan(
const fetchAcceptedPlan = useCallback(async () => {
if (!room || !profile) return;
try {
const res = await fetch(`/api/blindbox/plan?mode=latest&roomId=${room.id}&userId=${profile.id}`);
const res = await fetch(`/api/blindbox/plan?mode=latest&roomId=${room.id}`);
if (!res.ok) return;
const data = await res.json();
if (data.plan) {
@@ -65,7 +64,7 @@ export function useBlindboxPlan(
if (!profile) return;
(async () => {
try {
const res = await fetch(`/api/blindbox/plan?mode=pending&userId=${profile.id}`);
const res = await fetch("/api/blindbox/plan?mode=pending");
if (!res.ok) return;
const data = await res.json();
if (data.pending?.length) setPendingContracts(data.pending);
@@ -93,13 +92,10 @@ export function useBlindboxPlan(
});
n.onclick = () => { window.focus(); n.close(); };
}
const p = getCachedProfile();
if (p) {
fetch(`/api/blindbox/plan?mode=pending&userId=${p.id}`)
.then((r) => r.json())
.then((d) => { if (d.pending?.length) setPendingContracts(d.pending); })
.catch((e) => { console.error("refreshPendingContracts failed:", e); });
}
fetch("/api/blindbox/plan?mode=pending")
.then((r) => r.json())
.then((d) => { if (d.pending?.length) setPendingContracts(d.pending); })
.catch((e) => { console.error("refreshPendingContracts failed:", e); });
}, ms);
return () => clearTimeout(timer);
@@ -110,7 +106,7 @@ export function useBlindboxPlan(
setGenerating(true);
setPhase("planning");
setPlanStatusMessages([PLAN_STATUS_STEPS[0]]);
const payload = { roomId: room.id, userId: profile.id, availableTime: timeConfig };
const payload = { roomId: room.id, availableTime: timeConfig };
const stepRef = { current: 0 };
const fallbackTimer = setInterval(() => {
stepRef.current = (stepRef.current + 1) % PLAN_STATUS_STEPS.length;
@@ -195,7 +191,7 @@ export function useBlindboxPlan(
const res = await fetch("/api/blindbox/plan", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ planId, userId: profile.id, action: "update_plan", days: newDays }),
body: JSON.stringify({ planId, action: "update_plan", days: newDays }),
});
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || "保存失败");
} catch (e) {
@@ -212,7 +208,7 @@ export function useBlindboxPlan(
const res = await fetch("/api/blindbox/plan/refine", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id, instruction, days: planDays }),
body: JSON.stringify({ instruction, days: planDays }),
});
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || "AI 调整失败");
const data = await res.json();
@@ -233,7 +229,7 @@ export function useBlindboxPlan(
const res = await fetch("/api/blindbox/plan", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ planId, userId: profile.id, action: "accept" }),
body: JSON.stringify({ planId, action: "accept" }),
});
const data = await res.json().catch(() => ({}));
if (!res.ok) {
+2 -3
View File
@@ -77,7 +77,7 @@ export function useBlindboxRoom(code: string) {
const res = await fetch("/api/blindbox/room/join", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id, code }),
body: JSON.stringify({ code }),
});
if (res.ok) { setIsMember(true); fetchRoom(); }
} catch (e) { console.error("handleJoinRoom failed:", e); }
@@ -99,7 +99,7 @@ export function useBlindboxRoom(code: string) {
const patchRes = await fetch(`/api/blindbox/room/${room.code}`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id, city: cityName, address: addressLabel, lat, lng }),
body: JSON.stringify({ city: cityName, address: addressLabel, lat, lng }),
});
if (!patchRes.ok) throw new Error("保存位置失败");
setRoom((prev) => prev ? { ...prev, city: cityName, address: addressLabel, lat, lng } : prev);
@@ -123,7 +123,6 @@ export function useBlindboxRoom(code: string) {
const res = await fetch(`/api/blindbox/room/${room.code}`, {
method: "DELETE",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: profile.id }),
});
if (!res.ok) {
const data = await res.json();
+2 -2
View File
@@ -18,9 +18,9 @@ interface RoomsResponse {
rooms: RoomSummary[];
}
export function useBlindboxRooms(userId: string | undefined) {
export function useBlindboxRooms(enabled: boolean) {
const { data, error, isLoading, mutate } = useSWR<RoomsResponse>(
userId ? `/api/blindbox/rooms?userId=${userId}` : null,
enabled ? "/api/blindbox/rooms" : null,
fetcher,
{
revalidateOnFocus: true,