import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { usageApi } from "@/lib/api/usage"; import { resolveUsageRange } from "@/lib/usageRange"; import type { LogFilters, UsageRangeSelection } from "@/types/usage"; const DEFAULT_REFETCH_INTERVAL_MS = 30000; type UsageQueryOptions = { refetchInterval?: number | false; refetchIntervalInBackground?: boolean; }; type RequestLogsQueryArgs = { filters: LogFilters; range: UsageRangeSelection; page?: number; pageSize?: number; options?: UsageQueryOptions; }; type RequestLogsKey = { preset: UsageRangeSelection["preset"]; customStartDate?: number; customEndDate?: number; appType?: string; providerName?: string; model?: string; statusCode?: number; }; // Query keys export const usageKeys = { all: ["usage"] as const, summary: ( preset: UsageRangeSelection["preset"], customStartDate: number | undefined, customEndDate: number | undefined, appType?: string, ) => [ ...usageKeys.all, "summary", preset, customStartDate ?? 0, customEndDate ?? 0, appType ?? "all", ] as const, trends: ( preset: UsageRangeSelection["preset"], customStartDate: number | undefined, customEndDate: number | undefined, appType?: string, ) => [ ...usageKeys.all, "trends", preset, customStartDate ?? 0, customEndDate ?? 0, appType ?? "all", ] as const, providerStats: ( preset: UsageRangeSelection["preset"], customStartDate: number | undefined, customEndDate: number | undefined, appType?: string, ) => [ ...usageKeys.all, "provider-stats", preset, customStartDate ?? 0, customEndDate ?? 0, appType ?? "all", ] as const, modelStats: ( preset: UsageRangeSelection["preset"], customStartDate: number | undefined, customEndDate: number | undefined, appType?: string, ) => [ ...usageKeys.all, "model-stats", preset, customStartDate ?? 0, customEndDate ?? 0, appType ?? "all", ] as const, logs: (key: RequestLogsKey, page: number, pageSize: number) => [ ...usageKeys.all, "logs", key.preset, key.customStartDate ?? 0, key.customEndDate ?? 0, key.appType ?? "", key.providerName ?? "", key.model ?? "", key.statusCode ?? -1, page, pageSize, ] as const, detail: (requestId: string) => [...usageKeys.all, "detail", requestId] as const, pricing: () => [...usageKeys.all, "pricing"] as const, limits: (providerId: string, appType: string) => [...usageKeys.all, "limits", providerId, appType] as const, }; // Hooks export function useUsageSummary( range: UsageRangeSelection, appType?: string, options?: UsageQueryOptions, ) { const effectiveAppType = appType === "all" ? undefined : appType; return useQuery({ queryKey: usageKeys.summary( range.preset, range.customStartDate, range.customEndDate, appType, ), queryFn: () => { const { startDate, endDate } = resolveUsageRange(range); return usageApi.getUsageSummary(startDate, endDate, effectiveAppType); }, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); } export function useUsageTrends( range: UsageRangeSelection, appType?: string, options?: UsageQueryOptions, ) { const effectiveAppType = appType === "all" ? undefined : appType; return useQuery({ queryKey: usageKeys.trends( range.preset, range.customStartDate, range.customEndDate, appType, ), queryFn: () => { const { startDate, endDate } = resolveUsageRange(range); return usageApi.getUsageTrends(startDate, endDate, effectiveAppType); }, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); } export function useProviderStats( range: UsageRangeSelection, appType?: string, options?: UsageQueryOptions, ) { const effectiveAppType = appType === "all" ? undefined : appType; return useQuery({ queryKey: usageKeys.providerStats( range.preset, range.customStartDate, range.customEndDate, appType, ), queryFn: () => { const { startDate, endDate } = resolveUsageRange(range); return usageApi.getProviderStats(startDate, endDate, effectiveAppType); }, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); } export function useModelStats( range: UsageRangeSelection, appType?: string, options?: UsageQueryOptions, ) { const effectiveAppType = appType === "all" ? undefined : appType; return useQuery({ queryKey: usageKeys.modelStats( range.preset, range.customStartDate, range.customEndDate, appType, ), queryFn: () => { const { startDate, endDate } = resolveUsageRange(range); return usageApi.getModelStats(startDate, endDate, effectiveAppType); }, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); } export function useRequestLogs({ filters, range, page = 0, pageSize = 20, options, }: RequestLogsQueryArgs) { const key: RequestLogsKey = { preset: range.preset, customStartDate: range.customStartDate, customEndDate: range.customEndDate, appType: filters.appType, providerName: filters.providerName, model: filters.model, statusCode: filters.statusCode, }; return useQuery({ queryKey: usageKeys.logs(key, page, pageSize), queryFn: () => { const effectiveFilters = { ...filters, ...resolveUsageRange(range) }; return usageApi.getRequestLogs(effectiveFilters, page, pageSize); }, refetchInterval: options?.refetchInterval ?? DEFAULT_REFETCH_INTERVAL_MS, // 每30秒自动刷新 refetchIntervalInBackground: options?.refetchIntervalInBackground ?? false, }); } export function useRequestDetail(requestId: string) { return useQuery({ queryKey: usageKeys.detail(requestId), queryFn: () => usageApi.getRequestDetail(requestId), enabled: !!requestId, }); } export function useModelPricing() { return useQuery({ queryKey: usageKeys.pricing(), queryFn: usageApi.getModelPricing, }); } export function useProviderLimits(providerId: string, appType: string) { return useQuery({ queryKey: usageKeys.limits(providerId, appType), queryFn: () => usageApi.checkProviderLimits(providerId, appType), enabled: !!providerId && !!appType, }); } export function useUpdateModelPricing() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (params: { modelId: string; displayName: string; inputCost: string; outputCost: string; cacheReadCost: string; cacheCreationCost: string; }) => usageApi.updateModelPricing( params.modelId, params.displayName, params.inputCost, params.outputCost, params.cacheReadCost, params.cacheCreationCost, ), onSuccess: () => { queryClient.invalidateQueries({ queryKey: usageKeys.pricing() }); }, }); } export function useDeleteModelPricing() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (modelId: string) => usageApi.deleteModelPricing(modelId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: usageKeys.pricing() }); }, }); }