@@ -54,7 +54,8 @@ type AdminService interface {
CreateProxy ( ctx context . Context , input * CreateProxyInput ) ( * Proxy , error )
UpdateProxy ( ctx context . Context , id int64 , input * UpdateProxyInput ) ( * Proxy , error )
DeleteProxy ( ctx context . Context , id int64 ) error
G etProxyAccount s( ctx context . Context , proxyID int64 , page , pageSize int ) ( [ ] Account , int64 , error )
BatchDel ete Proxie s( ctx context . Context , ids [ ] int64 ) ( * ProxyBatchDeleteResult , error )
GetProxyAccounts ( ctx context . Context , proxyID int64 ) ( [ ] ProxyAccountSummary , error )
CheckProxyExists ( ctx context . Context , host string , port int , username , password string ) ( bool , error )
TestProxy ( ctx context . Context , id int64 ) ( * ProxyTestResult , error )
@@ -223,6 +224,16 @@ type GenerateRedeemCodesInput struct {
ValidityDays int // 订阅类型专用:有效天数
}
type ProxyBatchDeleteResult struct {
DeletedIDs [ ] int64 ` json:"deleted_ids" `
Skipped [ ] ProxyBatchDeleteSkipped ` json:"skipped" `
}
type ProxyBatchDeleteSkipped struct {
ID int64 ` json:"id" `
Reason string ` json:"reason" `
}
// ProxyTestResult represents the result of testing a proxy
type ProxyTestResult struct {
Success bool ` json:"success" `
@@ -257,6 +268,7 @@ type adminServiceImpl struct {
redeemCodeRepo RedeemCodeRepository
billingCacheService * BillingCacheService
proxyProber ProxyExitInfoProber
proxyLatencyCache ProxyLatencyCache
authCacheInvalidator APIKeyAuthCacheInvalidator
}
@@ -270,6 +282,7 @@ func NewAdminService(
redeemCodeRepo RedeemCodeRepository ,
billingCacheService * BillingCacheService ,
proxyProber ProxyExitInfoProber ,
proxyLatencyCache ProxyLatencyCache ,
authCacheInvalidator APIKeyAuthCacheInvalidator ,
) AdminService {
return & adminServiceImpl {
@@ -281,6 +294,7 @@ func NewAdminService(
redeemCodeRepo : redeemCodeRepo ,
billingCacheService : billingCacheService ,
proxyProber : proxyProber ,
proxyLatencyCache : proxyLatencyCache ,
authCacheInvalidator : authCacheInvalidator ,
}
}
@@ -1093,6 +1107,7 @@ func (s *adminServiceImpl) ListProxiesWithAccountCount(ctx context.Context, page
if err != nil {
return nil , 0 , err
}
s . attachProxyLatency ( ctx , proxies )
return proxies , result . Total , nil
}
@@ -1101,7 +1116,12 @@ func (s *adminServiceImpl) GetAllProxies(ctx context.Context) ([]Proxy, error) {
}
func ( s * adminServiceImpl ) GetAllProxiesWithAccountCount ( ctx context . Context ) ( [ ] ProxyWithAccountCount , error ) {
return s . proxyRepo . ListActiveWithAccountCount ( ctx )
proxies , err := s . proxyRepo . ListActiveWithAccountCount ( ctx )
if err != nil {
return nil , err
}
s . attachProxyLatency ( ctx , proxies )
return proxies , nil
}
func ( s * adminServiceImpl ) GetProxy ( ctx context . Context , id int64 ) ( * Proxy , error ) {
@@ -1121,6 +1141,8 @@ func (s *adminServiceImpl) CreateProxy(ctx context.Context, input *CreateProxyIn
if err := s . proxyRepo . Create ( ctx , proxy ) ; err != nil {
return nil , err
}
// Probe latency asynchronously so creation isn't blocked by network timeout.
go s . probeProxyLatency ( context . Background ( ) , proxy )
return proxy , nil
}
@@ -1159,12 +1181,53 @@ func (s *adminServiceImpl) UpdateProxy(ctx context.Context, id int64, input *Upd
}
func ( s * adminServiceImpl ) DeleteProxy ( ctx context . Context , id int64 ) error {
count , err := s . proxyRepo . CountAccountsByProxyID ( ctx , id )
if err != nil {
return err
}
if count > 0 {
return ErrProxyInUse
}
return s . proxyRepo . Delete ( ctx , id )
}
func ( s * adminServiceImpl ) G etProxyAccount s( ctx context . Context , proxyID int64 , page , pageSize int ) ( [ ] Account , int64 , error ) {
// Return mock data for now - would need a dedicated repository method
return [ ] Account { } , 0 , nil
func ( s * adminServiceImpl ) BatchDel ete Proxie s( ctx context . Context , ids [ ] int64 ) ( * ProxyBatchDeleteResult , error ) {
result := & ProxyBatchDeleteResult { }
if len ( ids ) == 0 {
return result , nil
}
for _ , id := range ids {
count , err := s . proxyRepo . CountAccountsByProxyID ( ctx , id )
if err != nil {
result . Skipped = append ( result . Skipped , ProxyBatchDeleteSkipped {
ID : id ,
Reason : err . Error ( ) ,
} )
continue
}
if count > 0 {
result . Skipped = append ( result . Skipped , ProxyBatchDeleteSkipped {
ID : id ,
Reason : ErrProxyInUse . Error ( ) ,
} )
continue
}
if err := s . proxyRepo . Delete ( ctx , id ) ; err != nil {
result . Skipped = append ( result . Skipped , ProxyBatchDeleteSkipped {
ID : id ,
Reason : err . Error ( ) ,
} )
continue
}
result . DeletedIDs = append ( result . DeletedIDs , id )
}
return result , nil
}
func ( s * adminServiceImpl ) GetProxyAccounts ( ctx context . Context , proxyID int64 ) ( [ ] ProxyAccountSummary , error ) {
return s . proxyRepo . ListAccountSummariesByProxyID ( ctx , proxyID )
}
func ( s * adminServiceImpl ) CheckProxyExists ( ctx context . Context , host string , port int , username , password string ) ( bool , error ) {
@@ -1264,12 +1327,24 @@ func (s *adminServiceImpl) TestProxy(ctx context.Context, id int64) (*ProxyTestR
proxyURL := proxy . URL ( )
exitInfo , latencyMs , err := s . proxyProber . ProbeProxy ( ctx , proxyURL )
if err != nil {
s . saveProxyLatency ( ctx , id , & ProxyLatencyInfo {
Success : false ,
Message : err . Error ( ) ,
UpdatedAt : time . Now ( ) ,
} )
return & ProxyTestResult {
Success : false ,
Message : err . Error ( ) ,
} , nil
}
latency := latencyMs
s . saveProxyLatency ( ctx , id , & ProxyLatencyInfo {
Success : true ,
LatencyMs : & latency ,
Message : "Proxy is accessible" ,
UpdatedAt : time . Now ( ) ,
} )
return & ProxyTestResult {
Success : true ,
Message : "Proxy is accessible" ,
@@ -1281,6 +1356,29 @@ func (s *adminServiceImpl) TestProxy(ctx context.Context, id int64) (*ProxyTestR
} , nil
}
func ( s * adminServiceImpl ) probeProxyLatency ( ctx context . Context , proxy * Proxy ) {
if s . proxyProber == nil || proxy == nil {
return
}
_ , latencyMs , err := s . proxyProber . ProbeProxy ( ctx , proxy . URL ( ) )
if err != nil {
s . saveProxyLatency ( ctx , proxy . ID , & ProxyLatencyInfo {
Success : false ,
Message : err . Error ( ) ,
UpdatedAt : time . Now ( ) ,
} )
return
}
latency := latencyMs
s . saveProxyLatency ( ctx , proxy . ID , & ProxyLatencyInfo {
Success : true ,
LatencyMs : & latency ,
Message : "Proxy is accessible" ,
UpdatedAt : time . Now ( ) ,
} )
}
// checkMixedChannelRisk 检查分组中是否存在混合渠道( Antigravity + Anthropic)
// 如果存在混合,返回错误提示用户确认
func ( s * adminServiceImpl ) checkMixedChannelRisk ( ctx context . Context , currentAccountID int64 , currentAccountPlatform string , groupIDs [ ] int64 ) error {
@@ -1330,6 +1428,46 @@ func (s *adminServiceImpl) checkMixedChannelRisk(ctx context.Context, currentAcc
return nil
}
func ( s * adminServiceImpl ) attachProxyLatency ( ctx context . Context , proxies [ ] ProxyWithAccountCount ) {
if s . proxyLatencyCache == nil || len ( proxies ) == 0 {
return
}
ids := make ( [ ] int64 , 0 , len ( proxies ) )
for i := range proxies {
ids = append ( ids , proxies [ i ] . ID )
}
latencies , err := s . proxyLatencyCache . GetProxyLatencies ( ctx , ids )
if err != nil {
log . Printf ( "Warning: load proxy latency cache failed: %v" , err )
return
}
for i := range proxies {
info := latencies [ proxies [ i ] . ID ]
if info == nil {
continue
}
if info . Success {
proxies [ i ] . LatencyStatus = "success"
proxies [ i ] . LatencyMs = info . LatencyMs
} else {
proxies [ i ] . LatencyStatus = "failed"
}
proxies [ i ] . LatencyMessage = info . Message
}
}
func ( s * adminServiceImpl ) saveProxyLatency ( ctx context . Context , proxyID int64 , info * ProxyLatencyInfo ) {
if s . proxyLatencyCache == nil || info == nil {
return
}
if err := s . proxyLatencyCache . SetProxyLatency ( ctx , proxyID , info ) ; err != nil {
log . Printf ( "Warning: store proxy latency cache failed: %v" , err )
}
}
// getAccountPlatform 根据账号 platform 判断混合渠道检查用的平台标识
func getAccountPlatform ( accountPlatform string ) string {
switch strings . ToLower ( strings . TrimSpace ( accountPlatform ) ) {