Files
Alfred/src/web/hooks/use-providers.ts
lanyuanxiaoyao 10b3928bee refactor: 代码审查修复 — 错误边界、DRY抽取、测试修复、合规性改进
- P1: server.ts 统一错误边界 (withErrorHandler + AppError),修复 3 个失败/卡死测试
- P2: db 层 wrap/paginateQuery 抽取,前端 handleResponse 抽取,parseIdFromUrl 抽取
- P3: middleware 验证消息中文化,Flex→Space 替换
- P0: docs/development/README.md 新增已知设计决策章节
- P3-11 setup 拆分已尝试回退(@testing-library/react preload 依赖无法拆分)
- P3-13 config 层测试从本次变更移除
2026-05-29 22:27:56 +08:00

147 lines
4.7 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type {
CreateProviderRequest,
Provider,
ProviderListResponse,
ProviderOptionsResponse,
ProviderResponse,
ProviderTestResponse,
ProviderTestResultResponse,
UpdateProviderRequest,
} from "../../shared/api";
import { handleResponse, handleVoidResponse } from "../utils/api";
const PROVIDERS_KEY = ["providers"] as const;
const MODELS_KEY = ["models"] as const;
export async function createProvider(data: CreateProviderRequest): Promise<Provider> {
const response = await fetch("/api/providers", {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
method: "POST",
});
return handleResponse(response, (data) => (data as ProviderResponse).provider);
}
export async function deleteProvider(id: string): Promise<void> {
const response = await fetch(`/api/providers/${id}`, { method: "DELETE" });
return handleVoidResponse(response);
}
export async function fetchProvider(id: string): Promise<Provider> {
const response = await fetch(`/api/providers/${id}`);
return handleResponse(response, (data) => (data as ProviderResponse).provider);
}
export async function fetchProviderList(params: {
keyword?: string;
page?: number;
pageSize?: number;
}): Promise<ProviderListResponse> {
const searchParams = new URLSearchParams();
if (params.page) searchParams.set("page", String(params.page));
if (params.pageSize) searchParams.set("pageSize", String(params.pageSize));
if (params.keyword) searchParams.set("keyword", params.keyword);
const qs = searchParams.toString();
const url = `/api/providers${qs ? `?${qs}` : ""}`;
const response = await fetch(url);
if (!response.ok) {
const body = (await response.json().catch(() => null)) as null | { error?: string };
throw new Error(body?.error ?? `HTTP ${response.status}`);
}
return response.json() as Promise<ProviderListResponse>;
}
export async function fetchProviderOptions(): Promise<ProviderOptionsResponse> {
const response = await fetch("/api/providers/options");
if (!response.ok) {
const body = (await response.json().catch(() => null)) as null | { error?: string };
throw new Error(body?.error ?? `HTTP ${response.status}`);
}
return response.json() as Promise<ProviderOptionsResponse>;
}
export async function testProviderConfig(data: CreateProviderRequest): Promise<ProviderTestResponse> {
const response = await fetch("/api/providers/test", {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
method: "POST",
});
if (!response.ok) {
const body = (await response.json().catch(() => null)) as null | { error?: string };
throw new Error(body?.error ?? `HTTP ${response.status}`);
}
const result = (await response.json()) as ProviderTestResultResponse;
return result.providerTestResponse;
}
export async function updateProvider(id: string, data: UpdateProviderRequest): Promise<Provider> {
const response = await fetch(`/api/providers/${id}`, {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
method: "PATCH",
});
return handleResponse(response, (data) => (data as ProviderResponse).provider);
}
export function useCreateProvider() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createProvider,
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
},
});
}
export function useDeleteProvider() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: deleteProvider,
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
void queryClient.invalidateQueries({ queryKey: MODELS_KEY });
},
});
}
export function useProvider(id: string) {
return useQuery({
enabled: !!id,
queryFn: () => fetchProvider(id),
queryKey: [...PROVIDERS_KEY, "detail", id],
});
}
export function useProviderList(params: { keyword?: string; page?: number; pageSize?: number }) {
return useQuery({
queryFn: () => fetchProviderList(params),
queryKey: [...PROVIDERS_KEY, "list", params],
});
}
export function useProviderOptions() {
return useQuery({
queryFn: fetchProviderOptions,
queryKey: [...PROVIDERS_KEY, "options"],
});
}
export function useTestProviderConfig() {
return useMutation({
mutationFn: testProviderConfig,
});
}
export function useUpdateProvider() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (args: { data: UpdateProviderRequest; id: string }) => updateProvider(args.id, args.data),
onSuccess: () => {
void queryClient.invalidateQueries({ queryKey: PROVIDERS_KEY });
},
});
}