// 系统监控页面JavaScript class SystemMonitor { constructor() { this.charts = {}; this.refreshInterval = null; this.refreshProgressInterval = null; this.isRefreshing = false; this.init(); } async init() { console.log('初始化系统监控页面...'); // 初始化图表 this.initCharts(); // 加载初始数据 await this.loadSystemStatus(); await this.loadRecentData(); // 启动自动刷新 this.startAutoRefresh(); // 启动刷新进度条 this.startRefreshProgress(); console.log('系统监控页面初始化完成'); } initCharts() { // 代理池趋势图 const proxyPoolCtx = document.getElementById('proxyPoolChart'); if (proxyPoolCtx) { this.charts.proxyPool = new Chart(proxyPoolCtx, { type: 'line', data: { labels: [], datasets: [{ label: '总代理数', data: [], borderColor: '#007bff', backgroundColor: 'rgba(0, 123, 255, 0.1)', tension: 0.4, fill: true }, { label: '可用代理', data: [], borderColor: '#28a745', backgroundColor: 'rgba(40, 167, 69, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', } }, scales: { y: { beginAtZero: true, ticks: { precision: 0 } } } } }); } // 任务执行率图 const taskRateCtx = document.getElementById('taskRateChart'); if (taskRateCtx) { this.charts.taskRate = new Chart(taskRateCtx, { type: 'line', data: { labels: [], datasets: [{ label: '成功率 (%)', data: [], borderColor: '#17a2b8', backgroundColor: 'rgba(23, 162, 184, 0.1)', tension: 0.4, fill: true }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', } }, scales: { y: { beginAtZero: true, max: 100, ticks: { callback: function(value) { return value + '%'; } } } } } }); } } async loadSystemStatus() { try { this.setRefreshing(true); // 获取系统状态 const statusResponse = await fetch('/api/dashboard/status'); const statusData = await statusResponse.json(); if (statusData.success) { this.updateSystemOverview(statusData.data); this.updateTasksStatus(statusData.data); this.updateProxyPoolStatus(statusData.data.proxies); this.updateResourceUsage(statusData.data); } // 更新最后刷新时间 this.updateLastRefreshTime(); } catch (error) { console.error('加载系统状态失败:', error); this.showAlert('加载系统状态失败: ' + error.message, 'danger'); } finally { this.setRefreshing(false); } } async loadRecentData() { try { // 加载图表数据 await this.updateCharts(); // 加载最近日志 await this.loadRecentLogs(); // 加载最近事件 await this.loadRecentEvents(); } catch (error) { console.error('加载最近数据失败:', error); } } updateSystemOverview(data) { // 更新系统状态指示器 const indicator = document.getElementById('systemStatusIndicator'); const statusText = document.getElementById('systemStatusText'); const healthBadge = document.getElementById('systemHealth'); if (data.proxies && data.proxies.valid > 0) { indicator.className = 'status-indicator online'; statusText.textContent = '在线'; healthBadge.textContent = '健康'; healthBadge.className = 'badge bg-success'; } else { indicator.className = 'status-indicator offline'; statusText.textContent = '异常'; healthBadge.textContent = '异常'; healthBadge.className = 'badge bg-danger'; } // 更新运行时间 document.getElementById('systemUptime').textContent = this.formatUptime(data.uptime); // 更新内存使用 const memoryHtml = `
已使用: ${data.memory.heapUsed}MB
总计: ${data.memory.heapTotal}MB `; document.getElementById('memoryUsage').innerHTML = memoryHtml; // 更新定时任务状态 if (data.scheduler) { const taskCount = data.scheduler.taskCount || 0; document.getElementById('schedulerStatus').innerHTML = `
${taskCount} 个任务运行中
自动调度正常 `; } } updateTasksStatus(data) { const container = document.getElementById('tasksStatus'); if (data.today_tasks) { const scrape = data.today_tasks.scrape; const validation = data.today_tasks.validation; container.innerHTML = `
抓取任务
成功: ${scrape.success} 失败: ${scrape.failed}
成功率: ${scrape.success_rate}%
验证任务
成功: ${validation.success} 失败: ${validation.failed}
成功率: ${validation.success_rate}%
`; } } updateProxyPoolStatus(proxies) { const container = document.getElementById('proxyPoolStatus'); container.innerHTML = `

