Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled

Full-stack web application for Telegram management
- Frontend: Vue 3 + Vben Admin
- Backend: NestJS
- Features: User management, group broadcast, statistics

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
你的用户名
2025-11-04 15:37:50 +08:00
commit 237c7802e5
3674 changed files with 525172 additions and 0 deletions

View File

@@ -0,0 +1,127 @@
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<string>()[RESPONSE_MESSAGE_KEY](message);
// 跳过响应包装装饰器
export const SKIP_RESPONSE_WRAP_KEY = 'skip_response_wrap';
export const SkipResponseWrap = () =>
Reflector.createDecorator<boolean>()[SKIP_RESPONSE_WRAP_KEY](true);
// 标准响应格式接口
export interface ApiResponse<T = any> {
success: boolean;
code: number;
data: T;
msg: string;
timestamp?: string;
path?: string;
requestId?: string;
}
@Injectable()
export class ResponseInterceptor<T> implements NestInterceptor<T, ApiResponse<T>> {
constructor(private readonly reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<ApiResponse<T>> {
const request = context.switchToHttp().getRequest();
const response = context.switchToHttp().getResponse();
// 检查是否跳过响应包装
const skipWrap = this.reflector.getAllAndOverride<boolean>(
SKIP_RESPONSE_WRAP_KEY,
[context.getHandler(), context.getClass()],
);
if (skipWrap) {
return next.handle();
}
// 获取自定义响应消息
const message = this.reflector.getAllAndOverride<string>(
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<T> = {
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 '操作完成';
}
}
}