Merge pull request #813 from FizzlyCode/fix/account-usage-display
fix: 修复账号列表五小时用量显示为 $0.00 的问题
This commit is contained in:
@@ -288,48 +288,32 @@ func (h *AccountHandler) List(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 窗口费用获取:lite 模式从快照缓存读取,非 lite 模式执行 PostgreSQL 查询后写入缓存
|
// 始终获取窗口费用(PostgreSQL 聚合查询)
|
||||||
if len(windowCostAccountIDs) > 0 {
|
if len(windowCostAccountIDs) > 0 {
|
||||||
if lite {
|
windowCosts = make(map[int64]float64)
|
||||||
// lite 模式:尝试从快照缓存读取
|
var mu sync.Mutex
|
||||||
cacheKey := buildWindowCostCacheKey(windowCostAccountIDs)
|
g, gctx := errgroup.WithContext(c.Request.Context())
|
||||||
if cached, ok := accountWindowCostCache.Get(cacheKey); ok {
|
g.SetLimit(10) // 限制并发数
|
||||||
if costs, ok := cached.Payload.(map[int64]float64); ok {
|
|
||||||
windowCosts = costs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 缓存未命中则 windowCosts 保持 nil(仅发生在服务刚启动时)
|
|
||||||
} else {
|
|
||||||
// 非 lite 模式:执行 PostgreSQL 聚合查询(高开销)
|
|
||||||
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() || 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 {
|
||||||
// 查询完毕后写入快照缓存,供 lite 模式使用
|
// 使用统一的窗口开始时间计算逻辑(考虑窗口过期情况)
|
||||||
cacheKey := buildWindowCostCacheKey(windowCostAccountIDs)
|
startTime := accCopy.GetCurrentWindowStartTime()
|
||||||
accountWindowCostCache.Set(cacheKey, windowCosts)
|
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
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
package admin
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var accountWindowCostCache = newSnapshotCache(30 * time.Second)
|
|
||||||
|
|
||||||
func buildWindowCostCacheKey(accountIDs []int64) string {
|
|
||||||
if len(accountIDs) == 0 {
|
|
||||||
return "accounts_window_cost_empty"
|
|
||||||
}
|
|
||||||
var b strings.Builder
|
|
||||||
b.Grow(len(accountIDs) * 6)
|
|
||||||
_, _ = b.WriteString("accounts_window_cost:")
|
|
||||||
for i, id := range accountIDs {
|
|
||||||
if i > 0 {
|
|
||||||
_ = b.WriteByte(',')
|
|
||||||
}
|
|
||||||
_, _ = b.WriteString(strconv.FormatInt(id, 10))
|
|
||||||
}
|
|
||||||
return b.String()
|
|
||||||
}
|
|
||||||
@@ -2640,6 +2640,12 @@ export default {
|
|||||||
allProtocols: '全部协议',
|
allProtocols: '全部协议',
|
||||||
allStatus: '全部状态',
|
allStatus: '全部状态',
|
||||||
searchProxies: '搜索代理...',
|
searchProxies: '搜索代理...',
|
||||||
|
protocols: {
|
||||||
|
http: 'HTTP',
|
||||||
|
https: 'HTTPS',
|
||||||
|
socks5: 'SOCKS5',
|
||||||
|
socks5h: 'SOCKS5H (远程 DNS)',
|
||||||
|
},
|
||||||
name: '名称',
|
name: '名称',
|
||||||
protocol: '协议',
|
protocol: '协议',
|
||||||
host: '主机',
|
host: '主机',
|
||||||
|
|||||||
Reference in New Issue
Block a user