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
e99b344b
Commit
e99b344b
authored
Dec 19, 2025
by
Forest
Browse files
refactor(backend): 引入端口接口模式
parent
7fd94ab7
Changes
45
Show whitespace changes
Inline
Side-by-side
backend/internal/service/account_usage_service.go
View file @
e99b344b
...
@@ -12,7 +12,7 @@ import (
...
@@ -12,7 +12,7 @@ import (
"time"
"time"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
)
)
// usageCache 用于缓存usage数据
// usageCache 用于缓存usage数据
...
@@ -67,15 +67,17 @@ type ClaudeUsageResponse struct {
...
@@ -67,15 +67,17 @@ type ClaudeUsageResponse struct {
// AccountUsageService 账号使用量查询服务
// AccountUsageService 账号使用量查询服务
type
AccountUsageService
struct
{
type
AccountUsageService
struct
{
repos
*
repository
.
Repositories
accountRepo
ports
.
AccountRepository
usageLogRepo
ports
.
UsageLogRepository
oauthService
*
OAuthService
oauthService
*
OAuthService
httpClient
*
http
.
Client
httpClient
*
http
.
Client
}
}
// NewAccountUsageService 创建AccountUsageService实例
// NewAccountUsageService 创建AccountUsageService实例
func
NewAccountUsageService
(
repos
*
repository
.
Repositor
ies
,
oauthService
*
OAuthService
)
*
AccountUsageService
{
func
NewAccountUsageService
(
accountRepo
ports
.
AccountRepository
,
usageLogRepo
ports
.
UsageLog
Repositor
y
,
oauthService
*
OAuthService
)
*
AccountUsageService
{
return
&
AccountUsageService
{
return
&
AccountUsageService
{
repos
:
repos
,
accountRepo
:
accountRepo
,
usageLogRepo
:
usageLogRepo
,
oauthService
:
oauthService
,
oauthService
:
oauthService
,
httpClient
:
&
http
.
Client
{
httpClient
:
&
http
.
Client
{
Timeout
:
30
*
time
.
Second
,
Timeout
:
30
*
time
.
Second
,
...
@@ -88,7 +90,7 @@ func NewAccountUsageService(repos *repository.Repositories, oauthService *OAuthS
...
@@ -88,7 +90,7 @@ func NewAccountUsageService(repos *repository.Repositories, oauthService *OAuthS
// Setup Token账号: 根据session_window推算5h窗口,7d数据不可用(没有profile scope)
// Setup Token账号: 根据session_window推算5h窗口,7d数据不可用(没有profile scope)
// API Key账号: 不支持usage查询
// API Key账号: 不支持usage查询
func
(
s
*
AccountUsageService
)
GetUsage
(
ctx
context
.
Context
,
accountID
int64
)
(
*
UsageInfo
,
error
)
{
func
(
s
*
AccountUsageService
)
GetUsage
(
ctx
context
.
Context
,
accountID
int64
)
(
*
UsageInfo
,
error
)
{
account
,
err
:=
s
.
repos
.
A
ccount
.
GetByID
(
ctx
,
accountID
)
account
,
err
:=
s
.
a
ccount
Repo
.
GetByID
(
ctx
,
accountID
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get account failed: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"get account failed: %w"
,
err
)
}
}
...
@@ -148,7 +150,7 @@ func (s *AccountUsageService) addWindowStats(ctx context.Context, account *model
...
@@ -148,7 +150,7 @@ func (s *AccountUsageService) addWindowStats(ctx context.Context, account *model
startTime
=
time
.
Now
()
.
Add
(
-
5
*
time
.
Hour
)
startTime
=
time
.
Now
()
.
Add
(
-
5
*
time
.
Hour
)
}
}
stats
,
err
:=
s
.
repos
.
U
sageLog
.
GetAccountWindowStats
(
ctx
,
account
.
ID
,
startTime
)
stats
,
err
:=
s
.
u
sageLog
Repo
.
GetAccountWindowStats
(
ctx
,
account
.
ID
,
startTime
)
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"Failed to get window stats for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"Failed to get window stats for account %d: %v"
,
account
.
ID
,
err
)
return
return
...
@@ -163,7 +165,7 @@ func (s *AccountUsageService) addWindowStats(ctx context.Context, account *model
...
@@ -163,7 +165,7 @@ func (s *AccountUsageService) addWindowStats(ctx context.Context, account *model
// GetTodayStats 获取账号今日统计
// GetTodayStats 获取账号今日统计
func
(
s
*
AccountUsageService
)
GetTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
WindowStats
,
error
)
{
func
(
s
*
AccountUsageService
)
GetTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
WindowStats
,
error
)
{
stats
,
err
:=
s
.
repos
.
U
sageLog
.
GetAccountTodayStats
(
ctx
,
accountID
)
stats
,
err
:=
s
.
u
sageLog
Repo
.
GetAccountTodayStats
(
ctx
,
accountID
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"get today stats failed: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"get today stats failed: %w"
,
err
)
}
}
...
...
backend/internal/service/admin_service.go
View file @
e99b344b
...
@@ -13,7 +13,8 @@ import (
...
@@ -13,7 +13,8 @@ import (
"time"
"time"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/repository"
"sub2api/internal/pkg/pagination"
"sub2api/internal/service/ports"
"golang.org/x/net/proxy"
"golang.org/x/net/proxy"
"gorm.io/gorm"
"gorm.io/gorm"
...
@@ -179,35 +180,45 @@ type ProxyTestResult struct {
...
@@ -179,35 +180,45 @@ type ProxyTestResult struct {
// adminServiceImpl implements AdminService
// adminServiceImpl implements AdminService
type
adminServiceImpl
struct
{
type
adminServiceImpl
struct
{
userRepo
*
repository
.
UserRepository
userRepo
ports
.
UserRepository
groupRepo
*
repository
.
GroupRepository
groupRepo
ports
.
GroupRepository
accountRepo
*
repository
.
AccountRepository
accountRepo
ports
.
AccountRepository
proxyRepo
*
repository
.
ProxyRepository
proxyRepo
ports
.
ProxyRepository
apiKeyRepo
*
repository
.
ApiKeyRepository
apiKeyRepo
ports
.
ApiKeyRepository
redeemCodeRepo
*
repository
.
RedeemCodeRepository
redeemCodeRepo
ports
.
RedeemCodeRepository
usageLogRepo
*
repository
.
UsageLogRepository
usageLogRepo
ports
.
UsageLogRepository
userSubRepo
*
repository
.
UserSubscriptionRepository
userSubRepo
ports
.
UserSubscriptionRepository
billingCacheService
*
BillingCacheService
billingCacheService
*
BillingCacheService
}
}
// NewAdminService creates a new AdminService
// NewAdminService creates a new AdminService
func
NewAdminService
(
repos
*
repository
.
Repositories
,
billingCacheService
*
BillingCacheService
)
AdminService
{
func
NewAdminService
(
userRepo
ports
.
UserRepository
,
groupRepo
ports
.
GroupRepository
,
accountRepo
ports
.
AccountRepository
,
proxyRepo
ports
.
ProxyRepository
,
apiKeyRepo
ports
.
ApiKeyRepository
,
redeemCodeRepo
ports
.
RedeemCodeRepository
,
usageLogRepo
ports
.
UsageLogRepository
,
userSubRepo
ports
.
UserSubscriptionRepository
,
billingCacheService
*
BillingCacheService
,
)
AdminService
{
return
&
adminServiceImpl
{
return
&
adminServiceImpl
{
userRepo
:
repos
.
User
,
userRepo
:
userRepo
,
groupRepo
:
repos
.
Group
,
groupRepo
:
groupRepo
,
accountRepo
:
repos
.
A
ccount
,
accountRepo
:
a
ccount
Repo
,
proxyRepo
:
repos
.
Proxy
,
proxyRepo
:
proxyRepo
,
apiKeyRepo
:
repos
.
A
piKey
,
apiKeyRepo
:
a
piKey
Repo
,
redeemCodeRepo
:
re
pos
.
Re
deemCode
,
redeemCodeRepo
:
redeemCode
Repo
,
usageLogRepo
:
repos
.
U
sageLog
,
usageLogRepo
:
u
sageLog
Repo
,
userSubRepo
:
repos
.
UserSubscription
,
userSubRepo
:
userSubRepo
,
billingCacheService
:
billingCacheService
,
billingCacheService
:
billingCacheService
,
}
}
}
}
// User management implementations
// User management implementations
func
(
s
*
adminServiceImpl
)
ListUsers
(
ctx
context
.
Context
,
page
,
pageSize
int
,
status
,
role
,
search
string
)
([]
model
.
User
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
ListUsers
(
ctx
context
.
Context
,
page
,
pageSize
int
,
status
,
role
,
search
string
)
([]
model
.
User
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
users
,
result
,
err
:=
s
.
userRepo
.
ListWithFilters
(
ctx
,
params
,
status
,
role
,
search
)
users
,
result
,
err
:=
s
.
userRepo
.
ListWithFilters
(
ctx
,
params
,
status
,
role
,
search
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -376,7 +387,7 @@ func (s *adminServiceImpl) UpdateUserBalance(ctx context.Context, userID int64,
...
@@ -376,7 +387,7 @@ func (s *adminServiceImpl) UpdateUserBalance(ctx context.Context, userID int64,
}
}
func
(
s
*
adminServiceImpl
)
GetUserAPIKeys
(
ctx
context
.
Context
,
userID
int64
,
page
,
pageSize
int
)
([]
model
.
ApiKey
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
GetUserAPIKeys
(
ctx
context
.
Context
,
userID
int64
,
page
,
pageSize
int
)
([]
model
.
ApiKey
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
keys
,
result
,
err
:=
s
.
apiKeyRepo
.
ListByUserID
(
ctx
,
userID
,
params
)
keys
,
result
,
err
:=
s
.
apiKeyRepo
.
ListByUserID
(
ctx
,
userID
,
params
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -397,7 +408,7 @@ func (s *adminServiceImpl) GetUserUsageStats(ctx context.Context, userID int64,
...
@@ -397,7 +408,7 @@ func (s *adminServiceImpl) GetUserUsageStats(ctx context.Context, userID int64,
// Group management implementations
// Group management implementations
func
(
s
*
adminServiceImpl
)
ListGroups
(
ctx
context
.
Context
,
page
,
pageSize
int
,
platform
,
status
string
,
isExclusive
*
bool
)
([]
model
.
Group
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
ListGroups
(
ctx
context
.
Context
,
page
,
pageSize
int
,
platform
,
status
string
,
isExclusive
*
bool
)
([]
model
.
Group
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
groups
,
result
,
err
:=
s
.
groupRepo
.
ListWithFilters
(
ctx
,
params
,
platform
,
status
,
isExclusive
)
groups
,
result
,
err
:=
s
.
groupRepo
.
ListWithFilters
(
ctx
,
params
,
platform
,
status
,
isExclusive
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -568,7 +579,7 @@ func (s *adminServiceImpl) DeleteGroup(ctx context.Context, id int64) error {
...
@@ -568,7 +579,7 @@ func (s *adminServiceImpl) DeleteGroup(ctx context.Context, id int64) error {
}
}
func
(
s
*
adminServiceImpl
)
GetGroupAPIKeys
(
ctx
context
.
Context
,
groupID
int64
,
page
,
pageSize
int
)
([]
model
.
ApiKey
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
GetGroupAPIKeys
(
ctx
context
.
Context
,
groupID
int64
,
page
,
pageSize
int
)
([]
model
.
ApiKey
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
keys
,
result
,
err
:=
s
.
apiKeyRepo
.
ListByGroupID
(
ctx
,
groupID
,
params
)
keys
,
result
,
err
:=
s
.
apiKeyRepo
.
ListByGroupID
(
ctx
,
groupID
,
params
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -578,7 +589,7 @@ func (s *adminServiceImpl) GetGroupAPIKeys(ctx context.Context, groupID int64, p
...
@@ -578,7 +589,7 @@ func (s *adminServiceImpl) GetGroupAPIKeys(ctx context.Context, groupID int64, p
// Account management implementations
// Account management implementations
func
(
s
*
adminServiceImpl
)
ListAccounts
(
ctx
context
.
Context
,
page
,
pageSize
int
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
ListAccounts
(
ctx
context
.
Context
,
page
,
pageSize
int
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
accounts
,
result
,
err
:=
s
.
accountRepo
.
ListWithFilters
(
ctx
,
params
,
platform
,
accountType
,
status
,
search
)
accounts
,
result
,
err
:=
s
.
accountRepo
.
ListWithFilters
(
ctx
,
params
,
platform
,
accountType
,
status
,
search
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -696,7 +707,7 @@ func (s *adminServiceImpl) SetAccountSchedulable(ctx context.Context, id int64,
...
@@ -696,7 +707,7 @@ func (s *adminServiceImpl) SetAccountSchedulable(ctx context.Context, id int64,
// Proxy management implementations
// Proxy management implementations
func
(
s
*
adminServiceImpl
)
ListProxies
(
ctx
context
.
Context
,
page
,
pageSize
int
,
protocol
,
status
,
search
string
)
([]
model
.
Proxy
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
ListProxies
(
ctx
context
.
Context
,
page
,
pageSize
int
,
protocol
,
status
,
search
string
)
([]
model
.
Proxy
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
proxies
,
result
,
err
:=
s
.
proxyRepo
.
ListWithFilters
(
ctx
,
params
,
protocol
,
status
,
search
)
proxies
,
result
,
err
:=
s
.
proxyRepo
.
ListWithFilters
(
ctx
,
params
,
protocol
,
status
,
search
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
@@ -781,7 +792,7 @@ func (s *adminServiceImpl) CheckProxyExists(ctx context.Context, host string, po
...
@@ -781,7 +792,7 @@ func (s *adminServiceImpl) CheckProxyExists(ctx context.Context, host string, po
// Redeem code management implementations
// Redeem code management implementations
func
(
s
*
adminServiceImpl
)
ListRedeemCodes
(
ctx
context
.
Context
,
page
,
pageSize
int
,
codeType
,
status
,
search
string
)
([]
model
.
RedeemCode
,
int64
,
error
)
{
func
(
s
*
adminServiceImpl
)
ListRedeemCodes
(
ctx
context
.
Context
,
page
,
pageSize
int
,
codeType
,
status
,
search
string
)
([]
model
.
RedeemCode
,
int64
,
error
)
{
params
:=
repository
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
params
:=
pagination
.
PaginationParams
{
Page
:
page
,
PageSize
:
pageSize
}
codes
,
result
,
err
:=
s
.
redeemCodeRepo
.
ListWithFilters
(
ctx
,
params
,
codeType
,
status
,
search
)
codes
,
result
,
err
:=
s
.
redeemCodeRepo
.
ListWithFilters
(
ctx
,
params
,
codeType
,
status
,
search
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
0
,
err
return
nil
,
0
,
err
...
...
backend/internal/service/api_key_service.go
View file @
e99b344b
...
@@ -8,8 +8,9 @@ import (
...
@@ -8,8 +8,9 @@ import (
"fmt"
"fmt"
"sub2api/internal/config"
"sub2api/internal/config"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/timezone"
"sub2api/internal/pkg/timezone"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"time"
"time"
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/v9"
...
@@ -47,20 +48,20 @@ type UpdateApiKeyRequest struct {
...
@@ -47,20 +48,20 @@ type UpdateApiKeyRequest struct {
// ApiKeyService API Key服务
// ApiKeyService API Key服务
type
ApiKeyService
struct
{
type
ApiKeyService
struct
{
apiKeyRepo
*
repository
.
ApiKeyRepository
apiKeyRepo
ports
.
ApiKeyRepository
userRepo
*
repository
.
UserRepository
userRepo
ports
.
UserRepository
groupRepo
*
repository
.
GroupRepository
groupRepo
ports
.
GroupRepository
userSubRepo
*
repository
.
UserSubscriptionRepository
userSubRepo
ports
.
UserSubscriptionRepository
rdb
*
redis
.
Client
rdb
*
redis
.
Client
cfg
*
config
.
Config
cfg
*
config
.
Config
}
}
// NewApiKeyService 创建API Key服务实例
// NewApiKeyService 创建API Key服务实例
func
NewApiKeyService
(
func
NewApiKeyService
(
apiKeyRepo
*
repository
.
ApiKeyRepository
,
apiKeyRepo
ports
.
ApiKeyRepository
,
userRepo
*
repository
.
UserRepository
,
userRepo
ports
.
UserRepository
,
groupRepo
*
repository
.
GroupRepository
,
groupRepo
ports
.
GroupRepository
,
userSubRepo
*
repository
.
UserSubscriptionRepository
,
userSubRepo
ports
.
UserSubscriptionRepository
,
rdb
*
redis
.
Client
,
rdb
*
redis
.
Client
,
cfg
*
config
.
Config
,
cfg
*
config
.
Config
,
)
*
ApiKeyService
{
)
*
ApiKeyService
{
...
@@ -237,7 +238,7 @@ func (s *ApiKeyService) Create(ctx context.Context, userID int64, req CreateApiK
...
@@ -237,7 +238,7 @@ func (s *ApiKeyService) Create(ctx context.Context, userID int64, req CreateApiK
}
}
// List 获取用户的API Key列表
// List 获取用户的API Key列表
func
(
s
*
ApiKeyService
)
List
(
ctx
context
.
Context
,
userID
int64
,
params
repository
.
PaginationParams
)
([]
model
.
ApiKey
,
*
repository
.
PaginationResult
,
error
)
{
func
(
s
*
ApiKeyService
)
List
(
ctx
context
.
Context
,
userID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
ApiKey
,
*
pagination
.
PaginationResult
,
error
)
{
keys
,
pagination
,
err
:=
s
.
apiKeyRepo
.
ListByUserID
(
ctx
,
userID
,
params
)
keys
,
pagination
,
err
:=
s
.
apiKeyRepo
.
ListByUserID
(
ctx
,
userID
,
params
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"list api keys: %w"
,
err
)
return
nil
,
nil
,
fmt
.
Errorf
(
"list api keys: %w"
,
err
)
...
...
backend/internal/service/auth_service.go
View file @
e99b344b
...
@@ -7,7 +7,7 @@ import (
...
@@ -7,7 +7,7 @@ import (
"log"
"log"
"sub2api/internal/config"
"sub2api/internal/config"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"time"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/golang-jwt/jwt/v5"
...
@@ -35,7 +35,7 @@ type JWTClaims struct {
...
@@ -35,7 +35,7 @@ type JWTClaims struct {
// AuthService 认证服务
// AuthService 认证服务
type
AuthService
struct
{
type
AuthService
struct
{
userRepo
*
repository
.
UserRepository
userRepo
ports
.
UserRepository
cfg
*
config
.
Config
cfg
*
config
.
Config
settingService
*
SettingService
settingService
*
SettingService
emailService
*
EmailService
emailService
*
EmailService
...
@@ -45,7 +45,7 @@ type AuthService struct {
...
@@ -45,7 +45,7 @@ type AuthService struct {
// NewAuthService 创建认证服务实例
// NewAuthService 创建认证服务实例
func
NewAuthService
(
func
NewAuthService
(
userRepo
*
repository
.
UserRepository
,
userRepo
ports
.
UserRepository
,
cfg
*
config
.
Config
,
cfg
*
config
.
Config
,
settingService
*
SettingService
,
settingService
*
SettingService
,
emailService
*
EmailService
,
emailService
*
EmailService
,
...
...
backend/internal/service/billing_cache_service.go
View file @
e99b344b
...
@@ -9,7 +9,7 @@ import (
...
@@ -9,7 +9,7 @@ import (
"time"
"time"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/v9"
)
)
...
@@ -81,12 +81,12 @@ type subscriptionCacheData struct {
...
@@ -81,12 +81,12 @@ type subscriptionCacheData struct {
// 负责余额和订阅数据的缓存管理,提供高性能的计费资格检查
// 负责余额和订阅数据的缓存管理,提供高性能的计费资格检查
type
BillingCacheService
struct
{
type
BillingCacheService
struct
{
rdb
*
redis
.
Client
rdb
*
redis
.
Client
userRepo
*
repository
.
UserRepository
userRepo
ports
.
UserRepository
subRepo
*
repository
.
UserSubscriptionRepository
subRepo
ports
.
UserSubscriptionRepository
}
}
// NewBillingCacheService 创建计费缓存服务
// NewBillingCacheService 创建计费缓存服务
func
NewBillingCacheService
(
rdb
*
redis
.
Client
,
userRepo
*
repository
.
UserRepository
,
subRepo
*
repository
.
UserSubscriptionRepository
)
*
BillingCacheService
{
func
NewBillingCacheService
(
rdb
*
redis
.
Client
,
userRepo
ports
.
UserRepository
,
subRepo
ports
.
UserSubscriptionRepository
)
*
BillingCacheService
{
return
&
BillingCacheService
{
return
&
BillingCacheService
{
rdb
:
rdb
,
rdb
:
rdb
,
userRepo
:
userRepo
,
userRepo
:
userRepo
,
...
...
backend/internal/service/email_service.go
View file @
e99b344b
...
@@ -11,7 +11,7 @@ import (
...
@@ -11,7 +11,7 @@ import (
"net/smtp"
"net/smtp"
"strconv"
"strconv"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"time"
"time"
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/v9"
...
@@ -51,12 +51,12 @@ type SmtpConfig struct {
...
@@ -51,12 +51,12 @@ type SmtpConfig struct {
// EmailService 邮件服务
// EmailService 邮件服务
type
EmailService
struct
{
type
EmailService
struct
{
settingRepo
*
repository
.
SettingRepository
settingRepo
ports
.
SettingRepository
rdb
*
redis
.
Client
rdb
*
redis
.
Client
}
}
// NewEmailService 创建邮件服务实例
// NewEmailService 创建邮件服务实例
func
NewEmailService
(
settingRepo
*
repository
.
SettingRepository
,
rdb
*
redis
.
Client
)
*
EmailService
{
func
NewEmailService
(
settingRepo
ports
.
SettingRepository
,
rdb
*
redis
.
Client
)
*
EmailService
{
return
&
EmailService
{
return
&
EmailService
{
settingRepo
:
settingRepo
,
settingRepo
:
settingRepo
,
rdb
:
rdb
,
rdb
:
rdb
,
...
...
backend/internal/service/gateway_service.go
View file @
e99b344b
...
@@ -21,7 +21,7 @@ import (
...
@@ -21,7 +21,7 @@ import (
"sub2api/internal/config"
"sub2api/internal/config"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/pkg/claude"
"sub2api/internal/pkg/claude"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"github.com/redis/go-redis/v9"
...
@@ -78,7 +78,10 @@ type ForwardResult struct {
...
@@ -78,7 +78,10 @@ type ForwardResult struct {
// GatewayService handles API gateway operations
// GatewayService handles API gateway operations
type
GatewayService
struct
{
type
GatewayService
struct
{
repos
*
repository
.
Repositories
accountRepo
ports
.
AccountRepository
usageLogRepo
ports
.
UsageLogRepository
userRepo
ports
.
UserRepository
userSubRepo
ports
.
UserSubscriptionRepository
rdb
*
redis
.
Client
rdb
*
redis
.
Client
cfg
*
config
.
Config
cfg
*
config
.
Config
oauthService
*
OAuthService
oauthService
*
OAuthService
...
@@ -90,7 +93,19 @@ type GatewayService struct {
...
@@ -90,7 +93,19 @@ type GatewayService struct {
}
}
// NewGatewayService creates a new GatewayService
// NewGatewayService creates a new GatewayService
func
NewGatewayService
(
repos
*
repository
.
Repositories
,
rdb
*
redis
.
Client
,
cfg
*
config
.
Config
,
oauthService
*
OAuthService
,
billingService
*
BillingService
,
rateLimitService
*
RateLimitService
,
billingCacheService
*
BillingCacheService
,
identityService
*
IdentityService
)
*
GatewayService
{
func
NewGatewayService
(
accountRepo
ports
.
AccountRepository
,
usageLogRepo
ports
.
UsageLogRepository
,
userRepo
ports
.
UserRepository
,
userSubRepo
ports
.
UserSubscriptionRepository
,
rdb
*
redis
.
Client
,
cfg
*
config
.
Config
,
oauthService
*
OAuthService
,
billingService
*
BillingService
,
rateLimitService
*
RateLimitService
,
billingCacheService
*
BillingCacheService
,
identityService
*
IdentityService
,
)
*
GatewayService
{
// 计算响应头超时时间
// 计算响应头超时时间
responseHeaderTimeout
:=
time
.
Duration
(
cfg
.
Gateway
.
ResponseHeaderTimeout
)
*
time
.
Second
responseHeaderTimeout
:=
time
.
Duration
(
cfg
.
Gateway
.
ResponseHeaderTimeout
)
*
time
.
Second
if
responseHeaderTimeout
==
0
{
if
responseHeaderTimeout
==
0
{
...
@@ -105,7 +120,10 @@ func NewGatewayService(repos *repository.Repositories, rdb *redis.Client, cfg *c
...
@@ -105,7 +120,10 @@ func NewGatewayService(repos *repository.Repositories, rdb *redis.Client, cfg *c
// 注意:不设置整体 Timeout,让流式响应可以无限时间传输
// 注意:不设置整体 Timeout,让流式响应可以无限时间传输
}
}
return
&
GatewayService
{
return
&
GatewayService
{
repos
:
repos
,
accountRepo
:
accountRepo
,
usageLogRepo
:
usageLogRepo
,
userRepo
:
userRepo
,
userSubRepo
:
userSubRepo
,
rdb
:
rdb
,
rdb
:
rdb
,
cfg
:
cfg
,
cfg
:
cfg
,
oauthService
:
oauthService
,
oauthService
:
oauthService
,
...
@@ -274,7 +292,7 @@ func (s *GatewayService) SelectAccountForModel(ctx context.Context, groupID *int
...
@@ -274,7 +292,7 @@ func (s *GatewayService) SelectAccountForModel(ctx context.Context, groupID *int
if
sessionHash
!=
""
{
if
sessionHash
!=
""
{
accountID
,
err
:=
s
.
rdb
.
Get
(
ctx
,
stickySessionPrefix
+
sessionHash
)
.
Int64
()
accountID
,
err
:=
s
.
rdb
.
Get
(
ctx
,
stickySessionPrefix
+
sessionHash
)
.
Int64
()
if
err
==
nil
&&
accountID
>
0
{
if
err
==
nil
&&
accountID
>
0
{
account
,
err
:=
s
.
repos
.
A
ccount
.
GetByID
(
ctx
,
accountID
)
account
,
err
:=
s
.
a
ccount
Repo
.
GetByID
(
ctx
,
accountID
)
// 使用IsSchedulable代替IsActive,确保限流/过载账号不会被选中
// 使用IsSchedulable代替IsActive,确保限流/过载账号不会被选中
// 同时检查模型支持
// 同时检查模型支持
if
err
==
nil
&&
account
.
IsSchedulable
()
&&
(
requestedModel
==
""
||
account
.
IsModelSupported
(
requestedModel
))
{
if
err
==
nil
&&
account
.
IsSchedulable
()
&&
(
requestedModel
==
""
||
account
.
IsModelSupported
(
requestedModel
))
{
...
@@ -289,9 +307,9 @@ func (s *GatewayService) SelectAccountForModel(ctx context.Context, groupID *int
...
@@ -289,9 +307,9 @@ func (s *GatewayService) SelectAccountForModel(ctx context.Context, groupID *int
var
accounts
[]
model
.
Account
var
accounts
[]
model
.
Account
var
err
error
var
err
error
if
groupID
!=
nil
{
if
groupID
!=
nil
{
accounts
,
err
=
s
.
repos
.
A
ccount
.
ListSchedulableByGroupID
(
ctx
,
*
groupID
)
accounts
,
err
=
s
.
a
ccount
Repo
.
ListSchedulableByGroupID
(
ctx
,
*
groupID
)
}
else
{
}
else
{
accounts
,
err
=
s
.
repos
.
A
ccount
.
ListSchedulable
(
ctx
)
accounts
,
err
=
s
.
a
ccount
Repo
.
ListSchedulable
(
ctx
)
}
}
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"query accounts failed: %w"
,
err
)
return
nil
,
fmt
.
Errorf
(
"query accounts failed: %w"
,
err
)
...
@@ -378,7 +396,7 @@ func (s *GatewayService) getOAuthToken(ctx context.Context, account *model.Accou
...
@@ -378,7 +396,7 @@ func (s *GatewayService) getOAuthToken(ctx context.Context, account *model.Accou
account
.
Credentials
[
"refresh_token"
]
=
tokenInfo
.
RefreshToken
account
.
Credentials
[
"refresh_token"
]
=
tokenInfo
.
RefreshToken
}
}
if
err
:=
s
.
repos
.
A
ccount
.
Update
(
ctx
,
account
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
Update
(
ctx
,
account
);
err
!=
nil
{
log
.
Printf
(
"Failed to update account credentials: %v"
,
err
)
log
.
Printf
(
"Failed to update account credentials: %v"
,
err
)
}
}
...
@@ -667,7 +685,7 @@ func (s *GatewayService) forceRefreshToken(ctx context.Context, account *model.A
...
@@ -667,7 +685,7 @@ func (s *GatewayService) forceRefreshToken(ctx context.Context, account *model.A
account
.
Credentials
[
"refresh_token"
]
=
tokenInfo
.
RefreshToken
account
.
Credentials
[
"refresh_token"
]
=
tokenInfo
.
RefreshToken
}
}
if
err
:=
s
.
repos
.
A
ccount
.
Update
(
ctx
,
account
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
Update
(
ctx
,
account
);
err
!=
nil
{
log
.
Printf
(
"Failed to update account credentials: %v"
,
err
)
log
.
Printf
(
"Failed to update account credentials: %v"
,
err
)
}
}
...
@@ -999,7 +1017,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -999,7 +1017,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
usageLog
.
SubscriptionID
=
&
subscription
.
ID
usageLog
.
SubscriptionID
=
&
subscription
.
ID
}
}
if
err
:=
s
.
repos
.
U
sageLog
.
Create
(
ctx
,
usageLog
);
err
!=
nil
{
if
err
:=
s
.
u
sageLog
Repo
.
Create
(
ctx
,
usageLog
);
err
!=
nil
{
log
.
Printf
(
"Create usage log failed: %v"
,
err
)
log
.
Printf
(
"Create usage log failed: %v"
,
err
)
}
}
...
@@ -1007,7 +1025,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -1007,7 +1025,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
if
isSubscriptionBilling
{
if
isSubscriptionBilling
{
// 订阅模式:更新订阅用量(使用 TotalCost 原始费用,不考虑倍率)
// 订阅模式:更新订阅用量(使用 TotalCost 原始费用,不考虑倍率)
if
cost
.
TotalCost
>
0
{
if
cost
.
TotalCost
>
0
{
if
err
:=
s
.
repos
.
UserSubscription
.
IncrementUsage
(
ctx
,
subscription
.
ID
,
cost
.
TotalCost
);
err
!=
nil
{
if
err
:=
s
.
userSubRepo
.
IncrementUsage
(
ctx
,
subscription
.
ID
,
cost
.
TotalCost
);
err
!=
nil
{
log
.
Printf
(
"Increment subscription usage failed: %v"
,
err
)
log
.
Printf
(
"Increment subscription usage failed: %v"
,
err
)
}
}
// 异步更新订阅缓存
// 异步更新订阅缓存
...
@@ -1022,7 +1040,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -1022,7 +1040,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
}
else
{
}
else
{
// 余额模式:扣除用户余额(使用 ActualCost 考虑倍率后的费用)
// 余额模式:扣除用户余额(使用 ActualCost 考虑倍率后的费用)
if
cost
.
ActualCost
>
0
{
if
cost
.
ActualCost
>
0
{
if
err
:=
s
.
repos
.
User
.
DeductBalance
(
ctx
,
user
.
ID
,
cost
.
ActualCost
);
err
!=
nil
{
if
err
:=
s
.
userRepo
.
DeductBalance
(
ctx
,
user
.
ID
,
cost
.
ActualCost
);
err
!=
nil
{
log
.
Printf
(
"Deduct balance failed: %v"
,
err
)
log
.
Printf
(
"Deduct balance failed: %v"
,
err
)
}
}
// 异步更新余额缓存
// 异步更新余额缓存
...
@@ -1037,7 +1055,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
...
@@ -1037,7 +1055,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
}
}
// 更新账号最后使用时间
// 更新账号最后使用时间
if
err
:=
s
.
repos
.
A
ccount
.
UpdateLastUsed
(
ctx
,
account
.
ID
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
UpdateLastUsed
(
ctx
,
account
.
ID
);
err
!=
nil
{
log
.
Printf
(
"Update last used failed: %v"
,
err
)
log
.
Printf
(
"Update last used failed: %v"
,
err
)
}
}
...
...
backend/internal/service/group_service.go
View file @
e99b344b
...
@@ -5,7 +5,8 @@ import (
...
@@ -5,7 +5,8 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/repository"
"sub2api/internal/pkg/pagination"
"sub2api/internal/service/ports"
"gorm.io/gorm"
"gorm.io/gorm"
)
)
...
@@ -34,11 +35,11 @@ type UpdateGroupRequest struct {
...
@@ -34,11 +35,11 @@ type UpdateGroupRequest struct {
// GroupService 分组管理服务
// GroupService 分组管理服务
type
GroupService
struct
{
type
GroupService
struct
{
groupRepo
*
repository
.
GroupRepository
groupRepo
ports
.
GroupRepository
}
}
// NewGroupService 创建分组服务实例
// NewGroupService 创建分组服务实例
func
NewGroupService
(
groupRepo
*
repository
.
GroupRepository
)
*
GroupService
{
func
NewGroupService
(
groupRepo
ports
.
GroupRepository
)
*
GroupService
{
return
&
GroupService
{
return
&
GroupService
{
groupRepo
:
groupRepo
,
groupRepo
:
groupRepo
,
}
}
...
@@ -84,7 +85,7 @@ func (s *GroupService) GetByID(ctx context.Context, id int64) (*model.Group, err
...
@@ -84,7 +85,7 @@ func (s *GroupService) GetByID(ctx context.Context, id int64) (*model.Group, err
}
}
// List 获取分组列表
// List 获取分组列表
func
(
s
*
GroupService
)
List
(
ctx
context
.
Context
,
params
repository
.
PaginationParams
)
([]
model
.
Group
,
*
repository
.
PaginationResult
,
error
)
{
func
(
s
*
GroupService
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Group
,
*
pagination
.
PaginationResult
,
error
)
{
groups
,
pagination
,
err
:=
s
.
groupRepo
.
List
(
ctx
,
params
)
groups
,
pagination
,
err
:=
s
.
groupRepo
.
List
(
ctx
,
params
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"list groups: %w"
,
err
)
return
nil
,
nil
,
fmt
.
Errorf
(
"list groups: %w"
,
err
)
...
...
backend/internal/service/oauth_service.go
View file @
e99b344b
...
@@ -12,7 +12,7 @@ import (
...
@@ -12,7 +12,7 @@ import (
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/pkg/oauth"
"sub2api/internal/pkg/oauth"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
"github.com/imroc/req/v3"
"github.com/imroc/req/v3"
)
)
...
@@ -20,11 +20,11 @@ import (
...
@@ -20,11 +20,11 @@ import (
// OAuthService handles OAuth authentication flows
// OAuthService handles OAuth authentication flows
type
OAuthService
struct
{
type
OAuthService
struct
{
sessionStore
*
oauth
.
SessionStore
sessionStore
*
oauth
.
SessionStore
proxyRepo
*
repository
.
ProxyRepository
proxyRepo
ports
.
ProxyRepository
}
}
// NewOAuthService creates a new OAuth service
// NewOAuthService creates a new OAuth service
func
NewOAuthService
(
proxyRepo
*
repository
.
ProxyRepository
)
*
OAuthService
{
func
NewOAuthService
(
proxyRepo
ports
.
ProxyRepository
)
*
OAuthService
{
return
&
OAuthService
{
return
&
OAuthService
{
sessionStore
:
oauth
.
NewSessionStore
(),
sessionStore
:
oauth
.
NewSessionStore
(),
proxyRepo
:
proxyRepo
,
proxyRepo
:
proxyRepo
,
...
...
backend/internal/service/ports/account.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"time"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
AccountRepository
interface
{
Create
(
ctx
context
.
Context
,
account
*
model
.
Account
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
Account
,
error
)
Update
(
ctx
context
.
Context
,
account
*
model
.
Account
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
platform
,
accountType
,
status
,
search
string
)
([]
model
.
Account
,
*
pagination
.
PaginationResult
,
error
)
ListByGroup
(
ctx
context
.
Context
,
groupID
int64
)
([]
model
.
Account
,
error
)
ListActive
(
ctx
context
.
Context
)
([]
model
.
Account
,
error
)
ListByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
model
.
Account
,
error
)
UpdateLastUsed
(
ctx
context
.
Context
,
id
int64
)
error
SetError
(
ctx
context
.
Context
,
id
int64
,
errorMsg
string
)
error
SetSchedulable
(
ctx
context
.
Context
,
id
int64
,
schedulable
bool
)
error
BindGroups
(
ctx
context
.
Context
,
accountID
int64
,
groupIDs
[]
int64
)
error
ListSchedulable
(
ctx
context
.
Context
)
([]
model
.
Account
,
error
)
ListSchedulableByGroupID
(
ctx
context
.
Context
,
groupID
int64
)
([]
model
.
Account
,
error
)
SetRateLimited
(
ctx
context
.
Context
,
id
int64
,
resetAt
time
.
Time
)
error
SetOverloaded
(
ctx
context
.
Context
,
id
int64
,
until
time
.
Time
)
error
ClearRateLimit
(
ctx
context
.
Context
,
id
int64
)
error
UpdateSessionWindow
(
ctx
context
.
Context
,
id
int64
,
start
,
end
*
time
.
Time
,
status
string
)
error
}
backend/internal/service/ports/api_key.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
ApiKeyRepository
interface
{
Create
(
ctx
context
.
Context
,
key
*
model
.
ApiKey
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
ApiKey
,
error
)
GetByKey
(
ctx
context
.
Context
,
key
string
)
(
*
model
.
ApiKey
,
error
)
Update
(
ctx
context
.
Context
,
key
*
model
.
ApiKey
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
ListByUserID
(
ctx
context
.
Context
,
userID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
ApiKey
,
*
pagination
.
PaginationResult
,
error
)
CountByUserID
(
ctx
context
.
Context
,
userID
int64
)
(
int64
,
error
)
ExistsByKey
(
ctx
context
.
Context
,
key
string
)
(
bool
,
error
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
ApiKey
,
*
pagination
.
PaginationResult
,
error
)
SearchApiKeys
(
ctx
context
.
Context
,
userID
int64
,
keyword
string
,
limit
int
)
([]
model
.
ApiKey
,
error
)
ClearGroupIDByGroupID
(
ctx
context
.
Context
,
groupID
int64
)
(
int64
,
error
)
CountByGroupID
(
ctx
context
.
Context
,
groupID
int64
)
(
int64
,
error
)
}
backend/internal/service/ports/group.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"gorm.io/gorm"
)
type
GroupRepository
interface
{
Create
(
ctx
context
.
Context
,
group
*
model
.
Group
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
Group
,
error
)
Update
(
ctx
context
.
Context
,
group
*
model
.
Group
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Group
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
platform
,
status
string
,
isExclusive
*
bool
)
([]
model
.
Group
,
*
pagination
.
PaginationResult
,
error
)
ListActive
(
ctx
context
.
Context
)
([]
model
.
Group
,
error
)
ListActiveByPlatform
(
ctx
context
.
Context
,
platform
string
)
([]
model
.
Group
,
error
)
ExistsByName
(
ctx
context
.
Context
,
name
string
)
(
bool
,
error
)
GetAccountCount
(
ctx
context
.
Context
,
groupID
int64
)
(
int64
,
error
)
DeleteAccountGroupsByGroupID
(
ctx
context
.
Context
,
groupID
int64
)
(
int64
,
error
)
DB
()
*
gorm
.
DB
}
backend/internal/service/ports/proxy.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
ProxyRepository
interface
{
Create
(
ctx
context
.
Context
,
proxy
*
model
.
Proxy
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
Proxy
,
error
)
Update
(
ctx
context
.
Context
,
proxy
*
model
.
Proxy
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Proxy
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
protocol
,
status
,
search
string
)
([]
model
.
Proxy
,
*
pagination
.
PaginationResult
,
error
)
ListActive
(
ctx
context
.
Context
)
([]
model
.
Proxy
,
error
)
ListActiveWithAccountCount
(
ctx
context
.
Context
)
([]
model
.
ProxyWithAccountCount
,
error
)
ExistsByHostPortAuth
(
ctx
context
.
Context
,
host
string
,
port
int
,
username
,
password
string
)
(
bool
,
error
)
CountAccountsByProxyID
(
ctx
context
.
Context
,
proxyID
int64
)
(
int64
,
error
)
}
backend/internal/service/ports/redeem_code.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
RedeemCodeRepository
interface
{
Create
(
ctx
context
.
Context
,
code
*
model
.
RedeemCode
)
error
CreateBatch
(
ctx
context
.
Context
,
codes
[]
model
.
RedeemCode
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
RedeemCode
,
error
)
GetByCode
(
ctx
context
.
Context
,
code
string
)
(
*
model
.
RedeemCode
,
error
)
Update
(
ctx
context
.
Context
,
code
*
model
.
RedeemCode
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
Use
(
ctx
context
.
Context
,
id
,
userID
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
RedeemCode
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
codeType
,
status
,
search
string
)
([]
model
.
RedeemCode
,
*
pagination
.
PaginationResult
,
error
)
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
limit
int
)
([]
model
.
RedeemCode
,
error
)
}
backend/internal/service/ports/setting.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
)
type
SettingRepository
interface
{
Get
(
ctx
context
.
Context
,
key
string
)
(
*
model
.
Setting
,
error
)
GetValue
(
ctx
context
.
Context
,
key
string
)
(
string
,
error
)
Set
(
ctx
context
.
Context
,
key
,
value
string
)
error
GetMultiple
(
ctx
context
.
Context
,
keys
[]
string
)
(
map
[
string
]
string
,
error
)
SetMultiple
(
ctx
context
.
Context
,
settings
map
[
string
]
string
)
error
GetAll
(
ctx
context
.
Context
)
(
map
[
string
]
string
,
error
)
Delete
(
ctx
context
.
Context
,
key
string
)
error
}
backend/internal/service/ports/usage_log.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"time"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
"sub2api/internal/pkg/usagestats"
)
type
UsageLogRepository
interface
{
Create
(
ctx
context
.
Context
,
log
*
model
.
UsageLog
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
UsageLog
,
error
)
Delete
(
ctx
context
.
Context
,
id
int64
)
error
ListByUser
(
ctx
context
.
Context
,
userID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByApiKey
(
ctx
context
.
Context
,
apiKeyID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByAccount
(
ctx
context
.
Context
,
accountID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByUserAndTimeRange
(
ctx
context
.
Context
,
userID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByApiKeyAndTimeRange
(
ctx
context
.
Context
,
apiKeyID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByAccountAndTimeRange
(
ctx
context
.
Context
,
accountID
int64
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
ListByModelAndTimeRange
(
ctx
context
.
Context
,
modelName
string
,
startTime
,
endTime
time
.
Time
)
([]
model
.
UsageLog
,
*
pagination
.
PaginationResult
,
error
)
GetAccountWindowStats
(
ctx
context
.
Context
,
accountID
int64
,
startTime
time
.
Time
)
(
*
usagestats
.
AccountStats
,
error
)
GetAccountTodayStats
(
ctx
context
.
Context
,
accountID
int64
)
(
*
usagestats
.
AccountStats
,
error
)
}
backend/internal/service/ports/user.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
UserRepository
interface
{
Create
(
ctx
context
.
Context
,
user
*
model
.
User
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
User
,
error
)
GetByEmail
(
ctx
context
.
Context
,
email
string
)
(
*
model
.
User
,
error
)
Update
(
ctx
context
.
Context
,
user
*
model
.
User
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
User
,
*
pagination
.
PaginationResult
,
error
)
ListWithFilters
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
status
,
role
,
search
string
)
([]
model
.
User
,
*
pagination
.
PaginationResult
,
error
)
UpdateBalance
(
ctx
context
.
Context
,
id
int64
,
amount
float64
)
error
DeductBalance
(
ctx
context
.
Context
,
id
int64
,
amount
float64
)
error
UpdateConcurrency
(
ctx
context
.
Context
,
id
int64
,
amount
int
)
error
ExistsByEmail
(
ctx
context
.
Context
,
email
string
)
(
bool
,
error
)
RemoveGroupFromAllowedGroups
(
ctx
context
.
Context
,
groupID
int64
)
(
int64
,
error
)
}
backend/internal/service/ports/user_subscription.go
0 → 100644
View file @
e99b344b
package
ports
import
(
"context"
"time"
"sub2api/internal/model"
"sub2api/internal/pkg/pagination"
)
type
UserSubscriptionRepository
interface
{
Create
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
)
error
GetByID
(
ctx
context
.
Context
,
id
int64
)
(
*
model
.
UserSubscription
,
error
)
GetByUserIDAndGroupID
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
*
model
.
UserSubscription
,
error
)
GetActiveByUserIDAndGroupID
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
*
model
.
UserSubscription
,
error
)
Update
(
ctx
context
.
Context
,
sub
*
model
.
UserSubscription
)
error
Delete
(
ctx
context
.
Context
,
id
int64
)
error
ListByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
model
.
UserSubscription
,
error
)
ListActiveByUserID
(
ctx
context
.
Context
,
userID
int64
)
([]
model
.
UserSubscription
,
error
)
ListByGroupID
(
ctx
context
.
Context
,
groupID
int64
,
params
pagination
.
PaginationParams
)
([]
model
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
,
userID
,
groupID
*
int64
,
status
string
)
([]
model
.
UserSubscription
,
*
pagination
.
PaginationResult
,
error
)
ExistsByUserIDAndGroupID
(
ctx
context
.
Context
,
userID
,
groupID
int64
)
(
bool
,
error
)
ExtendExpiry
(
ctx
context
.
Context
,
subscriptionID
int64
,
newExpiresAt
time
.
Time
)
error
UpdateStatus
(
ctx
context
.
Context
,
subscriptionID
int64
,
status
string
)
error
UpdateNotes
(
ctx
context
.
Context
,
subscriptionID
int64
,
notes
string
)
error
ActivateWindows
(
ctx
context
.
Context
,
id
int64
,
start
time
.
Time
)
error
ResetDailyUsage
(
ctx
context
.
Context
,
id
int64
,
newWindowStart
time
.
Time
)
error
ResetWeeklyUsage
(
ctx
context
.
Context
,
id
int64
,
newWindowStart
time
.
Time
)
error
ResetMonthlyUsage
(
ctx
context
.
Context
,
id
int64
,
newWindowStart
time
.
Time
)
error
IncrementUsage
(
ctx
context
.
Context
,
id
int64
,
costUSD
float64
)
error
BatchUpdateExpiredStatus
(
ctx
context
.
Context
)
(
int64
,
error
)
}
backend/internal/service/proxy_service.go
View file @
e99b344b
...
@@ -5,7 +5,8 @@ import (
...
@@ -5,7 +5,8 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/repository"
"sub2api/internal/pkg/pagination"
"sub2api/internal/service/ports"
"gorm.io/gorm"
"gorm.io/gorm"
)
)
...
@@ -37,11 +38,11 @@ type UpdateProxyRequest struct {
...
@@ -37,11 +38,11 @@ type UpdateProxyRequest struct {
// ProxyService 代理管理服务
// ProxyService 代理管理服务
type
ProxyService
struct
{
type
ProxyService
struct
{
proxyRepo
*
repository
.
ProxyRepository
proxyRepo
ports
.
ProxyRepository
}
}
// NewProxyService 创建代理服务实例
// NewProxyService 创建代理服务实例
func
NewProxyService
(
proxyRepo
*
repository
.
ProxyRepository
)
*
ProxyService
{
func
NewProxyService
(
proxyRepo
ports
.
ProxyRepository
)
*
ProxyService
{
return
&
ProxyService
{
return
&
ProxyService
{
proxyRepo
:
proxyRepo
,
proxyRepo
:
proxyRepo
,
}
}
...
@@ -80,7 +81,7 @@ func (s *ProxyService) GetByID(ctx context.Context, id int64) (*model.Proxy, err
...
@@ -80,7 +81,7 @@ func (s *ProxyService) GetByID(ctx context.Context, id int64) (*model.Proxy, err
}
}
// List 获取代理列表
// List 获取代理列表
func
(
s
*
ProxyService
)
List
(
ctx
context
.
Context
,
params
repository
.
PaginationParams
)
([]
model
.
Proxy
,
*
repository
.
PaginationResult
,
error
)
{
func
(
s
*
ProxyService
)
List
(
ctx
context
.
Context
,
params
pagination
.
PaginationParams
)
([]
model
.
Proxy
,
*
pagination
.
PaginationResult
,
error
)
{
proxies
,
pagination
,
err
:=
s
.
proxyRepo
.
List
(
ctx
,
params
)
proxies
,
pagination
,
err
:=
s
.
proxyRepo
.
List
(
ctx
,
params
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
nil
,
fmt
.
Errorf
(
"list proxies: %w"
,
err
)
return
nil
,
nil
,
fmt
.
Errorf
(
"list proxies: %w"
,
err
)
...
...
backend/internal/service/ratelimit_service.go
View file @
e99b344b
...
@@ -9,19 +9,19 @@ import (
...
@@ -9,19 +9,19 @@ import (
"sub2api/internal/config"
"sub2api/internal/config"
"sub2api/internal/model"
"sub2api/internal/model"
"sub2api/internal/
repository
"
"sub2api/internal/
service/ports
"
)
)
// RateLimitService 处理限流和过载状态管理
// RateLimitService 处理限流和过载状态管理
type
RateLimitService
struct
{
type
RateLimitService
struct
{
repos
*
repository
.
Repositor
ies
accountRepo
ports
.
Account
Repositor
y
cfg
*
config
.
Config
cfg
*
config
.
Config
}
}
// NewRateLimitService 创建RateLimitService实例
// NewRateLimitService 创建RateLimitService实例
func
NewRateLimitService
(
repos
*
repository
.
Repositor
ies
,
cfg
*
config
.
Config
)
*
RateLimitService
{
func
NewRateLimitService
(
accountRepo
ports
.
Account
Repositor
y
,
cfg
*
config
.
Config
)
*
RateLimitService
{
return
&
RateLimitService
{
return
&
RateLimitService
{
r
epo
s
:
r
epo
s
,
accountR
epo
:
accountR
epo
,
cfg
:
cfg
,
cfg
:
cfg
,
}
}
}
}
...
@@ -62,7 +62,7 @@ func (s *RateLimitService) HandleUpstreamError(ctx context.Context, account *mod
...
@@ -62,7 +62,7 @@ func (s *RateLimitService) HandleUpstreamError(ctx context.Context, account *mod
// handleAuthError 处理认证类错误(401/403),停止账号调度
// handleAuthError 处理认证类错误(401/403),停止账号调度
func
(
s
*
RateLimitService
)
handleAuthError
(
ctx
context
.
Context
,
account
*
model
.
Account
,
errorMsg
string
)
{
func
(
s
*
RateLimitService
)
handleAuthError
(
ctx
context
.
Context
,
account
*
model
.
Account
,
errorMsg
string
)
{
if
err
:=
s
.
repos
.
A
ccount
.
SetError
(
ctx
,
account
.
ID
,
errorMsg
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
SetError
(
ctx
,
account
.
ID
,
errorMsg
);
err
!=
nil
{
log
.
Printf
(
"SetError failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"SetError failed for account %d: %v"
,
account
.
ID
,
err
)
return
return
}
}
...
@@ -77,7 +77,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
...
@@ -77,7 +77,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
if
resetTimestamp
==
""
{
if
resetTimestamp
==
""
{
// 没有重置时间,使用默认5分钟
// 没有重置时间,使用默认5分钟
resetAt
:=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
resetAt
:=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
if
err
:=
s
.
repos
.
A
ccount
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
}
}
return
return
...
@@ -88,7 +88,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
...
@@ -88,7 +88,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
if
err
!=
nil
{
if
err
!=
nil
{
log
.
Printf
(
"Parse reset timestamp failed: %v"
,
err
)
log
.
Printf
(
"Parse reset timestamp failed: %v"
,
err
)
resetAt
:=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
resetAt
:=
time
.
Now
()
.
Add
(
5
*
time
.
Minute
)
if
err
:=
s
.
repos
.
A
ccount
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
}
}
return
return
...
@@ -97,7 +97,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
...
@@ -97,7 +97,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
resetAt
:=
time
.
Unix
(
ts
,
0
)
resetAt
:=
time
.
Unix
(
ts
,
0
)
// 标记限流状态
// 标记限流状态
if
err
:=
s
.
repos
.
A
ccount
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
SetRateLimited
(
ctx
,
account
.
ID
,
resetAt
);
err
!=
nil
{
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"SetRateLimited failed for account %d: %v"
,
account
.
ID
,
err
)
return
return
}
}
...
@@ -105,7 +105,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
...
@@ -105,7 +105,7 @@ func (s *RateLimitService) handle429(ctx context.Context, account *model.Account
// 根据重置时间反推5h窗口
// 根据重置时间反推5h窗口
windowEnd
:=
resetAt
windowEnd
:=
resetAt
windowStart
:=
resetAt
.
Add
(
-
5
*
time
.
Hour
)
windowStart
:=
resetAt
.
Add
(
-
5
*
time
.
Hour
)
if
err
:=
s
.
repos
.
A
ccount
.
UpdateSessionWindow
(
ctx
,
account
.
ID
,
&
windowStart
,
&
windowEnd
,
"rejected"
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
UpdateSessionWindow
(
ctx
,
account
.
ID
,
&
windowStart
,
&
windowEnd
,
"rejected"
);
err
!=
nil
{
log
.
Printf
(
"UpdateSessionWindow failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"UpdateSessionWindow failed for account %d: %v"
,
account
.
ID
,
err
)
}
}
...
@@ -121,7 +121,7 @@ func (s *RateLimitService) handle529(ctx context.Context, account *model.Account
...
@@ -121,7 +121,7 @@ func (s *RateLimitService) handle529(ctx context.Context, account *model.Account
}
}
until
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
cooldownMinutes
)
*
time
.
Minute
)
until
:=
time
.
Now
()
.
Add
(
time
.
Duration
(
cooldownMinutes
)
*
time
.
Minute
)
if
err
:=
s
.
repos
.
A
ccount
.
SetOverloaded
(
ctx
,
account
.
ID
,
until
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
SetOverloaded
(
ctx
,
account
.
ID
,
until
);
err
!=
nil
{
log
.
Printf
(
"SetOverloaded failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"SetOverloaded failed for account %d: %v"
,
account
.
ID
,
err
)
return
return
}
}
...
@@ -152,13 +152,13 @@ func (s *RateLimitService) UpdateSessionWindow(ctx context.Context, account *mod
...
@@ -152,13 +152,13 @@ func (s *RateLimitService) UpdateSessionWindow(ctx context.Context, account *mod
log
.
Printf
(
"Account %d: initializing 5h window from %v to %v (status: %s)"
,
account
.
ID
,
start
,
end
,
status
)
log
.
Printf
(
"Account %d: initializing 5h window from %v to %v (status: %s)"
,
account
.
ID
,
start
,
end
,
status
)
}
}
if
err
:=
s
.
repos
.
A
ccount
.
UpdateSessionWindow
(
ctx
,
account
.
ID
,
windowStart
,
windowEnd
,
status
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
UpdateSessionWindow
(
ctx
,
account
.
ID
,
windowStart
,
windowEnd
,
status
);
err
!=
nil
{
log
.
Printf
(
"UpdateSessionWindow failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"UpdateSessionWindow failed for account %d: %v"
,
account
.
ID
,
err
)
}
}
// 如果状态为allowed且之前有限流,说明窗口已重置,清除限流状态
// 如果状态为allowed且之前有限流,说明窗口已重置,清除限流状态
if
status
==
"allowed"
&&
account
.
IsRateLimited
()
{
if
status
==
"allowed"
&&
account
.
IsRateLimited
()
{
if
err
:=
s
.
repos
.
A
ccount
.
ClearRateLimit
(
ctx
,
account
.
ID
);
err
!=
nil
{
if
err
:=
s
.
a
ccount
Repo
.
ClearRateLimit
(
ctx
,
account
.
ID
);
err
!=
nil
{
log
.
Printf
(
"ClearRateLimit failed for account %d: %v"
,
account
.
ID
,
err
)
log
.
Printf
(
"ClearRateLimit failed for account %d: %v"
,
account
.
ID
,
err
)
}
}
}
}
...
@@ -166,5 +166,5 @@ func (s *RateLimitService) UpdateSessionWindow(ctx context.Context, account *mod
...
@@ -166,5 +166,5 @@ func (s *RateLimitService) UpdateSessionWindow(ctx context.Context, account *mod
// ClearRateLimit 清除账号的限流状态
// ClearRateLimit 清除账号的限流状态
func
(
s
*
RateLimitService
)
ClearRateLimit
(
ctx
context
.
Context
,
accountID
int64
)
error
{
func
(
s
*
RateLimitService
)
ClearRateLimit
(
ctx
context
.
Context
,
accountID
int64
)
error
{
return
s
.
repos
.
A
ccount
.
ClearRateLimit
(
ctx
,
accountID
)
return
s
.
a
ccount
Repo
.
ClearRateLimit
(
ctx
,
accountID
)
}
}
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