diff --git a/backend/internal/handler/admin/proxy_handler.go b/backend/internal/handler/admin/proxy_handler.go index 99557f9a..4fabd8ec 100644 --- a/backend/internal/handler/admin/proxy_handler.go +++ b/backend/internal/handler/admin/proxy_handler.go @@ -52,15 +52,15 @@ func (h *ProxyHandler) List(c *gin.Context) { status := c.Query("status") search := c.Query("search") - proxies, total, err := h.adminService.ListProxies(c.Request.Context(), page, pageSize, protocol, status, search) + proxies, total, err := h.adminService.ListProxiesWithAccountCount(c.Request.Context(), page, pageSize, protocol, status, search) if err != nil { response.ErrorFrom(c, err) return } - out := make([]dto.Proxy, 0, len(proxies)) + out := make([]dto.ProxyWithAccountCount, 0, len(proxies)) for i := range proxies { - out = append(out, *dto.ProxyFromService(&proxies[i])) + out = append(out, *dto.ProxyWithAccountCountFromService(&proxies[i])) } response.Paginated(c, out, total, page, pageSize) } diff --git a/backend/internal/repository/proxy_repo.go b/backend/internal/repository/proxy_repo.go index c24b2e2c..622b0aeb 100644 --- a/backend/internal/repository/proxy_repo.go +++ b/backend/internal/repository/proxy_repo.go @@ -133,6 +133,55 @@ func (r *proxyRepository) ListWithFilters(ctx context.Context, params pagination return outProxies, paginationResultFromTotal(int64(total), params), nil } +// ListWithFiltersAndAccountCount lists proxies with filters and includes account count per proxy +func (r *proxyRepository) ListWithFiltersAndAccountCount(ctx context.Context, params pagination.PaginationParams, protocol, status, search string) ([]service.ProxyWithAccountCount, *pagination.PaginationResult, error) { + q := r.client.Proxy.Query() + if protocol != "" { + q = q.Where(proxy.ProtocolEQ(protocol)) + } + if status != "" { + q = q.Where(proxy.StatusEQ(status)) + } + if search != "" { + q = q.Where(proxy.NameContainsFold(search)) + } + + total, err := q.Count(ctx) + if err != nil { + return nil, nil, err + } + + proxies, err := q. + Offset(params.Offset()). + Limit(params.Limit()). + Order(dbent.Desc(proxy.FieldID)). + All(ctx) + if err != nil { + return nil, nil, err + } + + // Get account counts + counts, err := r.GetAccountCountsForProxies(ctx) + if err != nil { + return nil, nil, err + } + + // Build result with account counts + result := make([]service.ProxyWithAccountCount, 0, len(proxies)) + for i := range proxies { + proxyOut := proxyEntityToService(proxies[i]) + if proxyOut == nil { + continue + } + result = append(result, service.ProxyWithAccountCount{ + Proxy: *proxyOut, + AccountCount: counts[proxyOut.ID], + }) + } + + return result, paginationResultFromTotal(int64(total), params), nil +} + func (r *proxyRepository) ListActive(ctx context.Context) ([]service.Proxy, error) { proxies, err := r.client.Proxy.Query(). Where(proxy.StatusEQ(service.StatusActive)). diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index 80acd440..0f2cf998 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -47,6 +47,7 @@ type AdminService interface { // Proxy management ListProxies(ctx context.Context, page, pageSize int, protocol, status, search string) ([]Proxy, int64, error) + ListProxiesWithAccountCount(ctx context.Context, page, pageSize int, protocol, status, search string) ([]ProxyWithAccountCount, int64, error) GetAllProxies(ctx context.Context) ([]Proxy, error) GetAllProxiesWithAccountCount(ctx context.Context) ([]ProxyWithAccountCount, error) GetProxy(ctx context.Context, id int64) (*Proxy, error) @@ -950,6 +951,15 @@ func (s *adminServiceImpl) ListProxies(ctx context.Context, page, pageSize int, return proxies, result.Total, nil } +func (s *adminServiceImpl) ListProxiesWithAccountCount(ctx context.Context, page, pageSize int, protocol, status, search string) ([]ProxyWithAccountCount, int64, error) { + params := pagination.PaginationParams{Page: page, PageSize: pageSize} + proxies, result, err := s.proxyRepo.ListWithFiltersAndAccountCount(ctx, params, protocol, status, search) + if err != nil { + return nil, 0, err + } + return proxies, result.Total, nil +} + func (s *adminServiceImpl) GetAllProxies(ctx context.Context) ([]Proxy, error) { return s.proxyRepo.ListActive(ctx) } diff --git a/backend/internal/service/proxy_service.go b/backend/internal/service/proxy_service.go index 044f9ffc..58408d04 100644 --- a/backend/internal/service/proxy_service.go +++ b/backend/internal/service/proxy_service.go @@ -20,6 +20,7 @@ type ProxyRepository interface { List(ctx context.Context, params pagination.PaginationParams) ([]Proxy, *pagination.PaginationResult, error) ListWithFilters(ctx context.Context, params pagination.PaginationParams, protocol, status, search string) ([]Proxy, *pagination.PaginationResult, error) + ListWithFiltersAndAccountCount(ctx context.Context, params pagination.PaginationParams, protocol, status, search string) ([]ProxyWithAccountCount, *pagination.PaginationResult, error) ListActive(ctx context.Context) ([]Proxy, error) ListActiveWithAccountCount(ctx context.Context) ([]ProxyWithAccountCount, error) diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts index 1a24ea95..f28048e2 100644 --- a/frontend/src/i18n/locales/en.ts +++ b/frontend/src/i18n/locales/en.ts @@ -1561,6 +1561,7 @@ export default { protocol: 'Protocol', address: 'Address', status: 'Status', + accounts: 'Accounts', actions: 'Actions' }, testConnection: 'Test Connection', diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts index 2a58e74b..a042c1dc 100644 --- a/frontend/src/i18n/locales/zh.ts +++ b/frontend/src/i18n/locales/zh.ts @@ -1647,6 +1647,7 @@ export default { protocol: '协议', address: '地址', status: '状态', + accounts: '账号数', actions: '操作', nameLabel: '名称', namePlaceholder: '请输入代理名称', diff --git a/frontend/src/views/admin/ProxiesView.vue b/frontend/src/views/admin/ProxiesView.vue index 6b173033..00b43ba2 100644 --- a/frontend/src/views/admin/ProxiesView.vue +++ b/frontend/src/views/admin/ProxiesView.vue @@ -85,6 +85,14 @@ + +