// BFF_TEACHER_ 错误码(B5 + G14 裁决,统一 BFF_ 前缀) // 越权防御 3 类错误码(president §2.7 裁决): // - BFF_TEACHER_UNAUTHORIZED (401): x-user-id 缺失或无效 // - BFF_TEACHER_FORBIDDEN_RESOURCE (403): teacherId 与资源无归属关系(场景 A) // - BFF_TEACHER_IDENTITY_MISMATCH (403): JWT teacherId 与 body teacherId 不一致(场景 B) // 其他聚合层错误码(contract §1.5) export type ErrorType = | "validation" | "not_found" | "unauthorized" | "forbidden_resource" | "identity_mismatch" | "conflict" | "business" | "bad_gateway" | "aggregation_failed" | "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; 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, }, }; } } /** 输入参数校验失败(400) */ export class ValidationError extends ApplicationError { readonly type = "validation" as const; readonly statusCode = 400; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_VALIDATION_FAILED", details); } } /** 资源不存在(404) */ export class NotFoundError extends ApplicationError { readonly type = "not_found" as const; readonly statusCode = 404; constructor(resource: string, id: string) { super(`${resource} not found: ${id}`, "BFF_TEACHER_NOT_FOUND", { resource, id, }); } } /** x-user-id 缺失或无效(401,president §2.7) */ export class UnauthorizedError extends ApplicationError { readonly type = "unauthorized" as const; readonly statusCode = 401; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_UNAUTHORIZED", details); } } /** teacherId 与资源无归属关系(403 场景 A,president §2.7) */ export class ForbiddenResourceError extends ApplicationError { readonly type = "forbidden_resource" as const; readonly statusCode = 403; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_FORBIDDEN_RESOURCE", details); } } /** JWT teacherId 与请求 body teacherId 不一致(403 场景 B,president §2.7) */ export class IdentityMismatchError extends ApplicationError { readonly type = "identity_mismatch" as const; readonly statusCode = 403; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_IDENTITY_MISMATCH", details); } } /** 并发冲突(409) */ export class ConflictError extends ApplicationError { readonly type = "conflict" as const; readonly statusCode = 409; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_CONFLICT", details); } } /** 业务规则违反(422) */ export class BusinessError extends ApplicationError { readonly type = "business" as const; readonly statusCode = 422; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_BUSINESS_ERROR", details); } } /** 下游 gRPC 不可达(502) */ export class BadGatewayError extends ApplicationError { readonly type = "bad_gateway" as const; readonly statusCode = 502; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_UPSTREAM_UNAVAILABLE", details); } } /** 聚合多下游时部分失败且无降级数据(500) */ export class AggregationFailedError extends ApplicationError { readonly type = "aggregation_failed" as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_AGGREGATION_FAILED", details); } } /** 兜底内部错误(500) */ export class InternalError extends ApplicationError { readonly type = "internal" as const; readonly statusCode = 500; constructor(message: string, details?: ErrorDetails) { super(message, "BFF_TEACHER_INTERNAL_ERROR", details); } }