// ============================================================ // API 响应通用类型 // ============================================================ /** 后端统一响应结构 */ export interface ApiResponse { /** 业务状态码:0 表示成功 */ code: number; /** 响应数据 */ data: T; /** 提示信息 */ message: string; /** 请求追踪 ID(可选,用于排查问题) */ traceId?: string; } /** 分页请求参数 */ export interface PaginationParams { page: number; pageSize: number; } /** 分页响应数据 */ export interface PaginatedData { /** 数据列表 */ list: T[]; /** 总条数 */ total: number; /** 当前页码 */ page: number; /** 每页条数 */ pageSize: number; } /** 分页响应包装 */ export type PaginatedResponse = ApiResponse>; /** 请求错误类型 */ export enum RequestErrorType { /** 网络错误(断网等) */ NETWORK = 'NETWORK', /** 请求超时 */ TIMEOUT = 'TIMEOUT', /** HTTP 状态码异常(4xx / 5xx) */ HTTP = 'HTTP', /** 业务错误(code ≠ 0) */ BUSINESS = 'BUSINESS', /** 请求被取消 */ CANCELLED = 'CANCELLED', /** 认证过期(refresh_token 失效,需重新登录) */ AUTH_EXPIRED = 'AUTH_EXPIRED', } /** 请求错误 */ export class RequestError extends Error { type: RequestErrorType; code?: number; httpStatus?: number; traceId?: string; constructor( type: RequestErrorType, message: string, options?: { code?: number; httpStatus?: number; traceId?: string }, ) { super(message); this.name = 'RequestError'; this.type = type; this.code = options?.code; this.httpStatus = options?.httpStatus; this.traceId = options?.traceId; } } // ---------- 自定义错误子类(全局统一捕获 → 事件总线触发)---------- /** * RefreshError — refresh_token 过期/无效,需重新登录。 * 全局响应拦截器自动捕获此错误并 emit AUTH_REQUIRED,调用方无需手动处理。 * * 用法:throw new RefreshError('登录已过期,请重新登录'); */ export class RefreshError extends RequestError { constructor(message = '登录已过期,请重新登录') { super(RequestErrorType.AUTH_EXPIRED, message); this.name = 'RefreshError'; } } /** 判断一个错误是否为 RefreshError(支持 instanceof 或 type 判断) */ export function isRefreshError(err: unknown): err is RefreshError { return err instanceof RefreshError || (err as RequestError)?.type === RequestErrorType.AUTH_EXPIRED; }