Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
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:
246
backend-nestjs/src/common/services/cache.service.ts
Normal file
246
backend-nestjs/src/common/services/cache.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
243
backend-nestjs/src/common/services/logger.service.ts
Normal file
243
backend-nestjs/src/common/services/logger.service.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
324
backend-nestjs/src/common/services/performance.service.ts
Normal file
324
backend-nestjs/src/common/services/performance.service.ts
Normal 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(),
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user