"git@web.lueluesay.top:chenxi/sub2api.git" did not exist on "e88b2890d19b95ea34d63bc5f4ee4f4a58f247d4"
Commit 078fefed authored by shaw's avatar shaw
Browse files

fix: 修复账号管理页面容量列显示为0的bug

parent 5b10af85
...@@ -240,77 +240,77 @@ func (h *AccountHandler) List(c *gin.Context) { ...@@ -240,77 +240,77 @@ func (h *AccountHandler) List(c *gin.Context) {
var windowCosts map[int64]float64 var windowCosts map[int64]float64
var activeSessions map[int64]int var activeSessions map[int64]int
var rpmCounts map[int64]int var rpmCounts map[int64]int
if !lite {
// Get current concurrency counts for all accounts // 始终获取并发数(Redis ZCARD,极低开销)
if h.concurrencyService != nil { if h.concurrencyService != nil {
if cc, ccErr := h.concurrencyService.GetAccountConcurrencyBatch(c.Request.Context(), accountIDs); ccErr == nil && cc != nil { if cc, ccErr := h.concurrencyService.GetAccountConcurrencyBatch(c.Request.Context(), accountIDs); ccErr == nil && cc != nil {
concurrencyCounts = cc concurrencyCounts = cc
}
} }
// 识别需要查询窗口费用、会话数和 RPM 的账号(Anthropic OAuth/SetupToken 且启用了相应功能) }
windowCostAccountIDs := make([]int64, 0)
sessionLimitAccountIDs := make([]int64, 0) // 识别需要查询窗口费用、会话数和 RPM 的账号(Anthropic OAuth/SetupToken 且启用了相应功能)
rpmAccountIDs := make([]int64, 0) windowCostAccountIDs := make([]int64, 0)
sessionIdleTimeouts := make(map[int64]time.Duration) // 各账号的会话空闲超时配置 sessionLimitAccountIDs := make([]int64, 0)
for i := range accounts { rpmAccountIDs := make([]int64, 0)
acc := &accounts[i] sessionIdleTimeouts := make(map[int64]time.Duration) // 各账号的会话空闲超时配置
if acc.IsAnthropicOAuthOrSetupToken() { for i := range accounts {
if acc.GetWindowCostLimit() > 0 { acc := &accounts[i]
windowCostAccountIDs = append(windowCostAccountIDs, acc.ID) if acc.IsAnthropicOAuthOrSetupToken() {
} if acc.GetWindowCostLimit() > 0 {
if acc.GetMaxSessions() > 0 { windowCostAccountIDs = append(windowCostAccountIDs, acc.ID)
sessionLimitAccountIDs = append(sessionLimitAccountIDs, acc.ID) }
sessionIdleTimeouts[acc.ID] = time.Duration(acc.GetSessionIdleTimeoutMinutes()) * time.Minute if acc.GetMaxSessions() > 0 {
} sessionLimitAccountIDs = append(sessionLimitAccountIDs, acc.ID)
if acc.GetBaseRPM() > 0 { sessionIdleTimeouts[acc.ID] = time.Duration(acc.GetSessionIdleTimeoutMinutes()) * time.Minute
rpmAccountIDs = append(rpmAccountIDs, acc.ID) }
} if acc.GetBaseRPM() > 0 {
rpmAccountIDs = append(rpmAccountIDs, acc.ID)
} }
} }
}
// 获取 RPM 计数(批量查询) // 始终获取 RPM 计数(Redis GET,极低开销)
if len(rpmAccountIDs) > 0 && h.rpmCache != nil { if len(rpmAccountIDs) > 0 && h.rpmCache != nil {
rpmCounts, _ = h.rpmCache.GetRPMBatch(c.Request.Context(), rpmAccountIDs) rpmCounts, _ = h.rpmCache.GetRPMBatch(c.Request.Context(), rpmAccountIDs)
if rpmCounts == nil { if rpmCounts == nil {
rpmCounts = make(map[int64]int) rpmCounts = make(map[int64]int)
}
} }
}
// 获取活跃会话数(批量查询,传入各账号的 idleTimeout 配置) // 始终获取活跃会话数(Redis ZCARD,低开销)
if len(sessionLimitAccountIDs) > 0 && h.sessionLimitCache != nil { if len(sessionLimitAccountIDs) > 0 && h.sessionLimitCache != nil {
activeSessions, _ = h.sessionLimitCache.GetActiveSessionCountBatch(c.Request.Context(), sessionLimitAccountIDs, sessionIdleTimeouts) activeSessions, _ = h.sessionLimitCache.GetActiveSessionCountBatch(c.Request.Context(), sessionLimitAccountIDs, sessionIdleTimeouts)
if activeSessions == nil { if activeSessions == nil {
activeSessions = make(map[int64]int) activeSessions = make(map[int64]int)
}
} }
}
// 获取窗口费用(并行查询) // 仅非 lite 模式获取窗口费用(PostgreSQL 聚合查询,高开销)
if len(windowCostAccountIDs) > 0 { if !lite && len(windowCostAccountIDs) > 0 {
windowCosts = make(map[int64]float64) windowCosts = make(map[int64]float64)
var mu sync.Mutex var mu sync.Mutex
g, gctx := errgroup.WithContext(c.Request.Context()) g, gctx := errgroup.WithContext(c.Request.Context())
g.SetLimit(10) // 限制并发数 g.SetLimit(10) // 限制并发数
for i := range accounts { for i := range accounts {
acc := &accounts[i] acc := &accounts[i]
if !acc.IsAnthropicOAuthOrSetupToken() || acc.GetWindowCostLimit() <= 0 { if !acc.IsAnthropicOAuthOrSetupToken() || acc.GetWindowCostLimit() <= 0 {
continue continue
}
accCopy := acc // 闭包捕获
g.Go(func() error {
// 使用统一的窗口开始时间计算逻辑(考虑窗口过期情况)
startTime := accCopy.GetCurrentWindowStartTime()
stats, err := h.accountUsageService.GetAccountWindowStats(gctx, accCopy.ID, startTime)
if err == nil && stats != nil {
mu.Lock()
windowCosts[accCopy.ID] = stats.StandardCost // 使用标准费用
mu.Unlock()
}
return nil // 不返回错误,允许部分失败
})
} }
_ = g.Wait() accCopy := acc // 闭包捕获
g.Go(func() error {
// 使用统一的窗口开始时间计算逻辑(考虑窗口过期情况)
startTime := accCopy.GetCurrentWindowStartTime()
stats, err := h.accountUsageService.GetAccountWindowStats(gctx, accCopy.ID, startTime)
if err == nil && stats != nil {
mu.Lock()
windowCosts[accCopy.ID] = stats.StandardCost // 使用标准费用
mu.Unlock()
}
return nil // 不返回错误,允许部分失败
})
} }
_ = g.Wait()
} }
// Build response with concurrency info // Build response with concurrency info
......
...@@ -546,18 +546,27 @@ const { ...@@ -546,18 +546,27 @@ const {
handlePageSizeChange: baseHandlePageSizeChange handlePageSizeChange: baseHandlePageSizeChange
} = useTableLoader<Account, any>({ } = useTableLoader<Account, any>({
fetchFn: adminAPI.accounts.list, fetchFn: adminAPI.accounts.list,
initialParams: { platform: '', type: '', status: '', group: '', search: '', lite: '1' } initialParams: { platform: '', type: '', status: '', group: '', search: '' }
}) })
const resetAutoRefreshCache = () => { const resetAutoRefreshCache = () => {
autoRefreshETag.value = null autoRefreshETag.value = null
} }
const isFirstLoad = ref(true)
const load = async () => { const load = async () => {
hasPendingListSync.value = false hasPendingListSync.value = false
resetAutoRefreshCache() resetAutoRefreshCache()
pendingTodayStatsRefresh.value = false pendingTodayStatsRefresh.value = false
if (isFirstLoad.value) {
;(params as any).lite = '1'
}
await baseLoad() await baseLoad()
if (isFirstLoad.value) {
isFirstLoad.value = false
delete (params as any).lite
}
await refreshTodayStatsBatch() await refreshTodayStatsBatch()
} }
...@@ -689,7 +698,7 @@ const refreshAccountsIncrementally = async () => { ...@@ -689,7 +698,7 @@ const refreshAccountsIncrementally = async () => {
type?: string type?: string
status?: string status?: string
search?: string search?: string
lite?: string
}, },
{ etag: autoRefreshETag.value } { etag: autoRefreshETag.value }
) )
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment