feat:代码优化与Mock数据清理

This commit is contained in:
liailing1026
2026-01-30 15:27:00 +08:00
parent 6e4d8f0b6d
commit 1682a8892d
24 changed files with 1364 additions and 3193 deletions

167
frontend/src/utils/retry.ts Normal file
View File

@@ -0,0 +1,167 @@
/**
* 重试工具函数
* @description 提供通用的重试机制,支持指数退避和自定义重试条件
*/
/**
* 重试选项配置
*/
export interface RetryOptions {
/** 最大重试次数,默认 3 */
maxRetries?: number
/** 初始延迟时间(毫秒),默认 2000 */
initialDelayMs?: number
/** 是否使用指数退避,默认 true */
useExponentialBackoff?: boolean
/** 最大延迟时间(毫秒),默认 30000 */
maxDelayMs?: number
/** 自定义重试条件函数,返回 true 表示应该重试 */
shouldRetry?: (error: any, attempt: number) => boolean
/** 重试前的回调函数 */
onRetry?: (error: any, attempt: number, delay: number) => void
}
/**
* 默认重试条件 - 检查是否是 rate limiting 错误
* @param error 错误对象
* @returns 是否应该重试
*/
export function defaultShouldRetry(error: any): boolean {
if (!error) return false
const message = error?.message || String(error)
// 检查 406 Not Acceptable、429 Too Many Requests 等 rate limiting 错误
const isRateLimit =
message.includes('406') ||
message.includes('429') ||
message.includes('Not Acceptable') ||
message.includes('Too Many Requests') ||
message.includes('rate limit')
return isRateLimit
}
/**
* 带重试机制的异步函数执行器
*
* @example
* ```typescript
* // 基本用法
* const result = await withRetry(() => api.someRequest())
*
* // 自定义重试次数和延迟
* const result = await withRetry(() => api.request(), {
* maxRetries: 5,
* initialDelayMs: 1000,
* useExponentialBackoff: true
* })
*
* // 自定义重试条件
* const result = await withRetry(() => api.request(), {
* shouldRetry: (error) => error?.code === 'NETWORK_ERROR'
* })
*
* // 带重试回调
* const result = await withRetry(() => api.request(), {
* onRetry: (error, attempt, delay) => {
* console.log(`第 ${attempt} 次重试,等待 ${delay}ms`, error.message)
* }
* })
* ```
*
* @param fn - 要执行的异步函数
* @param options - 重试选项配置
* @returns Promise resolves with the result of the function
* @throws 如果重试次数用尽,抛出最后一次的错误
*/
export async function withRetry<T>(
fn: () => Promise<T>,
options: RetryOptions = {},
): Promise<T> {
const {
maxRetries = 3,
initialDelayMs = 2000,
useExponentialBackoff = true,
maxDelayMs = 30000,
shouldRetry = defaultShouldRetry,
onRetry,
} = options
let lastError: any = null
let currentDelay = initialDelayMs
for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
try {
// 执行目标函数
return await fn()
} catch (error: any) {
lastError = error
// 判断是否应该重试
const shouldRetryAttempt = shouldRetry(error, attempt)
// 如果是最后一次尝试 或者 不应该重试,则抛出错误
if (attempt > maxRetries || !shouldRetryAttempt) {
if (attempt > maxRetries) {
console.error(
`❌ [withRetry] 已达到最大重试次数 (${maxRetries}),放弃请求`,
)
}
throw error
}
// 执行重试回调
if (onRetry) {
onRetry(error, attempt, currentDelay)
}
// 打印日志
console.log(
`⏳ [withRetry] 第 ${attempt} 次重试,等待 ${currentDelay}ms...`,
error?.message || String(error),
)
// 等待延迟时间
await new Promise((resolve) => setTimeout(resolve, currentDelay))
// 计算下一次延迟时间(指数退避)
if (useExponentialBackoff) {
currentDelay = Math.min(currentDelay * 2, maxDelayMs)
}
}
}
//理论上不会到达这里,因为循环内会 throw
throw lastError
}
/**
* 简化的重试装饰器 - 适用于类方法
*
* @example
* ```typescript
* class MyService {
* @retryable({ maxRetries: 3, initialDelayMs: 1000 })
* async fetchData() {
* return await api.request()
* }
* }
* ```
*
* @param options - 重试选项配置
* @returns 装饰器函数
*/
export function retryable(options: RetryOptions = {}) {
return function <T extends (...args: any[]) => Promise<any>>(
_target: any,
_propertyKey: string,
descriptor: TypedPropertyDescriptor<T>,
) {
const originalMethod = descriptor.value
if (!originalMethod) return descriptor
descriptor.value = function (this: any, ...args: Parameters<T>) {
return withRetry(() => originalMethod.apply(this, args), options)
} as T
return descriptor
} as MethodDecorator
}

View File

@@ -264,6 +264,24 @@ class WebSocketClient {
get id(): string | undefined {
return this.socket?.id
}
/**
* 监听事件
*/
on(event: string, callback: (...args: any[]) => void): void {
if (this.socket) {
this.socket.on(event, callback)
}
}
/**
* 取消监听事件
*/
off(event: string, callback?: (...args: any[]) => void): void {
if (this.socket) {
this.socket.off(event, callback)
}
}
}
// 导出单例