From 0d45d8669e4a7bb6d00c2320ec8ff6835423c64c Mon Sep 17 00:00:00 2001 From: wucm667 Date: Fri, 20 Mar 2026 10:22:54 +0800 Subject: [PATCH] fix: quota display shows stale cumulative usage after daily/weekly reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quota reset mechanism is lazy — quota_daily_used/quota_weekly_used in the database are only reset on the next IncrementQuotaUsed call. The scheduling layer (IsQuotaExceeded) correctly checks period expiry before enforcing limits, so the account remains usable. However, the API response mapper reads the raw DB value without checking expiry, causing the frontend to display cumulative usage (e.g. 110%) even after the reset period has passed. Add IsDailyQuotaPeriodExpired/IsWeeklyQuotaPeriodExpired methods and use them in the mapper to return used=0 when the period has expired. --- backend/internal/handler/dto/mappers.go | 6 ++++++ backend/internal/service/account.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go index d1d867ee..8c68b819 100644 --- a/backend/internal/handler/dto/mappers.go +++ b/backend/internal/handler/dto/mappers.go @@ -276,11 +276,17 @@ func AccountFromServiceShallow(a *service.Account) *Account { if limit := a.GetQuotaDailyLimit(); limit > 0 { out.QuotaDailyLimit = &limit used := a.GetQuotaDailyUsed() + if a.IsDailyQuotaPeriodExpired() { + used = 0 + } out.QuotaDailyUsed = &used } if limit := a.GetQuotaWeeklyLimit(); limit > 0 { out.QuotaWeeklyLimit = &limit used := a.GetQuotaWeeklyUsed() + if a.IsWeeklyQuotaPeriodExpired() { + used = 0 + } out.QuotaWeeklyUsed = &used } // 固定时间重置配置 diff --git a/backend/internal/service/account.go b/backend/internal/service/account.go index b6408f5f..d42c6a11 100644 --- a/backend/internal/service/account.go +++ b/backend/internal/service/account.go @@ -1543,6 +1543,24 @@ func isPeriodExpired(periodStart time.Time, dur time.Duration) bool { return time.Since(periodStart) >= dur } +// IsDailyQuotaPeriodExpired 检查日配额周期是否已过期(用于显示层判断是否需要将 used 归零) +func (a *Account) IsDailyQuotaPeriodExpired() bool { + start := a.getExtraTime("quota_daily_start") + if a.GetQuotaDailyResetMode() == "fixed" { + return a.isFixedDailyPeriodExpired(start) + } + return isPeriodExpired(start, 24*time.Hour) +} + +// IsWeeklyQuotaPeriodExpired 检查周配额周期是否已过期(用于显示层判断是否需要将 used 归零) +func (a *Account) IsWeeklyQuotaPeriodExpired() bool { + start := a.getExtraTime("quota_weekly_start") + if a.GetQuotaWeeklyResetMode() == "fixed" { + return a.isFixedWeeklyPeriodExpired(start) + } + return isPeriodExpired(start, 7*24*time.Hour) +} + // IsQuotaExceeded 检查 API Key 账号配额是否已超限(任一维度超限即返回 true) func (a *Account) IsQuotaExceeded() bool { // 总额度