mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-28 16:56:16 +08:00
* fix(subscription): display Codex free-plan 30-day quota window (#3651) Codex free accounts are metered on a rolling 30-day secondary window (limit_window_seconds = 2,592,000) instead of the weekly window used by paid plans. The backend already maps this correctly to the tier name "30_day", but the frontend TIER_I18N_KEYS whitelist had no entry for it, so SubscriptionQuotaView filtered the tier out. When that was the only surviving tier (as happens for free accounts), the whole quota footer rendered nothing — free accounts showed no remaining quota or reset time while paid accounts worked. - Add "30_day" -> subscription.thirtyDay to TIER_I18N_KEYS - Add the thirtyDay label to all four locales (zh/en/ja/zh-TW) - Add a unit test locking in window_seconds_to_tier_name mappings, including the 30-day case Fixes #3651 * fix(tray): render Codex free-plan 30-day window in tray menu The tray keeps its own tier-name whitelist (TIER_LABEL_GROUPS) independent of the frontend TIER_I18N_KEYS. Its month group only listed "monthly", so a free Codex account whose only tier is "30_day" produced empty labeled parts → format_subscription_summary returned None → the tray showed no quota, even though the footer now does. That breaks the invariant the existing gemini_summary_lite_only_still_renders test guards: any tier visible in the footer must not leave the tray blank. - Add TIER_THIRTY_DAY ("30_day") constant so the string is single-sourced across backend mapping, tray grouping, and the frontend whitelist - Map 2_592_000s explicitly to it in window_seconds_to_tier_name - Add TIER_THIRTY_DAY to the tray month ("m") group - Add codex_summary_thirty_day_only_still_renders regression test Follow-up to the review on #3651 / PR for the 30-day footer fix.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user