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
dabed96a
Unverified
Commit
dabed96a
authored
Feb 05, 2026
by
Wesley Liddick
Committed by
GitHub
Feb 05, 2026
Browse files
Merge pull request #486 from s-Joshua-s/feat/usage-filter-by-apikey
feat(gateway): filter /v1/usage stats by API Key instead of UserID
parents
36becd97
fa3ea5ee
Changes
5
Hide whitespace changes
Inline
Side-by-side
backend/internal/handler/gateway_handler.go
View file @
dabed96a
...
...
@@ -616,10 +616,10 @@ func (h *GatewayHandler) Usage(c *gin.Context) {
return
}
// Best-effort: 获取用量统计,失败不影响基础响应
// Best-effort: 获取用量统计
(按当前 API Key 过滤)
,失败不影响基础响应
var
usageData
gin
.
H
if
h
.
usageService
!=
nil
{
dashStats
,
err
:=
h
.
usageService
.
Get
User
DashboardStats
(
c
.
Request
.
Context
(),
subject
.
User
ID
)
dashStats
,
err
:=
h
.
usageService
.
Get
APIKey
DashboardStats
(
c
.
Request
.
Context
(),
apiKey
.
ID
)
if
err
==
nil
&&
dashStats
!=
nil
{
usageData
=
gin
.
H
{
"today"
:
gin
.
H
{
...
...
backend/internal/repository/usage_log_repo.go
View file @
dabed96a
...
...
@@ -1125,6 +1125,107 @@ func (r *usageLogRepository) GetUserDashboardStats(ctx context.Context, userID i
return
stats
,
nil
}
// getPerformanceStatsByAPIKey 获取指定 API Key 的 RPM 和 TPM(近5分钟平均值)
func
(
r
*
usageLogRepository
)
getPerformanceStatsByAPIKey
(
ctx
context
.
Context
,
apiKeyID
int64
)
(
rpm
,
tpm
int64
,
err
error
)
{
fiveMinutesAgo
:=
time
.
Now
()
.
Add
(
-
5
*
time
.
Minute
)
query
:=
`
SELECT
COUNT(*) as request_count,
COALESCE(SUM(input_tokens + output_tokens + cache_creation_tokens + cache_read_tokens), 0) as token_count
FROM usage_logs
WHERE created_at >= $1 AND api_key_id = $2`
args
:=
[]
any
{
fiveMinutesAgo
,
apiKeyID
}
var
requestCount
int64
var
tokenCount
int64
if
err
:=
scanSingleRow
(
ctx
,
r
.
sql
,
query
,
args
,
&
requestCount
,
&
tokenCount
);
err
!=
nil
{
return
0
,
0
,
err
}
return
requestCount
/
5
,
tokenCount
/
5
,
nil
}
// GetAPIKeyDashboardStats 获取指定 API Key 的仪表盘统计(按 api_key_id 过滤)
func
(
r
*
usageLogRepository
)
GetAPIKeyDashboardStats
(
ctx
context
.
Context
,
apiKeyID
int64
)
(
*
UserDashboardStats
,
error
)
{
stats
:=
&
UserDashboardStats
{}
today
:=
timezone
.
Today
()
// API Key 维度不需要统计 key 数量,设为 1
stats
.
TotalAPIKeys
=
1
stats
.
ActiveAPIKeys
=
1
// 累计 Token 统计
totalStatsQuery
:=
`
SELECT
COUNT(*) as total_requests,
COALESCE(SUM(input_tokens), 0) as total_input_tokens,
COALESCE(SUM(output_tokens), 0) as total_output_tokens,
COALESCE(SUM(cache_creation_tokens), 0) as total_cache_creation_tokens,
COALESCE(SUM(cache_read_tokens), 0) as total_cache_read_tokens,
COALESCE(SUM(total_cost), 0) as total_cost,
COALESCE(SUM(actual_cost), 0) as total_actual_cost,
COALESCE(AVG(duration_ms), 0) as avg_duration_ms
FROM usage_logs
WHERE api_key_id = $1
`
if
err
:=
scanSingleRow
(
ctx
,
r
.
sql
,
totalStatsQuery
,
[]
any
{
apiKeyID
},
&
stats
.
TotalRequests
,
&
stats
.
TotalInputTokens
,
&
stats
.
TotalOutputTokens
,
&
stats
.
TotalCacheCreationTokens
,
&
stats
.
TotalCacheReadTokens
,
&
stats
.
TotalCost
,
&
stats
.
TotalActualCost
,
&
stats
.
AverageDurationMs
,
);
err
!=
nil
{
return
nil
,
err
}
stats
.
TotalTokens
=
stats
.
TotalInputTokens
+
stats
.
TotalOutputTokens
+
stats
.
TotalCacheCreationTokens
+
stats
.
TotalCacheReadTokens
// 今日 Token 统计
todayStatsQuery
:=
`
SELECT
COUNT(*) as today_requests,
COALESCE(SUM(input_tokens), 0) as today_input_tokens,
COALESCE(SUM(output_tokens), 0) as today_output_tokens,
COALESCE(SUM(cache_creation_tokens), 0) as today_cache_creation_tokens,
COALESCE(SUM(cache_read_tokens), 0) as today_cache_read_tokens,
COALESCE(SUM(total_cost), 0) as today_cost,
COALESCE(SUM(actual_cost), 0) as today_actual_cost
FROM usage_logs
WHERE api_key_id = $1 AND created_at >= $2
`
if
err
:=
scanSingleRow
(
ctx
,
r
.
sql
,
todayStatsQuery
,
[]
any
{
apiKeyID
,
today
},
&
stats
.
TodayRequests
,
&
stats
.
TodayInputTokens
,
&
stats
.
TodayOutputTokens
,
&
stats
.
TodayCacheCreationTokens
,
&
stats
.
TodayCacheReadTokens
,
&
stats
.
TodayCost
,
&
stats
.
TodayActualCost
,
);
err
!=
nil
{
return
nil
,
err
}
stats
.
TodayTokens
=
stats
.
TodayInputTokens
+
stats
.
TodayOutputTokens
+
stats
.
TodayCacheCreationTokens
+
stats
.
TodayCacheReadTokens
// 性能指标:RPM 和 TPM(最近5分钟,按 API Key 过滤)
rpm
,
tpm
,
err
:=
r
.
getPerformanceStatsByAPIKey
(
ctx
,
apiKeyID
)
if
err
!=
nil
{
return
nil
,
err
}
stats
.
Rpm
=
rpm
stats
.
Tpm
=
tpm
return
stats
,
nil
}
// GetUserUsageTrendByUserID 获取指定用户的使用趋势
func
(
r
*
usageLogRepository
)
GetUserUsageTrendByUserID
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
,
granularity
string
)
(
results
[]
TrendDataPoint
,
err
error
)
{
dateFormat
:=
"YYYY-MM-DD"
...
...
backend/internal/server/api_contract_test.go
View file @
dabed96a
...
...
@@ -1610,6 +1610,10 @@ func (r *stubUsageLogRepo) GetUserDashboardStats(ctx context.Context, userID int
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
r
*
stubUsageLogRepo
)
GetAPIKeyDashboardStats
(
ctx
context
.
Context
,
apiKeyID
int64
)
(
*
usagestats
.
UserDashboardStats
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
}
func
(
r
*
stubUsageLogRepo
)
GetUserUsageTrendByUserID
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
,
granularity
string
)
([]
usagestats
.
TrendDataPoint
,
error
)
{
return
nil
,
errors
.
New
(
"not implemented"
)
}
...
...
backend/internal/service/account_usage_service.go
View file @
dabed96a
...
...
@@ -41,6 +41,7 @@ type UsageLogRepository interface {
// User dashboard stats
GetUserDashboardStats
(
ctx
context
.
Context
,
userID
int64
)
(
*
usagestats
.
UserDashboardStats
,
error
)
GetAPIKeyDashboardStats
(
ctx
context
.
Context
,
apiKeyID
int64
)
(
*
usagestats
.
UserDashboardStats
,
error
)
GetUserUsageTrendByUserID
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
,
granularity
string
)
([]
usagestats
.
TrendDataPoint
,
error
)
GetUserModelStats
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
)
([]
usagestats
.
ModelStat
,
error
)
...
...
backend/internal/service/usage_service.go
View file @
dabed96a
...
...
@@ -288,6 +288,15 @@ func (s *UsageService) GetUserDashboardStats(ctx context.Context, userID int64)
return
stats
,
nil
}
// GetAPIKeyDashboardStats returns dashboard summary stats filtered by API Key.
func
(
s
*
UsageService
)
GetAPIKeyDashboardStats
(
ctx
context
.
Context
,
apiKeyID
int64
)
(
*
usagestats
.
UserDashboardStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetAPIKeyDashboardStats
(
ctx
,
apiKeyID
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get api key dashboard stats: %w"
,
err
)
}
return
stats
,
nil
}
// GetUserUsageTrendByUserID returns per-user usage trend.
func
(
s
*
UsageService
)
GetUserUsageTrendByUserID
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
,
granularity
string
)
([]
usagestats
.
TrendDataPoint
,
error
)
{
trend
,
err
:=
s
.
usageRepo
.
GetUserUsageTrendByUserID
(
ctx
,
userID
,
startTime
,
endTime
,
granularity
)
...
...
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