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
bbf4024d
Commit
bbf4024d
authored
Dec 24, 2025
by
Forest
Browse files
refactor(usage): 移动 usage 查询到 services
parent
5831eb8a
Changes
14
Hide whitespace changes
Inline
Side-by-side
backend/.golangci.yml
View file @
bbf4024d
...
@@ -17,10 +17,17 @@ linters:
...
@@ -17,10 +17,17 @@ linters:
service-no-repository
:
service-no-repository
:
list-mode
:
original
list-mode
:
original
files
:
files
:
-
internal/service/**
-
"
**/
internal/service/**
"
deny
:
deny
:
-
pkg
:
sub2api/internal/repository
-
pkg
:
sub2api/internal/repository
desc
:
"
service
must
not
import
repository"
desc
:
"
service
must
not
import
repository"
handler-no-repository
:
list-mode
:
original
files
:
-
"
**/internal/handler/**"
deny
:
-
pkg
:
sub2api/internal/repository
desc
:
"
handler
must
not
import
repository"
errcheck
:
errcheck
:
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
# Report about not checking of errors in type assertions: `a := b.(MyStruct)`.
# Such cases aren't reported by default.
# Such cases aren't reported by default.
...
...
backend/cmd/server/wire_gen.go
View file @
bbf4024d
...
@@ -58,7 +58,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
...
@@ -58,7 +58,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
apiKeyHandler
:=
handler
.
NewAPIKeyHandler
(
apiKeyService
)
apiKeyHandler
:=
handler
.
NewAPIKeyHandler
(
apiKeyService
)
usageLogRepository
:=
repository
.
NewUsageLogRepository
(
db
)
usageLogRepository
:=
repository
.
NewUsageLogRepository
(
db
)
usageService
:=
service
.
NewUsageService
(
usageLogRepository
,
userRepository
)
usageService
:=
service
.
NewUsageService
(
usageLogRepository
,
userRepository
)
usageHandler
:=
handler
.
NewUsageHandler
(
usageService
,
usageLogRepository
,
apiKeyService
)
usageHandler
:=
handler
.
NewUsageHandler
(
usageService
,
apiKeyService
)
redeemCodeRepository
:=
repository
.
NewRedeemCodeRepository
(
db
)
redeemCodeRepository
:=
repository
.
NewRedeemCodeRepository
(
db
)
billingCache
:=
repository
.
NewBillingCache
(
client
)
billingCache
:=
repository
.
NewBillingCache
(
client
)
billingCacheService
:=
service
.
NewBillingCacheService
(
billingCache
,
userRepository
,
userSubscriptionRepository
)
billingCacheService
:=
service
.
NewBillingCacheService
(
billingCache
,
userRepository
,
userSubscriptionRepository
)
...
@@ -67,7 +67,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
...
@@ -67,7 +67,8 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
redeemService
:=
service
.
NewRedeemService
(
redeemCodeRepository
,
userRepository
,
subscriptionService
,
redeemCache
,
billingCacheService
)
redeemService
:=
service
.
NewRedeemService
(
redeemCodeRepository
,
userRepository
,
subscriptionService
,
redeemCache
,
billingCacheService
)
redeemHandler
:=
handler
.
NewRedeemHandler
(
redeemService
)
redeemHandler
:=
handler
.
NewRedeemHandler
(
redeemService
)
subscriptionHandler
:=
handler
.
NewSubscriptionHandler
(
subscriptionService
)
subscriptionHandler
:=
handler
.
NewSubscriptionHandler
(
subscriptionService
)
dashboardHandler
:=
admin
.
NewDashboardHandler
(
usageLogRepository
)
dashboardService
:=
service
.
NewDashboardService
(
usageLogRepository
)
dashboardHandler
:=
admin
.
NewDashboardHandler
(
dashboardService
)
accountRepository
:=
repository
.
NewAccountRepository
(
db
)
accountRepository
:=
repository
.
NewAccountRepository
(
db
)
proxyRepository
:=
repository
.
NewProxyRepository
(
db
)
proxyRepository
:=
repository
.
NewProxyRepository
(
db
)
proxyExitInfoProber
:=
repository
.
NewProxyExitInfoProber
()
proxyExitInfoProber
:=
repository
.
NewProxyExitInfoProber
()
...
@@ -83,7 +84,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
...
@@ -83,7 +84,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
accountUsageService
:=
service
.
NewAccountUsageService
(
accountRepository
,
usageLogRepository
,
claudeUsageFetcher
)
accountUsageService
:=
service
.
NewAccountUsageService
(
accountRepository
,
usageLogRepository
,
claudeUsageFetcher
)
httpUpstream
:=
repository
.
NewHTTPUpstream
(
configConfig
)
httpUpstream
:=
repository
.
NewHTTPUpstream
(
configConfig
)
accountTestService
:=
service
.
NewAccountTestService
(
accountRepository
,
oAuthService
,
openAIOAuthService
,
httpUpstream
)
accountTestService
:=
service
.
NewAccountTestService
(
accountRepository
,
oAuthService
,
openAIOAuthService
,
httpUpstream
)
accountHandler
:=
admin
.
NewAccountHandler
(
adminService
,
oAuthService
,
openAIOAuthService
,
rateLimitService
,
accountUsageService
,
accountTestService
,
usageLogRepository
)
accountHandler
:=
admin
.
NewAccountHandler
(
adminService
,
oAuthService
,
openAIOAuthService
,
rateLimitService
,
accountUsageService
,
accountTestService
)
oAuthHandler
:=
admin
.
NewOAuthHandler
(
oAuthService
)
oAuthHandler
:=
admin
.
NewOAuthHandler
(
oAuthService
)
openAIOAuthHandler
:=
admin
.
NewOpenAIOAuthHandler
(
openAIOAuthService
,
adminService
)
openAIOAuthHandler
:=
admin
.
NewOpenAIOAuthHandler
(
openAIOAuthService
,
adminService
)
proxyHandler
:=
admin
.
NewProxyHandler
(
adminService
)
proxyHandler
:=
admin
.
NewProxyHandler
(
adminService
)
...
@@ -95,7 +96,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
...
@@ -95,7 +96,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
updateService
:=
service
.
ProvideUpdateService
(
updateCache
,
gitHubReleaseClient
,
serviceBuildInfo
)
updateService
:=
service
.
ProvideUpdateService
(
updateCache
,
gitHubReleaseClient
,
serviceBuildInfo
)
systemHandler
:=
handler
.
ProvideSystemHandler
(
updateService
)
systemHandler
:=
handler
.
ProvideSystemHandler
(
updateService
)
adminSubscriptionHandler
:=
admin
.
NewSubscriptionHandler
(
subscriptionService
)
adminSubscriptionHandler
:=
admin
.
NewSubscriptionHandler
(
subscriptionService
)
adminUsageHandler
:=
admin
.
NewUsageHandler
(
usage
LogRepository
,
apiKeyRepository
,
usage
Service
,
adminService
)
adminUsageHandler
:=
admin
.
NewUsageHandler
(
usage
Service
,
apiKey
Service
,
adminService
)
adminHandlers
:=
handler
.
ProvideAdminHandlers
(
dashboardHandler
,
adminUserHandler
,
groupHandler
,
accountHandler
,
oAuthHandler
,
openAIOAuthHandler
,
proxyHandler
,
adminRedeemHandler
,
settingHandler
,
systemHandler
,
adminSubscriptionHandler
,
adminUsageHandler
)
adminHandlers
:=
handler
.
ProvideAdminHandlers
(
dashboardHandler
,
adminUserHandler
,
groupHandler
,
accountHandler
,
oAuthHandler
,
openAIOAuthHandler
,
proxyHandler
,
adminRedeemHandler
,
settingHandler
,
systemHandler
,
adminSubscriptionHandler
,
adminUsageHandler
)
gatewayCache
:=
repository
.
NewGatewayCache
(
client
)
gatewayCache
:=
repository
.
NewGatewayCache
(
client
)
pricingRemoteClient
:=
repository
.
NewPricingRemoteClient
()
pricingRemoteClient
:=
repository
.
NewPricingRemoteClient
()
...
...
backend/internal/handler/admin/account_handler.go
View file @
bbf4024d
...
@@ -7,7 +7,6 @@ import (
...
@@ -7,7 +7,6 @@ import (
"sub2api/internal/pkg/openai"
"sub2api/internal/pkg/openai"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/timezone"
"sub2api/internal/repository"
"sub2api/internal/service"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
...
@@ -33,11 +32,10 @@ type AccountHandler struct {
...
@@ -33,11 +32,10 @@ type AccountHandler struct {
rateLimitService
*
service
.
RateLimitService
rateLimitService
*
service
.
RateLimitService
accountUsageService
*
service
.
AccountUsageService
accountUsageService
*
service
.
AccountUsageService
accountTestService
*
service
.
AccountTestService
accountTestService
*
service
.
AccountTestService
usageLogRepo
*
repository
.
UsageLogRepository
}
}
// NewAccountHandler creates a new admin account handler
// NewAccountHandler creates a new admin account handler
func
NewAccountHandler
(
adminService
service
.
AdminService
,
oauthService
*
service
.
OAuthService
,
openaiOAuthService
*
service
.
OpenAIOAuthService
,
rateLimitService
*
service
.
RateLimitService
,
accountUsageService
*
service
.
AccountUsageService
,
accountTestService
*
service
.
AccountTestService
,
usageLogRepo
*
repository
.
UsageLogRepository
)
*
AccountHandler
{
func
NewAccountHandler
(
adminService
service
.
AdminService
,
oauthService
*
service
.
OAuthService
,
openaiOAuthService
*
service
.
OpenAIOAuthService
,
rateLimitService
*
service
.
RateLimitService
,
accountUsageService
*
service
.
AccountUsageService
,
accountTestService
*
service
.
AccountTestService
)
*
AccountHandler
{
return
&
AccountHandler
{
return
&
AccountHandler
{
adminService
:
adminService
,
adminService
:
adminService
,
oauthService
:
oauthService
,
oauthService
:
oauthService
,
...
@@ -45,7 +43,6 @@ func NewAccountHandler(adminService service.AdminService, oauthService *service.
...
@@ -45,7 +43,6 @@ func NewAccountHandler(adminService service.AdminService, oauthService *service.
rateLimitService
:
rateLimitService
,
rateLimitService
:
rateLimitService
,
accountUsageService
:
accountUsageService
,
accountUsageService
:
accountUsageService
,
accountTestService
:
accountTestService
,
accountTestService
:
accountTestService
,
usageLogRepo
:
usageLogRepo
,
}
}
}
}
...
@@ -314,7 +311,7 @@ func (h *AccountHandler) GetStats(c *gin.Context) {
...
@@ -314,7 +311,7 @@ func (h *AccountHandler) GetStats(c *gin.Context) {
endTime
:=
timezone
.
StartOfDay
(
now
.
AddDate
(
0
,
0
,
1
))
endTime
:=
timezone
.
StartOfDay
(
now
.
AddDate
(
0
,
0
,
1
))
startTime
:=
timezone
.
StartOfDay
(
now
.
AddDate
(
0
,
0
,
-
days
+
1
))
startTime
:=
timezone
.
StartOfDay
(
now
.
AddDate
(
0
,
0
,
-
days
+
1
))
stats
,
err
:=
h
.
usageLogRepo
.
GetAccountUsageStats
(
c
.
Request
.
Context
(),
accountID
,
startTime
,
endTime
)
stats
,
err
:=
h
.
accountUsageService
.
GetAccountUsageStats
(
c
.
Request
.
Context
(),
accountID
,
startTime
,
endTime
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get account stats: "
+
err
.
Error
())
response
.
InternalError
(
c
,
"Failed to get account stats: "
+
err
.
Error
())
return
return
...
...
backend/internal/handler/admin/dashboard_handler.go
View file @
bbf4024d
...
@@ -4,7 +4,7 @@ import (
...
@@ -4,7 +4,7 @@ import (
"strconv"
"strconv"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/timezone"
"sub2api/internal/
repository
"
"sub2api/internal/
service
"
"time"
"time"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
...
@@ -12,15 +12,15 @@ import (
...
@@ -12,15 +12,15 @@ import (
// DashboardHandler handles admin dashboard statistics
// DashboardHandler handles admin dashboard statistics
type
DashboardHandler
struct
{
type
DashboardHandler
struct
{
usageRepo
*
repository
.
UsageLogRepository
dashboardService
*
service
.
DashboardService
startTime
time
.
Time
// Server start time for uptime calculation
startTime
time
.
Time
// Server start time for uptime calculation
}
}
// NewDashboardHandler creates a new admin dashboard handler
// NewDashboardHandler creates a new admin dashboard handler
func
NewDashboardHandler
(
usageRepo
*
repository
.
UsageLogRepository
)
*
DashboardHandler
{
func
NewDashboardHandler
(
dashboardService
*
service
.
DashboardService
)
*
DashboardHandler
{
return
&
DashboardHandler
{
return
&
DashboardHandler
{
usageRepo
:
usageRepo
,
dashboardService
:
dashboardService
,
startTime
:
time
.
Now
(),
startTime
:
time
.
Now
(),
}
}
}
}
...
@@ -58,7 +58,7 @@ func parseTimeRange(c *gin.Context) (time.Time, time.Time) {
...
@@ -58,7 +58,7 @@ func parseTimeRange(c *gin.Context) (time.Time, time.Time) {
// GetStats handles getting dashboard statistics
// GetStats handles getting dashboard statistics
// GET /api/v1/admin/dashboard/stats
// GET /api/v1/admin/dashboard/stats
func
(
h
*
DashboardHandler
)
GetStats
(
c
*
gin
.
Context
)
{
func
(
h
*
DashboardHandler
)
GetStats
(
c
*
gin
.
Context
)
{
stats
,
err
:=
h
.
usageRepo
.
GetDashboardStats
(
c
.
Request
.
Context
())
stats
,
err
:=
h
.
dashboardService
.
GetDashboardStats
(
c
.
Request
.
Context
())
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get dashboard statistics"
)
response
.
Error
(
c
,
500
,
"Failed to get dashboard statistics"
)
return
return
...
@@ -142,7 +142,7 @@ func (h *DashboardHandler) GetUsageTrend(c *gin.Context) {
...
@@ -142,7 +142,7 @@ func (h *DashboardHandler) GetUsageTrend(c *gin.Context) {
}
}
}
}
trend
,
err
:=
h
.
usageRepo
.
GetUsageTrendWithFilters
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
userID
,
apiKeyID
)
trend
,
err
:=
h
.
dashboardService
.
GetUsageTrendWithFilters
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
userID
,
apiKeyID
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get usage trend"
)
response
.
Error
(
c
,
500
,
"Failed to get usage trend"
)
return
return
...
@@ -175,7 +175,7 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
...
@@ -175,7 +175,7 @@ func (h *DashboardHandler) GetModelStats(c *gin.Context) {
}
}
}
}
stats
,
err
:=
h
.
usageRepo
.
GetModelStatsWithFilters
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
userID
,
apiKeyID
,
0
)
stats
,
err
:=
h
.
dashboardService
.
GetModelStatsWithFilters
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
userID
,
apiKeyID
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get model statistics"
)
response
.
Error
(
c
,
500
,
"Failed to get model statistics"
)
return
return
...
@@ -200,7 +200,7 @@ func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
...
@@ -200,7 +200,7 @@ func (h *DashboardHandler) GetApiKeyUsageTrend(c *gin.Context) {
limit
=
5
limit
=
5
}
}
trend
,
err
:=
h
.
usageRepo
.
GetApiKeyUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
trend
,
err
:=
h
.
dashboardService
.
GetApiKeyUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get API key usage trend"
)
response
.
Error
(
c
,
500
,
"Failed to get API key usage trend"
)
return
return
...
@@ -226,7 +226,7 @@ func (h *DashboardHandler) GetUserUsageTrend(c *gin.Context) {
...
@@ -226,7 +226,7 @@ func (h *DashboardHandler) GetUserUsageTrend(c *gin.Context) {
limit
=
12
limit
=
12
}
}
trend
,
err
:=
h
.
usageRepo
.
GetUserUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
trend
,
err
:=
h
.
dashboardService
.
GetUserUsageTrend
(
c
.
Request
.
Context
(),
startTime
,
endTime
,
granularity
,
limit
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get user usage trend"
)
response
.
Error
(
c
,
500
,
"Failed to get user usage trend"
)
return
return
...
@@ -259,7 +259,7 @@ func (h *DashboardHandler) GetBatchUsersUsage(c *gin.Context) {
...
@@ -259,7 +259,7 @@ func (h *DashboardHandler) GetBatchUsersUsage(c *gin.Context) {
return
return
}
}
stats
,
err
:=
h
.
usageRepo
.
GetBatchUserUsageStats
(
c
.
Request
.
Context
(),
req
.
UserIDs
)
stats
,
err
:=
h
.
dashboardService
.
GetBatchUserUsageStats
(
c
.
Request
.
Context
(),
req
.
UserIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get user usage stats"
)
response
.
Error
(
c
,
500
,
"Failed to get user usage stats"
)
return
return
...
@@ -287,7 +287,7 @@ func (h *DashboardHandler) GetBatchApiKeysUsage(c *gin.Context) {
...
@@ -287,7 +287,7 @@ func (h *DashboardHandler) GetBatchApiKeysUsage(c *gin.Context) {
return
return
}
}
stats
,
err
:=
h
.
usageRepo
.
GetBatchApiKeyUsageStats
(
c
.
Request
.
Context
(),
req
.
ApiKeyIDs
)
stats
,
err
:=
h
.
dashboardService
.
GetBatchApiKeyUsageStats
(
c
.
Request
.
Context
(),
req
.
ApiKeyIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
Error
(
c
,
500
,
"Failed to get API key usage stats"
)
response
.
Error
(
c
,
500
,
"Failed to get API key usage stats"
)
return
return
...
...
backend/internal/handler/admin/usage_handler.go
View file @
bbf4024d
...
@@ -7,7 +7,7 @@ import (
...
@@ -7,7 +7,7 @@ import (
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/timezone"
"sub2api/internal/
repository
"
"sub2api/internal/
pkg/usagestats
"
"sub2api/internal/service"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
...
@@ -15,24 +15,21 @@ import (
...
@@ -15,24 +15,21 @@ import (
// UsageHandler handles admin usage-related requests
// UsageHandler handles admin usage-related requests
type
UsageHandler
struct
{
type
UsageHandler
struct
{
usageRepo
*
repository
.
UsageLogRepository
usageService
*
service
.
UsageService
apiKeyRepo
*
repository
.
ApiKeyRepository
apiKeyService
*
service
.
ApiKeyService
usageService
*
service
.
UsageService
adminService
service
.
AdminService
adminService
service
.
AdminService
}
}
// NewUsageHandler creates a new admin usage handler
// NewUsageHandler creates a new admin usage handler
func
NewUsageHandler
(
func
NewUsageHandler
(
usageRepo
*
repository
.
UsageLogRepository
,
apiKeyRepo
*
repository
.
ApiKeyRepository
,
usageService
*
service
.
UsageService
,
usageService
*
service
.
UsageService
,
apiKeyService
*
service
.
ApiKeyService
,
adminService
service
.
AdminService
,
adminService
service
.
AdminService
,
)
*
UsageHandler
{
)
*
UsageHandler
{
return
&
UsageHandler
{
return
&
UsageHandler
{
usageRepo
:
usageRepo
,
usageService
:
usageService
,
apiKeyRepo
:
apiKeyRepo
,
apiKeyService
:
apiKeyService
,
usageService
:
usageService
,
adminService
:
adminService
,
adminService
:
adminService
,
}
}
}
}
...
@@ -84,14 +81,14 @@ func (h *UsageHandler) List(c *gin.Context) {
...
@@ -84,14 +81,14 @@ func (h *UsageHandler) List(c *gin.Context) {
}
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
filters
:=
repository
.
UsageLogFilters
{
filters
:=
usagestats
.
UsageLogFilters
{
UserID
:
userID
,
UserID
:
userID
,
ApiKeyID
:
apiKeyID
,
ApiKeyID
:
apiKeyID
,
StartTime
:
startTime
,
StartTime
:
startTime
,
EndTime
:
endTime
,
EndTime
:
endTime
,
}
}
records
,
result
,
err
:=
h
.
usage
Repo
.
ListWithFilters
(
c
.
Request
.
Context
(),
params
,
filters
)
records
,
result
,
err
:=
h
.
usage
Service
.
ListWithFilters
(
c
.
Request
.
Context
(),
params
,
filters
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to list usage records: "
+
err
.
Error
())
response
.
InternalError
(
c
,
"Failed to list usage records: "
+
err
.
Error
())
return
return
...
@@ -179,7 +176,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
...
@@ -179,7 +176,7 @@ func (h *UsageHandler) Stats(c *gin.Context) {
}
}
// Get global stats
// Get global stats
stats
,
err
:=
h
.
usage
Repo
.
GetGlobalStats
(
c
.
Request
.
Context
(),
startTime
,
endTime
)
stats
,
err
:=
h
.
usage
Service
.
GetGlobalStats
(
c
.
Request
.
Context
(),
startTime
,
endTime
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get usage statistics: "
+
err
.
Error
())
response
.
InternalError
(
c
,
"Failed to get usage statistics: "
+
err
.
Error
())
return
return
...
@@ -237,7 +234,7 @@ func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
...
@@ -237,7 +234,7 @@ func (h *UsageHandler) SearchApiKeys(c *gin.Context) {
userID
=
id
userID
=
id
}
}
keys
,
err
:=
h
.
apiKey
Repo
.
SearchApiKeys
(
c
.
Request
.
Context
(),
userID
,
keyword
,
30
)
keys
,
err
:=
h
.
apiKey
Service
.
SearchApiKeys
(
c
.
Request
.
Context
(),
userID
,
keyword
,
30
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to search API keys: "
+
err
.
Error
())
response
.
InternalError
(
c
,
"Failed to search API keys: "
+
err
.
Error
())
return
return
...
...
backend/internal/handler/usage_handler.go
View file @
bbf4024d
...
@@ -8,7 +8,6 @@ import (
...
@@ -8,7 +8,6 @@ import (
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/timezone"
"sub2api/internal/repository"
"sub2api/internal/service"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
...
@@ -17,15 +16,13 @@ import (
...
@@ -17,15 +16,13 @@ import (
// UsageHandler handles usage-related requests
// UsageHandler handles usage-related requests
type
UsageHandler
struct
{
type
UsageHandler
struct
{
usageService
*
service
.
UsageService
usageService
*
service
.
UsageService
usageRepo
*
repository
.
UsageLogRepository
apiKeyService
*
service
.
ApiKeyService
apiKeyService
*
service
.
ApiKeyService
}
}
// NewUsageHandler creates a new UsageHandler
// NewUsageHandler creates a new UsageHandler
func
NewUsageHandler
(
usageService
*
service
.
UsageService
,
usageRepo
*
repository
.
UsageLogRepository
,
apiKeyService
*
service
.
ApiKeyService
)
*
UsageHandler
{
func
NewUsageHandler
(
usageService
*
service
.
UsageService
,
apiKeyService
*
service
.
ApiKeyService
)
*
UsageHandler
{
return
&
UsageHandler
{
return
&
UsageHandler
{
usageService
:
usageService
,
usageService
:
usageService
,
usageRepo
:
usageRepo
,
apiKeyService
:
apiKeyService
,
apiKeyService
:
apiKeyService
,
}
}
}
}
...
@@ -260,7 +257,7 @@ func (h *UsageHandler) DashboardStats(c *gin.Context) {
...
@@ -260,7 +257,7 @@ func (h *UsageHandler) DashboardStats(c *gin.Context) {
return
return
}
}
stats
,
err
:=
h
.
usage
Repo
.
GetUserDashboardStats
(
c
.
Request
.
Context
(),
user
.
ID
)
stats
,
err
:=
h
.
usage
Service
.
GetUserDashboardStats
(
c
.
Request
.
Context
(),
user
.
ID
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get dashboard statistics"
)
response
.
InternalError
(
c
,
"Failed to get dashboard statistics"
)
return
return
...
@@ -287,7 +284,7 @@ func (h *UsageHandler) DashboardTrend(c *gin.Context) {
...
@@ -287,7 +284,7 @@ func (h *UsageHandler) DashboardTrend(c *gin.Context) {
startTime
,
endTime
:=
parseUserTimeRange
(
c
)
startTime
,
endTime
:=
parseUserTimeRange
(
c
)
granularity
:=
c
.
DefaultQuery
(
"granularity"
,
"day"
)
granularity
:=
c
.
DefaultQuery
(
"granularity"
,
"day"
)
trend
,
err
:=
h
.
usage
Repo
.
GetUserUsageTrendByUserID
(
c
.
Request
.
Context
(),
user
.
ID
,
startTime
,
endTime
,
granularity
)
trend
,
err
:=
h
.
usage
Service
.
GetUserUsageTrendByUserID
(
c
.
Request
.
Context
(),
user
.
ID
,
startTime
,
endTime
,
granularity
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get usage trend"
)
response
.
InternalError
(
c
,
"Failed to get usage trend"
)
return
return
...
@@ -318,7 +315,7 @@ func (h *UsageHandler) DashboardModels(c *gin.Context) {
...
@@ -318,7 +315,7 @@ func (h *UsageHandler) DashboardModels(c *gin.Context) {
startTime
,
endTime
:=
parseUserTimeRange
(
c
)
startTime
,
endTime
:=
parseUserTimeRange
(
c
)
stats
,
err
:=
h
.
usage
Repo
.
GetUserModelStats
(
c
.
Request
.
Context
(),
user
.
ID
,
startTime
,
endTime
)
stats
,
err
:=
h
.
usage
Service
.
GetUserModelStats
(
c
.
Request
.
Context
(),
user
.
ID
,
startTime
,
endTime
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get model statistics"
)
response
.
InternalError
(
c
,
"Failed to get model statistics"
)
return
return
...
@@ -387,7 +384,7 @@ func (h *UsageHandler) DashboardApiKeysUsage(c *gin.Context) {
...
@@ -387,7 +384,7 @@ func (h *UsageHandler) DashboardApiKeysUsage(c *gin.Context) {
return
return
}
}
stats
,
err
:=
h
.
usage
Repo
.
GetBatchApiKeyUsageStats
(
c
.
Request
.
Context
(),
validApiKeyIDs
)
stats
,
err
:=
h
.
usage
Service
.
GetBatchApiKeyUsageStats
(
c
.
Request
.
Context
(),
validApiKeyIDs
)
if
err
!=
nil
{
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to get API key usage stats"
)
response
.
InternalError
(
c
,
"Failed to get API key usage stats"
)
return
return
...
...
backend/internal/pkg/usagestats/usage_log_types.go
0 → 100644
View file @
bbf4024d
package
usagestats
import
"time"
// DashboardStats 仪表盘统计
type
DashboardStats
struct
{
// 用户统计
TotalUsers
int64
`json:"total_users"`
TodayNewUsers
int64
`json:"today_new_users"`
// 今日新增用户数
ActiveUsers
int64
`json:"active_users"`
// 今日有请求的用户数
// API Key 统计
TotalApiKeys
int64
`json:"total_api_keys"`
ActiveApiKeys
int64
`json:"active_api_keys"`
// 状态为 active 的 API Key 数
// 账户统计
TotalAccounts
int64
`json:"total_accounts"`
NormalAccounts
int64
`json:"normal_accounts"`
// 正常账户数 (schedulable=true, status=active)
ErrorAccounts
int64
`json:"error_accounts"`
// 异常账户数 (status=error)
RateLimitAccounts
int64
`json:"ratelimit_accounts"`
// 限流账户数
OverloadAccounts
int64
`json:"overload_accounts"`
// 过载账户数
// 累计 Token 使用统计
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheCreationTokens
int64
`json:"total_cache_creation_tokens"`
TotalCacheReadTokens
int64
`json:"total_cache_read_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
// 累计标准计费
TotalActualCost
float64
`json:"total_actual_cost"`
// 累计实际扣除
// 今日 Token 使用统计
TodayRequests
int64
`json:"today_requests"`
TodayInputTokens
int64
`json:"today_input_tokens"`
TodayOutputTokens
int64
`json:"today_output_tokens"`
TodayCacheCreationTokens
int64
`json:"today_cache_creation_tokens"`
TodayCacheReadTokens
int64
`json:"today_cache_read_tokens"`
TodayTokens
int64
`json:"today_tokens"`
TodayCost
float64
`json:"today_cost"`
// 今日标准计费
TodayActualCost
float64
`json:"today_actual_cost"`
// 今日实际扣除
// 系统运行统计
AverageDurationMs
float64
`json:"average_duration_ms"`
// 平均响应时间
}
// TrendDataPoint represents a single point in trend data
type
TrendDataPoint
struct
{
Date
string
`json:"date"`
Requests
int64
`json:"requests"`
InputTokens
int64
`json:"input_tokens"`
OutputTokens
int64
`json:"output_tokens"`
CacheTokens
int64
`json:"cache_tokens"`
TotalTokens
int64
`json:"total_tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// ModelStat represents usage statistics for a single model
type
ModelStat
struct
{
Model
string
`json:"model"`
Requests
int64
`json:"requests"`
InputTokens
int64
`json:"input_tokens"`
OutputTokens
int64
`json:"output_tokens"`
TotalTokens
int64
`json:"total_tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// UserUsageTrendPoint represents user usage trend data point
type
UserUsageTrendPoint
struct
{
Date
string
`json:"date"`
UserID
int64
`json:"user_id"`
Email
string
`json:"email"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// ApiKeyUsageTrendPoint represents API key usage trend data point
type
ApiKeyUsageTrendPoint
struct
{
Date
string
`json:"date"`
ApiKeyID
int64
`json:"api_key_id"`
KeyName
string
`json:"key_name"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
}
// UserDashboardStats 用户仪表盘统计
type
UserDashboardStats
struct
{
// API Key 统计
TotalApiKeys
int64
`json:"total_api_keys"`
ActiveApiKeys
int64
`json:"active_api_keys"`
// 累计 Token 使用统计
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheCreationTokens
int64
`json:"total_cache_creation_tokens"`
TotalCacheReadTokens
int64
`json:"total_cache_read_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
// 累计标准计费
TotalActualCost
float64
`json:"total_actual_cost"`
// 累计实际扣除
// 今日 Token 使用统计
TodayRequests
int64
`json:"today_requests"`
TodayInputTokens
int64
`json:"today_input_tokens"`
TodayOutputTokens
int64
`json:"today_output_tokens"`
TodayCacheCreationTokens
int64
`json:"today_cache_creation_tokens"`
TodayCacheReadTokens
int64
`json:"today_cache_read_tokens"`
TodayTokens
int64
`json:"today_tokens"`
TodayCost
float64
`json:"today_cost"`
// 今日标准计费
TodayActualCost
float64
`json:"today_actual_cost"`
// 今日实际扣除
// 性能统计
AverageDurationMs
float64
`json:"average_duration_ms"`
}
// UsageLogFilters represents filters for usage log queries
type
UsageLogFilters
struct
{
UserID
int64
ApiKeyID
int64
StartTime
*
time
.
Time
EndTime
*
time
.
Time
}
// UsageStats represents usage statistics
type
UsageStats
struct
{
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheTokens
int64
`json:"total_cache_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
AverageDurationMs
float64
`json:"average_duration_ms"`
}
// BatchUserUsageStats represents usage stats for a single user
type
BatchUserUsageStats
struct
{
UserID
int64
`json:"user_id"`
TodayActualCost
float64
`json:"today_actual_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
}
// BatchApiKeyUsageStats represents usage stats for a single API key
type
BatchApiKeyUsageStats
struct
{
ApiKeyID
int64
`json:"api_key_id"`
TodayActualCost
float64
`json:"today_actual_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
}
// AccountUsageHistory represents daily usage history for an account
type
AccountUsageHistory
struct
{
Date
string
`json:"date"`
Label
string
`json:"label"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
ActualCost
float64
`json:"actual_cost"`
}
// AccountUsageSummary represents summary statistics for an account
type
AccountUsageSummary
struct
{
Days
int
`json:"days"`
ActualDaysUsed
int
`json:"actual_days_used"`
TotalCost
float64
`json:"total_cost"`
TotalStandardCost
float64
`json:"total_standard_cost"`
TotalRequests
int64
`json:"total_requests"`
TotalTokens
int64
`json:"total_tokens"`
AvgDailyCost
float64
`json:"avg_daily_cost"`
AvgDailyRequests
float64
`json:"avg_daily_requests"`
AvgDailyTokens
float64
`json:"avg_daily_tokens"`
AvgDurationMs
float64
`json:"avg_duration_ms"`
Today
*
struct
{
Date
string
`json:"date"`
Cost
float64
`json:"cost"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
}
`json:"today"`
HighestCostDay
*
struct
{
Date
string
`json:"date"`
Label
string
`json:"label"`
Cost
float64
`json:"cost"`
Requests
int64
`json:"requests"`
}
`json:"highest_cost_day"`
HighestRequestDay
*
struct
{
Date
string
`json:"date"`
Label
string
`json:"label"`
Requests
int64
`json:"requests"`
Cost
float64
`json:"cost"`
}
`json:"highest_request_day"`
}
// AccountUsageStatsResponse represents the full usage statistics response for an account
type
AccountUsageStatsResponse
struct
{
History
[]
AccountUsageHistory
`json:"history"`
Summary
AccountUsageSummary
`json:"summary"`
Models
[]
ModelStat
`json:"models"`
}
backend/internal/repository/usage_log_repo.go
View file @
bbf4024d
...
@@ -113,46 +113,7 @@ func (r *UsageLogRepository) GetUserStats(ctx context.Context, userID int64, sta
...
@@ -113,46 +113,7 @@ func (r *UsageLogRepository) GetUserStats(ctx context.Context, userID int64, sta
}
}
// DashboardStats 仪表盘统计
// DashboardStats 仪表盘统计
type
DashboardStats
struct
{
type
DashboardStats
=
usagestats
.
DashboardStats
// 用户统计
TotalUsers
int64
`json:"total_users"`
TodayNewUsers
int64
`json:"today_new_users"`
// 今日新增用户数
ActiveUsers
int64
`json:"active_users"`
// 今日有请求的用户数
// API Key 统计
TotalApiKeys
int64
`json:"total_api_keys"`
ActiveApiKeys
int64
`json:"active_api_keys"`
// 状态为 active 的 API Key 数
// 账户统计
TotalAccounts
int64
`json:"total_accounts"`
NormalAccounts
int64
`json:"normal_accounts"`
// 正常账户数 (schedulable=true, status=active)
ErrorAccounts
int64
`json:"error_accounts"`
// 异常账户数 (status=error)
RateLimitAccounts
int64
`json:"ratelimit_accounts"`
// 限流账户数
OverloadAccounts
int64
`json:"overload_accounts"`
// 过载账户数
// 累计 Token 使用统计
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheCreationTokens
int64
`json:"total_cache_creation_tokens"`
TotalCacheReadTokens
int64
`json:"total_cache_read_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
// 累计标准计费
TotalActualCost
float64
`json:"total_actual_cost"`
// 累计实际扣除
// 今日 Token 使用统计
TodayRequests
int64
`json:"today_requests"`
TodayInputTokens
int64
`json:"today_input_tokens"`
TodayOutputTokens
int64
`json:"today_output_tokens"`
TodayCacheCreationTokens
int64
`json:"today_cache_creation_tokens"`
TodayCacheReadTokens
int64
`json:"today_cache_read_tokens"`
TodayTokens
int64
`json:"today_tokens"`
TodayCost
float64
`json:"today_cost"`
// 今日标准计费
TodayActualCost
float64
`json:"today_actual_cost"`
// 今日实际扣除
// 系统运行统计
AverageDurationMs
float64
`json:"average_duration_ms"`
// 平均响应时间
}
func
(
r
*
UsageLogRepository
)
GetDashboardStats
(
ctx
context
.
Context
)
(
*
DashboardStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetDashboardStats
(
ctx
context
.
Context
)
(
*
DashboardStats
,
error
)
{
var
stats
DashboardStats
var
stats
DashboardStats
...
@@ -398,47 +359,16 @@ func (r *UsageLogRepository) GetAccountWindowStats(ctx context.Context, accountI
...
@@ -398,47 +359,16 @@ func (r *UsageLogRepository) GetAccountWindowStats(ctx context.Context, accountI
}
}
// TrendDataPoint represents a single point in trend data
// TrendDataPoint represents a single point in trend data
type
TrendDataPoint
struct
{
type
TrendDataPoint
=
usagestats
.
TrendDataPoint
Date
string
`json:"date"`
Requests
int64
`json:"requests"`
InputTokens
int64
`json:"input_tokens"`
OutputTokens
int64
`json:"output_tokens"`
CacheTokens
int64
`json:"cache_tokens"`
TotalTokens
int64
`json:"total_tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// ModelStat represents usage statistics for a single model
// ModelStat represents usage statistics for a single model
type
ModelStat
struct
{
type
ModelStat
=
usagestats
.
ModelStat
Model
string
`json:"model"`
Requests
int64
`json:"requests"`
InputTokens
int64
`json:"input_tokens"`
OutputTokens
int64
`json:"output_tokens"`
TotalTokens
int64
`json:"total_tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// UserUsageTrendPoint represents user usage trend data point
// UserUsageTrendPoint represents user usage trend data point
type
UserUsageTrendPoint
struct
{
type
UserUsageTrendPoint
=
usagestats
.
UserUsageTrendPoint
Date
string
`json:"date"`
UserID
int64
`json:"user_id"`
Email
string
`json:"email"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
// 标准计费
ActualCost
float64
`json:"actual_cost"`
// 实际扣除
}
// ApiKeyUsageTrendPoint represents API key usage trend data point
// ApiKeyUsageTrendPoint represents API key usage trend data point
type
ApiKeyUsageTrendPoint
struct
{
type
ApiKeyUsageTrendPoint
=
usagestats
.
ApiKeyUsageTrendPoint
Date
string
`json:"date"`
ApiKeyID
int64
`json:"api_key_id"`
KeyName
string
`json:"key_name"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
}
// GetApiKeyUsageTrend returns usage trend data grouped by API key and date
// GetApiKeyUsageTrend returns usage trend data grouped by API key and date
func
(
r
*
UsageLogRepository
)
GetApiKeyUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
ApiKeyUsageTrendPoint
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetApiKeyUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
ApiKeyUsageTrendPoint
,
error
)
{
...
@@ -531,34 +461,7 @@ func (r *UsageLogRepository) GetUserUsageTrend(ctx context.Context, startTime, e
...
@@ -531,34 +461,7 @@ func (r *UsageLogRepository) GetUserUsageTrend(ctx context.Context, startTime, e
}
}
// UserDashboardStats 用户仪表盘统计
// UserDashboardStats 用户仪表盘统计
type
UserDashboardStats
struct
{
type
UserDashboardStats
=
usagestats
.
UserDashboardStats
// API Key 统计
TotalApiKeys
int64
`json:"total_api_keys"`
ActiveApiKeys
int64
`json:"active_api_keys"`
// 累计 Token 使用统计
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheCreationTokens
int64
`json:"total_cache_creation_tokens"`
TotalCacheReadTokens
int64
`json:"total_cache_read_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
// 累计标准计费
TotalActualCost
float64
`json:"total_actual_cost"`
// 累计实际扣除
// 今日 Token 使用统计
TodayRequests
int64
`json:"today_requests"`
TodayInputTokens
int64
`json:"today_input_tokens"`
TodayOutputTokens
int64
`json:"today_output_tokens"`
TodayCacheCreationTokens
int64
`json:"today_cache_creation_tokens"`
TodayCacheReadTokens
int64
`json:"today_cache_read_tokens"`
TodayTokens
int64
`json:"today_tokens"`
TodayCost
float64
`json:"today_cost"`
// 今日标准计费
TodayActualCost
float64
`json:"today_actual_cost"`
// 今日实际扣除
// 性能统计
AverageDurationMs
float64
`json:"average_duration_ms"`
}
// GetUserDashboardStats 获取用户专属的仪表盘统计
// GetUserDashboardStats 获取用户专属的仪表盘统计
func
(
r
*
UsageLogRepository
)
GetUserDashboardStats
(
ctx
context
.
Context
,
userID
int64
)
(
*
UserDashboardStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetUserDashboardStats
(
ctx
context
.
Context
,
userID
int64
)
(
*
UserDashboardStats
,
error
)
{
...
@@ -705,12 +608,7 @@ func (r *UsageLogRepository) GetUserModelStats(ctx context.Context, userID int64
...
@@ -705,12 +608,7 @@ func (r *UsageLogRepository) GetUserModelStats(ctx context.Context, userID int64
}
}
// UsageLogFilters represents filters for usage log queries
// UsageLogFilters represents filters for usage log queries
type
UsageLogFilters
struct
{
type
UsageLogFilters
=
usagestats
.
UsageLogFilters
UserID
int64
ApiKeyID
int64
StartTime
*
time
.
Time
EndTime
*
time
.
Time
}
// ListWithFilters lists usage logs with optional filters (for admin)
// ListWithFilters lists usage logs with optional filters (for admin)
func
(
r
*
UsageLogRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
filters
UsageLogFilters
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
filters
UsageLogFilters
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
...
@@ -758,23 +656,10 @@ func (r *UsageLogRepository) ListWithFilters(ctx context.Context, params paginat
...
@@ -758,23 +656,10 @@ func (r *UsageLogRepository) ListWithFilters(ctx context.Context, params paginat
}
}
// UsageStats represents usage statistics
// UsageStats represents usage statistics
type
UsageStats
struct
{
type
UsageStats
=
usagestats
.
UsageStats
TotalRequests
int64
`json:"total_requests"`
TotalInputTokens
int64
`json:"total_input_tokens"`
TotalOutputTokens
int64
`json:"total_output_tokens"`
TotalCacheTokens
int64
`json:"total_cache_tokens"`
TotalTokens
int64
`json:"total_tokens"`
TotalCost
float64
`json:"total_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
AverageDurationMs
float64
`json:"average_duration_ms"`
}
// BatchUserUsageStats represents usage stats for a single user
// BatchUserUsageStats represents usage stats for a single user
type
BatchUserUsageStats
struct
{
type
BatchUserUsageStats
=
usagestats
.
BatchUserUsageStats
UserID
int64
`json:"user_id"`
TodayActualCost
float64
`json:"today_actual_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
}
// GetBatchUserUsageStats gets today and total actual_cost for multiple users
// GetBatchUserUsageStats gets today and total actual_cost for multiple users
func
(
r
*
UsageLogRepository
)
GetBatchUserUsageStats
(
ctx
context
.
Context
,
userIDs
[]
int64
)
(
map
[
int64
]
*
BatchUserUsageStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetBatchUserUsageStats
(
ctx
context
.
Context
,
userIDs
[]
int64
)
(
map
[
int64
]
*
BatchUserUsageStats
,
error
)
{
...
@@ -834,11 +719,7 @@ func (r *UsageLogRepository) GetBatchUserUsageStats(ctx context.Context, userIDs
...
@@ -834,11 +719,7 @@ func (r *UsageLogRepository) GetBatchUserUsageStats(ctx context.Context, userIDs
}
}
// BatchApiKeyUsageStats represents usage stats for a single API key
// BatchApiKeyUsageStats represents usage stats for a single API key
type
BatchApiKeyUsageStats
struct
{
type
BatchApiKeyUsageStats
=
usagestats
.
BatchApiKeyUsageStats
ApiKeyID
int64
`json:"api_key_id"`
TodayActualCost
float64
`json:"today_actual_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
}
// GetBatchApiKeyUsageStats gets today and total actual_cost for multiple API keys
// GetBatchApiKeyUsageStats gets today and total actual_cost for multiple API keys
func
(
r
*
UsageLogRepository
)
GetBatchApiKeyUsageStats
(
ctx
context
.
Context
,
apiKeyIDs
[]
int64
)
(
map
[
int64
]
*
BatchApiKeyUsageStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetBatchApiKeyUsageStats
(
ctx
context
.
Context
,
apiKeyIDs
[]
int64
)
(
map
[
int64
]
*
BatchApiKeyUsageStats
,
error
)
{
...
@@ -1012,53 +893,13 @@ func (r *UsageLogRepository) GetGlobalStats(ctx context.Context, startTime, endT
...
@@ -1012,53 +893,13 @@ func (r *UsageLogRepository) GetGlobalStats(ctx context.Context, startTime, endT
}
}
// AccountUsageHistory represents daily usage history for an account
// AccountUsageHistory represents daily usage history for an account
type
AccountUsageHistory
struct
{
type
AccountUsageHistory
=
usagestats
.
AccountUsageHistory
Date
string
`json:"date"`
Label
string
`json:"label"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
ActualCost
float64
`json:"actual_cost"`
}
// AccountUsageSummary represents summary statistics for an account
// AccountUsageSummary represents summary statistics for an account
type
AccountUsageSummary
struct
{
type
AccountUsageSummary
=
usagestats
.
AccountUsageSummary
Days
int
`json:"days"`
ActualDaysUsed
int
`json:"actual_days_used"`
TotalCost
float64
`json:"total_cost"`
TotalStandardCost
float64
`json:"total_standard_cost"`
TotalRequests
int64
`json:"total_requests"`
TotalTokens
int64
`json:"total_tokens"`
AvgDailyCost
float64
`json:"avg_daily_cost"`
AvgDailyRequests
float64
`json:"avg_daily_requests"`
AvgDailyTokens
float64
`json:"avg_daily_tokens"`
AvgDurationMs
float64
`json:"avg_duration_ms"`
Today
*
struct
{
Date
string
`json:"date"`
Cost
float64
`json:"cost"`
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
}
`json:"today"`
HighestCostDay
*
struct
{
Date
string
`json:"date"`
Label
string
`json:"label"`
Cost
float64
`json:"cost"`
Requests
int64
`json:"requests"`
}
`json:"highest_cost_day"`
HighestRequestDay
*
struct
{
Date
string
`json:"date"`
Label
string
`json:"label"`
Requests
int64
`json:"requests"`
Cost
float64
`json:"cost"`
}
`json:"highest_request_day"`
}
// AccountUsageStatsResponse represents the full usage statistics response for an account
// AccountUsageStatsResponse represents the full usage statistics response for an account
type
AccountUsageStatsResponse
struct
{
type
AccountUsageStatsResponse
=
usagestats
.
AccountUsageStatsResponse
History
[]
AccountUsageHistory
`json:"history"`
Summary
AccountUsageSummary
`json:"summary"`
Models
[]
ModelStat
`json:"models"`
}
// GetAccountUsageStats returns comprehensive usage statistics for an account over a time range
// GetAccountUsageStats returns comprehensive usage statistics for an account over a time range
func
(
r
*
UsageLogRepository
)
GetAccountUsageStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
(
*
AccountUsageStatsResponse
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetAccountUsageStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
(
*
AccountUsageStatsResponse
,
error
)
{
...
...
backend/internal/service/account_usage_service.go
View file @
bbf4024d
...
@@ -8,6 +8,7 @@ import (
...
@@ -8,6 +8,7 @@ import (
"time"
"time"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/pkg/usagestats"
"sub2api/internal/service/ports"
"sub2api/internal/service/ports"
)
)
...
@@ -176,6 +177,14 @@ func (s *AccountUsageService) GetTodayStats(ctx context.Context, accountID int64
...
@@ -176,6 +177,14 @@ func (s *AccountUsageService) GetTodayStats(ctx context.Context, accountID int64
},
nil
},
nil
}
}
func
(
s
*
AccountUsageService
)
GetAccountUsageStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
(
*
usagestats
.
AccountUsageStatsResponse
,
error
)
{
stats
,
err
:=
s
.
usageLogRepo
.
GetAccountUsageStats
(
ctx
,
accountID
,
startTime
,
endTime
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get account usage stats failed: %w"
,
err
)
}
return
stats
,
nil
}
// fetchOAuthUsage 从Anthropic API获取OAuth账号的使用量
// fetchOAuthUsage 从Anthropic API获取OAuth账号的使用量
func
(
s
*
AccountUsageService
)
fetchOAuthUsage
(
ctx
context
.
Context
,
account
*
model
.
Account
)
(
*
UsageInfo
,
error
)
{
func
(
s
*
AccountUsageService
)
fetchOAuthUsage
(
ctx
context
.
Context
,
account
*
model
.
Account
)
(
*
UsageInfo
,
error
)
{
accessToken
:=
account
.
GetCredential
(
"access_token"
)
accessToken
:=
account
.
GetCredential
(
"access_token"
)
...
...
backend/internal/service/api_key_service.go
View file @
bbf4024d
...
@@ -455,3 +455,11 @@ func (s *ApiKeyService) canUserBindGroupInternal(user *model.User, group *model.
...
@@ -455,3 +455,11 @@ func (s *ApiKeyService) canUserBindGroupInternal(user *model.User, group *model.
// 标准类型分组:使用原有逻辑
// 标准类型分组:使用原有逻辑
return
user
.
CanBindGroup
(
group
.
ID
,
group
.
IsExclusive
)
return
user
.
CanBindGroup
(
group
.
ID
,
group
.
IsExclusive
)
}
}
func
(
s
*
ApiKeyService
)
SearchApiKeys
(
ctx
context
.
Context
,
userID
int64
,
keyword
string
,
limit
int
)
([]
model
.
ApiKey
,
error
)
{
keys
,
err
:=
s
.
apiKeyRepo
.
SearchApiKeys
(
ctx
,
userID
,
keyword
,
limit
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"search api keys: %w"
,
err
)
}
return
keys
,
nil
}
backend/internal/service/dashboard_service.go
0 → 100644
View file @
bbf4024d
package
service
import
(
"context"
"fmt"
"time"
"sub2api/internal/pkg/usagestats"
"sub2api/internal/service/ports"
)
// DashboardService provides aggregated statistics for admin dashboard.
type
DashboardService
struct
{
usageRepo
ports
.
UsageLogRepository
}
func
NewDashboardService
(
usageRepo
ports
.
UsageLogRepository
)
*
DashboardService
{
return
&
DashboardService
{
usageRepo
:
usageRepo
,
}
}
func
(
s
*
DashboardService
)
GetDashboardStats
(
ctx
context
.
Context
)
(
*
usagestats
.
DashboardStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetDashboardStats
(
ctx
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get dashboard stats: %w"
,
err
)
}
return
stats
,
nil
}
func
(
s
*
DashboardService
)
GetUsageTrendWithFilters
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
userID
,
apiKeyID
int64
)
([]
usagestats
.
TrendDataPoint
,
error
)
{
trend
,
err
:=
s
.
usageRepo
.
GetUsageTrendWithFilters
(
ctx
,
startTime
,
endTime
,
granularity
,
userID
,
apiKeyID
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get usage trend with filters: %w"
,
err
)
}
return
trend
,
nil
}
func
(
s
*
DashboardService
)
GetModelStatsWithFilters
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
userID
,
apiKeyID
int64
)
([]
usagestats
.
ModelStat
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetModelStatsWithFilters
(
ctx
,
startTime
,
endTime
,
userID
,
apiKeyID
,
0
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get model stats with filters: %w"
,
err
)
}
return
stats
,
nil
}
func
(
s
*
DashboardService
)
GetApiKeyUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
usagestats
.
ApiKeyUsageTrendPoint
,
error
)
{
trend
,
err
:=
s
.
usageRepo
.
GetApiKeyUsageTrend
(
ctx
,
startTime
,
endTime
,
granularity
,
limit
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get api key usage trend: %w"
,
err
)
}
return
trend
,
nil
}
func
(
s
*
DashboardService
)
GetUserUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
usagestats
.
UserUsageTrendPoint
,
error
)
{
trend
,
err
:=
s
.
usageRepo
.
GetUserUsageTrend
(
ctx
,
startTime
,
endTime
,
granularity
,
limit
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get user usage trend: %w"
,
err
)
}
return
trend
,
nil
}
func
(
s
*
DashboardService
)
GetBatchUserUsageStats
(
ctx
context
.
Context
,
userIDs
[]
int64
)
(
map
[
int64
]
*
usagestats
.
BatchUserUsageStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetBatchUserUsageStats
(
ctx
,
userIDs
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get batch user usage stats: %w"
,
err
)
}
return
stats
,
nil
}
func
(
s
*
DashboardService
)
GetBatchApiKeyUsageStats
(
ctx
context
.
Context
,
apiKeyIDs
[]
int64
)
(
map
[
int64
]
*
usagestats
.
BatchApiKeyUsageStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetBatchApiKeyUsageStats
(
ctx
,
apiKeyIDs
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get batch api key usage stats: %w"
,
err
)
}
return
stats
,
nil
}
backend/internal/service/ports/usage_log.go
View file @
bbf4024d
...
@@ -25,4 +25,25 @@ type UsageLogRepository interface {
...
@@ -25,4 +25,25 @@ type UsageLogRepository interface {
GetAccountWindowStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
time
.
Time
)
(
*
usagestats
.
AccountStats
,
error
)
GetAccountWindowStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
time
.
Time
)
(
*
usagestats
.
AccountStats
,
error
)
GetAccountTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
usagestats
.
AccountStats
,
error
)
GetAccountTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
usagestats
.
AccountStats
,
error
)
// Admin dashboard stats
GetDashboardStats
(
ctx
context
.
Context
)
(
*
usagestats
.
DashboardStats
,
error
)
GetUsageTrendWithFilters
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
userID
,
apiKeyID
int64
)
([]
usagestats
.
TrendDataPoint
,
error
)
GetModelStatsWithFilters
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
userID
,
apiKeyID
,
accountID
int64
)
([]
usagestats
.
ModelStat
,
error
)
GetApiKeyUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
usagestats
.
ApiKeyUsageTrendPoint
,
error
)
GetUserUsageTrend
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
,
granularity
string
,
limit
int
)
([]
usagestats
.
UserUsageTrendPoint
,
error
)
GetBatchUserUsageStats
(
ctx
context
.
Context
,
userIDs
[]
int64
)
(
map
[
int64
]
*
usagestats
.
BatchUserUsageStats
,
error
)
GetBatchApiKeyUsageStats
(
ctx
context
.
Context
,
apiKeyIDs
[]
int64
)
(
map
[
int64
]
*
usagestats
.
BatchApiKeyUsageStats
,
error
)
// User dashboard stats
GetUserDashboardStats
(
ctx
context
.
Context
,
userID
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
)
// Admin usage listing/stats
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
filters
usagestats
.
UsageLogFilters
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
GetGlobalStats
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
)
(
*
usagestats
.
UsageStats
,
error
)
// Account stats
GetAccountUsageStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
(
*
usagestats
.
AccountUsageStatsResponse
,
error
)
}
}
backend/internal/service/usage_service.go
View file @
bbf4024d
...
@@ -6,6 +6,7 @@ import (
...
@@ -6,6 +6,7 @@ import (
"fmt"
"fmt"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/usagestats"
"sub2api/internal/service/ports"
"sub2api/internal/service/ports"
"time"
"time"
...
@@ -282,3 +283,57 @@ func (s *UsageService) Delete(ctx context.Context, id int64) error {
...
@@ -282,3 +283,57 @@ func (s *UsageService) Delete(ctx context.Context, id int64) error {
}
}
return
nil
return
nil
}
}
// GetUserDashboardStats returns per-user dashboard summary stats.
func
(
s
*
UsageService
)
GetUserDashboardStats
(
ctx
context
.
Context
,
userID
int64
)
(
*
usagestats
.
UserDashboardStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetUserDashboardStats
(
ctx
,
userID
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get user 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
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get user usage trend: %w"
,
err
)
}
return
trend
,
nil
}
// GetUserModelStats returns per-user model usage stats.
func
(
s
*
UsageService
)
GetUserModelStats
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
)
([]
usagestats
.
ModelStat
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetUserModelStats
(
ctx
,
userID
,
startTime
,
endTime
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get user model stats: %w"
,
err
)
}
return
stats
,
nil
}
// GetBatchApiKeyUsageStats returns today/total actual_cost for given api keys.
func
(
s
*
UsageService
)
GetBatchApiKeyUsageStats
(
ctx
context
.
Context
,
apiKeyIDs
[]
int64
)
(
map
[
int64
]
*
usagestats
.
BatchApiKeyUsageStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetBatchApiKeyUsageStats
(
ctx
,
apiKeyIDs
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get batch api key usage stats: %w"
,
err
)
}
return
stats
,
nil
}
// ListWithFilters lists usage logs with admin filters.
func
(
s
*
UsageService
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
filters
usagestats
.
UsageLogFilters
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
logs
,
result
,
err
:=
s
.
usageRepo
.
ListWithFilters
(
ctx
,
params
,
filters
)
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"list usage logs with filters: %w"
,
err
)
}
return
logs
,
result
,
nil
}
// GetGlobalStats returns global usage stats for a time range.
func
(
s
*
UsageService
)
GetGlobalStats
(
ctx
context
.
Context
,
startTime
,
endTime
time
.
Time
)
(
*
usagestats
.
UsageStats
,
error
)
{
stats
,
err
:=
s
.
usageRepo
.
GetGlobalStats
(
ctx
,
startTime
,
endTime
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get global usage stats: %w"
,
err
)
}
return
stats
,
nil
}
backend/internal/service/wire.go
View file @
bbf4024d
...
@@ -56,6 +56,7 @@ var ProviderSet = wire.NewSet(
...
@@ -56,6 +56,7 @@ var ProviderSet = wire.NewSet(
NewProxyService
,
NewProxyService
,
NewRedeemService
,
NewRedeemService
,
NewUsageService
,
NewUsageService
,
NewDashboardService
,
ProvidePricingService
,
ProvidePricingService
,
NewBillingService
,
NewBillingService
,
NewBillingCacheService
,
NewBillingCacheService
,
...
...
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