Files
map-client-vue/web/src/utils/error.ts
2025-10-16 12:45:05 +08:00

214 lines
4.3 KiB
TypeScript

/**
* AppError - 统一错误处理
*
* 错误层次结构:
* - AppError (基类)
* - ValidationError (验证错误)
* - NetworkError (网络错误)
* - APIError (API 错误)
* - ServiceError (服务错误)
* - StorageError (存储错误)
*/
import { logger } from './logger'
/**
* 错误代码枚举
*/
export enum ErrorCode {
// 通用错误 1xxx
UNKNOWN = 1000,
INVALID_ARGUMENT = 1001,
NOT_FOUND = 1002,
ALREADY_EXISTS = 1003,
PERMISSION_DENIED = 1004,
// 网络错误 2xxx
NETWORK_ERROR = 2000,
TIMEOUT = 2001,
CONNECTION_FAILED = 2002,
// API 错误 3xxx
API_ERROR = 3000,
API_UNAUTHORIZED = 3001,
API_RATE_LIMIT = 3002,
API_INVALID_RESPONSE = 3003,
// 服务错误 4xxx
SERVICE_ERROR = 4000,
MODEL_NOT_AVAILABLE = 4001,
STREAMING_ERROR = 4002,
TOOL_EXECUTION_ERROR = 4003,
// 存储错误 5xxx
STORAGE_ERROR = 5000,
STORAGE_QUOTA_EXCEEDED = 5001,
STORAGE_READ_ERROR = 5002,
STORAGE_WRITE_ERROR = 5003
}
/**
* 应用错误基类
*/
export class AppError extends Error {
public readonly code: ErrorCode
public readonly timestamp: Date
public readonly context?: Record<string, any>
public readonly isOperational: boolean
constructor(
message: string,
code: ErrorCode = ErrorCode.UNKNOWN,
context?: Record<string, any>,
isOperational = true
) {
super(message)
this.name = this.constructor.name
this.code = code
this.timestamp = new Date()
this.context = context
this.isOperational = isOperational
// 捕获堆栈跟踪
Error.captureStackTrace(this, this.constructor)
// 记录错误日志
this.logError()
}
private logError(): void {
logger.error(
`${this.name}: ${this.message}`,
'AppError',
this,
{
code: this.code,
context: this.context
}
)
}
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
timestamp: this.timestamp,
context: this.context,
stack: this.stack
}
}
}
/**
* 验证错误
*/
export class ValidationError extends AppError {
constructor(message: string, context?: Record<string, any>) {
super(message, ErrorCode.INVALID_ARGUMENT, context)
}
}
/**
* 网络错误
*/
export class NetworkError extends AppError {
constructor(
message: string,
code: ErrorCode = ErrorCode.NETWORK_ERROR,
context?: Record<string, any>
) {
super(message, code, context)
}
}
/**
* API 错误
*/
export class APIError extends AppError {
constructor(
message: string,
code: ErrorCode = ErrorCode.API_ERROR,
context?: Record<string, any>
) {
super(message, code, context)
}
}
/**
* 服务错误
*/
export class ServiceError extends AppError {
constructor(
message: string,
code: ErrorCode = ErrorCode.SERVICE_ERROR,
context?: Record<string, any>
) {
super(message, code, context)
}
}
/**
* 存储错误
*/
export class StorageError extends AppError {
constructor(
message: string,
code: ErrorCode = ErrorCode.STORAGE_ERROR,
context?: Record<string, any>
) {
super(message, code, context)
}
}
/**
* 错误处理器
*/
export class ErrorHandler {
/**
* 处理错误
*/
static handle(error: Error | AppError): void {
if (error instanceof AppError) {
// 应用错误
if (error.isOperational) {
// 可操作错误,显示给用户
this.showErrorMessage(error.message)
} else {
// 程序错误,记录日志
logger.error('Programmer error:', 'ErrorHandler', error)
}
} else {
// 未知错误
logger.error('Unknown error:', 'ErrorHandler', error)
this.showErrorMessage('发生了未知错误,请稍后重试')
}
}
/**
* 显示错误消息(可以集成到 UI 组件)
*/
private static showErrorMessage(message: string): void {
// TODO: 集成到 Naive UI 的 Message 组件
console.error('User Error:', message)
}
/**
* 包装异步函数,自动处理错误
*/
static async wrap<T>(
fn: () => Promise<T>,
errorMessage?: string
): Promise<T | null> {
try {
return await fn()
} catch (error) {
if (errorMessage) {
logger.error(errorMessage, 'ErrorHandler', error)
}
this.handle(error as Error)
return null
}
}
}