- 新增API密钥创建表单界面 - 实现API密钥名称输入验证 - 添加表单提交和取消功能 - 集成密钥管理弹窗组件 - 支持密钥信息的双向数据绑定 - 添加表单校验规则和错误提示 - 实现密钥创建成功后的消息通知 - 提供密钥管理模块的基础架构支持
353 lines
8.1 KiB
TypeScript
353 lines
8.1 KiB
TypeScript
/**
|
|
* 增强的 API 请求工具
|
|
*/
|
|
import axios, { AxiosRequestConfig, AxiosError } from 'axios';
|
|
import { apiPerformanceMonitor } from './performance';
|
|
import { memoryCache } from './cache-manager';
|
|
import { getToken } from './token-util';
|
|
import { API_BASE_URL, TOKEN_HEADER_NAME } from '@/config/setting';
|
|
|
|
// 请求配置接口
|
|
interface EnhancedRequestConfig extends AxiosRequestConfig {
|
|
// 缓存配置
|
|
cache?: {
|
|
enabled: boolean;
|
|
expiry?: number;
|
|
key?: string;
|
|
tags?: string[];
|
|
};
|
|
// 重试配置
|
|
retry?: {
|
|
times: number;
|
|
delay: number;
|
|
condition?: (error: AxiosError) => boolean;
|
|
};
|
|
// 性能监控
|
|
performance?: boolean;
|
|
// 请求去重
|
|
dedupe?: boolean;
|
|
// 超时重试
|
|
timeoutRetry?: boolean;
|
|
}
|
|
|
|
// 请求队列管理
|
|
class RequestQueue {
|
|
private pendingRequests = new Map<string, Promise<any>>();
|
|
|
|
// 生成请求键
|
|
private generateKey(config: AxiosRequestConfig): string {
|
|
const { method, url, params, data } = config;
|
|
return `${method}_${url}_${JSON.stringify(params)}_${JSON.stringify(data)}`;
|
|
}
|
|
|
|
// 添加请求到队列
|
|
add<T>(config: AxiosRequestConfig, executor: () => Promise<T>): Promise<T> {
|
|
const key = this.generateKey(config);
|
|
|
|
if (this.pendingRequests.has(key)) {
|
|
return this.pendingRequests.get(key);
|
|
}
|
|
|
|
const promise = executor().finally(() => {
|
|
this.pendingRequests.delete(key);
|
|
});
|
|
|
|
this.pendingRequests.set(key, promise);
|
|
return promise;
|
|
}
|
|
|
|
// 清除队列
|
|
clear(): void {
|
|
this.pendingRequests.clear();
|
|
}
|
|
}
|
|
|
|
// 重试机制
|
|
class RetryManager {
|
|
static async retry<T>(
|
|
fn: () => Promise<T>,
|
|
config: { times: number; delay: number; condition?: (error: any) => boolean }
|
|
): Promise<T> {
|
|
let lastError: any;
|
|
|
|
for (let i = 0; i <= config.times; i++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
lastError = error;
|
|
|
|
// 检查是否应该重试
|
|
if (i < config.times && (!config.condition || config.condition(error as AxiosError))) {
|
|
await this.delay(config.delay * Math.pow(2, i)); // 指数退避
|
|
console.warn(`请求重试 ${i + 1}/${config.times}:`, error);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
|
|
private static delay(ms: number): Promise<void> {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
}
|
|
|
|
// 增强的请求类
|
|
export class EnhancedRequest {
|
|
private instance = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
timeout: 30000
|
|
});
|
|
|
|
private requestQueue = new RequestQueue();
|
|
|
|
constructor() {
|
|
this.setupInterceptors();
|
|
}
|
|
|
|
private setupInterceptors() {
|
|
// 请求拦截器
|
|
this.instance.interceptors.request.use(
|
|
(config) => {
|
|
// 添加认证头
|
|
const token = getToken();
|
|
if (token && config.headers) {
|
|
config.headers[TOKEN_HEADER_NAME] = token;
|
|
}
|
|
|
|
// 添加请求时间戳
|
|
(config as any).startTime = Date.now();
|
|
|
|
return config;
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// 响应拦截器
|
|
this.instance.interceptors.response.use(
|
|
(response) => {
|
|
// 记录性能数据
|
|
const config = response.config as any;
|
|
if (config.startTime) {
|
|
const duration = Date.now() - config.startTime;
|
|
apiPerformanceMonitor.recordApiCall(config.url, duration);
|
|
}
|
|
|
|
return response;
|
|
},
|
|
(error) => {
|
|
// 记录错误的性能数据
|
|
const config = error.config as any;
|
|
if (config && config.startTime) {
|
|
const duration = Date.now() - config.startTime;
|
|
apiPerformanceMonitor.recordApiCall(config.url, duration);
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
}
|
|
|
|
// 通用请求方法
|
|
async request<T = any>(config: EnhancedRequestConfig): Promise<T> {
|
|
const {
|
|
cache,
|
|
retry,
|
|
performance = true,
|
|
dedupe = true,
|
|
timeoutRetry = true,
|
|
...axiosConfig
|
|
} = config;
|
|
|
|
// 生成缓存键
|
|
const cacheKey = cache?.key || this.generateCacheKey(axiosConfig);
|
|
|
|
// 尝试从缓存获取
|
|
if (cache?.enabled) {
|
|
const cachedData = memoryCache.get<T>(cacheKey);
|
|
if (cachedData !== null) {
|
|
return cachedData;
|
|
}
|
|
}
|
|
|
|
// 请求执行器
|
|
const executor = async (): Promise<T> => {
|
|
const response = await this.instance.request<T>(axiosConfig);
|
|
|
|
// 缓存响应数据
|
|
if (cache?.enabled && response.data) {
|
|
memoryCache.set(
|
|
cacheKey,
|
|
response.data,
|
|
cache.expiry,
|
|
cache.tags
|
|
);
|
|
}
|
|
|
|
return response.data;
|
|
};
|
|
|
|
// 请求去重
|
|
if (dedupe) {
|
|
return this.requestQueue.add(axiosConfig, async () => {
|
|
// 重试机制
|
|
if (retry) {
|
|
return RetryManager.retry(executor, {
|
|
...retry,
|
|
condition: retry.condition || this.shouldRetry
|
|
});
|
|
}
|
|
|
|
return executor();
|
|
});
|
|
}
|
|
|
|
// 重试机制
|
|
if (retry) {
|
|
return RetryManager.retry(executor, {
|
|
...retry,
|
|
condition: retry.condition || this.shouldRetry
|
|
});
|
|
}
|
|
|
|
return executor();
|
|
}
|
|
|
|
// GET 请求
|
|
get<T = any>(url: string, config?: EnhancedRequestConfig): Promise<T> {
|
|
return this.request<T>({
|
|
...config,
|
|
method: 'GET',
|
|
url
|
|
});
|
|
}
|
|
|
|
// POST 请求
|
|
post<T = any>(url: string, data?: any, config?: EnhancedRequestConfig): Promise<T> {
|
|
return this.request<T>({
|
|
...config,
|
|
method: 'POST',
|
|
url,
|
|
data
|
|
});
|
|
}
|
|
|
|
// PUT 请求
|
|
put<T = any>(url: string, data?: any, config?: EnhancedRequestConfig): Promise<T> {
|
|
return this.request<T>({
|
|
...config,
|
|
method: 'PUT',
|
|
url,
|
|
data
|
|
});
|
|
}
|
|
|
|
// DELETE 请求
|
|
delete<T = any>(url: string, config?: EnhancedRequestConfig): Promise<T> {
|
|
return this.request<T>({
|
|
...config,
|
|
method: 'DELETE',
|
|
url
|
|
});
|
|
}
|
|
|
|
// 批量请求
|
|
async batch<T = any>(requests: EnhancedRequestConfig[]): Promise<T[]> {
|
|
const promises = requests.map(config => this.request<T>(config));
|
|
return Promise.all(promises);
|
|
}
|
|
|
|
// 并发控制请求
|
|
async concurrent<T = any>(
|
|
requests: EnhancedRequestConfig[],
|
|
limit: number = 5
|
|
): Promise<T[]> {
|
|
const results: T[] = [];
|
|
|
|
for (let i = 0; i < requests.length; i += limit) {
|
|
const batch = requests.slice(i, i + limit);
|
|
const batchResults = await this.batch<T>(batch);
|
|
results.push(...batchResults);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
// 生成缓存键
|
|
private generateCacheKey(config: AxiosRequestConfig): string {
|
|
const { method, url, params, data } = config;
|
|
return `api_${method}_${url}_${JSON.stringify(params)}_${JSON.stringify(data)}`;
|
|
}
|
|
|
|
// 判断是否应该重试
|
|
private shouldRetry(error: AxiosError): boolean {
|
|
// 网络错误或超时错误重试
|
|
if (!error.response) {
|
|
return true;
|
|
}
|
|
|
|
// 5xx 服务器错误重试
|
|
const status = error.response.status;
|
|
return status >= 500 && status < 600;
|
|
}
|
|
|
|
// 清除缓存
|
|
clearCache(tags?: string[]): void {
|
|
if (tags) {
|
|
memoryCache.clearByTags(tags);
|
|
} else {
|
|
memoryCache.clear();
|
|
}
|
|
}
|
|
|
|
// 取消所有请求
|
|
cancelAll(): void {
|
|
this.requestQueue.clear();
|
|
}
|
|
}
|
|
|
|
// 全局实例
|
|
export const enhancedRequest = new EnhancedRequest();
|
|
|
|
// 便捷方法
|
|
export const { get, post, put, delete: del, batch, concurrent } = enhancedRequest;
|
|
|
|
// 带缓存的 GET 请求
|
|
export function cachedGet<T = any>(
|
|
url: string,
|
|
config?: Omit<EnhancedRequestConfig, 'cache'> & {
|
|
expiry?: number;
|
|
tags?: string[];
|
|
}
|
|
): Promise<T> {
|
|
const { expiry = 5 * 60 * 1000, tags, ...restConfig } = config || {};
|
|
|
|
return enhancedRequest.get<T>(url, {
|
|
...restConfig,
|
|
cache: {
|
|
enabled: true,
|
|
expiry,
|
|
tags
|
|
}
|
|
});
|
|
}
|
|
|
|
// 带重试的请求
|
|
export function retryRequest<T = any>(
|
|
config: EnhancedRequestConfig,
|
|
retryTimes: number = 3,
|
|
retryDelay: number = 1000
|
|
): Promise<T> {
|
|
return enhancedRequest.request<T>({
|
|
...config,
|
|
retry: {
|
|
times: retryTimes,
|
|
delay: retryDelay
|
|
}
|
|
});
|
|
}
|