mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-02 18:41:35 +08:00
feat(usage): add official subscription quota template with unified tier rendering
Changes: - Add official_subscription template type for Claude/Codex/Gemini - Replace implicit 'category=official auto-query' with explicit opt-in template - Default disabled; users enable via usage script modal with configurable interval - Unify tier→label mapping across subscription and script paths via labeled_tier_parts() - Fix tray rendering: week aliases (seven_day/opus/sonnet) now use highest utilization - Add depth guard: official_subscription checks enabled flag in query_provider_usage_inner - Add cache invalidation symmetry: invalidate_subscription() for disabled providers - i18n: add templateOfficialSubscription + hint in zh/en/ja/zh-TW Backend (Rust): - provider.rs: add TEMPLATE_TYPE_OFFICIAL_SUBSCRIPTION branch, flatten SubscriptionQuota→UsageData - tray.rs: extract labeled_tier_parts() shared by both summary functions, use max_by for multi-alias groups - usage_cache.rs: add invalidate_subscription() method - Test coverage: add week-alias highest-utilization tests for both paths Frontend (TypeScript): - UsageScriptModal: add official_subscription to templates, auto-detect for official providers - ProviderCard: gate useUsageQuery with !isOfficialSubscriptionUsage, pass autoQueryInterval to footer - SubscriptionQuotaFooter: accept autoQueryInterval prop, default 0 (disabled) - constants.ts: add TEMPLATE_TYPES.OFFICIAL_SUBSCRIPTION Fixes tier rendering regression where: - Claude/Codex: seven_day was missed (only weekly_limit matched) → lost 7-day window in tray - Gemini: gemini_pro/flash/flash_lite fell through to fallback → leaked machine names - Multi-window (opus+sonnet): find() took first, not worst → underestimated utilization and emoji color All tests pass (cargo test + cargo clippy clean).
This commit is contained in:
@@ -15,6 +15,7 @@ use std::str::FromStr;
|
||||
const TEMPLATE_TYPE_GITHUB_COPILOT: &str = "github_copilot";
|
||||
const TEMPLATE_TYPE_TOKEN_PLAN: &str = "token_plan";
|
||||
const TEMPLATE_TYPE_BALANCE: &str = "balance";
|
||||
const TEMPLATE_TYPE_OFFICIAL_SUBSCRIPTION: &str = "official_subscription";
|
||||
const COPILOT_UNIT_PREMIUM: &str = "requests";
|
||||
|
||||
/// 获取所有供应商
|
||||
@@ -596,6 +597,50 @@ async fn query_provider_usage_inner(
|
||||
.map_err(|e| format!("Failed to query balance: {e}"));
|
||||
}
|
||||
|
||||
// ── 官方订阅额度查询路径 ──
|
||||
if template_type == TEMPLATE_TYPE_OFFICIAL_SUBSCRIPTION {
|
||||
if !usage_script.map(|s| s.enabled).unwrap_or(false) {
|
||||
return Ok(crate::provider::UsageResult {
|
||||
success: false,
|
||||
data: None,
|
||||
error: Some("Usage query is disabled".to_string()),
|
||||
});
|
||||
}
|
||||
|
||||
let quota = crate::services::subscription::get_subscription_quota(app_type.as_str())
|
||||
.await
|
||||
.map_err(|e| format!("Failed to query subscription quota: {e}"))?;
|
||||
|
||||
if !quota.success {
|
||||
return Ok(crate::provider::UsageResult {
|
||||
success: false,
|
||||
data: None,
|
||||
error: quota.error.or(quota.credential_message),
|
||||
});
|
||||
}
|
||||
|
||||
let data: Vec<crate::provider::UsageData> = quota
|
||||
.tiers
|
||||
.iter()
|
||||
.map(|tier| crate::provider::UsageData {
|
||||
plan_name: Some(tier.name.clone()),
|
||||
remaining: Some(100.0 - tier.utilization),
|
||||
total: Some(100.0),
|
||||
used: Some(tier.utilization),
|
||||
unit: Some("%".to_string()),
|
||||
is_valid: Some(true),
|
||||
invalid_message: None,
|
||||
extra: tier.resets_at.clone(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
return Ok(crate::provider::UsageResult {
|
||||
success: true,
|
||||
data: if data.is_empty() { None } else { Some(data) },
|
||||
error: None,
|
||||
});
|
||||
}
|
||||
|
||||
// ── 通用 JS 脚本路径 ──
|
||||
ProviderService::query_usage(state, app_type, provider_id)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user