// 系统监控页面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.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 = `
${message}
`;
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();
}
});