1+ /**
2+ * AppError - 应用错误类和错误类型定义
3+ */
4+
5+ import { t } from 'i18next'
6+ import { logger } from './LoggerService'
7+
8+ // ============================================
9+ // 错误类型枚举
10+ // ============================================
11+ export enum ErrorCategory {
12+ NETWORK = 'NETWORK' ,
13+ API = 'API' ,
14+ VALIDATION = 'VALIDATION' ,
15+ AUTHENTICATION = 'AUTH' ,
16+ AUTHORIZATION = 'FORBIDDEN' ,
17+ NOT_FOUND = 'NOT_FOUND' ,
18+ TIMEOUT = 'TIMEOUT' ,
19+ FILE_SYSTEM = 'FILE_SYSTEM' ,
20+ CONFIGURATION = 'CONFIG' ,
21+ UNKNOWN = 'UNKNOWN' ,
22+ }
23+
24+ // ============================================
25+ // 错误严重级别
26+ // ============================================
27+ export enum ErrorSeverity {
28+ LOW = 'low' ,
29+ MEDIUM = 'medium' ,
30+ HIGH = 'high' ,
31+ CRITICAL = 'critical' ,
32+ }
33+
34+ // ============================================
35+ // 错误分类到 i18n key 的映射
36+ // ============================================
37+ export const ERROR_CATEGORY_I18N_KEYS : Record < ErrorCategory , string > = {
38+ [ ErrorCategory . NETWORK ] : 'error_network' ,
39+ [ ErrorCategory . API ] : 'error_api' ,
40+ [ ErrorCategory . VALIDATION ] : 'error_validation' ,
41+ [ ErrorCategory . AUTHENTICATION ] : 'error_auth' ,
42+ [ ErrorCategory . AUTHORIZATION ] : 'error_forbidden' ,
43+ [ ErrorCategory . NOT_FOUND ] : 'error_not_found' ,
44+ [ ErrorCategory . TIMEOUT ] : 'error_timeout' ,
45+ [ ErrorCategory . FILE_SYSTEM ] : 'error_file_system' ,
46+ [ ErrorCategory . CONFIGURATION ] : 'error_config' ,
47+ [ ErrorCategory . UNKNOWN ] : 'error_unknown' ,
48+ }
49+
50+ // ============================================
51+ // 应用错误类
52+ // ============================================
53+ export class AppError extends Error {
54+ readonly category : ErrorCategory
55+ readonly severity : ErrorSeverity
56+ readonly code : string
57+ readonly context ?: Record < string , unknown >
58+ readonly timestamp : Date
59+ readonly originalError ?: Error
60+
61+ constructor (
62+ message : string ,
63+ category : ErrorCategory = ErrorCategory . UNKNOWN ,
64+ severity : ErrorSeverity = ErrorSeverity . MEDIUM ,
65+ code : string = 'UNKNOWN_ERROR' ,
66+ context ?: Record < string , unknown > ,
67+ originalError ?: Error
68+ ) {
69+ super ( message )
70+ this . name = 'AppError'
71+ this . category = category
72+ this . severity = severity
73+ this . code = code
74+ this . context = context
75+ this . timestamp = new Date ( )
76+ this . originalError = originalError
77+
78+ if ( 'captureStackTrace' in Error && typeof Error . captureStackTrace === 'function' ) {
79+ Error . captureStackTrace ( this , AppError )
80+ }
81+ }
82+
83+ getUserMessage ( ) : string {
84+ const i18nKey = `errors.${ this . code } `
85+ const i18nMessage = t ( i18nKey , { defaultValue : '' } )
86+ if ( i18nMessage ) {
87+ return i18nMessage
88+ }
89+
90+ if ( import . meta. env . DEV && ! i18nMessage ) {
91+ logger . warn ( `Missing i18n key: ${ i18nKey } ` , 'ErrorService' , { code : this . code , category : this . category } )
92+ }
93+
94+ const i18nKey2 = ERROR_CATEGORY_I18N_KEYS [ this . category ]
95+ return i18nKey2 ? t ( i18nKey2 , { defaultValue : this . message } ) : this . message
96+ }
97+
98+ shouldShowToUser ( ) : boolean {
99+ return this . severity !== ErrorSeverity . LOW
100+ }
101+
102+ shouldReport ( ) : boolean {
103+ return this . severity === ErrorSeverity . HIGH || this . severity === ErrorSeverity . CRITICAL
104+ }
105+
106+ toJSON ( ) : Record < string , unknown > {
107+ return {
108+ name : this . name ,
109+ message : this . message ,
110+ category : this . category ,
111+ severity : this . severity ,
112+ code : this . code ,
113+ context : import . meta. env . DEV ? this . context : undefined ,
114+ timestamp : this . timestamp . toISOString ( ) ,
115+ stack : import . meta. env . DEV ? this . stack : undefined ,
116+ }
117+ }
118+
119+ // ==========================================
120+ // 工厂方法
121+ // ==========================================
122+
123+ static network ( message : string , original ?: Error , context ?: Record < string , unknown > ) : AppError {
124+ return new AppError ( message , ErrorCategory . NETWORK , ErrorSeverity . HIGH , 'NETWORK_ERROR' , context , original )
125+ }
126+
127+ static api ( message : string , code : string = 'API_ERROR' , context ?: Record < string , unknown > , original ?: Error ) : AppError {
128+ return new AppError ( message , ErrorCategory . API , ErrorSeverity . HIGH , code , context , original )
129+ }
130+
131+ static validation ( message : string , field ?: string , context ?: Record < string , unknown > ) : AppError {
132+ return new AppError ( message , ErrorCategory . VALIDATION , ErrorSeverity . MEDIUM , 'VALIDATION_ERROR' , { ...context , field } )
133+ }
134+
135+ static auth ( message : string = t ( 'error_auth_failed' ) ) : AppError {
136+ return new AppError ( message , ErrorCategory . AUTHENTICATION , ErrorSeverity . HIGH , 'AUTH_ERROR' )
137+ }
138+
139+ static forbidden ( message : string = t ( 'error_no_permission' ) ) : AppError {
140+ return new AppError ( message , ErrorCategory . AUTHORIZATION , ErrorSeverity . HIGH , 'FORBIDDEN_ERROR' )
141+ }
142+
143+ static notFound ( resource : string , context ?: Record < string , unknown > ) : AppError {
144+ return new AppError (
145+ t ( 'error_resource_not_found' , { resource } ) ,
146+ ErrorCategory . NOT_FOUND ,
147+ ErrorSeverity . MEDIUM ,
148+ 'NOT_FOUND_ERROR' ,
149+ context
150+ )
151+ }
152+
153+ static timeout ( operation : string , timeoutMs : number ) : AppError {
154+ return new AppError (
155+ t ( 'error_operation_timeout' , { operation } ) ,
156+ ErrorCategory . TIMEOUT ,
157+ ErrorSeverity . HIGH ,
158+ 'TIMEOUT_ERROR' ,
159+ { timeout : timeoutMs }
160+ )
161+ }
162+
163+ static fileSystem ( message : string , original ?: Error ) : AppError {
164+ return new AppError ( message , ErrorCategory . FILE_SYSTEM , ErrorSeverity . HIGH , 'FILE_SYSTEM_ERROR' , undefined , original )
165+ }
166+
167+ static config ( message : string ) : AppError {
168+ return new AppError ( message , ErrorCategory . CONFIGURATION , ErrorSeverity . CRITICAL , 'CONFIG_ERROR' )
169+ }
170+ }
0 commit comments