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
14b155c6
Unverified
Commit
14b155c6
authored
Dec 19, 2025
by
Wesley Liddick
Committed by
GitHub
Dec 19, 2025
Browse files
Merge pull request #7 from NepetaLemon/refactor/ports-pattern
refactor(backend): 引入端口接口模式
parents
7fd94ab7
e99b344b
Changes
45
Hide whitespace changes
Inline
Side-by-side
backend/cmd/server/wire_gen.go
View file @
14b155c6
...
...
@@ -57,32 +57,21 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
usageService
:=
service
.
NewUsageService
(
usageLogRepository
,
userRepository
)
usageHandler
:=
handler
.
NewUsageHandler
(
usageService
,
usageLogRepository
,
apiKeyService
)
redeemCodeRepository
:=
repository
.
NewRedeemCodeRepository
(
db
)
accountRepository
:=
repository
.
NewAccountRepository
(
db
)
proxyRepository
:=
repository
.
NewProxyRepository
(
db
)
repositories
:=
&
repository
.
Repositories
{
User
:
userRepository
,
ApiKey
:
apiKeyRepository
,
Group
:
groupRepository
,
Account
:
accountRepository
,
Proxy
:
proxyRepository
,
RedeemCode
:
redeemCodeRepository
,
UsageLog
:
usageLogRepository
,
Setting
:
settingRepository
,
UserSubscription
:
userSubscriptionRepository
,
}
billingCacheService
:=
service
.
NewBillingCacheService
(
client
,
userRepository
,
userSubscriptionRepository
)
subscriptionService
:=
service
.
NewSubscriptionService
(
r
epositor
ies
,
billingCacheService
)
subscriptionService
:=
service
.
NewSubscriptionService
(
groupR
epositor
y
,
userSubscriptionRepository
,
billingCacheService
)
redeemService
:=
service
.
NewRedeemService
(
redeemCodeRepository
,
userRepository
,
subscriptionService
,
client
,
billingCacheService
)
redeemHandler
:=
handler
.
NewRedeemHandler
(
redeemService
)
subscriptionHandler
:=
handler
.
NewSubscriptionHandler
(
subscriptionService
)
adminService
:=
service
.
NewAdminService
(
repositories
,
billingCacheService
)
accountRepository
:=
repository
.
NewAccountRepository
(
db
)
proxyRepository
:=
repository
.
NewProxyRepository
(
db
)
adminService
:=
service
.
NewAdminService
(
userRepository
,
groupRepository
,
accountRepository
,
proxyRepository
,
apiKeyRepository
,
redeemCodeRepository
,
usageLogRepository
,
userSubscriptionRepository
,
billingCacheService
)
dashboardHandler
:=
admin
.
NewDashboardHandler
(
adminService
,
usageLogRepository
)
adminUserHandler
:=
admin
.
NewUserHandler
(
adminService
)
groupHandler
:=
admin
.
NewGroupHandler
(
adminService
)
oAuthService
:=
service
.
NewOAuthService
(
proxyRepository
)
rateLimitService
:=
service
.
NewRateLimitService
(
r
epositor
ies
,
configConfig
)
accountUsageService
:=
service
.
NewAccountUsageService
(
repositories
,
oAuthService
)
accountTestService
:=
service
.
NewAccountTestService
(
r
epositor
ies
,
oAuthService
)
rateLimitService
:=
service
.
NewRateLimitService
(
accountR
epositor
y
,
configConfig
)
accountUsageService
:=
service
.
NewAccountUsageService
(
accountRepository
,
usageLogRepository
,
oAuthService
)
accountTestService
:=
service
.
NewAccountTestService
(
accountR
epositor
y
,
oAuthService
)
accountHandler
:=
admin
.
NewAccountHandler
(
adminService
,
oAuthService
,
rateLimitService
,
accountUsageService
,
accountTestService
)
oAuthHandler
:=
admin
.
NewOAuthHandler
(
oAuthService
,
adminService
)
proxyHandler
:=
admin
.
NewProxyHandler
(
adminService
)
...
...
@@ -98,7 +87,7 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
}
billingService
:=
service
.
NewBillingService
(
configConfig
,
pricingService
)
identityService
:=
service
.
NewIdentityService
(
client
)
gatewayService
:=
service
.
NewGatewayService
(
repositories
,
client
,
configConfig
,
oAuthService
,
billingService
,
rateLimitService
,
billingCacheService
,
identityService
)
gatewayService
:=
service
.
NewGatewayService
(
accountRepository
,
usageLogRepository
,
userRepository
,
userSubscriptionRepository
,
client
,
configConfig
,
oAuthService
,
billingService
,
rateLimitService
,
billingCacheService
,
identityService
)
concurrencyService
:=
service
.
NewConcurrencyService
(
client
)
gatewayHandler
:=
handler
.
NewGatewayHandler
(
gatewayService
,
userService
,
concurrencyService
,
billingCacheService
)
handlerSettingHandler
:=
handler
.
ProvideSettingHandler
(
settingService
,
buildInfo
)
...
...
@@ -132,6 +121,17 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
Concurrency
:
concurrencyService
,
Identity
:
identityService
,
}
repositories
:=
&
repository
.
Repositories
{
User
:
userRepository
,
ApiKey
:
apiKeyRepository
,
Group
:
groupRepository
,
Account
:
accountRepository
,
Proxy
:
proxyRepository
,
RedeemCode
:
redeemCodeRepository
,
UsageLog
:
usageLogRepository
,
Setting
:
settingRepository
,
UserSubscription
:
userSubscriptionRepository
,
}
engine
:=
server
.
ProvideRouter
(
configConfig
,
handlers
,
services
,
repositories
)
httpServer
:=
server
.
ProvideHTTPServer
(
configConfig
,
engine
)
v
:=
provideCleanup
(
db
,
client
,
services
)
...
...
backend/internal/handler/admin/subscription_handler.go
View file @
14b155c6
...
...
@@ -4,15 +4,15 @@ import (
"strconv"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/repository"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
)
// toResponsePagination converts
repository
.PaginationResult to response.PaginationResult
func
toResponsePagination
(
p
*
repository
.
PaginationResult
)
*
response
.
PaginationResult
{
// toResponsePagination converts
pagination
.PaginationResult to response.PaginationResult
func
toResponsePagination
(
p
*
pagination
.
PaginationResult
)
*
response
.
PaginationResult
{
if
p
==
nil
{
return
nil
}
...
...
backend/internal/handler/admin/usage_handler.go
View file @
14b155c6
...
...
@@ -4,6 +4,7 @@ import (
"strconv"
"time"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/repository"
...
...
@@ -14,10 +15,10 @@ import (
// UsageHandler handles admin usage-related requests
type
UsageHandler
struct
{
usageRepo
*
repository
.
UsageLogRepository
apiKeyRepo
*
repository
.
ApiKeyRepository
usageService
*
service
.
UsageService
adminService
service
.
AdminService
usageRepo
*
repository
.
UsageLogRepository
apiKeyRepo
*
repository
.
ApiKeyRepository
usageService
*
service
.
UsageService
adminService
service
.
AdminService
}
// NewUsageHandler creates a new admin usage handler
...
...
@@ -82,7 +83,7 @@ func (h *UsageHandler) List(c *gin.Context) {
endTime
=
&
t
}
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
filters
:=
repository
.
UsageLogFilters
{
UserID
:
userID
,
ApiKeyID
:
apiKeyID
,
...
...
backend/internal/handler/api_key_handler.go
View file @
14b155c6
...
...
@@ -4,8 +4,8 @@ import (
"strconv"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/repository"
"sub2api/internal/service"
"github.com/gin-gonic/gin"
...
...
@@ -53,7 +53,7 @@ func (h *APIKeyHandler) List(c *gin.Context) {
}
page
,
pageSize
:=
response
.
ParsePagination
(
c
)
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
keys
,
result
,
err
:=
h
.
apiKeyService
.
List
(
c
.
Request
.
Context
(),
user
.
ID
,
params
)
if
err
!=
nil
{
...
...
backend/internal/handler/usage_handler.go
View file @
14b155c6
...
...
@@ -5,6 +5,7 @@ import (
"time"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/response"
"sub2api/internal/pkg/timezone"
"sub2api/internal/repository"
...
...
@@ -68,9 +69,9 @@ func (h *UsageHandler) List(c *gin.Context) {
apiKeyID
=
id
}
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
var
records
[]
model
.
UsageLog
var
result
*
repository
.
PaginationResult
var
result
*
pagination
.
PaginationResult
var
err
error
if
apiKeyID
>
0
{
...
...
@@ -362,7 +363,7 @@ func (h *UsageHandler) DashboardApiKeysUsage(c *gin.Context) {
}
// Verify ownership of all requested API keys
userApiKeys
,
_
,
err
:=
h
.
apiKeyService
.
List
(
c
.
Request
.
Context
(),
user
.
ID
,
repository
.
PaginationParams
{
Page
:
1
,
PageSize
:
1000
})
userApiKeys
,
_
,
err
:=
h
.
apiKeyService
.
List
(
c
.
Request
.
Context
(),
user
.
ID
,
pagination
.
PaginationParams
{
Page
:
1
,
PageSize
:
1000
})
if
err
!=
nil
{
response
.
InternalError
(
c
,
"Failed to verify API key ownership"
)
return
...
...
backend/internal/pkg/pagination/pagination.go
0 → 100644
View file @
14b155c6
package
pagination
// PaginationParams 分页参数
type
PaginationParams
struct
{
Page
int
PageSize
int
}
// PaginationResult 分页结果
type
PaginationResult
struct
{
Total
int64
Page
int
PageSize
int
Pages
int
}
// DefaultPagination 默认分页参数
func
DefaultPagination
()
PaginationParams
{
return
PaginationParams
{
Page
:
1
,
PageSize
:
20
,
}
}
// Offset 计算偏移量
func
(
p
PaginationParams
)
Offset
()
int
{
if
p
.
Page
<
1
{
p
.
Page
=
1
}
return
(
p
.
Page
-
1
)
*
p
.
PageSize
}
// Limit 获取限制数
func
(
p
PaginationParams
)
Limit
()
int
{
if
p
.
PageSize
<
1
{
return
20
}
if
p
.
PageSize
>
100
{
return
100
}
return
p
.
PageSize
}
backend/internal/pkg/response/response.go
View file @
14b155c6
...
...
@@ -90,7 +90,7 @@ func Paginated(c *gin.Context, items interface{}, total int64, page, pageSize in
})
}
// PaginationResult 分页结果(与
repository
.PaginationResult兼容)
// PaginationResult 分页结果(与
pagination
.PaginationResult兼容)
type
PaginationResult
struct
{
Total
int64
Page
int
...
...
backend/internal/pkg/usagestats/account_stats.go
0 → 100644
View file @
14b155c6
package
usagestats
// AccountStats 账号使用统计
type
AccountStats
struct
{
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
}
backend/internal/repository/account_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"time"
"gorm.io/gorm"
...
...
@@ -47,12 +48,12 @@ func (r *AccountRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
Account
{},
id
)
.
Error
}
func
(
r
*
AccountRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
)
([]
model
.
Account
,
*
PaginationResult
,
error
)
{
func
(
r
*
AccountRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
{
return
r
.
ListWithFilters
(
ctx
,
params
,
""
,
""
,
""
,
""
)
}
// ListWithFilters lists accounts with optional filtering by platform, type, status, and search query
func
(
r
*
AccountRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
*
PaginationResult
,
error
)
{
func
(
r
*
AccountRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
{
var
accounts
[]
model
.
Account
var
total
int64
...
...
@@ -94,7 +95,7 @@ func (r *AccountRepository) ListWithFilters(ctx context.Context, params Paginati
pages
++
}
return
accounts
,
&
PaginationResult
{
return
accounts
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -226,7 +227,7 @@ func (r *AccountRepository) SetRateLimited(ctx context.Context, id int64, resetA
now
:=
time
.
Now
()
return
r
.
db
.
WithContext
(
ctx
)
.
Model
(
&
model
.
Account
{})
.
Where
(
"id = ?"
,
id
)
.
Updates
(
map
[
string
]
interface
{}{
"rate_limited_at"
:
now
,
"rate_limited_at"
:
now
,
"rate_limit_reset_at"
:
resetAt
,
})
.
Error
}
...
...
backend/internal/repository/api_key_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
...
...
@@ -45,7 +46,7 @@ func (r *ApiKeyRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
ApiKey
{},
id
)
.
Error
}
func
(
r
*
ApiKeyRepository
)
ListByUserID
(
ctx
context
.
Context
,
userID
int64
,
params
PaginationParams
)
([]
model
.
ApiKey
,
*
PaginationResult
,
error
)
{
func
(
r
*
ApiKeyRepository
)
ListByUserID
(
ctx
context
.
Context
,
userID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
ApiKey
,
*
pagination
.
PaginationResult
,
error
)
{
var
keys
[]
model
.
ApiKey
var
total
int64
...
...
@@ -64,7 +65,7 @@ func (r *ApiKeyRepository) ListByUserID(ctx context.Context, userID int64, param
pages
++
}
return
keys
,
&
PaginationResult
{
return
keys
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -84,7 +85,7 @@ func (r *ApiKeyRepository) ExistsByKey(ctx context.Context, key string) (bool, e
return
count
>
0
,
err
}
func
(
r
*
ApiKeyRepository
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
PaginationParams
)
([]
model
.
ApiKey
,
*
PaginationResult
,
error
)
{
func
(
r
*
ApiKeyRepository
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
ApiKey
,
*
pagination
.
PaginationResult
,
error
)
{
var
keys
[]
model
.
ApiKey
var
total
int64
...
...
@@ -103,7 +104,7 @@ func (r *ApiKeyRepository) ListByGroupID(ctx context.Context, groupID int64, par
pages
++
}
return
keys
,
&
PaginationResult
{
return
keys
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
backend/internal/repository/group_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
...
...
@@ -36,12 +37,12 @@ func (r *GroupRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
Group
{},
id
)
.
Error
}
func
(
r
*
GroupRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
)
([]
model
.
Group
,
*
PaginationResult
,
error
)
{
func
(
r
*
GroupRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Group
,
*
pagination
.
PaginationResult
,
error
)
{
return
r
.
ListWithFilters
(
ctx
,
params
,
""
,
""
,
nil
)
}
// ListWithFilters lists groups with optional filtering by platform, status, and is_exclusive
func
(
r
*
GroupRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
platform
,
status
string
,
isExclusive
*
bool
)
([]
model
.
Group
,
*
PaginationResult
,
error
)
{
func
(
r
*
GroupRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
platform
,
status
string
,
isExclusive
*
bool
)
([]
model
.
Group
,
*
pagination
.
PaginationResult
,
error
)
{
var
groups
[]
model
.
Group
var
total
int64
...
...
@@ -77,7 +78,7 @@ func (r *GroupRepository) ListWithFilters(ctx context.Context, params Pagination
pages
++
}
return
groups
,
&
PaginationResult
{
return
groups
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
backend/internal/repository/proxy_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
...
...
@@ -36,12 +37,12 @@ func (r *ProxyRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
Proxy
{},
id
)
.
Error
}
func
(
r
*
ProxyRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
)
([]
model
.
Proxy
,
*
PaginationResult
,
error
)
{
func
(
r
*
ProxyRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Proxy
,
*
pagination
.
PaginationResult
,
error
)
{
return
r
.
ListWithFilters
(
ctx
,
params
,
""
,
""
,
""
)
}
// ListWithFilters lists proxies with optional filtering by protocol, status, and search query
func
(
r
*
ProxyRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
protocol
,
status
,
search
string
)
([]
model
.
Proxy
,
*
PaginationResult
,
error
)
{
func
(
r
*
ProxyRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
protocol
,
status
,
search
string
)
([]
model
.
Proxy
,
*
pagination
.
PaginationResult
,
error
)
{
var
proxies
[]
model
.
Proxy
var
total
int64
...
...
@@ -72,7 +73,7 @@ func (r *ProxyRepository) ListWithFilters(ctx context.Context, params Pagination
pages
++
}
return
proxies
,
&
PaginationResult
{
return
proxies
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
backend/internal/repository/redeem_code_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"time"
"gorm.io/gorm"
...
...
@@ -46,12 +47,12 @@ func (r *RedeemCodeRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
RedeemCode
{},
id
)
.
Error
}
func
(
r
*
RedeemCodeRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
)
([]
model
.
RedeemCode
,
*
PaginationResult
,
error
)
{
func
(
r
*
RedeemCodeRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
RedeemCode
,
*
pagination
.
PaginationResult
,
error
)
{
return
r
.
ListWithFilters
(
ctx
,
params
,
""
,
""
,
""
)
}
// ListWithFilters lists redeem codes with optional filtering by type, status, and search query
func
(
r
*
RedeemCodeRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
codeType
,
status
,
search
string
)
([]
model
.
RedeemCode
,
*
PaginationResult
,
error
)
{
func
(
r
*
RedeemCodeRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
codeType
,
status
,
search
string
)
([]
model
.
RedeemCode
,
*
pagination
.
PaginationResult
,
error
)
{
var
codes
[]
model
.
RedeemCode
var
total
int64
...
...
@@ -82,7 +83,7 @@ func (r *RedeemCodeRepository) ListWithFilters(ctx context.Context, params Pagin
pages
++
}
return
codes
,
&
PaginationResult
{
return
codes
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
backend/internal/repository/repository.go
View file @
14b155c6
...
...
@@ -12,44 +12,3 @@ type Repositories struct {
Setting
*
SettingRepository
UserSubscription
*
UserSubscriptionRepository
}
// PaginationParams 分页参数
type
PaginationParams
struct
{
Page
int
PageSize
int
}
// PaginationResult 分页结果
type
PaginationResult
struct
{
Total
int64
Page
int
PageSize
int
Pages
int
}
// DefaultPagination 默认分页参数
func
DefaultPagination
()
PaginationParams
{
return
PaginationParams
{
Page
:
1
,
PageSize
:
20
,
}
}
// Offset 计算偏移量
func
(
p
PaginationParams
)
Offset
()
int
{
if
p
.
Page
<
1
{
p
.
Page
=
1
}
return
(
p
.
Page
-
1
)
*
p
.
PageSize
}
// Limit 获取限制数
func
(
p
PaginationParams
)
Limit
()
int
{
if
p
.
PageSize
<
1
{
return
20
}
if
p
.
PageSize
>
100
{
return
100
}
return
p
.
PageSize
}
backend/internal/repository/usage_log_repo.go
View file @
14b155c6
...
...
@@ -3,7 +3,9 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/usagestats"
"time"
"gorm.io/gorm"
...
...
@@ -30,7 +32,7 @@ func (r *UsageLogRepository) GetByID(ctx context.Context, id int64) (*model.Usag
return
&
log
,
nil
}
func
(
r
*
UsageLogRepository
)
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
params
PaginationParams
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
var
total
int64
...
...
@@ -49,7 +51,7 @@ func (r *UsageLogRepository) ListByUser(ctx context.Context, userID int64, param
pages
++
}
return
logs
,
&
PaginationResult
{
return
logs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -57,7 +59,7 @@ func (r *UsageLogRepository) ListByUser(ctx context.Context, userID int64, param
},
nil
}
func
(
r
*
UsageLogRepository
)
ListByApiKey
(
ctx
context
.
Context
,
apiKeyID
int64
,
params
PaginationParams
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByApiKey
(
ctx
context
.
Context
,
apiKeyID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
var
total
int64
...
...
@@ -76,7 +78,7 @@ func (r *UsageLogRepository) ListByApiKey(ctx context.Context, apiKeyID int64, p
pages
++
}
return
logs
,
&
PaginationResult
{
return
logs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -270,7 +272,7 @@ func (r *UsageLogRepository) GetDashboardStats(ctx context.Context) (*DashboardS
return
&
stats
,
nil
}
func
(
r
*
UsageLogRepository
)
ListByAccount
(
ctx
context
.
Context
,
accountID
int64
,
params
PaginationParams
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByAccount
(
ctx
context
.
Context
,
accountID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
var
total
int64
...
...
@@ -289,7 +291,7 @@ func (r *UsageLogRepository) ListByAccount(ctx context.Context, accountID int64,
pages
++
}
return
logs
,
&
PaginationResult
{
return
logs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -297,7 +299,7 @@ func (r *UsageLogRepository) ListByAccount(ctx context.Context, accountID int64,
},
nil
}
func
(
r
*
UsageLogRepository
)
ListByUserAndTimeRange
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByUserAndTimeRange
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
err
:=
r
.
db
.
WithContext
(
ctx
)
.
Where
(
"user_id = ? AND created_at >= ? AND created_at < ?"
,
userID
,
startTime
,
endTime
)
.
...
...
@@ -306,7 +308,7 @@ func (r *UsageLogRepository) ListByUserAndTimeRange(ctx context.Context, userID
return
logs
,
nil
,
err
}
func
(
r
*
UsageLogRepository
)
ListByApiKeyAndTimeRange
(
ctx
context
.
Context
,
apiKeyID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByApiKeyAndTimeRange
(
ctx
context
.
Context
,
apiKeyID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
err
:=
r
.
db
.
WithContext
(
ctx
)
.
Where
(
"api_key_id = ? AND created_at >= ? AND created_at < ?"
,
apiKeyID
,
startTime
,
endTime
)
.
...
...
@@ -315,7 +317,7 @@ func (r *UsageLogRepository) ListByApiKeyAndTimeRange(ctx context.Context, apiKe
return
logs
,
nil
,
err
}
func
(
r
*
UsageLogRepository
)
ListByAccountAndTimeRange
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByAccountAndTimeRange
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
err
:=
r
.
db
.
WithContext
(
ctx
)
.
Where
(
"account_id = ? AND created_at >= ? AND created_at < ?"
,
accountID
,
startTime
,
endTime
)
.
...
...
@@ -324,7 +326,7 @@ func (r *UsageLogRepository) ListByAccountAndTimeRange(ctx context.Context, acco
return
logs
,
nil
,
err
}
func
(
r
*
UsageLogRepository
)
ListByModelAndTimeRange
(
ctx
context
.
Context
,
modelName
string
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListByModelAndTimeRange
(
ctx
context
.
Context
,
modelName
string
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
err
:=
r
.
db
.
WithContext
(
ctx
)
.
Where
(
"model = ? AND created_at >= ? AND created_at < ?"
,
modelName
,
startTime
,
endTime
)
.
...
...
@@ -337,15 +339,8 @@ func (r *UsageLogRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
UsageLog
{},
id
)
.
Error
}
// AccountStats 账号使用统计
type
AccountStats
struct
{
Requests
int64
`json:"requests"`
Tokens
int64
`json:"tokens"`
Cost
float64
`json:"cost"`
}
// GetAccountTodayStats 获取账号今日统计
func
(
r
*
UsageLogRepository
)
GetAccountTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
AccountStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetAccountTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
usagestats
.
AccountStats
,
error
)
{
today
:=
timezone
.
Today
()
var
stats
struct
{
...
...
@@ -367,7 +362,7 @@ func (r *UsageLogRepository) GetAccountTodayStats(ctx context.Context, accountID
return
nil
,
err
}
return
&
AccountStats
{
return
&
usagestats
.
AccountStats
{
Requests
:
stats
.
Requests
,
Tokens
:
stats
.
Tokens
,
Cost
:
stats
.
Cost
,
...
...
@@ -375,7 +370,7 @@ func (r *UsageLogRepository) GetAccountTodayStats(ctx context.Context, accountID
}
// GetAccountWindowStats 获取账号时间窗口内的统计
func
(
r
*
UsageLogRepository
)
GetAccountWindowStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
time
.
Time
)
(
*
AccountStats
,
error
)
{
func
(
r
*
UsageLogRepository
)
GetAccountWindowStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
time
.
Time
)
(
*
usagestats
.
AccountStats
,
error
)
{
var
stats
struct
{
Requests
int64
`gorm:"column:requests"`
Tokens
int64
`gorm:"column:tokens"`
...
...
@@ -395,7 +390,7 @@ func (r *UsageLogRepository) GetAccountWindowStats(ctx context.Context, accountI
return
nil
,
err
}
return
&
AccountStats
{
return
&
usagestats
.
AccountStats
{
Requests
:
stats
.
Requests
,
Tokens
:
stats
.
Tokens
,
Cost
:
stats
.
Cost
,
...
...
@@ -500,11 +495,11 @@ func (r *UsageLogRepository) GetModelStats(ctx context.Context, startTime, endTi
// 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"`
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
...
...
@@ -780,7 +775,7 @@ type UsageLogFilters struct {
}
// ListWithFilters lists usage logs with optional filters (for admin)
func
(
r
*
UsageLogRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
filters
UsageLogFilters
)
([]
model
.
UsageLog
,
*
PaginationResult
,
error
)
{
func
(
r
*
UsageLogRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
filters
UsageLogFilters
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
{
var
logs
[]
model
.
UsageLog
var
total
int64
...
...
@@ -816,7 +811,7 @@ func (r *UsageLogRepository) ListWithFilters(ctx context.Context, params Paginat
pages
++
}
return
logs
,
&
PaginationResult
{
return
logs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -838,7 +833,7 @@ type UsageStats struct {
// BatchUserUsageStats represents usage stats for a single user
type
BatchUserUsageStats
struct
{
UserID
int64
`json:"user_id"`
UserID
int64
`json:"user_id"`
TodayActualCost
float64
`json:"today_actual_cost"`
TotalActualCost
float64
`json:"total_actual_cost"`
}
...
...
backend/internal/repository/user_repo.go
View file @
14b155c6
...
...
@@ -3,6 +3,7 @@ package repository
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
...
...
@@ -45,12 +46,12 @@ func (r *UserRepository) Delete(ctx context.Context, id int64) error {
return
r
.
db
.
WithContext
(
ctx
)
.
Delete
(
&
model
.
User
{},
id
)
.
Error
}
func
(
r
*
UserRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
)
([]
model
.
User
,
*
PaginationResult
,
error
)
{
func
(
r
*
UserRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
User
,
*
pagination
.
PaginationResult
,
error
)
{
return
r
.
ListWithFilters
(
ctx
,
params
,
""
,
""
,
""
)
}
// ListWithFilters lists users with optional filtering by status, role, and search query
func
(
r
*
UserRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
PaginationParams
,
status
,
role
,
search
string
)
([]
model
.
User
,
*
PaginationResult
,
error
)
{
func
(
r
*
UserRepository
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
status
,
role
,
search
string
)
([]
model
.
User
,
*
pagination
.
PaginationResult
,
error
)
{
var
users
[]
model
.
User
var
total
int64
...
...
@@ -81,7 +82,7 @@ func (r *UserRepository) ListWithFilters(ctx context.Context, params PaginationP
pages
++
}
return
users
,
&
PaginationResult
{
return
users
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -127,4 +128,3 @@ func (r *UserRepository) RemoveGroupFromAllowedGroups(ctx context.Context, group
Update
(
"allowed_groups"
,
gorm
.
Expr
(
"array_remove(allowed_groups, ?)"
,
groupID
))
return
result
.
RowsAffected
,
result
.
Error
}
backend/internal/repository/user_subscription_repo.go
View file @
14b155c6
...
...
@@ -5,6 +5,7 @@ import (
"time"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
...
...
@@ -100,7 +101,7 @@ func (r *UserSubscriptionRepository) ListActiveByUserID(ctx context.Context, use
}
// ListByGroupID 获取分组的所有订阅(分页)
func
(
r
*
UserSubscriptionRepository
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
PaginationParams
)
([]
model
.
UserSubscription
,
*
PaginationResult
,
error
)
{
func
(
r
*
UserSubscriptionRepository
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
{
var
subs
[]
model
.
UserSubscription
var
total
int64
...
...
@@ -126,7 +127,7 @@ func (r *UserSubscriptionRepository) ListByGroupID(ctx context.Context, groupID
pages
++
}
return
subs
,
&
PaginationResult
{
return
subs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
@@ -135,7 +136,7 @@ func (r *UserSubscriptionRepository) ListByGroupID(ctx context.Context, groupID
}
// List 获取所有订阅(分页,支持筛选)
func
(
r
*
UserSubscriptionRepository
)
List
(
ctx
context
.
Context
,
params
PaginationParams
,
userID
,
groupID
*
int64
,
status
string
)
([]
model
.
UserSubscription
,
*
PaginationResult
,
error
)
{
func
(
r
*
UserSubscriptionRepository
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
userID
,
groupID
*
int64
,
status
string
)
([]
model
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
{
var
subs
[]
model
.
UserSubscription
var
total
int64
...
...
@@ -172,7 +173,7 @@ func (r *UserSubscriptionRepository) List(ctx context.Context, params Pagination
pages
++
}
return
subs
,
&
PaginationResult
{
return
subs
,
&
pagination
.
PaginationResult
{
Total
:
total
,
Page
:
params
.
Page
,
PageSize
:
params
.
Limit
(),
...
...
backend/internal/repository/wire.go
View file @
14b155c6
package
repository
import
(
"sub2api/internal/service/ports"
"github.com/google/wire"
)
...
...
@@ -16,4 +18,15 @@ var ProviderSet = wire.NewSet(
NewSettingRepository
,
NewUserSubscriptionRepository
,
wire
.
Struct
(
new
(
Repositories
),
"*"
),
// Bind concrete repositories to service port interfaces
wire
.
Bind
(
new
(
ports
.
UserRepository
),
new
(
*
UserRepository
)),
wire
.
Bind
(
new
(
ports
.
ApiKeyRepository
),
new
(
*
ApiKeyRepository
)),
wire
.
Bind
(
new
(
ports
.
GroupRepository
),
new
(
*
GroupRepository
)),
wire
.
Bind
(
new
(
ports
.
AccountRepository
),
new
(
*
AccountRepository
)),
wire
.
Bind
(
new
(
ports
.
ProxyRepository
),
new
(
*
ProxyRepository
)),
wire
.
Bind
(
new
(
ports
.
RedeemCodeRepository
),
new
(
*
RedeemCodeRepository
)),
wire
.
Bind
(
new
(
ports
.
UsageLogRepository
),
new
(
*
UsageLogRepository
)),
wire
.
Bind
(
new
(
ports
.
SettingRepository
),
new
(
*
SettingRepository
)),
wire
.
Bind
(
new
(
ports
.
UserSubscriptionRepository
),
new
(
*
UserSubscriptionRepository
)),
)
backend/internal/service/account_service.go
View file @
14b155c6
...
...
@@ -5,7 +5,8 @@ import (
"errors"
"fmt"
"sub2api/internal/model"
"sub2api/internal/repository"
"sub2api/internal/pkg/pagination"
"sub2api/internal/service/ports"
"gorm.io/gorm"
)
...
...
@@ -41,12 +42,12 @@ type UpdateAccountRequest struct {
// AccountService 账号管理服务
type
AccountService
struct
{
accountRepo
*
repository
.
AccountRepository
groupRepo
*
repository
.
GroupRepository
accountRepo
ports
.
AccountRepository
groupRepo
ports
.
GroupRepository
}
// NewAccountService 创建账号服务实例
func
NewAccountService
(
accountRepo
*
repository
.
AccountRepository
,
groupRepo
*
repository
.
GroupRepository
)
*
AccountService
{
func
NewAccountService
(
accountRepo
ports
.
AccountRepository
,
groupRepo
ports
.
GroupRepository
)
*
AccountService
{
return
&
AccountService
{
accountRepo
:
accountRepo
,
groupRepo
:
groupRepo
,
...
...
@@ -108,7 +109,7 @@ func (s *AccountService) GetByID(ctx context.Context, id int64) (*model.Account,
}
// List 获取账号列表
func
(
s
*
AccountService
)
List
(
ctx
context
.
Context
,
params
repository
.
PaginationParams
)
([]
model
.
Account
,
*
repository
.
PaginationResult
,
error
)
{
func
(
s
*
AccountService
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
{
accounts
,
pagination
,
err
:=
s
.
accountRepo
.
List
(
ctx
,
params
)
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"list accounts: %w"
,
err
)
...
...
backend/internal/service/account_test_service.go
View file @
14b155c6
...
...
@@ -16,7 +16,7 @@ import (
"time"
"sub2api/internal/pkg/claude"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
...
...
@@ -37,15 +37,15 @@ type TestEvent struct {
// AccountTestService handles account testing operations
type
AccountTestService
struct
{
r
epo
s
*
repository
.
Repositor
ies
accountR
epo
ports
.
Account
Repositor
y
oauthService
*
OAuthService
httpClient
*
http
.
Client
}
// NewAccountTestService creates a new AccountTestService
func
NewAccountTestService
(
repos
*
repository
.
Repositor
ies
,
oauthService
*
OAuthService
)
*
AccountTestService
{
func
NewAccountTestService
(
accountRepo
ports
.
Account
Repositor
y
,
oauthService
*
OAuthService
)
*
AccountTestService
{
return
&
AccountTestService
{
r
epo
s
:
r
epo
s
,
accountR
epo
:
accountR
epo
,
oauthService
:
oauthService
,
httpClient
:
&
http
.
Client
{
Timeout
:
60
*
time
.
Second
,
...
...
@@ -105,7 +105,7 @@ func (s *AccountTestService) TestAccountConnection(c *gin.Context, accountID int
ctx
:=
c
.
Request
.
Context
()
// Get account
account
,
err
:=
s
.
repos
.
A
ccount
.
GetByID
(
ctx
,
accountID
)
account
,
err
:=
s
.
a
ccount
Repo
.
GetByID
(
ctx
,
accountID
)
if
err
!=
nil
{
return
s
.
sendErrorAndEnd
(
c
,
"Account not found"
)
}
...
...
Prev
1
2
3
Next
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