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:
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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 });
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)}`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user