refactor: 提取 buildNavUrl 工具函数,统一 room API 错误处理

- 将 MatchResult.tsx 和 profile/page.tsx 中重复的导航 URL 构建逻辑提取到 src/lib/navigation.ts
- 7 个 room API 路由从 return NextResponse.json({ error }, { status }) 统一改为 throw ApiError,由 apiHandler 统一捕获
This commit is contained in:
2026-02-26 19:31:00 +08:00
parent c9e20d4c95
commit 9641acbcbd
10 changed files with 21 additions and 58 deletions
+1 -6
View File
@@ -22,12 +22,7 @@ export const POST = apiHandler(async (req, { params }) => {
return data; return data;
}); });
if (!updated) { if (!updated) throw new ApiError("房间不存在或已过期", 404);
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
notify(id); notify(id);
+1 -6
View File
@@ -59,12 +59,7 @@ export const POST = apiHandler(async (req, { params }) => {
return data; return data;
}); });
if (!updated) { if (!updated) throw new ApiError("房间不存在或已过期", 404);
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
notify(id); notify(id);
+2 -7
View File
@@ -1,7 +1,7 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { atomicUpdateRoom } from "@/lib/store"; import { atomicUpdateRoom } from "@/lib/store";
import { notify } from "@/lib/roomEvents"; import { notify } from "@/lib/roomEvents";
import { apiHandler } from "@/lib/api"; import { apiHandler, ApiError } from "@/lib/api";
export const POST = apiHandler(async (req, { params }) => { export const POST = apiHandler(async (req, { params }) => {
const { id } = await params; const { id } = await params;
@@ -27,12 +27,7 @@ export const POST = apiHandler(async (req, { params }) => {
return data; return data;
}); });
if (!updated) { if (!updated) throw new ApiError("房间不存在或已过期", 404);
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
notify(id); notify(id);
+2 -8
View File
@@ -1,18 +1,12 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { buildRoomStatus } from "@/lib/buildRoomStatus"; import { buildRoomStatus } from "@/lib/buildRoomStatus";
import { apiHandler } from "@/lib/api"; import { apiHandler, ApiError } from "@/lib/api";
export const GET = apiHandler(async (_req, { params }) => { export const GET = apiHandler(async (_req, { params }) => {
const { id } = await params; const { id } = await params;
const status = await buildRoomStatus(id); const status = await buildRoomStatus(id);
if (!status) throw new ApiError("房间不存在或已过期", 404);
if (!status) {
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
return NextResponse.json(status); return NextResponse.json(status);
}); });
+1 -6
View File
@@ -40,12 +40,7 @@ export const POST = apiHandler(async (req, { params }) => {
return data; return data;
}); });
if (!updated) { if (!updated) throw new ApiError("房间不存在或已过期", 404);
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
notify(id); notify(id);
+1 -6
View File
@@ -32,12 +32,7 @@ export const POST = apiHandler(async (req, { params }) => {
return data; return data;
}); });
if (!updated) { if (!updated) throw new ApiError("房间不存在或已过期", 404);
return NextResponse.json(
{ error: "房间不存在或已过期" },
{ status: 404 },
);
}
notify(id); notify(id);
+1 -6
View File
@@ -150,12 +150,7 @@ export const POST = apiHandler(async (req) => {
restaurants = results.slice(0, 15); restaurants = results.slice(0, 15);
} }
if (restaurants.length === 0) { if (restaurants.length === 0) throw new ApiError(sceneConfig.emptyError, 404);
return NextResponse.json(
{ error: sceneConfig.emptyError },
{ status: 404 },
);
}
const roomId = await createRoom(restaurants, userId, sceneConfig.key); const roomId = await createRoom(restaurants, userId, sceneConfig.key);
return NextResponse.json({ roomId, restaurants }); return NextResponse.json({ roomId, restaurants });
+2 -5
View File
@@ -29,6 +29,7 @@ import RestaurantImage from "@/components/RestaurantImage";
import { ProfileCardSkeleton, RecordItemSkeleton } from "@/components/Skeleton"; import { ProfileCardSkeleton, RecordItemSkeleton } from "@/components/Skeleton";
import { getUserId, getCachedProfile, setCachedProfile, setCachedPreferences, logout } from "@/lib/userId"; import { getUserId, getCachedProfile, setCachedProfile, setCachedPreferences, logout } from "@/lib/userId";
import { getAvatarBg, AVATARS } from "@/lib/avatars"; import { getAvatarBg, AVATARS } from "@/lib/avatars";
import { buildNavUrl } from "@/lib/navigation";
import type { UserProfile, UserPreferences, DecisionRecord, FavoriteRecord, Restaurant } from "@/types"; import type { UserProfile, UserPreferences, DecisionRecord, FavoriteRecord, Restaurant } from "@/types";
function firstImage(r: Restaurant): string { function firstImage(r: Restaurant): string {
@@ -279,10 +280,6 @@ export default function ProfilePage() {
if (!profile) return null; if (!profile) return null;
const amapNavUrl = (r: Restaurant) =>
r.location
? `https://uri.amap.com/marker?position=${r.location}&name=${encodeURIComponent(r.name)}&callnative=1`
: `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(r.name)}`;
return ( return (
<div className="h-dvh bg-background pb-16 overflow-y-auto scrollbar-none"> <div className="h-dvh bg-background pb-16 overflow-y-auto scrollbar-none">
@@ -567,7 +564,7 @@ export default function ProfilePage() {
{history.map((d) => ( {history.map((d) => (
<a <a
key={d.id} key={d.id}
href={amapNavUrl(d.restaurantData)} href={buildNavUrl(d.restaurantData)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex gap-3 rounded-xl bg-elevated p-2.5 transition-colors active:bg-subtle" className="flex gap-3 rounded-xl bg-elevated p-2.5 transition-colors active:bg-subtle"
+1 -8
View File
@@ -35,6 +35,7 @@ import ShareCardModal from "@/components/ShareCardModal";
import RestaurantImage from "@/components/RestaurantImage"; import RestaurantImage from "@/components/RestaurantImage";
import AuthModal from "@/components/AuthModal"; import AuthModal from "@/components/AuthModal";
import Button from "@/components/Button"; import Button from "@/components/Button";
import { buildNavUrl } from "@/lib/navigation";
import { useToast } from "@/hooks/useToast"; import { useToast } from "@/hooks/useToast";
interface MatchResultProps { interface MatchResultProps {
@@ -52,14 +53,6 @@ interface MatchResultProps {
scene?: SceneType; scene?: SceneType;
} }
function buildNavUrl(restaurant: Restaurant): string {
if (restaurant.location) {
const [lng, lat] = restaurant.location.split(",");
return `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodeURIComponent(restaurant.name)}&callnative=1`;
}
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name)}`;
}
function NoMatchResult({ function NoMatchResult({
onReset, onReset,
resetting, resetting,
+9
View File
@@ -0,0 +1,9 @@
import type { Restaurant } from "@/types";
export function buildNavUrl(restaurant: Restaurant): string {
if (restaurant.location) {
const [lng, lat] = restaurant.location.split(",");
return `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodeURIComponent(restaurant.name)}&callnative=1`;
}
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name)}`;
}