fix(admin-service): 修复维护拦截器路径检测和错误处理

问题:添加系统维护检测后站内通知功能失效

修复:
1. 使用 request.url 获取完整路径(包含 /api/v1 前缀)
2. 同时支持带前缀和不带前缀的路径检测
3. 添加 try-catch 错误处理,数据库错误时放行请求而非阻断
4. 添加日志记录便于调试

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-01-02 05:01:32 -08:00
parent c392142562
commit fea0b42223
1 changed files with 56 additions and 34 deletions

View File

@ -6,6 +6,7 @@ import {
HttpException,
HttpStatus,
Inject,
Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import {
@ -19,6 +20,8 @@ import {
*/
@Injectable()
export class MaintenanceInterceptor implements NestInterceptor {
private readonly logger = new Logger(MaintenanceInterceptor.name);
constructor(
@Inject(SYSTEM_MAINTENANCE_REPOSITORY)
private readonly maintenanceRepo: ISystemMaintenanceRepository,
@ -28,46 +31,65 @@ export class MaintenanceInterceptor implements NestInterceptor {
context: ExecutionContext,
next: CallHandler,
): Promise<Observable<any>> {
const request = context.switchToHttp().getRequest();
const path = request.path;
try {
const request = context.switchToHttp().getRequest();
// 获取请求路径,优先使用 url去除查询参数
const path = (request.url?.split('?')[0] || request.path) as string;
// 白名单路径 - 这些路径在维护期间也可以访问
const whitelist = [
'/mobile/system/maintenance-status', // 维护状态检查
'/health', // 健康检查
'/api/health', // 健康检查(带前缀)
];
// 白名单路径 - 这些路径在维护期间也可以访问
// 支持带 /api/v1 前缀和不带前缀的两种情况
const whitelist = [
'/api/v1/mobile/system/maintenance-status',
'/mobile/system/maintenance-status',
'/api/v1/health',
'/health',
];
// 只拦截移动端 API以 /mobile 开头的路径)
if (!path.startsWith('/mobile')) {
return next.handle();
}
// 检查是否是移动端 API需要同时检查带前缀和不带前缀的路径)
const isMobileApi =
path.includes('/mobile/') ||
path.startsWith('/mobile');
// 检查是否在白名单中
if (whitelist.some((p) => path.startsWith(p))) {
return next.handle();
}
// 非移动端 API直接放行
if (!isMobileApi) {
return next.handle();
}
// 检查是否在维护中
const activeMaintenance = await this.maintenanceRepo.findActiveMaintenance();
// 检查是否在白名单中
if (whitelist.some((p) => path === p || path.startsWith(p + '/'))) {
return next.handle();
}
if (activeMaintenance) {
throw new HttpException(
{
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
error: 'Service Unavailable',
message: '系统维护中',
maintenance: {
inMaintenance: true,
title: activeMaintenance.title,
message: activeMaintenance.message,
endTime: activeMaintenance.endTime,
// 检查是否在维护中
const activeMaintenance = await this.maintenanceRepo.findActiveMaintenance();
if (activeMaintenance) {
this.logger.warn(`系统维护中,阻断请求: ${path}`);
throw new HttpException(
{
statusCode: HttpStatus.SERVICE_UNAVAILABLE,
error: 'Service Unavailable',
message: '系统维护中',
maintenance: {
inMaintenance: true,
title: activeMaintenance.title,
message: activeMaintenance.message,
endTime: activeMaintenance.endTime,
},
},
},
HttpStatus.SERVICE_UNAVAILABLE,
);
}
HttpStatus.SERVICE_UNAVAILABLE,
);
}
return next.handle();
return next.handle();
} catch (error) {
// 如果是我们抛出的 HttpException直接重新抛出
if (error instanceof HttpException) {
throw error;
}
// 其他错误(如数据库错误),记录日志但不阻断请求
this.logger.error(`维护状态检查失败,放行请求: ${error.message}`);
return next.handle();
}
}
}