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,246 @@
import { Injectable, Logger } from '@nestjs/common';
import { RedisService } from '@shared/redis/redis.service';
export interface CacheOptions {
ttl?: number; // 过期时间(秒)
prefix?: string; // 缓存键前缀
}
@Injectable()
export class CacheService {
private readonly logger = new Logger(CacheService.name);
private readonly defaultTTL = 300; // 5分钟
private readonly defaultPrefix = 'tg_cache';
constructor(private readonly redisService: RedisService) {}
/**
* 获取缓存
*/
async get<T>(key: string, options?: CacheOptions): Promise<T | null> {
try {
const fullKey = this.buildKey(key, options?.prefix);
const data = await this.redisService.get(fullKey);
if (data) {
this.logger.debug(`缓存命中: ${fullKey}`);
return JSON.parse(data);
}
return null;
} catch (error) {
this.logger.warn(`获取缓存失败: ${error.message}`);
return null;
}
}
/**
* 设置缓存
*/
async set<T>(key: string, value: T, options?: CacheOptions): Promise<void> {
try {
const fullKey = this.buildKey(key, options?.prefix);
const ttl = options?.ttl || this.defaultTTL;
await this.redisService.set(fullKey, JSON.stringify(value), ttl);
this.logger.debug(`缓存设置: ${fullKey}, TTL: ${ttl}s`);
} catch (error) {
this.logger.warn(`设置缓存失败: ${error.message}`);
}
}
/**
* 删除缓存
*/
async del(key: string, options?: CacheOptions): Promise<void> {
try {
const fullKey = this.buildKey(key, options?.prefix);
await this.redisService.del(fullKey);
this.logger.debug(`缓存删除: ${fullKey}`);
} catch (error) {
this.logger.warn(`删除缓存失败: ${error.message}`);
}
}
/**
* 批量删除缓存(通过模式匹配)
*/
async delByPattern(pattern: string, options?: CacheOptions): Promise<void> {
try {
const fullPattern = this.buildKey(pattern, options?.prefix);
await this.redisService.clearCache(fullPattern);
this.logger.debug(`批量删除缓存: ${fullPattern}`);
} catch (error) {
this.logger.warn(`批量删除缓存失败: ${error.message}`);
}
}
/**
* 获取或设置缓存
*/
async getOrSet<T>(
key: string,
factory: () => Promise<T>,
options?: CacheOptions
): Promise<T> {
const cached = await this.get<T>(key, options);
if (cached !== null) {
return cached;
}
const data = await factory();
await this.set(key, data, options);
return data;
}
/**
* 检查缓存是否存在
*/
async exists(key: string, options?: CacheOptions): Promise<boolean> {
try {
const fullKey = this.buildKey(key, options?.prefix);
return await this.redisService.exists(fullKey);
} catch (error) {
this.logger.warn(`检查缓存存在失败: ${error.message}`);
return false;
}
}
/**
* 设置缓存过期时间
*/
async expire(key: string, ttl: number, options?: CacheOptions): Promise<void> {
try {
const fullKey = this.buildKey(key, options?.prefix);
// Redis service doesn't have expire method, so we'll get and set with new TTL
const value = await this.get(key, options);
if (value !== null) {
await this.set(key, value, { ...options, ttl });
}
this.logger.debug(`设置缓存过期时间: ${fullKey}, TTL: ${ttl}s`);
} catch (error) {
this.logger.warn(`设置缓存过期时间失败: ${error.message}`);
}
}
/**
* 获取缓存剩余过期时间
*/
async ttl(key: string, options?: CacheOptions): Promise<number> {
try {
const fullKey = this.buildKey(key, options?.prefix);
return await this.redisService.ttl(fullKey);
} catch (error) {
this.logger.warn(`获取缓存TTL失败: ${error.message}`);
return -1;
}
}
/**
* 增加计数器
*/
async increment(key: string, increment = 1, options?: CacheOptions): Promise<number> {
try {
// Get current value or 0 if doesn't exist
const currentValue = await this.get<number>(key, options) || 0;
const newValue = currentValue + increment;
// Set new value with TTL
const ttl = options?.ttl || this.defaultTTL;
await this.set(key, newValue, { ...options, ttl });
return newValue;
} catch (error) {
this.logger.warn(`递增计数器失败: ${error.message}`);
return 0;
}
}
/**
* 减少计数器
*/
async decrement(key: string, decrement = 1, options?: CacheOptions): Promise<number> {
return this.increment(key, -decrement, options);
}
/**
* 清空所有缓存
*/
async flushAll(): Promise<void> {
try {
await this.redisService.clearCache('*');
this.logger.log('清空所有缓存');
} catch (error) {
this.logger.error(`清空缓存失败: ${error.message}`);
}
}
/**
* 构建完整的缓存键
*/
private buildKey(key: string, prefix?: string): string {
const actualPrefix = prefix || this.defaultPrefix;
return `${actualPrefix}:${key}`;
}
/**
* 缓存预热 - TG账号
*/
async warmupTgAccounts(): Promise<void> {
this.logger.log('开始TG账号缓存预热...');
// 这里可以预加载常用的TG账号数据
// 实际实现时需要注入相关服务
}
/**
* 缓存预热 - 系统配置
*/
async warmupSystemConfig(): Promise<void> {
this.logger.log('开始系统配置缓存预热...');
// 这里可以预加载系统配置
}
/**
* 缓存统计信息
*/
async getCacheStats(): Promise<any> {
try {
// Since Redis service doesn't have info method, provide basic stats
return {
memory: {
used_memory: 0,
used_memory_peak: 0
},
keyspace: {
db0: {
keys: 0,
expires: 0
}
},
timestamp: new Date(),
};
} catch (error) {
this.logger.warn(`获取缓存统计失败: ${error.message}`);
return null;
}
}
/**
* 解析Redis信息
*/
private parseRedisInfo(info: string): any {
const result: any = {};
const lines = info.split('\r\n');
for (const line of lines) {
if (line.includes(':')) {
const [key, value] = line.split(':');
result[key] = isNaN(Number(value)) ? value : Number(value);
}
}
return result;
}
}

