mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-25 13:45:03 +08:00
feat(api): add frontend usage API and query hooks
Add TypeScript types for usage statistics. Implement usage API with Tauri invoke calls. Add TanStack Query hooks for usage data fetching.
This commit is contained in:
+97
-47
@@ -1,33 +1,24 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import type {
|
||||
UsageSummary,
|
||||
DailyStats,
|
||||
ProviderStats,
|
||||
ModelStats,
|
||||
RequestLog,
|
||||
LogFilters,
|
||||
ModelPricing,
|
||||
ProviderLimitStatus,
|
||||
} from "@/types/usage";
|
||||
import type { UsageResult } from "@/types";
|
||||
import type { AppId } from "./types";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
export const usageApi = {
|
||||
async query(providerId: string, appId: AppId): Promise<UsageResult> {
|
||||
try {
|
||||
return await invoke("queryProviderUsage", {
|
||||
providerId: providerId,
|
||||
app: appId,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
// 提取错误消息:优先使用后端返回的错误信息
|
||||
const message =
|
||||
typeof error === "string"
|
||||
? error
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: "";
|
||||
|
||||
// 如果没有错误消息,使用国际化的默认提示
|
||||
return {
|
||||
success: false,
|
||||
error: message || i18n.t("errors.usage_query_failed"),
|
||||
};
|
||||
}
|
||||
// Provider usage script methods
|
||||
query: async (providerId: string, appId: AppId): Promise<UsageResult> => {
|
||||
return invoke("queryProviderUsage", { providerId, app: appId });
|
||||
},
|
||||
|
||||
async testScript(
|
||||
testScript: async (
|
||||
providerId: string,
|
||||
appId: AppId,
|
||||
scriptCode: string,
|
||||
@@ -36,30 +27,89 @@ export const usageApi = {
|
||||
baseUrl?: string,
|
||||
accessToken?: string,
|
||||
userId?: string,
|
||||
): Promise<UsageResult> {
|
||||
try {
|
||||
return await invoke("testUsageScript", {
|
||||
providerId: providerId,
|
||||
app: appId,
|
||||
scriptCode: scriptCode,
|
||||
timeout: timeout,
|
||||
apiKey: apiKey,
|
||||
baseUrl: baseUrl,
|
||||
accessToken: accessToken,
|
||||
userId: userId,
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const message =
|
||||
typeof error === "string"
|
||||
? error
|
||||
: error instanceof Error
|
||||
? error.message
|
||||
: "";
|
||||
): Promise<UsageResult> => {
|
||||
return invoke("testUsageScript", {
|
||||
providerId,
|
||||
app: appId,
|
||||
scriptCode,
|
||||
timeout,
|
||||
apiKey,
|
||||
baseUrl,
|
||||
accessToken,
|
||||
userId,
|
||||
});
|
||||
},
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: message || i18n.t("errors.usage_query_failed"),
|
||||
};
|
||||
}
|
||||
// Proxy usage statistics methods
|
||||
getUsageSummary: async (
|
||||
startDate?: number,
|
||||
endDate?: number,
|
||||
): Promise<UsageSummary> => {
|
||||
return invoke("get_usage_summary", { startDate, endDate });
|
||||
},
|
||||
|
||||
getUsageTrends: async (days: number): Promise<DailyStats[]> => {
|
||||
return invoke("get_usage_trends", { days });
|
||||
},
|
||||
|
||||
getProviderStats: async (): Promise<ProviderStats[]> => {
|
||||
return invoke("get_provider_stats");
|
||||
},
|
||||
|
||||
getModelStats: async (): Promise<ModelStats[]> => {
|
||||
return invoke("get_model_stats");
|
||||
},
|
||||
|
||||
getRequestLogs: async (
|
||||
filters: LogFilters,
|
||||
limit: number = 50,
|
||||
offset: number = 0,
|
||||
): Promise<RequestLog[]> => {
|
||||
return invoke("get_request_logs", {
|
||||
providerId: filters.providerId,
|
||||
model: filters.model,
|
||||
statusCode: filters.statusCode,
|
||||
startDate: filters.startDate,
|
||||
endDate: filters.endDate,
|
||||
limit,
|
||||
offset,
|
||||
});
|
||||
},
|
||||
|
||||
getRequestDetail: async (requestId: string): Promise<RequestLog | null> => {
|
||||
return invoke("get_request_detail", { requestId });
|
||||
},
|
||||
|
||||
getModelPricing: async (): Promise<ModelPricing[]> => {
|
||||
return invoke("get_model_pricing");
|
||||
},
|
||||
|
||||
updateModelPricing: async (
|
||||
modelId: string,
|
||||
displayName: string,
|
||||
inputCost: string,
|
||||
outputCost: string,
|
||||
cacheReadCost: string,
|
||||
cacheCreationCost: string,
|
||||
): Promise<void> => {
|
||||
return invoke("update_model_pricing", {
|
||||
modelId,
|
||||
displayName,
|
||||
inputCost,
|
||||
outputCost,
|
||||
cacheReadCost,
|
||||
cacheCreationCost,
|
||||
});
|
||||
},
|
||||
|
||||
deleteModelPricing: async (modelId: string): Promise<void> => {
|
||||
return invoke("delete_model_pricing", { modelId });
|
||||
},
|
||||
|
||||
checkProviderLimits: async (
|
||||
providerId: string,
|
||||
appType: string,
|
||||
): Promise<ProviderLimitStatus> => {
|
||||
return invoke("check_provider_limits", { providerId, appType });
|
||||
},
|
||||
};
|
||||
|
||||
+25
-14
@@ -1,22 +1,22 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { usageApi } from '@/lib/api/usage';
|
||||
import type { LogFilters } from '@/types/usage';
|
||||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||||
import { usageApi } from "@/lib/api/usage";
|
||||
import type { LogFilters } from "@/types/usage";
|
||||
|
||||
// Query keys
|
||||
export const usageKeys = {
|
||||
all: ['usage'] as const,
|
||||
all: ["usage"] as const,
|
||||
summary: (startDate?: number, endDate?: number) =>
|
||||
[...usageKeys.all, 'summary', startDate, endDate] as const,
|
||||
trends: (days: number) => [...usageKeys.all, 'trends', days] as const,
|
||||
providerStats: () => [...usageKeys.all, 'provider-stats'] as const,
|
||||
modelStats: () => [...usageKeys.all, 'model-stats'] as const,
|
||||
[...usageKeys.all, "summary", startDate, endDate] as const,
|
||||
trends: (days: number) => [...usageKeys.all, "trends", days] as const,
|
||||
providerStats: () => [...usageKeys.all, "provider-stats"] as const,
|
||||
modelStats: () => [...usageKeys.all, "model-stats"] as const,
|
||||
logs: (filters: LogFilters, limit: number, offset: number) =>
|
||||
[...usageKeys.all, 'logs', filters, limit, offset] as const,
|
||||
[...usageKeys.all, "logs", filters, limit, offset] as const,
|
||||
detail: (requestId: string) =>
|
||||
[...usageKeys.all, 'detail', requestId] as const,
|
||||
pricing: () => [...usageKeys.all, 'pricing'] as const,
|
||||
[...usageKeys.all, "detail", requestId] as const,
|
||||
pricing: () => [...usageKeys.all, "pricing"] as const,
|
||||
limits: (providerId: string, appType: string) =>
|
||||
[...usageKeys.all, 'limits', providerId, appType] as const,
|
||||
[...usageKeys.all, "limits", providerId, appType] as const,
|
||||
};
|
||||
|
||||
// Hooks
|
||||
@@ -51,7 +51,7 @@ export function useModelStats() {
|
||||
export function useRequestLogs(
|
||||
filters: LogFilters,
|
||||
limit: number = 50,
|
||||
offset: number = 0
|
||||
offset: number = 0,
|
||||
) {
|
||||
return useQuery({
|
||||
queryKey: usageKeys.logs(filters, limit, offset),
|
||||
@@ -100,10 +100,21 @@ export function useUpdateModelPricing() {
|
||||
params.inputCost,
|
||||
params.outputCost,
|
||||
params.cacheReadCost,
|
||||
params.cacheCreationCost
|
||||
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() });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
+8
-1
@@ -10,6 +10,7 @@ export interface TokenUsage {
|
||||
export interface RequestLog {
|
||||
requestId: string;
|
||||
providerId: string;
|
||||
providerName?: string;
|
||||
appType: string;
|
||||
model: string;
|
||||
inputTokens: number;
|
||||
@@ -41,6 +42,8 @@ export interface UsageSummary {
|
||||
totalCost: string;
|
||||
totalInputTokens: number;
|
||||
totalOutputTokens: number;
|
||||
totalCacheCreationTokens: number;
|
||||
totalCacheReadTokens: number;
|
||||
successRate: number;
|
||||
}
|
||||
|
||||
@@ -49,6 +52,10 @@ export interface DailyStats {
|
||||
requestCount: number;
|
||||
totalCost: string;
|
||||
totalTokens: number;
|
||||
totalInputTokens: number;
|
||||
totalOutputTokens: number;
|
||||
totalCacheCreationTokens: number;
|
||||
totalCacheReadTokens: number;
|
||||
}
|
||||
|
||||
export interface ProviderStats {
|
||||
@@ -87,7 +94,7 @@ export interface ProviderLimitStatus {
|
||||
monthlyExceeded: boolean;
|
||||
}
|
||||
|
||||
export type TimeRange = '7d' | '30d' | '90d';
|
||||
export type TimeRange = "1d" | "7d" | "30d";
|
||||
|
||||
export interface StatsFilters {
|
||||
timeRange: TimeRange;
|
||||
|
||||
Reference in New Issue
Block a user