fix: 修复账号管理页面容量列显示为0的bug
This commit is contained in:
@@ -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)
|
||||||
|
rpmAccountIDs := make([]int64, 0)
|
||||||
|
sessionIdleTimeouts := make(map[int64]time.Duration) // 各账号的会话空闲超时配置
|
||||||
|
for i := range accounts {
|
||||||
|
acc := &accounts[i]
|
||||||
|
if acc.IsAnthropicOAuthOrSetupToken() {
|
||||||
|
if acc.GetWindowCostLimit() > 0 {
|
||||||
|
windowCostAccountIDs = append(windowCostAccountIDs, acc.ID)
|
||||||
|
}
|
||||||
|
if acc.GetMaxSessions() > 0 {
|
||||||
|
sessionLimitAccountIDs = append(sessionLimitAccountIDs, acc.ID)
|
||||||
|
sessionIdleTimeouts[acc.ID] = time.Duration(acc.GetSessionIdleTimeoutMinutes()) * time.Minute
|
||||||
|
}
|
||||||
|
if acc.GetBaseRPM() > 0 {
|
||||||
|
rpmAccountIDs = append(rpmAccountIDs, acc.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 识别需要查询窗口费用、会话数和 RPM 的账号(Anthropic OAuth/SetupToken 且启用了相应功能)
|
}
|
||||||
windowCostAccountIDs := make([]int64, 0)
|
|
||||||
sessionLimitAccountIDs := make([]int64, 0)
|
// 始终获取 RPM 计数(Redis GET,极低开销)
|
||||||
rpmAccountIDs := make([]int64, 0)
|
if len(rpmAccountIDs) > 0 && h.rpmCache != nil {
|
||||||
sessionIdleTimeouts := make(map[int64]time.Duration) // 各账号的会话空闲超时配置
|
rpmCounts, _ = h.rpmCache.GetRPMBatch(c.Request.Context(), rpmAccountIDs)
|
||||||
|
if rpmCounts == nil {
|
||||||
|
rpmCounts = make(map[int64]int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 始终获取活跃会话数(Redis ZCARD,低开销)
|
||||||
|
if len(sessionLimitAccountIDs) > 0 && h.sessionLimitCache != nil {
|
||||||
|
activeSessions, _ = h.sessionLimitCache.GetActiveSessionCountBatch(c.Request.Context(), sessionLimitAccountIDs, sessionIdleTimeouts)
|
||||||
|
if activeSessions == nil {
|
||||||
|
activeSessions = make(map[int64]int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 仅非 lite 模式获取窗口费用(PostgreSQL 聚合查询,高开销)
|
||||||
|
if !lite && len(windowCostAccountIDs) > 0 {
|
||||||
|
windowCosts = make(map[int64]float64)
|
||||||
|
var mu sync.Mutex
|
||||||
|
g, gctx := errgroup.WithContext(c.Request.Context())
|
||||||
|
g.SetLimit(10) // 限制并发数
|
||||||
|
|
||||||
for i := range accounts {
|
for i := range accounts {
|
||||||
acc := &accounts[i]
|
acc := &accounts[i]
|
||||||
if acc.IsAnthropicOAuthOrSetupToken() {
|
if !acc.IsAnthropicOAuthOrSetupToken() || acc.GetWindowCostLimit() <= 0 {
|
||||||
if acc.GetWindowCostLimit() > 0 {
|
continue
|
||||||
windowCostAccountIDs = append(windowCostAccountIDs, acc.ID)
|
|
||||||
}
|
|
||||||
if acc.GetMaxSessions() > 0 {
|
|
||||||
sessionLimitAccountIDs = append(sessionLimitAccountIDs, acc.ID)
|
|
||||||
sessionIdleTimeouts[acc.ID] = time.Duration(acc.GetSessionIdleTimeoutMinutes()) * time.Minute
|
|
||||||
}
|
|
||||||
if acc.GetBaseRPM() > 0 {
|
|
||||||
rpmAccountIDs = append(rpmAccountIDs, acc.ID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
accCopy := acc // 闭包捕获
|
||||||
|
g.Go(func() error {
|
||||||
// 获取 RPM 计数(批量查询)
|
// 使用统一的窗口开始时间计算逻辑(考虑窗口过期情况)
|
||||||
if len(rpmAccountIDs) > 0 && h.rpmCache != nil {
|
startTime := accCopy.GetCurrentWindowStartTime()
|
||||||
rpmCounts, _ = h.rpmCache.GetRPMBatch(c.Request.Context(), rpmAccountIDs)
|
stats, err := h.accountUsageService.GetAccountWindowStats(gctx, accCopy.ID, startTime)
|
||||||
if rpmCounts == nil {
|
if err == nil && stats != nil {
|
||||||
rpmCounts = make(map[int64]int)
|
mu.Lock()
|
||||||
}
|
windowCosts[accCopy.ID] = stats.StandardCost // 使用标准费用
|
||||||
}
|
mu.Unlock()
|
||||||
|
|
||||||
// 获取活跃会话数(批量查询,传入各账号的 idleTimeout 配置)
|
|
||||||
if len(sessionLimitAccountIDs) > 0 && h.sessionLimitCache != nil {
|
|
||||||
activeSessions, _ = h.sessionLimitCache.GetActiveSessionCountBatch(c.Request.Context(), sessionLimitAccountIDs, sessionIdleTimeouts)
|
|
||||||
if activeSessions == nil {
|
|
||||||
activeSessions = make(map[int64]int)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取窗口费用(并行查询)
|
|
||||||
if len(windowCostAccountIDs) > 0 {
|
|
||||||
windowCosts = make(map[int64]float64)
|
|
||||||
var mu sync.Mutex
|
|
||||||
g, gctx := errgroup.WithContext(c.Request.Context())
|
|
||||||
g.SetLimit(10) // 限制并发数
|
|
||||||
|
|
||||||
for i := range accounts {
|
|
||||||
acc := &accounts[i]
|
|
||||||
if !acc.IsAnthropicOAuthOrSetupToken() || acc.GetWindowCostLimit() <= 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
accCopy := acc // 闭包捕获
|
return nil // 不返回错误,允许部分失败
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
_ = g.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build response with concurrency info
|
// Build response with concurrency info
|
||||||
|
|||||||
@@ -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 () => {
|
|||||||
type?: string
|
type?: string
|
||||||
status?: string
|
status?: string
|
||||||
search?: string
|
search?: string
|
||||||
lite?: string
|
|
||||||
},
|
},
|
||||||
{ etag: autoRefreshETag.value }
|
{ etag: autoRefreshETag.value }
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user