fix: 外部 API 错误处理 + 导航 URL 校验 + reset/narrow 错误反馈
- #22: 高德 API fetch 加 try/catch,失败返回 503 而非泛化 500 - #23: buildNavUrl 校验 location.split(",") 结果长度和非空 - #24: handleReset/handleNarrow 检查 res.ok,失败时 toast 提示
This commit is contained in:
@@ -15,8 +15,13 @@ export const GET = apiHandler(async (req) => {
|
|||||||
url.searchParams.set("location", `${lng},${lat}`);
|
url.searchParams.set("location", `${lng},${lat}`);
|
||||||
url.searchParams.set("extensions", "base");
|
url.searchParams.set("extensions", "base");
|
||||||
|
|
||||||
const res = await fetch(url.toString());
|
let data;
|
||||||
const data = await res.json();
|
try {
|
||||||
|
const res = await fetch(url.toString());
|
||||||
|
data = await res.json();
|
||||||
|
} catch {
|
||||||
|
throw new ApiError("位置服务暂时不可用,请稍后重试", 503);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status !== "1" || !data.regeocode) {
|
if (data.status !== "1" || !data.regeocode) {
|
||||||
return NextResponse.json({ name: null });
|
return NextResponse.json({ name: null });
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
import { apiHandler } from "@/lib/api";
|
import { apiHandler, ApiError } from "@/lib/api";
|
||||||
import { requireAmapApiKey } from "@/lib/amap";
|
import { requireAmapApiKey } from "@/lib/amap";
|
||||||
|
|
||||||
export const GET = apiHandler(async (req) => {
|
export const GET = apiHandler(async (req) => {
|
||||||
@@ -13,8 +13,13 @@ export const GET = apiHandler(async (req) => {
|
|||||||
url.searchParams.set("keywords", keywords);
|
url.searchParams.set("keywords", keywords);
|
||||||
url.searchParams.set("datatype", "poi");
|
url.searchParams.set("datatype", "poi");
|
||||||
|
|
||||||
const res = await fetch(url.toString());
|
let data;
|
||||||
const data = await res.json();
|
try {
|
||||||
|
const res = await fetch(url.toString());
|
||||||
|
data = await res.json();
|
||||||
|
} catch {
|
||||||
|
throw new ApiError("位置服务暂时不可用,请稍后重试", 503);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.status !== "1" || !data.tips) return NextResponse.json([]);
|
if (data.status !== "1" || !data.tips) return NextResponse.json([]);
|
||||||
|
|
||||||
|
|||||||
@@ -141,8 +141,13 @@ export const POST = apiHandler(async (req) => {
|
|||||||
url.searchParams.set("keywords", cuisine);
|
url.searchParams.set("keywords", cuisine);
|
||||||
}
|
}
|
||||||
|
|
||||||
const amapRes = await fetch(url.toString());
|
let amapData;
|
||||||
const amapData = await amapRes.json();
|
try {
|
||||||
|
const amapRes = await fetch(url.toString());
|
||||||
|
amapData = await amapRes.json();
|
||||||
|
} catch {
|
||||||
|
throw new ApiError("位置服务暂时不可用,请稍后重试", 503);
|
||||||
|
}
|
||||||
|
|
||||||
let restaurants: Restaurant[] = [];
|
let restaurants: Restaurant[] = [];
|
||||||
if (amapData.status === "1" && amapData.pois?.length > 0) {
|
if (amapData.status === "1" && amapData.pois?.length > 0) {
|
||||||
|
|||||||
+32
-14
@@ -11,6 +11,7 @@ import { useRoomPolling } from "@/hooks/useRoomPolling";
|
|||||||
import { getUserId } from "@/lib/userId";
|
import { getUserId } from "@/lib/userId";
|
||||||
import { joinRoom } from "@/lib/room";
|
import { joinRoom } from "@/lib/room";
|
||||||
import { getSceneConfig } from "@/lib/sceneConfig";
|
import { getSceneConfig } from "@/lib/sceneConfig";
|
||||||
|
import { useToast } from "@/hooks/useToast";
|
||||||
|
|
||||||
export default function RoomPage() {
|
export default function RoomPage() {
|
||||||
const params = useParams<{ id: string }>();
|
const params = useParams<{ id: string }>();
|
||||||
@@ -22,6 +23,7 @@ export default function RoomPage() {
|
|||||||
const [joinFailed, setJoinFailed] = useState(false);
|
const [joinFailed, setJoinFailed] = useState(false);
|
||||||
const [showLeaveConfirm, setShowLeaveConfirm] = useState(false);
|
const [showLeaveConfirm, setShowLeaveConfirm] = useState(false);
|
||||||
const leavingRef = useRef(false);
|
const leavingRef = useRef(false);
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts,
|
userCount, match, matchType, matchLikes, runnerUps, likeCounts, swipeCounts,
|
||||||
@@ -74,22 +76,38 @@ export default function RoomPage() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleReset = useCallback(async () => {
|
const handleReset = useCallback(async () => {
|
||||||
await fetch(`/api/room/${roomId}/reset`, {
|
try {
|
||||||
method: "POST",
|
const res = await fetch(`/api/room/${roomId}/reset`, {
|
||||||
headers: { "Content-Type": "application/json" },
|
method: "POST",
|
||||||
body: JSON.stringify({ userId }),
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
body: JSON.stringify({ userId }),
|
||||||
await mutate();
|
});
|
||||||
}, [roomId, userId, mutate]);
|
if (!res.ok) {
|
||||||
|
const data = await res.json().catch(() => ({}));
|
||||||
|
throw new Error(data.error || "重置失败");
|
||||||
|
}
|
||||||
|
await mutate();
|
||||||
|
} catch (e) {
|
||||||
|
toast.show(e instanceof Error ? e.message : "重置失败");
|
||||||
|
}
|
||||||
|
}, [roomId, userId, mutate, toast]);
|
||||||
|
|
||||||
const handleNarrow = useCallback(async (restaurantIds: string[]) => {
|
const handleNarrow = useCallback(async (restaurantIds: string[]) => {
|
||||||
await fetch(`/api/room/${roomId}/reset`, {
|
try {
|
||||||
method: "POST",
|
const res = await fetch(`/api/room/${roomId}/reset`, {
|
||||||
headers: { "Content-Type": "application/json" },
|
method: "POST",
|
||||||
body: JSON.stringify({ userId, restaurantIds }),
|
headers: { "Content-Type": "application/json" },
|
||||||
});
|
body: JSON.stringify({ userId, restaurantIds }),
|
||||||
await mutate();
|
});
|
||||||
}, [roomId, userId, mutate]);
|
if (!res.ok) {
|
||||||
|
const data = await res.json().catch(() => ({}));
|
||||||
|
throw new Error(data.error || "缩小范围失败");
|
||||||
|
}
|
||||||
|
await mutate();
|
||||||
|
} catch (e) {
|
||||||
|
toast.show(e instanceof Error ? e.message : "操作失败");
|
||||||
|
}
|
||||||
|
}, [roomId, userId, mutate, toast]);
|
||||||
|
|
||||||
if (notFound || joinFailed) {
|
if (notFound || joinFailed) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import type { Restaurant } from "@/types";
|
|||||||
|
|
||||||
export function buildNavUrl(restaurant: Restaurant): string {
|
export function buildNavUrl(restaurant: Restaurant): string {
|
||||||
if (restaurant.location) {
|
if (restaurant.location) {
|
||||||
const [lng, lat] = restaurant.location.split(",");
|
const parts = restaurant.location.split(",");
|
||||||
return `https://uri.amap.com/marker?position=${lng},${lat}&name=${encodeURIComponent(restaurant.name)}&callnative=1`;
|
if (parts.length === 2 && parts[0] && parts[1]) {
|
||||||
|
return `https://uri.amap.com/marker?position=${parts[0]},${parts[1]}&name=${encodeURIComponent(restaurant.name)}&callnative=1`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name)}`;
|
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(restaurant.name)}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user