View File

@@ -0,0 +1,243 @@
import { Injectable, LoggerService as NestLoggerService } from '@nestjs/common';
import { createLogger, format, transports, Logger as WinstonLogger } from 'winston';
import * as DailyRotateFile from 'winston-daily-rotate-file';
import { join } from 'path';
@Injectable()
export class LoggerService implements NestLoggerService {
private logger: WinstonLogger;
constructor() {
this.createLogger();
}
private createLogger() {
// 日志格式
const logFormat = format.combine(
format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
format.errors({ stack: true }),
format.json(),
format.printf(({ timestamp, level, message, context, stack, ...meta }) => {
let log = `${timestamp} [${level.toUpperCase()}]`;
if (context) {
log += ` [${context}]`;
}
log += ` ${message}`;
if (Object.keys(meta).length > 0) {
log += ` ${JSON.stringify(meta)}`;
}
if (stack) {
log += `\n${stack}`;
}
return log;
})
);
// 控制台日志格式
const consoleFormat = format.combine(
format.colorize(),
format.timestamp({ format: 'HH:mm:ss' }),
format.printf(({ timestamp, level, message, context }) => {
let log = `${timestamp} ${level}`;
if (context) {
log += ` [${context}]`;
}
log += ` ${message}`;
return log;
})
);
// 日志目录
const logDir = join(process.cwd(), 'logs');
// 创建Winston logger
this.logger = createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
transports: [
// 控制台输出
new transports.Console({
format: consoleFormat,
level: process.env.NODE_ENV === 'development' ? 'debug' : 'info',
}),
// 信息日志文件(按日期滚动)
new DailyRotateFile({
filename: join(logDir, 'app-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '14d',
level: 'info',
format: logFormat,
}),
// 错误日志文件(按日期滚动)
new DailyRotateFile({
filename: join(logDir, 'error-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '30d',
level: 'error',
format: logFormat,
}),
// 调试日志文件(只在开发环境)
...(process.env.NODE_ENV === 'development' ? [
new DailyRotateFile({
filename: join(logDir, 'debug-%DATE%.log'),
datePattern: 'YYYY-MM-DD',
maxSize: '20m',
maxFiles: '7d',
level: 'debug',
format: logFormat,
})
] : []),
],
});
}
log(message: any, context?: string) {
this.logger.info(message, { context });
}
error(message: any, stack?: string, context?: string) {
this.logger.error(message, { stack, context });
}
warn(message: any, context?: string) {
this.logger.warn(message, { context });
}
debug(message: any, context?: string) {
this.logger.debug(message, { context });
}
verbose(message: any, context?: string) {
this.logger.verbose(message, { context });
}
/**
* 记录HTTP请求
*/
logRequest(req: any, res: any, responseTime: number) {
const { method, url, headers, body, query, params, ip } = req;
const { statusCode } = res;
this.logger.info('HTTP Request', {
context: 'HttpRequest',
method,
url,
statusCode,
responseTime: `${responseTime}ms`,
ip,
userAgent: headers['user-agent'],
requestId: headers['x-request-id'],
body: this.sanitizeBody(body),
query,
params,
});
}
/**
* 记录数据库操作
*/
logDatabase(operation: string, table: string, executionTime: number, query?: string) {
this.logger.debug('Database Operation', {
context: 'Database',
operation,
table,
executionTime: `${executionTime}ms`,
query: query?.substring(0, 500), // 限制查询长度
});
}
/**
* 记录业务操作
*/
logBusiness(operation: string, userId?: number, details?: any) {
this.logger.info('Business Operation', {
context: 'Business',
operation,
userId,
details,
});
}
/**
* 记录安全事件
*/
logSecurity(event: string, userId?: number, ip?: string, details?: any) {
this.logger.warn('Security Event', {
context: 'Security',
event,
userId,
ip,
details,
});
}
/**
* 记录性能指标
*/
logPerformance(operation: string, duration: number, details?: any) {
if (duration > 1000) { // 超过1秒的操作记录为警告
this.logger.warn('Slow Operation', {
context: 'Performance',
operation,
duration: `${duration}ms`,
details,
});
} else {
this.logger.debug('Performance Metric', {
context: 'Performance',
operation,
duration: `${duration}ms`,
details,
});
}
}
/**
* 清理敏感信息
*/
private sanitizeBody(body: any): any {
if (!body || typeof body !== 'object') {
return body;
}
const sensitiveFields = [
'password',
'token',
'secret',
'key',
'authorization',
'cookie',
'session',
];
const sanitized = { ...body };
for (const field of sensitiveFields) {
if (field in sanitized) {
sanitized[field] = '***';
}
}
return sanitized;
}
/**
* 获取Winston logger实例
*/
getWinstonLogger(): WinstonLogger {
return this.logger;
}
}

