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 @@
+
+
+
+
+
+ {{ current }}
+ /
+ {{ max }}
+
+
+
+
+
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 @@
-
- {{ value }}
+
+
@@ -535,6 +538,7 @@ import EmptyState from '@/components/common/EmptyState.vue'
import GroupBadge from '@/components/common/GroupBadge.vue'
import Select from '@/components/common/Select.vue'
import UserAttributesConfigModal from '@/components/user/UserAttributesConfigModal.vue'
+import UserConcurrencyCell from '@/components/user/UserConcurrencyCell.vue'
import UserCreateModal from '@/components/admin/user/UserCreateModal.vue'
import UserEditModal from '@/components/admin/user/UserEditModal.vue'
import UserApiKeysModal from '@/components/admin/user/UserApiKeysModal.vue'