From 8824400c3e722f66ebd6706509d8a7397ef61be7 Mon Sep 17 00:00:00 2001 From: liuxiongfeng Date: Tue, 3 Feb 2026 12:08:13 +0800 Subject: [PATCH] =?UTF-8?q?feat(accounts):=20=E8=B4=A6=E5=8F=B7=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=98=BE=E7=A4=BA=20Antigravity=20scope=20=E7=BA=A7?= =?UTF-8?q?=E5=88=AB=E9=99=90=E6=B5=81=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 DTO 新增 scope_rate_limits 字段,从 extra 提取限流信息 - 前端状态列显示 scope 级限流徽章(Claude/Gemini/Image) - 清除速率限制时同时清除账号级和 scope 级限流(已有实现) Cherry-picked from slovx2/sub2api: 66f49b67 --- backend/internal/handler/dto/mappers.go | 11 +++ backend/internal/handler/dto/types.go | 8 ++ .../account/AccountStatusIndicator.vue | 78 +++++++++++++++++++ frontend/src/i18n/locales/en.ts | 1 + frontend/src/i18n/locales/zh.ts | 1 + frontend/src/types/index.ts | 3 + 6 files changed, 102 insertions(+) diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go index 886a5535..5663e299 100644 --- a/backend/internal/handler/dto/mappers.go +++ b/backend/internal/handler/dto/mappers.go @@ -204,6 +204,17 @@ func AccountFromServiceShallow(a *service.Account) *Account { } } + if scopeLimits := a.GetAntigravityScopeRateLimits(); len(scopeLimits) > 0 { + out.ScopeRateLimits = make(map[string]ScopeRateLimitInfo, len(scopeLimits)) + now := time.Now() + for scope, remainingSec := range scopeLimits { + out.ScopeRateLimits[scope] = ScopeRateLimitInfo{ + ResetAt: now.Add(time.Duration(remainingSec) * time.Second), + RemainingSec: remainingSec, + } + } + } + return out } diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go index 4cfaef5f..0a58391c 100644 --- a/backend/internal/handler/dto/types.go +++ b/backend/internal/handler/dto/types.go @@ -2,6 +2,11 @@ package dto import "time" +type ScopeRateLimitInfo struct { + ResetAt time.Time `json:"reset_at"` + RemainingSec int64 `json:"remaining_sec"` +} + type User struct { ID int64 `json:"id"` Email string `json:"email"` @@ -108,6 +113,9 @@ type Account struct { RateLimitResetAt *time.Time `json:"rate_limit_reset_at"` OverloadUntil *time.Time `json:"overload_until"` + // Antigravity scope 级限流状态(从 extra 提取) + ScopeRateLimits map[string]ScopeRateLimitInfo `json:"scope_rate_limits,omitempty"` + TempUnschedulableUntil *time.Time `json:"temp_unschedulable_until"` TempUnschedulableReason string `json:"temp_unschedulable_reason"` diff --git a/frontend/src/components/account/AccountStatusIndicator.vue b/frontend/src/components/account/AccountStatusIndicator.vue index 02c962f1..6bb19b87 100644 --- a/frontend/src/components/account/AccountStatusIndicator.vue +++ b/frontend/src/components/account/AccountStatusIndicator.vue @@ -56,6 +56,65 @@ > + + +
+ + + 429 + + +
+ {{ t('admin.accounts.status.rateLimitedUntil', { time: formatTime(account.rate_limit_reset_at) }) }} +
+
+
+ + + + + +
+ + + 529 + + +
+ {{ t('admin.accounts.status.overloadedUntil', { time: formatTime(account.overload_until) }) }} +
+
+
@@ -81,6 +140,25 @@ const isRateLimited = computed(() => { return new Date(props.account.rate_limit_reset_at) > new Date() }) +// Computed: active scope rate limits (Antigravity) +const activeScopeRateLimits = computed(() => { + const scopeLimits = props.account.scope_rate_limits + if (!scopeLimits) return [] + const now = new Date() + return Object.entries(scopeLimits) + .filter(([, info]) => new Date(info.reset_at) > now) + .map(([scope, info]) => ({ scope, reset_at: info.reset_at })) +}) + +const formatScopeName = (scope: string): string => { + const names: Record = { + claude: 'Claude', + gemini_text: 'Gemini', + gemini_image: 'Image' + } + return names[scope] || scope +} + // Computed: is overloaded (529) const isOverloaded = computed(() => { if (!props.account.overload_until) return false diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index d0b38605..beb0bdf3 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -1183,6 +1183,7 @@ export default { overloaded: 'Overloaded', tempUnschedulable: 'Temp Unschedulable', rateLimitedUntil: 'Rate limited until {time}', + scopeRateLimitedUntil: '{scope} rate limited until {time}', overloadedUntil: 'Overloaded until {time}', viewTempUnschedDetails: 'View temp unschedulable details' }, diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index a1067689..328ad1bc 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -1305,6 +1305,7 @@ export default { overloaded: '过载中', tempUnschedulable: '临时不可调度', rateLimitedUntil: '限流中,重置时间:{time}', + scopeRateLimitedUntil: '{scope} 限流中,重置时间:{time}', overloadedUntil: '负载过重,重置时间:{time}', viewTempUnschedDetails: '查看临时不可调度详情' }, diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 7c6cbf52..0d904ab5 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -559,6 +559,9 @@ export interface Account { temp_unschedulable_until: string | null temp_unschedulable_reason: string | null + // Antigravity scope 级限流状态 + scope_rate_limits?: Record + // Session window fields (5-hour window) session_window_start: string | null session_window_end: string | null