View File

@@ -0,0 +1,324 @@
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { CacheService } from './cache.service';
import { AnalyticsService } from '@modules/analytics/services/analytics.service';
@Injectable()
export class PerformanceService {
private readonly logger = new Logger(PerformanceService.name);
constructor(
private readonly cacheService: CacheService,
private readonly analyticsService: AnalyticsService,
) {}
/**
* 获取系统性能概览
*/
async getPerformanceOverview(): Promise<any> {
const now = new Date();
const oneHourAgo = new Date(now.getTime() - 60 * 60 * 1000);
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000);
const [
currentMetrics,
hourlyMetrics,
dailyMetrics,
cacheStats,
slowQueries,
] = await Promise.all([
this.getCurrentSystemMetrics(),
this.getPerformanceMetrics(oneHourAgo, now),
this.getPerformanceMetrics(oneDayAgo, now),
this.cacheService.getCacheStats(),
this.getSlowQueries(oneDayAgo, now),
]);
return {
timestamp: now,
current: currentMetrics,
hourly: hourlyMetrics,
daily: dailyMetrics,
cache: cacheStats,
slowQueries,
};
}
/**
* 获取当前系统指标
*/
async getCurrentSystemMetrics(): Promise<any> {
const memoryUsage = process.memoryUsage();
const cpuUsage = process.cpuUsage();
return {
uptime: process.uptime(),
memory: {
rss: memoryUsage.rss,
heapTotal: memoryUsage.heapTotal,
heapUsed: memoryUsage.heapUsed,
external: memoryUsage.external,
heapUsedPercentage: (memoryUsage.heapUsed / memoryUsage.heapTotal) * 100,
},
cpu: {
user: cpuUsage.user,
system: cpuUsage.system,
},
eventLoop: {
delay: await this.getEventLoopDelay(),
},
};
}
/**
* 获取性能指标
*/
async getPerformanceMetrics(startDate: Date, endDate: Date): Promise<any> {
try {
const metrics = await this.analyticsService.getPerformanceAnalytics(
startDate.toISOString(),
endDate.toISOString(),
);
const responseTimeMetrics = metrics.filter(m => m.metricName === 'api_response_time');
if (responseTimeMetrics.length === 0) {
return {
averageResponseTime: 0,
requestCount: 0,
errorRate: 0,
};
}
const totalRequests = responseTimeMetrics.reduce((sum, m) => sum + m.count, 0);
const averageResponseTime = responseTimeMetrics.reduce((sum, m) => sum + (m.averageValue * m.count), 0) / totalRequests;
return {
averageResponseTime,
requestCount: totalRequests,
minResponseTime: Math.min(...responseTimeMetrics.map(m => m.minValue)),
maxResponseTime: Math.max(...responseTimeMetrics.map(m => m.maxValue)),
};
} catch (error) {
this.logger.warn(`获取性能指标失败: ${error.message}`);
return {
averageResponseTime: 0,
requestCount: 0,
errorRate: 0,
};
}
}
/**
* 获取慢查询列表
*/
async getSlowQueries(startDate: Date, endDate: Date, limit = 10): Promise<any[]> {
try {
const slowQueries = await this.analyticsService.queryAnalytics({
metricType: 'slow_query',
startDate: startDate.toISOString(),
endDate: endDate.toISOString(),
limit,
});
return Array.isArray(slowQueries) ? slowQueries : [];
} catch (error) {
this.logger.warn(`获取慢查询失败: ${error.message}`);
return [];
}
}
/**
* 获取事件循环延迟
*/
private async getEventLoopDelay(): Promise<number> {
return new Promise((resolve) => {
const start = Date.now();
setImmediate(() => {
resolve(Date.now() - start);
});
});
}
/**
* 内存使用分析
*/
async analyzeMemoryUsage(): Promise<any> {
const memoryUsage = process.memoryUsage();
const { heapUsed, heapTotal, rss, external } = memoryUsage;
// 计算内存使用百分比
const heapUsedPercentage = (heapUsed / heapTotal) * 100;
// 内存警告阈值
const warnings = [];
if (heapUsedPercentage > 80) {
warnings.push('堆内存使用率过高');
}
if (rss > 1024 * 1024 * 1024) { // 1GB
warnings.push('RSS内存使用过高');
}
if (external > 500 * 1024 * 1024) { // 500MB
warnings.push('外部内存使用过高');
}
return {
usage: memoryUsage,
heapUsedPercentage,
warnings,
recommendations: this.getMemoryRecommendations(warnings),
};
}
/**
* 获取内存优化建议
*/
private getMemoryRecommendations(warnings: string[]): string[] {
const recommendations = [];
if (warnings.some(w => w.includes('堆内存'))) {
recommendations.push('考虑增加Node.js堆内存限制');
recommendations.push('检查是否存在内存泄漏');
recommendations.push('优化数据结构和缓存策略');
}
if (warnings.some(w => w.includes('RSS'))) {
recommendations.push('检查是否有未释放的原生资源');
recommendations.push('考虑重启应用释放内存');
}
if (warnings.some(w => w.includes('外部内存'))) {
recommendations.push('检查Buffer和原生模块的使用');
recommendations.push('优化文件处理和网络请求');
}
return recommendations;
}
/**
* 定时清理性能数据
*/
@Cron(CronExpression.EVERY_DAY_AT_3AM)
async cleanupPerformanceData(): Promise<void> {
this.logger.log('开始清理性能数据...');
try {
// 清理7天前的性能日志
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
// 这里可以添加具体的清理逻辑
// 例如删除过期的分析记录
this.logger.log('性能数据清理完成');
} catch (error) {
this.logger.error(`性能数据清理失败: ${error.message}`);
}
}
/**
* 定时生成性能报告
*/
@Cron(CronExpression.EVERY_HOUR)
async generatePerformanceReport(): Promise<void> {
try {
const overview = await this.getPerformanceOverview();
const memoryAnalysis = await this.analyzeMemoryUsage();
// 如果有性能问题,记录警告
if (memoryAnalysis.warnings.length > 0) {
this.logger.warn(`性能警告: ${memoryAnalysis.warnings.join(', ')}`);
// 记录性能警告事件
await this.analyticsService.recordEvent({
eventType: 'system_event',
eventName: 'performance_warning',
entityType: 'system',
eventData: {
warnings: memoryAnalysis.warnings,
recommendations: memoryAnalysis.recommendations,
memoryUsage: memoryAnalysis.usage,
},
});
}
// 缓存性能报告供API查询
await this.cacheService.set('performance:latest_report', {
overview,
memoryAnalysis,
generatedAt: new Date(),
}, { ttl: 3600 }); // 缓存1小时
} catch (error) {
this.logger.error(`生成性能报告失败: ${error.message}`);
}
}
/**
* 获取最新性能报告
*/
async getLatestPerformanceReport(): Promise<any> {
return await this.cacheService.get('performance:latest_report') ||
await this.getPerformanceOverview();
}
/**
* 性能优化建议
*/
async getOptimizationSuggestions(): Promise<any> {
const overview = await this.getPerformanceOverview();
const suggestions = [];
// 响应时间建议
if (overview.hourly.averageResponseTime > 1000) {
suggestions.push({
type: 'response_time',
severity: 'high',
message: '平均响应时间过长,建议优化数据库查询和缓存策略',
actions: [
'添加数据库索引',
'增加缓存层',
'优化SQL查询',
'考虑使用CDN',
],
});
}
// 内存使用建议
const memoryUsage = overview.current.memory.heapUsedPercentage;
if (memoryUsage > 80) {
suggestions.push({
type: 'memory',
severity: 'high',
message: '内存使用率过高,可能存在内存泄漏',
actions: [
'检查未释放的事件监听器',
'优化数据缓存策略',
'使用内存分析工具',
'考虑水平扩展',
],
});
}
// 缓存命中率建议
if (overview.cache && overview.cache.memory.keyspace_hit_rate < 0.8) {
suggestions.push({
type: 'cache',
severity: 'medium',
message: '缓存命中率较低,建议优化缓存策略',
actions: [
'调整缓存过期时间',
'增加缓存预热',
'优化缓存键设计',
'分析缓存使用模式',
],
});
}
return {
suggestions,
overview: overview.current,
generatedAt: new Date(),
};
}
}