feat: 添加货币和汇率管理功能
refactor: 重构前端路由和登录逻辑 docs: 更新业务闭环、任务和架构文档 style: 调整代码格式和文件结构 chore: 更新依赖项和配置文件
This commit is contained in:
412
server/src/core/exception/ExceptionMonitor.ts
Normal file
412
server/src/core/exception/ExceptionMonitor.ts
Normal file
@@ -0,0 +1,412 @@
|
||||
import { DomainEventBus } from '../runtime/DomainEventBus';
|
||||
|
||||
// 异常信息
|
||||
export interface ExceptionInfo {
|
||||
id: string;
|
||||
type: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
service: string;
|
||||
context?: Record<string, any>;
|
||||
severity: 'low' | 'medium' | 'high' | 'critical';
|
||||
status: 'open' | 'processing' | 'resolved' | 'ignored';
|
||||
createdAt: Date;
|
||||
lastUpdated: Date;
|
||||
resolvedAt?: Date;
|
||||
resolvedBy?: string;
|
||||
}
|
||||
|
||||
// 异常统计
|
||||
export interface ExceptionStats {
|
||||
total: number;
|
||||
bySeverity: {
|
||||
low: number;
|
||||
medium: number;
|
||||
high: number;
|
||||
critical: number;
|
||||
};
|
||||
byStatus: {
|
||||
open: number;
|
||||
processing: number;
|
||||
resolved: number;
|
||||
ignored: number;
|
||||
};
|
||||
byService: Record<string, number>;
|
||||
byType: Record<string, number>;
|
||||
averageResolutionTime: number; // 平均解决时间(分钟)
|
||||
last24Hours: number;
|
||||
last7Days: number;
|
||||
}
|
||||
|
||||
// 异常监控
|
||||
export class ExceptionMonitor {
|
||||
private static instance: ExceptionMonitor;
|
||||
private exceptions: Map<string, ExceptionInfo> = new Map();
|
||||
private eventBus: DomainEventBus;
|
||||
private severityThresholds: Record<string, number> = {
|
||||
low: 10,
|
||||
medium: 5,
|
||||
high: 2,
|
||||
critical: 1
|
||||
};
|
||||
|
||||
private constructor() {
|
||||
this.eventBus = DomainEventBus.getInstance();
|
||||
}
|
||||
|
||||
static getInstance(): ExceptionMonitor {
|
||||
if (!ExceptionMonitor.instance) {
|
||||
ExceptionMonitor.instance = new ExceptionMonitor();
|
||||
}
|
||||
return ExceptionMonitor.instance;
|
||||
}
|
||||
|
||||
// 记录异常
|
||||
async recordException(exception: Omit<ExceptionInfo, 'id' | 'status' | 'createdAt' | 'lastUpdated'>): Promise<ExceptionInfo> {
|
||||
const id = `exception_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const newException: ExceptionInfo = {
|
||||
...exception,
|
||||
id,
|
||||
status: 'open',
|
||||
createdAt: new Date(),
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.exceptions.set(id, newException);
|
||||
this.eventBus.publish('exception.recorded', newException);
|
||||
|
||||
// 检查是否需要触发告警
|
||||
await this.checkAlertThreshold(newException);
|
||||
|
||||
return newException;
|
||||
}
|
||||
|
||||
// 获取异常信息
|
||||
getException(exceptionId: string): ExceptionInfo | undefined {
|
||||
return this.exceptions.get(exceptionId);
|
||||
}
|
||||
|
||||
// 获取所有异常
|
||||
getAllExceptions(filters?: {
|
||||
severity?: string;
|
||||
status?: string;
|
||||
service?: string;
|
||||
type?: string;
|
||||
startDate?: Date;
|
||||
endDate?: Date;
|
||||
}): ExceptionInfo[] {
|
||||
let result = Array.from(this.exceptions.values());
|
||||
|
||||
if (filters) {
|
||||
if (filters.severity) {
|
||||
result = result.filter(e => e.severity === filters.severity);
|
||||
}
|
||||
|
||||
if (filters.status) {
|
||||
result = result.filter(e => e.status === filters.status);
|
||||
}
|
||||
|
||||
if (filters.service) {
|
||||
result = result.filter(e => e.service === filters.service);
|
||||
}
|
||||
|
||||
if (filters.type) {
|
||||
result = result.filter(e => e.type === filters.type);
|
||||
}
|
||||
|
||||
if (filters.startDate) {
|
||||
result = result.filter(e => e.createdAt >= filters.startDate!);
|
||||
}
|
||||
|
||||
if (filters.endDate) {
|
||||
result = result.filter(e => e.createdAt <= filters.endDate!);
|
||||
}
|
||||
}
|
||||
|
||||
return result.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
||||
}
|
||||
|
||||
// 更新异常状态
|
||||
async updateExceptionStatus(exceptionId: string, status: 'open' | 'processing' | 'resolved' | 'ignored', resolvedBy?: string): Promise<ExceptionInfo | null> {
|
||||
const exception = this.exceptions.get(exceptionId);
|
||||
if (!exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedException = {
|
||||
...exception,
|
||||
status,
|
||||
lastUpdated: new Date(),
|
||||
resolvedAt: status === 'resolved' ? new Date() : exception.resolvedAt,
|
||||
resolvedBy: status === 'resolved' ? resolvedBy : exception.resolvedBy
|
||||
};
|
||||
|
||||
this.exceptions.set(exceptionId, updatedException);
|
||||
this.eventBus.publish('exception.status.updated', updatedException);
|
||||
return updatedException;
|
||||
}
|
||||
|
||||
// 更新异常信息
|
||||
async updateException(exceptionId: string, updates: Partial<ExceptionInfo>): Promise<ExceptionInfo | null> {
|
||||
const exception = this.exceptions.get(exceptionId);
|
||||
if (!exception) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedException = {
|
||||
...exception,
|
||||
...updates,
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.exceptions.set(exceptionId, updatedException);
|
||||
this.eventBus.publish('exception.updated', updatedException);
|
||||
return updatedException;
|
||||
}
|
||||
|
||||
// 检查告警阈值
|
||||
private async checkAlertThreshold(exception: ExceptionInfo): Promise<void> {
|
||||
const threshold = this.severityThresholds[exception.severity];
|
||||
if (!threshold) return;
|
||||
|
||||
// 检查过去24小时内相同类型的异常数量
|
||||
const last24Hours = new Date();
|
||||
last24Hours.setHours(last24Hours.getHours() - 24);
|
||||
|
||||
const similarExceptions = Array.from(this.exceptions.values()).filter(e =>
|
||||
e.type === exception.type &&
|
||||
e.service === exception.service &&
|
||||
e.createdAt >= last24Hours
|
||||
);
|
||||
|
||||
if (similarExceptions.length >= threshold) {
|
||||
this.eventBus.publish('exception.alert', {
|
||||
exception,
|
||||
count: similarExceptions.length,
|
||||
threshold,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 获取异常统计
|
||||
getExceptionStats(): ExceptionStats {
|
||||
const exceptions = Array.from(this.exceptions.values());
|
||||
const total = exceptions.length;
|
||||
|
||||
const bySeverity = {
|
||||
low: 0,
|
||||
medium: 0,
|
||||
high: 0,
|
||||
critical: 0
|
||||
};
|
||||
|
||||
const byStatus = {
|
||||
open: 0,
|
||||
processing: 0,
|
||||
resolved: 0,
|
||||
ignored: 0
|
||||
};
|
||||
|
||||
const byService: Record<string, number> = {};
|
||||
const byType: Record<string, number> = {};
|
||||
|
||||
let totalResolutionTime = 0;
|
||||
let resolvedExceptions = 0;
|
||||
|
||||
const last24Hours = new Date();
|
||||
last24Hours.setHours(last24Hours.getHours() - 24);
|
||||
|
||||
const last7Days = new Date();
|
||||
last7Days.setDate(last7Days.getDate() - 7);
|
||||
|
||||
let exceptionsLast24Hours = 0;
|
||||
let exceptionsLast7Days = 0;
|
||||
|
||||
for (const exception of exceptions) {
|
||||
bySeverity[exception.severity]++;
|
||||
byStatus[exception.status]++;
|
||||
|
||||
byService[exception.service] = (byService[exception.service] || 0) + 1;
|
||||
byType[exception.type] = (byType[exception.type] || 0) + 1;
|
||||
|
||||
if (exception.status === 'resolved' && exception.resolvedAt) {
|
||||
const resolutionTime = (exception.resolvedAt.getTime() - exception.createdAt.getTime()) / (1000 * 60); // 转换为分钟
|
||||
totalResolutionTime += resolutionTime;
|
||||
resolvedExceptions++;
|
||||
}
|
||||
|
||||
if (exception.createdAt >= last24Hours) {
|
||||
exceptionsLast24Hours++;
|
||||
}
|
||||
|
||||
if (exception.createdAt >= last7Days) {
|
||||
exceptionsLast7Days++;
|
||||
}
|
||||
}
|
||||
|
||||
const averageResolutionTime = resolvedExceptions > 0
|
||||
? totalResolutionTime / resolvedExceptions
|
||||
: 0;
|
||||
|
||||
return {
|
||||
total,
|
||||
bySeverity,
|
||||
byStatus,
|
||||
byService,
|
||||
byType,
|
||||
averageResolutionTime,
|
||||
last24Hours: exceptionsLast24Hours,
|
||||
last7Days: exceptionsLast7Days
|
||||
};
|
||||
}
|
||||
|
||||
// 获取服务异常统计
|
||||
getServiceExceptionStats(service: string): ExceptionStats {
|
||||
const serviceExceptions = Array.from(this.exceptions.values()).filter(e => e.service === service);
|
||||
|
||||
const total = serviceExceptions.length;
|
||||
|
||||
const bySeverity = {
|
||||
low: 0,
|
||||
medium: 0,
|
||||
high: 0,
|
||||
critical: 0
|
||||
};
|
||||
|
||||
const byStatus = {
|
||||
open: 0,
|
||||
processing: 0,
|
||||
resolved: 0,
|
||||
ignored: 0
|
||||
};
|
||||
|
||||
const byType: Record<string, number> = {};
|
||||
|
||||
let totalResolutionTime = 0;
|
||||
let resolvedExceptions = 0;
|
||||
|
||||
const last24Hours = new Date();
|
||||
last24Hours.setHours(last24Hours.getHours() - 24);
|
||||
|
||||
const last7Days = new Date();
|
||||
last7Days.setDate(last7Days.getDate() - 7);
|
||||
|
||||
let exceptionsLast24Hours = 0;
|
||||
let exceptionsLast7Days = 0;
|
||||
|
||||
for (const exception of serviceExceptions) {
|
||||
bySeverity[exception.severity]++;
|
||||
byStatus[exception.status]++;
|
||||
|
||||
byType[exception.type] = (byType[exception.type] || 0) + 1;
|
||||
|
||||
if (exception.status === 'resolved' && exception.resolvedAt) {
|
||||
const resolutionTime = (exception.resolvedAt.getTime() - exception.createdAt.getTime()) / (1000 * 60); // 转换为分钟
|
||||
totalResolutionTime += resolutionTime;
|
||||
resolvedExceptions++;
|
||||
}
|
||||
|
||||
if (exception.createdAt >= last24Hours) {
|
||||
exceptionsLast24Hours++;
|
||||
}
|
||||
|
||||
if (exception.createdAt >= last7Days) {
|
||||
exceptionsLast7Days++;
|
||||
}
|
||||
}
|
||||
|
||||
const averageResolutionTime = resolvedExceptions > 0
|
||||
? totalResolutionTime / resolvedExceptions
|
||||
: 0;
|
||||
|
||||
return {
|
||||
total,
|
||||
bySeverity,
|
||||
byStatus,
|
||||
byService: { [service]: total },
|
||||
byType,
|
||||
averageResolutionTime,
|
||||
last24Hours: exceptionsLast24Hours,
|
||||
last7Days: exceptionsLast7Days
|
||||
};
|
||||
}
|
||||
|
||||
// 清理旧异常
|
||||
async cleanupOldExceptions(days: number = 30): Promise<number> {
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setDate(cutoffDate.getDate() - days);
|
||||
|
||||
let removedCount = 0;
|
||||
|
||||
for (const [id, exception] of this.exceptions.entries()) {
|
||||
if (exception.createdAt < cutoffDate && exception.status === 'resolved') {
|
||||
this.exceptions.delete(id);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.eventBus.publish('exception.cleanup', {
|
||||
removedCount,
|
||||
cutoffDate,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
}
|
||||
|
||||
// 生成异常报告
|
||||
async generateExceptionReport(startDate: Date, endDate: Date): Promise<{
|
||||
summary: ExceptionStats;
|
||||
topExceptions: ExceptionInfo[];
|
||||
serviceStats: Record<string, ExceptionStats>;
|
||||
}> {
|
||||
const filteredExceptions = Array.from(this.exceptions.values()).filter(
|
||||
e => e.createdAt >= startDate && e.createdAt <= endDate
|
||||
);
|
||||
|
||||
// 按频率排序的异常类型
|
||||
const typeFrequency: Record<string, number> = {};
|
||||
for (const exception of filteredExceptions) {
|
||||
typeFrequency[exception.type] = (typeFrequency[exception.type] || 0) + 1;
|
||||
}
|
||||
|
||||
const sortedTypes = Object.entries(typeFrequency)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 10)
|
||||
.map(([type]) => type);
|
||||
|
||||
const topExceptions = filteredExceptions
|
||||
.filter(e => sortedTypes.includes(e.type))
|
||||
.slice(0, 20);
|
||||
|
||||
// 按服务分组的统计
|
||||
const serviceStats: Record<string, ExceptionStats> = {};
|
||||
const services = Array.from(new Set(filteredExceptions.map(e => e.service)));
|
||||
for (const service of services) {
|
||||
serviceStats[service] = this.getServiceExceptionStats(service);
|
||||
}
|
||||
|
||||
return {
|
||||
summary: this.getExceptionStats(),
|
||||
topExceptions,
|
||||
serviceStats
|
||||
};
|
||||
}
|
||||
|
||||
// 设置严重性阈值
|
||||
setSeverityThresholds(thresholds: Record<string, number>): void {
|
||||
this.severityThresholds = {
|
||||
...this.severityThresholds,
|
||||
...thresholds
|
||||
};
|
||||
}
|
||||
|
||||
// 获取严重性阈值
|
||||
getSeverityThresholds(): Record<string, number> {
|
||||
return { ...this.severityThresholds };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user