- 新增auth模块处理认证逻辑 - 新增security模块处理安全相关功能 - 新增user-mgmt模块管理用户相关操作 - 新增shared模块存放共享组件 - 重构admin模块,添加DTO和Guards - 为admin模块添加测试文件结构
179 lines
4.4 KiB
TypeScript
179 lines
4.4 KiB
TypeScript
/**
|
|
* 超时拦截器
|
|
*
|
|
* 功能描述:
|
|
* - 实现API接口的超时控制逻辑
|
|
* - 在超时时自动取消请求并返回错误
|
|
* - 记录超时事件的详细日志
|
|
*
|
|
* 使用场景:
|
|
* - 全局超时控制
|
|
* - 防止资源泄漏
|
|
* - 提升系统稳定性
|
|
*
|
|
* @author kiro-ai
|
|
* @version 1.0.0
|
|
* @since 2025-12-24
|
|
*/
|
|
|
|
import {
|
|
Injectable,
|
|
NestInterceptor,
|
|
ExecutionContext,
|
|
CallHandler,
|
|
RequestTimeoutException,
|
|
Logger
|
|
} from '@nestjs/common';
|
|
import { Reflector } from '@nestjs/core';
|
|
import { Observable, throwError, TimeoutError } from 'rxjs';
|
|
import { catchError, timeout } from 'rxjs/operators';
|
|
import { TIMEOUT_KEY, TimeoutConfig } from '../decorators/timeout.decorator';
|
|
|
|
/**
|
|
* 超时响应接口
|
|
*/
|
|
interface TimeoutResponse {
|
|
/** 请求是否成功 */
|
|
success: boolean;
|
|
/** 响应消息 */
|
|
message: string;
|
|
/** 错误代码 */
|
|
error_code: string;
|
|
/** 超时信息 */
|
|
timeout_info: {
|
|
/** 超时时间(毫秒) */
|
|
timeout_ms: number;
|
|
/** 超时发生时间 */
|
|
timestamp: string;
|
|
};
|
|
}
|
|
|
|
@Injectable()
|
|
export class TimeoutInterceptor implements NestInterceptor {
|
|
private readonly logger = new Logger(TimeoutInterceptor.name);
|
|
|
|
constructor(private readonly reflector: Reflector) {}
|
|
|
|
/**
|
|
* 拦截器处理函数
|
|
*
|
|
* 业务逻辑:
|
|
* 1. 获取超时配置
|
|
* 2. 应用超时控制
|
|
* 3. 处理超时异常
|
|
* 4. 记录超时日志
|
|
*
|
|
* @param context 执行上下文
|
|
* @param next 调用处理器
|
|
* @returns 可观察对象
|
|
*/
|
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
// 1. 获取超时配置
|
|
const timeoutConfig = this.getTimeoutConfig(context);
|
|
|
|
if (!timeoutConfig) {
|
|
// 没有配置超时,直接执行
|
|
return next.handle();
|
|
}
|
|
|
|
// 2. 获取请求信息用于日志记录
|
|
const request = context.switchToHttp().getRequest();
|
|
const startTime = Date.now();
|
|
|
|
// 3. 应用超时控制
|
|
return next.handle().pipe(
|
|
timeout(timeoutConfig.timeout),
|
|
catchError((error) => {
|
|
if (error instanceof TimeoutError) {
|
|
// 4. 处理超时异常
|
|
const duration = Date.now() - startTime;
|
|
|
|
// 5. 记录超时日志
|
|
if (timeoutConfig.logTimeout !== false) {
|
|
this.logger.warn('请求超时', {
|
|
operation: 'request_timeout',
|
|
method: request.method,
|
|
url: request.url,
|
|
timeout_ms: timeoutConfig.timeout,
|
|
actual_duration_ms: duration,
|
|
userAgent: request.get('User-Agent'),
|
|
ip: request.ip,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
}
|
|
|
|
// 6. 构建超时响应
|
|
const timeoutResponse: TimeoutResponse = {
|
|
success: false,
|
|
message: timeoutConfig.message || '请求超时,请稍后重试',
|
|
error_code: 'REQUEST_TIMEOUT',
|
|
timeout_info: {
|
|
timeout_ms: timeoutConfig.timeout,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
};
|
|
|
|
// 7. 抛出超时异常
|
|
return throwError(() => new RequestTimeoutException(timeoutResponse));
|
|
}
|
|
|
|
// 其他异常直接抛出
|
|
return throwError(() => error);
|
|
})
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 获取超时配置
|
|
*
|
|
* @param context 执行上下文
|
|
* @returns 超时配置或null
|
|
*/
|
|
private getTimeoutConfig(context: ExecutionContext): TimeoutConfig | null {
|
|
// 从方法装饰器获取配置
|
|
const methodConfig = this.reflector.get<TimeoutConfig>(
|
|
TIMEOUT_KEY,
|
|
context.getHandler()
|
|
);
|
|
|
|
if (methodConfig) {
|
|
return methodConfig;
|
|
}
|
|
|
|
// 从类装饰器获取配置
|
|
const classConfig = this.reflector.get<TimeoutConfig>(
|
|
TIMEOUT_KEY,
|
|
context.getClass()
|
|
);
|
|
|
|
return classConfig || null;
|
|
}
|
|
|
|
/**
|
|
* 获取默认超时配置
|
|
*
|
|
* @returns 默认超时配置
|
|
*/
|
|
private getDefaultTimeoutConfig(): TimeoutConfig {
|
|
return {
|
|
timeout: 30000, // 默认30秒
|
|
message: '请求超时,请稍后重试',
|
|
logTimeout: true
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 验证超时配置
|
|
*
|
|
* @param config 超时配置
|
|
* @returns 是否有效
|
|
*/
|
|
private isValidTimeoutConfig(config: TimeoutConfig): boolean {
|
|
return (
|
|
config &&
|
|
typeof config.timeout === 'number' &&
|
|
config.timeout > 0 &&
|
|
config.timeout <= 600000 // 最大10分钟
|
|
);
|
|
}
|
|
} |