export type ErrorType = | 'validation' | 'not_found' | 'permission_denied' | 'conflict' | 'business' | 'database' | 'internal'; export interface ErrorDetails { [key: string]: unknown; } export abstract class ApplicationError extends Error { abstract readonly type: ErrorType; abstract readonly statusCode: number; readonly code: string; readonly details?: ErrorDetails; // FIX #1: traceId 改为可写,以便 GlobalErrorFilter 注入请求级 traceId traceId?: string; constructor(message: string, code: string, details?: ErrorDetails) { super(message); this.name = this.constructor.name; this.code = code; this.details = details; } toJSON(): Record { return { success: false, error: { code: this.code, message: this.message, details: this.details, traceId: this.traceId, }, }; } } export class ValidationError extends ApplicationError { readonly type = 'validation' as const; readonly statusCode = 400; constructor(message: string, details?: ErrorDetails) { super(message, 'MSG_VALIDATION_ERROR', details); } } export class NotFoundError extends ApplicationError { readonly type = 'not_found' as const; readonly statusCode = 404; constructor(resource: string, id: string) { super(`${resource} not found: ${id}`, 'MSG_NOT_FOUND', { resource, id }); } } export class PermissionDeniedError extends ApplicationError { readonly type = 'permission_denied' as const; readonly statusCode = 403; constructor(permission: string) { super(`Permission denied: ${permission}`, 'MSG_PERMISSION_DENIED', { permission }); } } export class ConflictError extends ApplicationError { readonly type = 'conflict' as const; readonly statusCode = 409; constructor(message: string, details?: ErrorDetails) { super(message, 'MSG_CONFLICT', details); } } export class BusinessError extends ApplicationError { readonly type = 'business' as const; readonly statusCode = 422; constructor(message: string, details?: ErrorDetails) { super(message, 'MSG_BUSINESS_ERROR', details); } } export class DatabaseError extends ApplicationError { readonly type = 'database' as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, 'MSG_DATABASE_ERROR', details); } } export class InternalError extends ApplicationError { readonly type = 'internal' as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, 'MSG_INTERNAL_ERROR', details); } }