// 供应商管理功能模块 import { providerStats, updateProviderStats } from './constants.js'; import { showToast } from './utils.js'; // 保存初始服务器时间和运行时间 let initialServerTime = null; let initialUptime = null; let initialLoadTime = null; /** * 加载系统信息 */ async function loadSystemInfo() { try { const response = await fetch('/api/system'); const data = await response.json(); const nodeVersionEl = document.getElementById('nodeVersion'); const serverTimeEl = document.getElementById('serverTime'); const memoryUsageEl = document.getElementById('memoryUsage'); const uptimeEl = document.getElementById('uptime'); if (nodeVersionEl) nodeVersionEl.textContent = data.nodeVersion || '--'; if (memoryUsageEl) memoryUsageEl.textContent = data.memoryUsage || '--'; // 保存初始时间用于本地计算 if (data.serverTime && data.uptime !== undefined) { initialServerTime = new Date(data.serverTime); initialUptime = data.uptime; initialLoadTime = Date.now(); } // 初始显示 if (serverTimeEl) serverTimeEl.textContent = data.serverTime || '--'; if (uptimeEl) uptimeEl.textContent = data.uptime ? formatUptime(data.uptime) : '--'; } catch (error) { console.error('Failed to load system info:', error); } } /** * 更新服务器时间和运行时间显示(本地计算) */ function updateTimeDisplay() { if (!initialServerTime || initialUptime === null || !initialLoadTime) { return; } const serverTimeEl = document.getElementById('serverTime'); const uptimeEl = document.getElementById('uptime'); // 计算经过的秒数 const elapsedSeconds = Math.floor((Date.now() - initialLoadTime) / 1000); // 更新服务器时间 if (serverTimeEl) { const currentServerTime = new Date(initialServerTime.getTime() + elapsedSeconds * 1000); serverTimeEl.textContent = currentServerTime.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); } // 更新运行时间 if (uptimeEl) { const currentUptime = initialUptime + elapsedSeconds; uptimeEl.textContent = formatUptime(currentUptime); } } /** * 加载提供商列表 */ async function loadProviders() { try { const response = await fetch('/api/providers'); const data = await response.json(); renderProviders(data); } catch (error) { console.error('Failed to load providers:', error); } } /** * 渲染提供商列表 * @param {Object} providers - 提供商数据 */ function renderProviders(providers) { const container = document.getElementById('providersList'); if (!container) return; container.innerHTML = ''; // 检查是否有供应商池数据 const hasProviders = Object.keys(providers).length > 0; const statsGrid = document.querySelector('#providers .stats-grid'); if (hasProviders) { // 显示统计卡片 if (statsGrid) statsGrid.style.display = 'grid'; // 计算总统计 let totalAccounts = 0; let totalHealthy = 0; // 定义提供商显示顺序 const providerDisplayOrder = [ 'gemini-cli-oauth', 'openai-custom', 'claude-custom', 'claude-kiro-oauth', 'openai-qwen-oauth', 'openaiResponses-custom' ]; // 获取所有提供商类型并按指定顺序排序 const allProviderTypes = Object.keys(providers); const sortedProviderTypes = providerDisplayOrder.filter(type => allProviderTypes.includes(type)) .concat(allProviderTypes.filter(type => !providerDisplayOrder.includes(type))); // 按照排序后的提供商类型渲染 sortedProviderTypes.forEach((providerType) => { const accounts = providers[providerType]; const providerDiv = document.createElement('div'); providerDiv.className = 'provider-item'; providerDiv.dataset.providerType = providerType; providerDiv.style.cursor = 'pointer'; const healthyCount = accounts.filter(acc => acc.isHealthy).length; const totalCount = accounts.length; const usageCount = accounts.reduce((sum, acc) => sum + (acc.usageCount || 0), 0); const errorCount = accounts.reduce((sum, acc) => sum + (acc.errorCount || 0), 0); totalAccounts += totalCount; totalHealthy += healthyCount; // 更新全局统计变量 if (!providerStats.providerTypeStats[providerType]) { providerStats.providerTypeStats[providerType] = { totalAccounts: 0, healthyAccounts: 0, totalUsage: 0, totalErrors: 0, lastUpdate: null }; } const typeStats = providerStats.providerTypeStats[providerType]; typeStats.totalAccounts = totalCount; typeStats.healthyAccounts = healthyCount; typeStats.totalUsage = usageCount; typeStats.totalErrors = errorCount; typeStats.lastUpdate = new Date().toISOString(); providerDiv.innerHTML = `
${providerType}
${healthyCount}/${totalCount} 健康
总账户 ${totalCount}
健康账户 ${healthyCount}
使用次数 ${usageCount}
错误次数 ${errorCount}
`; // 添加点击事件 - 整个供应商组都可以点击 providerDiv.addEventListener('click', (e) => { e.preventDefault(); openProviderManager(providerType); }); container.appendChild(providerDiv); }); // 更新统计卡片数据 const activeProviders = Object.keys(providers).length; updateProviderStatsDisplay(activeProviders, totalHealthy, totalAccounts); } else { // 隐藏统计卡片 if (statsGrid) statsGrid.style.display = 'none'; // 显示无数据提示 container.innerHTML = '

暂无供应商池配置

'; } } /** * 更新提供商统计信息 * @param {number} activeProviders - 活跃提供商数 * @param {number} healthyProviders - 健康提供商数 * @param {number} totalAccounts - 总账户数 */ function updateProviderStatsDisplay(activeProviders, healthyProviders, totalAccounts) { // 更新全局统计变量 const newStats = { activeProviders, healthyProviders, totalAccounts, lastUpdateTime: new Date().toISOString() }; updateProviderStats(newStats); // 计算总请求数和错误数 let totalUsage = 0; let totalErrors = 0; Object.values(providerStats.providerTypeStats).forEach(typeStats => { totalUsage += typeStats.totalUsage || 0; totalErrors += typeStats.totalErrors || 0; }); const finalStats = { ...newStats, totalRequests: totalUsage, totalErrors: totalErrors }; updateProviderStats(finalStats); // 修改:根据使用次数统计"活跃提供商"和"活动连接" // "活跃提供商":统计有使用次数(usageCount > 0)的提供商类型数量 let activeProvidersByUsage = 0; Object.entries(providerStats.providerTypeStats).forEach(([providerType, typeStats]) => { if (typeStats.totalUsage > 0) { activeProvidersByUsage++; } }); // "活动连接":统计所有提供商账户的使用次数总和 const activeConnections = totalUsage; // 更新页面显示 const activeProvidersEl = document.getElementById('activeProviders'); const healthyProvidersEl = document.getElementById('healthyProviders'); const activeConnectionsEl = document.getElementById('activeConnections'); if (activeProvidersEl) activeProvidersEl.textContent = activeProvidersByUsage; if (healthyProvidersEl) healthyProvidersEl.textContent = healthyProviders; if (activeConnectionsEl) activeConnectionsEl.textContent = activeConnections; // 打印调试信息到控制台 console.log('Provider Stats Updated:', { activeProviders, activeProvidersByUsage, healthyProviders, totalAccounts, totalUsage, totalErrors, providerTypeStats: providerStats.providerTypeStats }); } /** * 打开供应商管理模态框 * @param {string} providerType - 提供商类型 */ async function openProviderManager(providerType) { try { const response = await fetch(`/api/providers/${encodeURIComponent(providerType)}`); const data = await response.json(); showProviderManagerModal(data); } catch (error) { console.error('Failed to load provider details:', error); showToast('加载供应商详情失败', 'error'); } } // 导入工具函数 import { formatUptime } from './utils.js'; export { loadSystemInfo, updateTimeDisplay, loadProviders, renderProviders, updateProviderStatsDisplay, openProviderManager };