mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 21:30:17 +08:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 179f72da93 | |||
| 3023dc14e0 |
@@ -312,6 +312,12 @@ pub const TIER_WEEKLY_LIMIT: &str = "weekly_limit";
|
||||
/// 映射到 `subscription.monthly`。
|
||||
pub const TIER_MONTHLY: &str = "monthly";
|
||||
|
||||
/// Codex 免费方案的 30 天(月)滚动窗口 tier 名。付费方案的次要窗口是 7 天
|
||||
/// (`seven_day`),免费方案则是 30 天。由 `window_seconds_to_tier_name` 产出、
|
||||
/// tray 的月分组渲染、前端 `TIER_I18N_KEYS` 映射到 `subscription.thirtyDay`
|
||||
/// 三处共用同一标识。见 #3651。
|
||||
pub const TIER_THIRTY_DAY: &str = "30_day";
|
||||
|
||||
/// Gemini 用量分组名称(按模型而非时间窗口)。`classify_gemini_model` 输出。
|
||||
pub const TIER_GEMINI_PRO: &str = "gemini_pro";
|
||||
pub const TIER_GEMINI_FLASH: &str = "gemini_flash";
|
||||
@@ -632,8 +638,12 @@ struct CodexUsageResponse {
|
||||
/// 根据窗口秒数映射到 tier 名称(与 Claude 的命名兼容以复用前端 i18n)
|
||||
fn window_seconds_to_tier_name(secs: i64) -> String {
|
||||
match secs {
|
||||
18000 => "five_hour".to_string(),
|
||||
604800 => "seven_day".to_string(),
|
||||
18000 => TIER_FIVE_HOUR.to_string(),
|
||||
604800 => TIER_SEVEN_DAY.to_string(),
|
||||
// Codex 免费方案的 30 天窗口。显式映射到常量,与 tray 月分组、前端
|
||||
// TIER_I18N_KEYS 保持同一标识(否则动态回退虽也得到 "30_day",但字符串
|
||||
// 分散在多处、易和托盘/前端白名单脱节)。见 #3651。
|
||||
2_592_000 => TIER_THIRTY_DAY.to_string(),
|
||||
s => {
|
||||
let hours = s / 3600;
|
||||
if hours >= 24 {
|
||||
@@ -1340,3 +1350,21 @@ fn now_millis() -> i64 {
|
||||
.unwrap_or_default()
|
||||
.as_millis() as i64
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn window_seconds_map_to_expected_tier_names() {
|
||||
// 官方特例窗口
|
||||
assert_eq!(window_seconds_to_tier_name(18000), TIER_FIVE_HOUR);
|
||||
assert_eq!(window_seconds_to_tier_name(604800), TIER_SEVEN_DAY);
|
||||
// Codex 免费方案的次要窗口是 30 天(30 * 24 * 3600 = 2_592_000 秒)。
|
||||
// 前端 TIER_I18N_KEYS 与 tray 月分组都需要认得 "30_day",见 #3651。
|
||||
assert_eq!(window_seconds_to_tier_name(2_592_000), TIER_THIRTY_DAY);
|
||||
// 其他窗口按小时/天回退命名
|
||||
assert_eq!(window_seconds_to_tier_name(3600), "1_hour");
|
||||
assert_eq!(window_seconds_to_tier_name(86400), "1_day");
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -19,8 +19,13 @@ const W_TIER_NAMES: &[&str] = &[
|
||||
crate::services::subscription::TIER_SEVEN_DAY_OPUS,
|
||||
crate::services::subscription::TIER_SEVEN_DAY_SONNET,
|
||||
];
|
||||
// 火山方舟 Agent/Coding Plan 的月窗口(5h/周/月 三档)。
|
||||
const M_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_MONTHLY];
|
||||
// 月窗口分组:火山方舟 Agent/Coding Plan 的月窗口(5h/周/月 三档),
|
||||
// 以及 Codex 免费方案的 30 天窗口(#3651)——两者都归入 "m" 档,避免免费
|
||||
// Codex 账号在托盘里空白(前端 footer 能看到、托盘却不显示的不对称)。
|
||||
const M_TIER_NAMES: &[&str] = &[
|
||||
crate::services::subscription::TIER_MONTHLY,
|
||||
crate::services::subscription::TIER_THIRTY_DAY,
|
||||
];
|
||||
const GEMINI_PRO_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_GEMINI_PRO];
|
||||
const GEMINI_FLASH_TIER_NAMES: &[&str] = &[crate::services::subscription::TIER_GEMINI_FLASH];
|
||||
const GEMINI_FLASH_LITE_TIER_NAMES: &[&str] =
|
||||
@@ -882,7 +887,7 @@ mod tests {
|
||||
use crate::services::subscription::{
|
||||
CredentialStatus, QuotaTier, SubscriptionQuota, TIER_FIVE_HOUR, TIER_GEMINI_FLASH,
|
||||
TIER_GEMINI_FLASH_LITE, TIER_GEMINI_PRO, TIER_MONTHLY, TIER_SEVEN_DAY, TIER_SEVEN_DAY_OPUS,
|
||||
TIER_SEVEN_DAY_SONNET, TIER_WEEKLY_LIMIT,
|
||||
TIER_SEVEN_DAY_SONNET, TIER_THIRTY_DAY, TIER_WEEKLY_LIMIT,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@@ -964,6 +969,16 @@ mod tests {
|
||||
assert!(s.contains("l80%"), "expected l80% in {s}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn codex_summary_thirty_day_only_still_renders() {
|
||||
// Codex 免费方案的唯一 tier 是 30 天窗口。前端 footer 已能显示(TIER_I18N_KEYS
|
||||
// 有 "30_day"),托盘也必须能显示——否则就是这条不变量要防的非对称:footer
|
||||
// 能看到、托盘却空白。30_day 归入 "m" 月分组。见 #3651。
|
||||
let quota = make_quota("codex", true, vec![tier(TIER_THIRTY_DAY, 85.0)]);
|
||||
let s = format_subscription_summary("a).expect("should format");
|
||||
assert!(s.contains("m85%"), "expected m85% in {s}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn gemini_summary_emoji_reflects_highest_tier_including_lite() {
|
||||
// lite 是利用率最高的那条 → emoji 必须是红色,不能被 pro/flash 掩盖。
|
||||
|
||||
@@ -27,6 +27,8 @@ export const TIER_I18N_KEYS: Record<string, string> = {
|
||||
seven_day: "subscription.sevenDay",
|
||||
seven_day_opus: "subscription.sevenDayOpus",
|
||||
seven_day_sonnet: "subscription.sevenDaySonnet",
|
||||
// Codex 免费方案的次要窗口是 30 天(付费方案为 7 天)
|
||||
"30_day": "subscription.thirtyDay",
|
||||
// Gemini 模型分类
|
||||
gemini_pro: "subscription.geminiPro",
|
||||
gemini_flash: "subscription.geminiFlash",
|
||||
|
||||
@@ -2903,6 +2903,7 @@
|
||||
"sevenDay": "7-Day",
|
||||
"sevenDayOpus": "7-Day (Opus)",
|
||||
"sevenDaySonnet": "7-Day (Sonnet)",
|
||||
"thirtyDay": "30-Day",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
|
||||
@@ -2903,6 +2903,7 @@
|
||||
"sevenDay": "7日間",
|
||||
"sevenDayOpus": "7日間(Opus)",
|
||||
"sevenDaySonnet": "7日間(Sonnet)",
|
||||
"thirtyDay": "30日間",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
|
||||
@@ -2875,6 +2875,7 @@
|
||||
"sevenDay": "7 天",
|
||||
"sevenDayOpus": "7 天 (Opus)",
|
||||
"sevenDaySonnet": "7 天 (Sonnet)",
|
||||
"thirtyDay": "30 天",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
|
||||
@@ -2903,6 +2903,7 @@
|
||||
"sevenDay": "7天",
|
||||
"sevenDayOpus": "7天(Opus)",
|
||||
"sevenDaySonnet": "7天(Sonnet)",
|
||||
"thirtyDay": "30天",
|
||||
"geminiPro": "Pro",
|
||||
"geminiFlash": "Flash",
|
||||
"geminiFlashLite": "Flash Lite",
|
||||
|
||||
Reference in New Issue
Block a user