${proxies.total}

总代理数

${proxies.valid}

可用代理

${proxies.invalid}

无效代理

可用率: ${proxies.validRate}
`; } updateResourceUsage(data) { if (data.memory) { const memoryPercent = Math.round((data.memory.heapUsed / data.memory.heapTotal) * 100); document.getElementById('memoryPercent').textContent = memoryPercent + '%'; document.getElementById('memoryProgressBar').style.width = memoryPercent + '%'; } if (data.proxies) { const validRate = parseFloat(data.proxies.validRate); document.getElementById('proxyValidRate').textContent = data.proxies.validRate; document.getElementById('proxyValidProgressBar').style.width = validRate + '%'; } // 模拟CPU使用率(实际项目中可以从系统API获取) const cpuUsage = Math.round(Math.random() * 30 + 10); // 10-40% document.getElementById('cpuUsage').textContent = cpuUsage + '%'; document.getElementById('cpuProgressBar').style.width = cpuUsage + '%'; } async updateCharts() { try { // 更新代理池趋势图 const proxyResponse = await fetch('/api/dashboard/charts/proxies'); const proxyResult = await proxyResponse.json(); if (proxyResult.success && this.charts.proxyPool) { const labels = proxyResult.data.map(item => new Date(item.date).toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' }) ); // 计算累计数据 let totalRunning = 0; let validRunning = 0; const totalData = []; const validData = []; proxyResult.data.forEach(item => { totalRunning += item.total_added || 0; validRunning += item.valid_added || 0; totalData.push(totalRunning); validData.push(validRunning); }); this.charts.proxyPool.data.labels = labels; this.charts.proxyPool.data.datasets[0].data = totalData; this.charts.proxyPool.data.datasets[1].data = validData; this.charts.proxyPool.update(); } // 更新任务执行率图 const taskResponse = await fetch('/api/history/stats/summary'); const taskResult = await taskResponse.json(); if (taskResult.success && this.charts.taskRate) { // 使用最近7天的数据 const labels = []; const successRates = []; for (let i = 6; i >= 0; i--) { const date = new Date(); date.setDate(date.getDate() - i); labels.push(date.toLocaleDateString('zh-CN', { month: 'short', day: 'numeric' })); successRates.push(Math.random() * 30 + 70); // 模拟70-100%成功率 } this.charts.taskRate.data.labels = labels; this.charts.taskRate.data.datasets[0].data = successRates; this.charts.taskRate.update(); } } catch (error) { console.error('更新图表失败:', error); } } async loadRecentLogs() { try { const response = await fetch('/api/history/logs/system?limit=10'); const result = await response.json(); if (result.success) { this.renderRecentLogs(result.data); } } catch (error) { console.error('加载最近日志失败:', error); } } async loadRecentEvents() { try { const response = await fetch('/api/history?limit=10'); const result = await response.json(); if (result.success) { this.renderRecentEvents(result.data); } } catch (error) { console.error('加载最近事件失败:', error); } } renderRecentLogs(logs) { const container = document.getElementById('recentLogs'); if (!logs || logs.length === 0) { container.innerHTML = '
暂无日志
'; return; } container.innerHTML = logs.map(log => `
${log.level.toUpperCase()}
${log.message}
${this.formatDateTime(log.timestamp)} - ${log.source}
`).join(''); } renderRecentEvents(events) { const container = document.getElementById('recentEvents'); if (!events || events.length === 0) { container.innerHTML = '
暂无事件
'; return; } container.innerHTML = events.map(event => `
${event.task_name}
${this.formatDateTime(event.start_time)} - ${this.getStatusText(event.status)}
${event.result_summary ? `
${event.result_summary}
` : ''}
${this.getStatusText(event.status)}
`).join(''); } startAutoRefresh() { // 每30秒刷新一次数据 this.refreshInterval = setInterval(() => { this.loadSystemStatus(); }, 30000); // 每5分钟刷新最近数据 setInterval(() => { this.loadRecentData(); }, 300000); } startRefreshProgress() { this.refreshProgressInterval = setInterval(() => { const progressBar = document.getElementById('refreshProgress'); if (progressBar) { const currentWidth = parseFloat(progressBar.style.width) || 100; const newWidth = Math.max(0, currentWidth - 3.33); // 30秒内从100%到0% progressBar.style.width = newWidth + '%'; } }, 1000); } updateLastRefreshTime() { const now = new Date(); const timeString = now.toLocaleTimeString('zh-CN'); document.getElementById('lastUpdateTime').textContent = timeString; } setRefreshing(refreshing) { this.isRefreshing = refreshing; if (refreshing) { // 重置进度条 const progressBar = document.getElementById('refreshProgress'); if (progressBar) { progressBar.style.width = '100%'; } } } // 工具函数 formatUptime(seconds) { const hours = Math.floor(seconds / 3600); const minutes = Math.floor((seconds % 3600) / 60); if (hours > 24) { const days = Math.floor(hours / 24); const remainingHours = hours % 24; return `${days}天 ${remainingHours}小时 ${minutes}分钟`; } return `${hours}小时 ${minutes}分钟`; } formatDateTime(dateString) { const date = new Date(dateString); return date.toLocaleString('zh-CN', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } getLogLevelClass(level) { const classes = { 'error': 'bg-danger', 'warning': 'bg-warning', 'info': 'bg-info', 'debug': 'bg-secondary' }; return classes[level] || 'bg-secondary'; } getStatusClass(status) { const classes = { 'success': 'bg-success', 'failed': 'bg-danger', 'running': 'bg-warning', 'pending': 'bg-secondary' }; return classes[status] || 'bg-secondary'; } getStatusText(status) { const texts = { 'success': '成功', 'failed': '失败', 'running': '运行中', 'pending': '等待中' }; return texts[status] || status; } getTaskIcon(taskType) { const icons = { 'scrape': 'download', 'validation': 'check2-square', 'health_check': 'heart-pulse' }; return icons[taskType] || 'gear'; } getTaskColor(status) { const colors = { 'success': 'success', 'failed': 'danger', 'running': 'warning', 'pending': 'secondary' }; return colors[status] || 'secondary'; } showAlert(message, type = 'info') { const alertContainer = document.getElementById('alertContainer'); if (!alertContainer) return; const alertId = 'alert-' + Date.now(); const alertHtml = ` `; alertContainer.insertAdjacentHTML('beforeend', alertHtml); // 自动移除提示 setTimeout(() => { const alertElement = document.getElementById(alertId); if (alertElement) { alertElement.remove(); } }, 5000); } getAlertIcon(type) { const icons = { 'success': 'check-circle-fill', 'danger': 'exclamation-triangle-fill', 'warning': 'exclamation-triangle-fill', 'info': 'info-circle-fill' }; return icons[type] || 'info-circle-fill'; } stopAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); this.refreshInterval = null; } if (this.refreshProgressInterval) { clearInterval(this.refreshProgressInterval); this.refreshProgressInterval = null; } } } // 全局函数(供HTML调用) let systemMonitor; async function refreshMonitoring() { if (systemMonitor && !systemMonitor.isRefreshing) { await systemMonitor.loadSystemStatus(); await systemMonitor.loadRecentData(); systemMonitor.showAlert('监控数据已刷新', 'info'); } } // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', () => { systemMonitor = new SystemMonitor(); }); // 页面卸载时清理 window.addEventListener('beforeunload', () => { if (systemMonitor) { systemMonitor.stopAutoRefresh(); } });