import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { Reflector } from '@nestjs/core'; // 响应格式装饰器 export const RESPONSE_MESSAGE_KEY = 'response_message'; export const ResponseMessage = (message: string) => Reflector.createDecorator()[RESPONSE_MESSAGE_KEY](message); // 跳过响应包装装饰器 export const SKIP_RESPONSE_WRAP_KEY = 'skip_response_wrap'; export const SkipResponseWrap = () => Reflector.createDecorator()[SKIP_RESPONSE_WRAP_KEY](true); // 标准响应格式接口 export interface ApiResponse { success: boolean; code: number; data: T; msg: string; timestamp?: string; path?: string; requestId?: string; } @Injectable() export class ResponseInterceptor implements NestInterceptor> { constructor(private readonly reflector: Reflector) {} intercept(context: ExecutionContext, next: CallHandler): Observable> { const request = context.switchToHttp().getRequest(); const response = context.switchToHttp().getResponse(); // 检查是否跳过响应包装 const skipWrap = this.reflector.getAllAndOverride( SKIP_RESPONSE_WRAP_KEY, [context.getHandler(), context.getClass()], ); if (skipWrap) { return next.handle(); } // 获取自定义响应消息 const message = this.reflector.getAllAndOverride( RESPONSE_MESSAGE_KEY, [context.getHandler(), context.getClass()], ); return next.handle().pipe( map((data) => { // 如果数据已经是标准格式,直接返回 if (this.isApiResponse(data)) { return { ...data, timestamp: new Date().toISOString(), path: request.url, requestId: request.headers['x-request-id'] || request.headers['request-id'], }; } // 包装成标准响应格式 const result: ApiResponse = { success: true, code: response.statusCode || 200, data: data, msg: message || this.getDefaultMessage(response.statusCode), timestamp: new Date().toISOString(), path: request.url, requestId: request.headers['x-request-id'] || request.headers['request-id'], }; return result; }), ); } /** * 检查数据是否已经是API响应格式 */ private isApiResponse(data: any): data is ApiResponse { return ( data && typeof data === 'object' && 'success' in data && 'code' in data && 'data' in data && 'msg' in data ); } /** * 根据状态码获取默认消息 */ private getDefaultMessage(statusCode: number): string { switch (statusCode) { case 200: return '操作成功'; case 201: return '创建成功'; case 204: return '操作成功'; case 400: return '请求参数错误'; case 401: return '未授权访问'; case 403: return '禁止访问'; case 404: return '资源不存在'; case 409: return '资源冲突'; case 422: return '请求参数验证失败'; case 500: return '服务器内部错误'; default: return '操作完成'; } } }