refactor(P1): 5 项代码质量改进 — 消除重复、拆分巨型组件、统一基础设施
Task 4: 统一 amap.ts 为完整 API 客户端 - 扩展 amap.ts 为统一客户端(amapFetch 8s 超时 + 错误处理) - 导出 searchPlaceText/searchPlaceAround/getInputTips/reverseGeocode/getTransitDirection - 精简 4 个 location route 为单行调用,blindboxPlanGen 删除 ~80 行内联 API 代码 Task 2: 抽取 ShareCardShell 消除三兄弟重复 - 新建 ShareCardShell.tsx 共享外框/背景/品牌头/QR 底部 - RestaurantShareCard 406→268 行,BlindboxShareCard 341→173 行,BlindboxPlanShareCard 277→159 行 Task 3: 拆分 BlindboxPlan.tsx (742→371 行) - 提取 planUtils.ts (guessCategory + formatDuration) - 提取 PoiSearchField / SortablePlanItem / PlanItemEditModal 三个独立组件 Task 1: 拆分 blindbox/[code]/page.tsx 上帝组件 (1300→509 行) - 提取 useBlindboxRoom / useBlindboxIdeas / useBlindboxPlan / useBlindboxDraw 四个 hooks - 提取 BlindboxPoolPhase / BlindboxRevealPhase 两个子组件 - 主页面仅保留 phase 协调 + hook 组装 + 子组件渲染 Task 5: 统一 SWR 数据获取层 - 新建 fetcher.ts (FetchError 携带 status,401 不重试) - 新建 useBlindboxRooms / useAchievements / useFavorites SWR hooks - useRoomPolling 改用共享 fetcher - blindbox 大厅/成就/个人中心页面删除手写 fetch 样板代码 - JWT 过期时自动弹出登录框而非反复重试
This commit is contained in:
@@ -3,13 +3,11 @@ import { createRequest, parseJsonResponse } from "@/__tests__/helpers/api-test-u
|
||||
|
||||
vi.mock("@/lib/prisma", () => ({ prisma: {} }));
|
||||
|
||||
const mockSearchPlaceText = vi.fn();
|
||||
vi.mock("@/lib/amap", () => ({
|
||||
requireAmapApiKey: vi.fn().mockReturnValue("test-key"),
|
||||
searchPlaceText: (...args: unknown[]) => mockSearchPlaceText(...args),
|
||||
}));
|
||||
|
||||
const mockFetch = vi.fn();
|
||||
vi.stubGlobal("fetch", mockFetch);
|
||||
|
||||
import { GET } from "./route";
|
||||
|
||||
const mockCtx = { params: Promise.resolve({}) };
|
||||
@@ -20,21 +18,17 @@ beforeEach(() => {
|
||||
|
||||
describe("GET /api/location/search", () => {
|
||||
it("returns search results", async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () =>
|
||||
Promise.resolve({
|
||||
status: "1",
|
||||
pois: [
|
||||
{
|
||||
id: "poi-1",
|
||||
name: "星巴克",
|
||||
address: "南京路1号",
|
||||
location: "121.4,31.2",
|
||||
business: { rating: "4.5", cost: "40" },
|
||||
},
|
||||
],
|
||||
}),
|
||||
});
|
||||
mockSearchPlaceText.mockResolvedValue([
|
||||
{
|
||||
id: "poi-1",
|
||||
name: "星巴克",
|
||||
address: "南京路1号",
|
||||
lat: 31.2,
|
||||
lng: 121.4,
|
||||
rating: 4.5,
|
||||
cost: 40,
|
||||
},
|
||||
]);
|
||||
|
||||
const req = createRequest("/api/location/search?keywords=星巴克");
|
||||
const res = await GET(req, mockCtx);
|
||||
@@ -48,9 +42,7 @@ describe("GET /api/location/search", () => {
|
||||
});
|
||||
|
||||
it("returns empty when no results", async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
json: () => Promise.resolve({ status: "1", pois: [] }),
|
||||
});
|
||||
mockSearchPlaceText.mockResolvedValue([]);
|
||||
|
||||
const req = createRequest("/api/location/search?keywords=不存在的地方");
|
||||
const res = await GET(req, mockCtx);
|
||||
@@ -65,7 +57,8 @@ describe("GET /api/location/search", () => {
|
||||
});
|
||||
|
||||
it("returns 503 when API unavailable", async () => {
|
||||
mockFetch.mockRejectedValue(new Error("network error"));
|
||||
const { ApiError } = await import("@/lib/api");
|
||||
mockSearchPlaceText.mockRejectedValue(new ApiError("位置服务暂时不可用,请稍后重试", 503));
|
||||
|
||||
const req = createRequest("/api/location/search?keywords=test");
|
||||
const res = await GET(req, mockCtx);
|
||||
|
||||
Reference in New Issue
Block a user