Files

111 lines
3.7 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Dashboard Stats Module
* 职责:根据 Alpine.store('data') 计算账号统计数据
*
* 调用时机:
* - dashboard 组件 init() 时
* - $store.data 更新时(通过 $watch 监听)
*
* 统计维度:
* - total: 启用账号总数(排除禁用账号)
* - active: 有可用配额的账号数
* - limited: 配额受限或失效的账号数
* - subscription: 按订阅级别分类ultra/pro/free
*
* @module DashboardStats
*/
window.DashboardStats = window.DashboardStats || {};
/**
* 更新账号统计数据
*
* 统计逻辑:
* 1. 仅统计启用的账号enabled !== false
* 2. 检查账号下所有追踪模型的配额
* 3. 如果任一追踪模型配额 <= 5%,则标记为 limited (Rate Limited Cooldown)
* 4. 如果所有追踪模型配额 > 5%,则标记为 active
* 5. 状态非 'ok' 的账号归为 limited
*
* @param {object} component - Dashboard 组件实例Alpine.js 上下文)
* @param {object} component.stats - 统计数据对象(会被修改)
* @param {number} component.stats.total - 启用账号总数
* @param {number} component.stats.active - 活跃账号数
* @param {number} component.stats.limited - 受限账号数
* @param {object} component.stats.subscription - 订阅级别分布
* @returns {void}
*/
window.DashboardStats.updateStats = function(component) {
const accounts = Alpine.store('data').accounts;
let active = 0, limited = 0;
// Only count enabled accounts in statistics
const enabledAccounts = accounts.filter(acc => acc.enabled !== false);
enabledAccounts.forEach(acc => {
if (acc.status === 'ok') {
const limits = Object.entries(acc.limits || {});
if (limits.length === 0) {
// No limit data available, consider limited to be safe
limited++;
return;
}
// Check if ANY tracked model is rate limited (<= 5%)
// We consider all models in the limits object as "tracked"
const hasRateLimitedModel = limits.some(([_, l]) => {
// Treat null/undefined fraction as 0 (limited)
if (!l || l.remainingFraction === null || l.remainingFraction === undefined) return true;
return l.remainingFraction <= 0.05;
});
if (hasRateLimitedModel) {
limited++;
} else {
active++;
}
} else {
limited++;
}
});
// TOTAL shows only enabled accounts
// Disabled accounts are excluded from all statistics
component.stats.total = enabledAccounts.length;
component.stats.active = active;
component.stats.limited = limited;
// Calculate model usage for rate limit details
let totalLimitedModels = 0;
let totalTrackedModels = 0;
enabledAccounts.forEach(acc => {
const limits = Object.entries(acc.limits || {});
limits.forEach(([id, l]) => {
totalTrackedModels++;
if (!l || l.remainingFraction == null || l.remainingFraction <= 0.05) {
totalLimitedModels++;
}
});
});
component.stats.modelUsage = {
limited: totalLimitedModels,
total: totalTrackedModels
};
// Calculate subscription tier distribution
const subscription = { ultra: 0, pro: 0, free: 0 };
enabledAccounts.forEach(acc => {
const tier = acc.subscription?.tier || 'free';
if (tier === 'ultra') {
subscription.ultra++;
} else if (tier === 'pro') {
subscription.pro++;
} else {
subscription.free++;
}
});
component.stats.subscription = subscription;
};