Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
078fefed
"git@web.lueluesay.top:chenxi/sub2api.git" did not exist on "e88b2890d19b95ea34d63bc5f4ee4f4a58f247d4"
Commit
078fefed
authored
Mar 05, 2026
by
shaw
Browse files
fix: 修复账号管理页面容量列显示为0的bug
parent
5b10af85
Changes
2
Hide whitespace changes
Inline
Side-by-side
backend/internal/handler/admin/account_handler.go
View file @
078fefed
...
@@ -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
...
...
frontend/src/views/admin/AccountsView.vue
View file @
078fefed
...
@@ -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
}
)
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment