feat: 添加货币和汇率管理功能
refactor: 重构前端路由和登录逻辑 docs: 更新业务闭环、任务和架构文档 style: 调整代码格式和文件结构 chore: 更新依赖项和配置文件
This commit is contained in:
357
server/src/core/exception/ExceptionKnowledgeBase.ts
Normal file
357
server/src/core/exception/ExceptionKnowledgeBase.ts
Normal file
@@ -0,0 +1,357 @@
|
||||
import { DomainEventBus } from '../runtime/DomainEventBus';
|
||||
|
||||
// 异常知识条目
|
||||
export interface ExceptionKnowledgeItem {
|
||||
id: string;
|
||||
exceptionType: string;
|
||||
messagePattern: string;
|
||||
description: string;
|
||||
cause: string;
|
||||
solution: string;
|
||||
prevention: string;
|
||||
severity: 'low' | 'medium' | 'high' | 'critical';
|
||||
frequency: number;
|
||||
references?: string[];
|
||||
createdAt: Date;
|
||||
lastUpdated: Date;
|
||||
lastSeen: Date;
|
||||
}
|
||||
|
||||
// 异常知识查询结果
|
||||
export interface ExceptionKnowledgeQueryResult {
|
||||
items: ExceptionKnowledgeItem[];
|
||||
total: number;
|
||||
page: number;
|
||||
pageSize: number;
|
||||
query: string;
|
||||
filters?: Record<string, string>;
|
||||
}
|
||||
|
||||
// 异常知识统计
|
||||
export interface ExceptionKnowledgeStats {
|
||||
totalItems: number;
|
||||
bySeverity: {
|
||||
low: number;
|
||||
medium: number;
|
||||
high: number;
|
||||
critical: number;
|
||||
};
|
||||
byType: Record<string, number>;
|
||||
mostFrequent: ExceptionKnowledgeItem[];
|
||||
recentlyUpdated: ExceptionKnowledgeItem[];
|
||||
averageFrequency: number;
|
||||
}
|
||||
|
||||
// 异常知识库
|
||||
export class ExceptionKnowledgeBase {
|
||||
private static instance: ExceptionKnowledgeBase;
|
||||
private knowledgeItems: Map<string, ExceptionKnowledgeItem> = new Map();
|
||||
private eventBus: DomainEventBus;
|
||||
|
||||
private constructor() {
|
||||
this.eventBus = DomainEventBus.getInstance();
|
||||
}
|
||||
|
||||
static getInstance(): ExceptionKnowledgeBase {
|
||||
if (!ExceptionKnowledgeBase.instance) {
|
||||
ExceptionKnowledgeBase.instance = new ExceptionKnowledgeBase();
|
||||
}
|
||||
return ExceptionKnowledgeBase.instance;
|
||||
}
|
||||
|
||||
// 添加异常知识条目
|
||||
async addKnowledgeItem(item: Omit<ExceptionKnowledgeItem, 'id' | 'frequency' | 'createdAt' | 'lastUpdated' | 'lastSeen'>): Promise<ExceptionKnowledgeItem> {
|
||||
const id = `knowledge_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const newItem: ExceptionKnowledgeItem = {
|
||||
...item,
|
||||
id,
|
||||
frequency: 1,
|
||||
createdAt: new Date(),
|
||||
lastUpdated: new Date(),
|
||||
lastSeen: new Date()
|
||||
};
|
||||
|
||||
this.knowledgeItems.set(id, newItem);
|
||||
this.eventBus.publish('exception.knowledge.added', newItem);
|
||||
return newItem;
|
||||
}
|
||||
|
||||
// 获取异常知识条目
|
||||
getKnowledgeItem(itemId: string): ExceptionKnowledgeItem | undefined {
|
||||
return this.knowledgeItems.get(itemId);
|
||||
}
|
||||
|
||||
// 获取所有异常知识条目
|
||||
getAllKnowledgeItems(filters?: {
|
||||
severity?: string;
|
||||
exceptionType?: string;
|
||||
minFrequency?: number;
|
||||
}): ExceptionKnowledgeItem[] {
|
||||
let result = Array.from(this.knowledgeItems.values());
|
||||
|
||||
if (filters) {
|
||||
if (filters.severity) {
|
||||
result = result.filter(item => item.severity === filters.severity);
|
||||
}
|
||||
|
||||
if (filters.exceptionType) {
|
||||
result = result.filter(item => item.exceptionType === filters.exceptionType);
|
||||
}
|
||||
|
||||
if (filters.minFrequency !== undefined) {
|
||||
result = result.filter(item => item.frequency >= filters.minFrequency!);
|
||||
}
|
||||
}
|
||||
|
||||
return result.sort((a, b) => b.frequency - a.frequency);
|
||||
}
|
||||
|
||||
// 更新异常知识条目
|
||||
async updateKnowledgeItem(itemId: string, updates: Partial<ExceptionKnowledgeItem>): Promise<ExceptionKnowledgeItem | null> {
|
||||
const item = this.knowledgeItems.get(itemId);
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedItem = {
|
||||
...item,
|
||||
...updates,
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.knowledgeItems.set(itemId, updatedItem);
|
||||
this.eventBus.publish('exception.knowledge.updated', updatedItem);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
// 删除异常知识条目
|
||||
async deleteKnowledgeItem(itemId: string): Promise<boolean> {
|
||||
const item = this.knowledgeItems.get(itemId);
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.knowledgeItems.delete(itemId);
|
||||
this.eventBus.publish('exception.knowledge.deleted', item);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 搜索异常知识
|
||||
searchKnowledge(query: string, page: number = 1, pageSize: number = 10, filters?: Record<string, string>): ExceptionKnowledgeQueryResult {
|
||||
let results = Array.from(this.knowledgeItems.values());
|
||||
|
||||
// 应用过滤器
|
||||
if (filters) {
|
||||
for (const [key, value] of Object.entries(filters)) {
|
||||
if (key === 'severity') {
|
||||
results = results.filter(item => item.severity === value);
|
||||
} else if (key === 'exceptionType') {
|
||||
results = results.filter(item => item.exceptionType === value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索查询
|
||||
if (query) {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
results = results.filter(item =>
|
||||
item.exceptionType.toLowerCase().includes(lowerQuery) ||
|
||||
item.messagePattern.toLowerCase().includes(lowerQuery) ||
|
||||
item.description.toLowerCase().includes(lowerQuery) ||
|
||||
item.cause.toLowerCase().includes(lowerQuery) ||
|
||||
item.solution.toLowerCase().includes(lowerQuery)
|
||||
);
|
||||
}
|
||||
|
||||
// 排序
|
||||
results.sort((a, b) => b.frequency - a.frequency);
|
||||
|
||||
// 分页
|
||||
const total = results.length;
|
||||
const start = (page - 1) * pageSize;
|
||||
const end = start + pageSize;
|
||||
const paginatedResults = results.slice(start, end);
|
||||
|
||||
return {
|
||||
items: paginatedResults,
|
||||
total,
|
||||
page,
|
||||
pageSize,
|
||||
query,
|
||||
filters
|
||||
};
|
||||
}
|
||||
|
||||
// 匹配异常
|
||||
matchException(exceptionType: string, message: string): ExceptionKnowledgeItem[] {
|
||||
const results: ExceptionKnowledgeItem[] = [];
|
||||
|
||||
for (const item of this.knowledgeItems.values()) {
|
||||
// 检查异常类型匹配
|
||||
if (item.exceptionType === exceptionType) {
|
||||
// 检查消息模式匹配
|
||||
try {
|
||||
const regex = new RegExp(item.messagePattern);
|
||||
if (regex.test(message)) {
|
||||
results.push(item);
|
||||
}
|
||||
} catch (error) {
|
||||
// 正则表达式错误,使用简单字符串匹配
|
||||
if (message.includes(item.messagePattern)) {
|
||||
results.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按频率排序
|
||||
return results.sort((a, b) => b.frequency - a.frequency);
|
||||
}
|
||||
|
||||
// 增加异常频率
|
||||
async incrementFrequency(itemId: string): Promise<ExceptionKnowledgeItem | null> {
|
||||
const item = this.knowledgeItems.get(itemId);
|
||||
if (!item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedItem = {
|
||||
...item,
|
||||
frequency: item.frequency + 1,
|
||||
lastSeen: new Date(),
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.knowledgeItems.set(itemId, updatedItem);
|
||||
this.eventBus.publish('exception.knowledge.frequency.updated', updatedItem);
|
||||
return updatedItem;
|
||||
}
|
||||
|
||||
// 批量增加异常频率
|
||||
async batchIncrementFrequency(itemIds: string[]): Promise<ExceptionKnowledgeItem[]> {
|
||||
const updatedItems: ExceptionKnowledgeItem[] = [];
|
||||
|
||||
for (const itemId of itemIds) {
|
||||
const updatedItem = await this.incrementFrequency(itemId);
|
||||
if (updatedItem) {
|
||||
updatedItems.push(updatedItem);
|
||||
}
|
||||
}
|
||||
|
||||
return updatedItems;
|
||||
}
|
||||
|
||||
// 获取异常知识统计
|
||||
getKnowledgeStats(): ExceptionKnowledgeStats {
|
||||
const items = Array.from(this.knowledgeItems.values());
|
||||
const totalItems = items.length;
|
||||
|
||||
const bySeverity = {
|
||||
low: 0,
|
||||
medium: 0,
|
||||
high: 0,
|
||||
critical: 0
|
||||
};
|
||||
|
||||
const byType: Record<string, number> = {};
|
||||
|
||||
let totalFrequency = 0;
|
||||
|
||||
for (const item of items) {
|
||||
bySeverity[item.severity]++;
|
||||
byType[item.exceptionType] = (byType[item.exceptionType] || 0) + 1;
|
||||
totalFrequency += item.frequency;
|
||||
}
|
||||
|
||||
const mostFrequent = [...items].sort((a, b) => b.frequency - a.frequency).slice(0, 10);
|
||||
const recentlyUpdated = [...items].sort((a, b) => b.lastUpdated.getTime() - a.lastUpdated.getTime()).slice(0, 10);
|
||||
|
||||
const averageFrequency = totalItems > 0 ? totalFrequency / totalItems : 0;
|
||||
|
||||
return {
|
||||
totalItems,
|
||||
bySeverity,
|
||||
byType,
|
||||
mostFrequent,
|
||||
recentlyUpdated,
|
||||
averageFrequency
|
||||
};
|
||||
}
|
||||
|
||||
// 导入异常知识
|
||||
async importKnowledge(items: Omit<ExceptionKnowledgeItem, 'id' | 'frequency' | 'createdAt' | 'lastUpdated' | 'lastSeen'>[]): Promise<{
|
||||
success: number;
|
||||
failed: number;
|
||||
}> {
|
||||
let success = 0;
|
||||
let failed = 0;
|
||||
|
||||
for (const item of items) {
|
||||
try {
|
||||
await this.addKnowledgeItem(item);
|
||||
success++;
|
||||
} catch (error) {
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
return { success, failed };
|
||||
}
|
||||
|
||||
// 导出异常知识
|
||||
exportKnowledge(filters?: {
|
||||
severity?: string;
|
||||
exceptionType?: string;
|
||||
minFrequency?: number;
|
||||
}): ExceptionKnowledgeItem[] {
|
||||
return this.getAllKnowledgeItems(filters);
|
||||
}
|
||||
|
||||
// 生成异常知识报告
|
||||
async generateKnowledgeReport(): Promise<{
|
||||
stats: ExceptionKnowledgeStats;
|
||||
mostCommonExceptions: ExceptionKnowledgeItem[];
|
||||
leastCommonExceptions: ExceptionKnowledgeItem[];
|
||||
recentlyAdded: ExceptionKnowledgeItem[];
|
||||
}> {
|
||||
const items = Array.from(this.knowledgeItems.values());
|
||||
const stats = this.getKnowledgeStats();
|
||||
|
||||
const mostCommonExceptions = [...items].sort((a, b) => b.frequency - a.frequency).slice(0, 20);
|
||||
const leastCommonExceptions = [...items].sort((a, b) => a.frequency - b.frequency).slice(0, 20);
|
||||
const recentlyAdded = [...items].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).slice(0, 20);
|
||||
|
||||
return {
|
||||
stats,
|
||||
mostCommonExceptions,
|
||||
leastCommonExceptions,
|
||||
recentlyAdded
|
||||
};
|
||||
}
|
||||
|
||||
// 清理旧的异常知识
|
||||
async cleanupOldKnowledge(days: number = 365): Promise<number> {
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setDate(cutoffDate.getDate() - days);
|
||||
|
||||
let removedCount = 0;
|
||||
|
||||
for (const [id, item] of this.knowledgeItems.entries()) {
|
||||
if (item.lastSeen < cutoffDate && item.frequency < 5) {
|
||||
this.knowledgeItems.delete(id);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedCount > 0) {
|
||||
this.eventBus.publish('exception.knowledge.cleanup', {
|
||||
removedCount,
|
||||
cutoffDate,
|
||||
timestamp: new Date()
|
||||
});
|
||||
}
|
||||
|
||||
return removedCount;
|
||||
}
|
||||
}
|
||||
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 };
|
||||
}
|
||||
}
|
||||
398
server/src/core/exception/IntelligentExceptionHandler.ts
Normal file
398
server/src/core/exception/IntelligentExceptionHandler.ts
Normal file
@@ -0,0 +1,398 @@
|
||||
import { DomainEventBus } from '../runtime/DomainEventBus';
|
||||
import { ExceptionInfo } from './ExceptionMonitor';
|
||||
|
||||
// 异常处理策略
|
||||
export interface ExceptionHandlingStrategy {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
exceptionType: string;
|
||||
severity: 'low' | 'medium' | 'high' | 'critical';
|
||||
actions: ExceptionAction[];
|
||||
enabled: boolean;
|
||||
createdAt: Date;
|
||||
lastUpdated: Date;
|
||||
}
|
||||
|
||||
// 异常处理动作
|
||||
export interface ExceptionAction {
|
||||
type: 'retry' | 'fallback' | 'alert' | 'auto-fix' | 'escalate';
|
||||
parameters?: Record<string, any>;
|
||||
order: number;
|
||||
}
|
||||
|
||||
// 异常处理结果
|
||||
export interface ExceptionHandlingResult {
|
||||
id: string;
|
||||
exceptionId: string;
|
||||
strategyId: string;
|
||||
actions: {
|
||||
type: string;
|
||||
success: boolean;
|
||||
message: string;
|
||||
timestamp: Date;
|
||||
}[];
|
||||
overallStatus: 'success' | 'partial' | 'failure';
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
// 智能异常处理
|
||||
export class IntelligentExceptionHandler {
|
||||
private static instance: IntelligentExceptionHandler;
|
||||
private strategies: Map<string, ExceptionHandlingStrategy> = new Map();
|
||||
private handlingResults: Map<string, ExceptionHandlingResult> = new Map();
|
||||
private eventBus: DomainEventBus;
|
||||
|
||||
private constructor() {
|
||||
this.eventBus = DomainEventBus.getInstance();
|
||||
}
|
||||
|
||||
static getInstance(): IntelligentExceptionHandler {
|
||||
if (!IntelligentExceptionHandler.instance) {
|
||||
IntelligentExceptionHandler.instance = new IntelligentExceptionHandler();
|
||||
}
|
||||
return IntelligentExceptionHandler.instance;
|
||||
}
|
||||
|
||||
// 创建异常处理策略
|
||||
async createStrategy(strategy: Omit<ExceptionHandlingStrategy, 'id' | 'createdAt' | 'lastUpdated'>): Promise<ExceptionHandlingStrategy> {
|
||||
const id = `strategy_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
const newStrategy: ExceptionHandlingStrategy = {
|
||||
...strategy,
|
||||
id,
|
||||
createdAt: new Date(),
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.strategies.set(id, newStrategy);
|
||||
this.eventBus.publish('exception.strategy.created', newStrategy);
|
||||
return newStrategy;
|
||||
}
|
||||
|
||||
// 获取异常处理策略
|
||||
getStrategy(strategyId: string): ExceptionHandlingStrategy | undefined {
|
||||
return this.strategies.get(strategyId);
|
||||
}
|
||||
|
||||
// 获取所有异常处理策略
|
||||
getAllStrategies(filters?: {
|
||||
exceptionType?: string;
|
||||
severity?: string;
|
||||
enabled?: boolean;
|
||||
}): ExceptionHandlingStrategy[] {
|
||||
let result = Array.from(this.strategies.values());
|
||||
|
||||
if (filters) {
|
||||
if (filters.exceptionType) {
|
||||
result = result.filter(s => s.exceptionType === filters.exceptionType);
|
||||
}
|
||||
|
||||
if (filters.severity) {
|
||||
result = result.filter(s => s.severity === filters.severity);
|
||||
}
|
||||
|
||||
if (filters.enabled !== undefined) {
|
||||
result = result.filter(s => s.enabled === filters.enabled);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 更新异常处理策略
|
||||
async updateStrategy(strategyId: string, updates: Partial<ExceptionHandlingStrategy>): Promise<ExceptionHandlingStrategy | null> {
|
||||
const strategy = this.strategies.get(strategyId);
|
||||
if (!strategy) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedStrategy = {
|
||||
...strategy,
|
||||
...updates,
|
||||
lastUpdated: new Date()
|
||||
};
|
||||
|
||||
this.strategies.set(strategyId, updatedStrategy);
|
||||
this.eventBus.publish('exception.strategy.updated', updatedStrategy);
|
||||
return updatedStrategy;
|
||||
}
|
||||
|
||||
// 启用/禁用异常处理策略
|
||||
async toggleStrategy(strategyId: string, enabled: boolean): Promise<boolean> {
|
||||
const strategy = this.strategies.get(strategyId);
|
||||
if (!strategy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
strategy.enabled = enabled;
|
||||
strategy.lastUpdated = new Date();
|
||||
this.strategies.set(strategyId, strategy);
|
||||
this.eventBus.publish('exception.strategy.toggled', strategy);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 处理异常
|
||||
async handleException(exception: ExceptionInfo): Promise<ExceptionHandlingResult> {
|
||||
// 查找匹配的策略
|
||||
const strategies = this.getAllStrategies({
|
||||
exceptionType: exception.type,
|
||||
enabled: true
|
||||
});
|
||||
|
||||
// 按严重性排序,选择最匹配的策略
|
||||
strategies.sort((a, b) => {
|
||||
const severityOrder = { critical: 4, high: 3, medium: 2, low: 1 };
|
||||
return severityOrder[b.severity] - severityOrder[a.severity];
|
||||
});
|
||||
|
||||
const strategy = strategies[0];
|
||||
const actions: ExceptionHandlingResult['actions'] = [];
|
||||
let overallStatus: 'success' | 'partial' | 'failure' = 'failure';
|
||||
|
||||
if (strategy) {
|
||||
// 按顺序执行策略中的动作
|
||||
const sortedActions = [...strategy.actions].sort((a, b) => a.order - b.order);
|
||||
let successCount = 0;
|
||||
|
||||
for (const action of sortedActions) {
|
||||
const result = await this.executeAction(action, exception);
|
||||
actions.push(result);
|
||||
if (result.success) {
|
||||
successCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (successCount === sortedActions.length) {
|
||||
overallStatus = 'success';
|
||||
} else if (successCount > 0) {
|
||||
overallStatus = 'partial';
|
||||
}
|
||||
} else {
|
||||
// 默认动作:告警
|
||||
const result = await this.executeAction({
|
||||
type: 'alert',
|
||||
order: 1
|
||||
}, exception);
|
||||
actions.push(result);
|
||||
overallStatus = result.success ? 'success' : 'failure';
|
||||
}
|
||||
|
||||
const handlingResult: ExceptionHandlingResult = {
|
||||
id: `result_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||
exceptionId: exception.id,
|
||||
strategyId: strategy?.id || 'default',
|
||||
actions,
|
||||
overallStatus,
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
this.handlingResults.set(handlingResult.id, handlingResult);
|
||||
this.eventBus.publish('exception.handled', handlingResult);
|
||||
return handlingResult;
|
||||
}
|
||||
|
||||
// 执行异常处理动作
|
||||
private async executeAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
try {
|
||||
switch (action.type) {
|
||||
case 'retry':
|
||||
return await this.executeRetryAction(action, exception);
|
||||
case 'fallback':
|
||||
return await this.executeFallbackAction(action, exception);
|
||||
case 'alert':
|
||||
return await this.executeAlertAction(action, exception);
|
||||
case 'auto-fix':
|
||||
return await this.executeAutoFixAction(action, exception);
|
||||
case 'escalate':
|
||||
return await this.executeEscalateAction(action, exception);
|
||||
default:
|
||||
return {
|
||||
type: action.type,
|
||||
success: false,
|
||||
message: `Unknown action type: ${action.type}`,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
type: action.type,
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : 'Unknown error',
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 执行重试动作
|
||||
private async executeRetryAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
const maxRetries = action.parameters?.maxRetries || 3;
|
||||
const delayMs = action.parameters?.delayMs || 1000;
|
||||
|
||||
for (let i = 0; i < maxRetries; i++) {
|
||||
try {
|
||||
// 这里应该有实际的重试逻辑
|
||||
// 暂时模拟重试
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
|
||||
const success = Math.random() > 0.3; // 70% 成功率
|
||||
if (success) {
|
||||
return {
|
||||
type: 'retry',
|
||||
success: true,
|
||||
message: `Successfully retried after ${i + 1} attempt(s)`,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
// 重试失败,继续下一次
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'retry',
|
||||
success: false,
|
||||
message: `Failed after ${maxRetries} attempts`,
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
// 执行回退动作
|
||||
private async executeFallbackAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
// 这里应该有实际的回退逻辑
|
||||
// 暂时模拟回退
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
return {
|
||||
type: 'fallback',
|
||||
success: true,
|
||||
message: 'Fallback executed successfully',
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
// 执行告警动作
|
||||
private async executeAlertAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
// 这里应该有实际的告警逻辑
|
||||
// 暂时模拟告警
|
||||
await new Promise(resolve => setTimeout(resolve, 200));
|
||||
|
||||
this.eventBus.publish('exception.alert', {
|
||||
exception,
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'alert',
|
||||
success: true,
|
||||
message: 'Alert sent successfully',
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
// 执行自动修复动作
|
||||
private async executeAutoFixAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
// 这里应该有实际的自动修复逻辑
|
||||
// 暂时模拟自动修复
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
const success = Math.random() > 0.2; // 80% 成功率
|
||||
return {
|
||||
type: 'auto-fix',
|
||||
success,
|
||||
message: success ? 'Auto-fix executed successfully' : 'Auto-fix failed',
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
// 执行升级动作
|
||||
private async executeEscalateAction(action: ExceptionAction, exception: ExceptionInfo): Promise<ExceptionHandlingResult['actions'][0]> {
|
||||
// 这里应该有实际的升级逻辑
|
||||
// 暂时模拟升级
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
|
||||
this.eventBus.publish('exception.escalated', {
|
||||
exception,
|
||||
timestamp: new Date()
|
||||
});
|
||||
|
||||
return {
|
||||
type: 'escalate',
|
||||
success: true,
|
||||
message: 'Exception escalated successfully',
|
||||
timestamp: new Date()
|
||||
};
|
||||
}
|
||||
|
||||
// 获取异常处理结果
|
||||
getHandlingResult(resultId: string): ExceptionHandlingResult | undefined {
|
||||
return this.handlingResults.get(resultId);
|
||||
}
|
||||
|
||||
// 获取异常的处理结果
|
||||
getExceptionHandlingResults(exceptionId: string): ExceptionHandlingResult[] {
|
||||
return Array.from(this.handlingResults.values()).filter(r => r.exceptionId === exceptionId);
|
||||
}
|
||||
|
||||
// 获取策略的处理结果
|
||||
getStrategyHandlingResults(strategyId: string): ExceptionHandlingResult[] {
|
||||
return Array.from(this.handlingResults.values()).filter(r => r.strategyId === strategyId);
|
||||
}
|
||||
|
||||
// 批量处理异常
|
||||
async batchHandleExceptions(exceptionIds: string[], exceptionMonitor: any): Promise<ExceptionHandlingResult[]> {
|
||||
const results: ExceptionHandlingResult[] = [];
|
||||
|
||||
for (const exceptionId of exceptionIds) {
|
||||
const exception = exceptionMonitor.getException(exceptionId);
|
||||
if (exception) {
|
||||
const result = await this.handleException(exception);
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
// 生成异常处理报告
|
||||
async generateHandlingReport(startDate: Date, endDate: Date): Promise<{
|
||||
summary: {
|
||||
totalExceptions: number;
|
||||
successfullyHandled: number;
|
||||
partiallyHandled: number;
|
||||
failedHandled: number;
|
||||
mostUsedStrategies: string[];
|
||||
};
|
||||
details: ExceptionHandlingResult[];
|
||||
}> {
|
||||
const filteredResults = Array.from(this.handlingResults.values()).filter(
|
||||
r => r.timestamp >= startDate && r.timestamp <= endDate
|
||||
);
|
||||
|
||||
const successfullyHandled = filteredResults.filter(r => r.overallStatus === 'success').length;
|
||||
const partiallyHandled = filteredResults.filter(r => r.overallStatus === 'partial').length;
|
||||
const failedHandled = filteredResults.filter(r => r.overallStatus === 'failure').length;
|
||||
|
||||
// 统计最常用的策略
|
||||
const strategyUsage: Record<string, number> = {};
|
||||
for (const result of filteredResults) {
|
||||
strategyUsage[result.strategyId] = (strategyUsage[result.strategyId] || 0) + 1;
|
||||
}
|
||||
|
||||
const mostUsedStrategies = Object.entries(strategyUsage)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.slice(0, 5)
|
||||
.map(([strategyId]) => strategyId);
|
||||
|
||||
return {
|
||||
summary: {
|
||||
totalExceptions: filteredResults.length,
|
||||
successfullyHandled,
|
||||
partiallyHandled,
|
||||
failedHandled,
|
||||
mostUsedStrategies
|
||||
},
|
||||
details: filteredResults
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user