diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go index ab1831d8..ef205dc8 100644 --- a/backend/cmd/server/wire_gen.go +++ b/backend/cmd/server/wire_gen.go @@ -102,7 +102,9 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { proxyExitInfoProber := repository.NewProxyExitInfoProber(configConfig) proxyLatencyCache := repository.NewProxyLatencyCache(redisClient) adminService := service.NewAdminService(userRepository, groupRepository, accountRepository, proxyRepository, apiKeyRepository, redeemCodeRepository, userGroupRateRepository, billingCacheService, proxyExitInfoProber, proxyLatencyCache, apiKeyAuthCacheInvalidator) - adminUserHandler := admin.NewUserHandler(adminService) + concurrencyCache := repository.ProvideConcurrencyCache(redisClient, configConfig) + concurrencyService := service.ProvideConcurrencyService(concurrencyCache, accountRepository, configConfig) + adminUserHandler := admin.NewUserHandler(adminService, concurrencyService) groupHandler := admin.NewGroupHandler(adminService) claudeOAuthClient := repository.NewClaudeOAuthClient() oAuthService := service.NewOAuthService(proxyRepository, claudeOAuthClient) @@ -126,13 +128,11 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) { accountUsageService := service.NewAccountUsageService(accountRepository, usageLogRepository, claudeUsageFetcher, geminiQuotaService, antigravityQuotaFetcher, usageCache, identityCache) geminiTokenProvider := service.NewGeminiTokenProvider(accountRepository, geminiTokenCache, geminiOAuthService) gatewayCache := repository.NewGatewayCache(redisClient) - antigravityTokenProvider := service.NewAntigravityTokenProvider(accountRepository, geminiTokenCache, antigravityOAuthService) schedulerOutboxRepository := repository.NewSchedulerOutboxRepository(db) schedulerSnapshotService := service.ProvideSchedulerSnapshotService(schedulerCache, schedulerOutboxRepository, accountRepository, groupRepository, configConfig) + antigravityTokenProvider := service.NewAntigravityTokenProvider(accountRepository, geminiTokenCache, antigravityOAuthService) antigravityGatewayService := service.NewAntigravityGatewayService(accountRepository, gatewayCache, schedulerSnapshotService, antigravityTokenProvider, rateLimitService, httpUpstream, settingService) accountTestService := service.NewAccountTestService(accountRepository, geminiTokenProvider, antigravityGatewayService, httpUpstream, configConfig) - concurrencyCache := repository.ProvideConcurrencyCache(redisClient, configConfig) - concurrencyService := service.ProvideConcurrencyService(concurrencyCache, accountRepository, configConfig) crsSyncService := service.NewCRSSyncService(accountRepository, proxyRepository, oAuthService, openAIOAuthService, geminiOAuthService, configConfig) sessionLimitCache := repository.ProvideSessionLimitCache(redisClient, configConfig) accountHandler := admin.NewAccountHandler(adminService, oAuthService, openAIOAuthService, geminiOAuthService, antigravityOAuthService, rateLimitService, accountUsageService, accountTestService, concurrencyService, crsSyncService, sessionLimitCache, compositeTokenCacheInvalidator) diff --git a/backend/internal/handler/admin/admin_basic_handlers_test.go b/backend/internal/handler/admin/admin_basic_handlers_test.go index e0f731e1..20a25222 100644 --- a/backend/internal/handler/admin/admin_basic_handlers_test.go +++ b/backend/internal/handler/admin/admin_basic_handlers_test.go @@ -16,7 +16,7 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) { router := gin.New() adminSvc := newStubAdminService() - userHandler := NewUserHandler(adminSvc) + userHandler := NewUserHandler(adminSvc, nil) groupHandler := NewGroupHandler(adminSvc) proxyHandler := NewProxyHandler(adminSvc) redeemHandler := NewRedeemHandler(adminSvc) diff --git a/backend/internal/handler/admin/user_handler.go b/backend/internal/handler/admin/user_handler.go index 1c772e7d..efb9abb5 100644 --- a/backend/internal/handler/admin/user_handler.go +++ b/backend/internal/handler/admin/user_handler.go @@ -11,15 +11,23 @@ import ( "github.com/gin-gonic/gin" ) +// UserWithConcurrency wraps AdminUser with current concurrency info +type UserWithConcurrency struct { + dto.AdminUser + CurrentConcurrency int `json:"current_concurrency"` +} + // UserHandler handles admin user management type UserHandler struct { - adminService service.AdminService + adminService service.AdminService + concurrencyService *service.ConcurrencyService } // NewUserHandler creates a new admin user handler -func NewUserHandler(adminService service.AdminService) *UserHandler { +func NewUserHandler(adminService service.AdminService, concurrencyService *service.ConcurrencyService) *UserHandler { return &UserHandler{ - adminService: adminService, + adminService: adminService, + concurrencyService: concurrencyService, } } @@ -87,10 +95,30 @@ func (h *UserHandler) List(c *gin.Context) { return } - out := make([]dto.AdminUser, 0, len(users)) - for i := range users { - out = append(out, *dto.UserFromServiceAdmin(&users[i])) + // Batch get current concurrency (nil map if unavailable) + var loadInfo map[int64]*service.UserLoadInfo + if len(users) > 0 && h.concurrencyService != nil { + usersConcurrency := make([]service.UserWithConcurrency, len(users)) + for i := range users { + usersConcurrency[i] = service.UserWithConcurrency{ + ID: users[i].ID, + MaxConcurrency: users[i].Concurrency, + } + } + loadInfo, _ = h.concurrencyService.GetUsersLoadBatch(c.Request.Context(), usersConcurrency) } + + // Build response with concurrency info + out := make([]UserWithConcurrency, len(users)) + for i := range users { + out[i] = UserWithConcurrency{ + AdminUser: *dto.UserFromServiceAdmin(&users[i]), + } + if info := loadInfo[users[i].ID]; info != nil { + out[i].CurrentConcurrency = info.CurrentConcurrency + } + } + response.Paginated(c, out, total, page, pageSize) } diff --git a/frontend/src/components/user/UserConcurrencyCell.vue b/frontend/src/components/user/UserConcurrencyCell.vue new file mode 100644 index 00000000..2edffa39 --- /dev/null +++ b/frontend/src/components/user/UserConcurrencyCell.vue @@ -0,0 +1,43 @@ + + + diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 84e3a380..03445ed5 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -43,6 +43,8 @@ export interface AdminUser extends User { notes: string // 用户专属分组倍率配置 (group_id -> rate_multiplier) group_rates?: Record + // 当前并发数(仅管理员列表接口返回) + current_concurrency?: number } export interface LoginRequest { diff --git a/frontend/src/views/admin/UsersView.vue b/frontend/src/views/admin/UsersView.vue index 6812d084..063171a3 100644 --- a/frontend/src/views/admin/UsersView.vue +++ b/frontend/src/views/admin/UsersView.vue @@ -342,8 +342,11 @@ -