mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-19 06:14:45 +08:00
feat(admin): 分组管理新增容量列(并发/会话/RPM 实时聚合)
复用 GroupCapacityService,在 admin 分组列表中添加容量列, 显示每个分组的实时并发/会话/RPM 使用量和上限。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
131
backend/internal/service/group_capacity_service.go
Normal file
131
backend/internal/service/group_capacity_service.go
Normal file
@@ -0,0 +1,131 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// GroupCapacitySummary holds aggregated capacity for a single group.
|
||||
type GroupCapacitySummary struct {
|
||||
GroupID int64 `json:"group_id"`
|
||||
ConcurrencyUsed int `json:"concurrency_used"`
|
||||
ConcurrencyMax int `json:"concurrency_max"`
|
||||
SessionsUsed int `json:"sessions_used"`
|
||||
SessionsMax int `json:"sessions_max"`
|
||||
RPMUsed int `json:"rpm_used"`
|
||||
RPMMax int `json:"rpm_max"`
|
||||
}
|
||||
|
||||
// GroupCapacityService aggregates per-group capacity from runtime data.
|
||||
type GroupCapacityService struct {
|
||||
accountRepo AccountRepository
|
||||
groupRepo GroupRepository
|
||||
concurrencyService *ConcurrencyService
|
||||
sessionLimitCache SessionLimitCache
|
||||
rpmCache RPMCache
|
||||
}
|
||||
|
||||
// NewGroupCapacityService creates a new GroupCapacityService.
|
||||
func NewGroupCapacityService(
|
||||
accountRepo AccountRepository,
|
||||
groupRepo GroupRepository,
|
||||
concurrencyService *ConcurrencyService,
|
||||
sessionLimitCache SessionLimitCache,
|
||||
rpmCache RPMCache,
|
||||
) *GroupCapacityService {
|
||||
return &GroupCapacityService{
|
||||
accountRepo: accountRepo,
|
||||
groupRepo: groupRepo,
|
||||
concurrencyService: concurrencyService,
|
||||
sessionLimitCache: sessionLimitCache,
|
||||
rpmCache: rpmCache,
|
||||
}
|
||||
}
|
||||
|
||||
// GetAllGroupCapacity returns capacity summary for all active groups.
|
||||
func (s *GroupCapacityService) GetAllGroupCapacity(ctx context.Context) ([]GroupCapacitySummary, error) {
|
||||
groups, err := s.groupRepo.ListActive(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := make([]GroupCapacitySummary, 0, len(groups))
|
||||
for i := range groups {
|
||||
cap, err := s.getGroupCapacity(ctx, groups[i].ID)
|
||||
if err != nil {
|
||||
// Skip groups with errors, return partial results
|
||||
continue
|
||||
}
|
||||
cap.GroupID = groups[i].ID
|
||||
results = append(results, cap)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (s *GroupCapacityService) getGroupCapacity(ctx context.Context, groupID int64) (GroupCapacitySummary, error) {
|
||||
accounts, err := s.accountRepo.ListSchedulableByGroupID(ctx, groupID)
|
||||
if err != nil {
|
||||
return GroupCapacitySummary{}, err
|
||||
}
|
||||
if len(accounts) == 0 {
|
||||
return GroupCapacitySummary{}, nil
|
||||
}
|
||||
|
||||
// Collect account IDs and config values
|
||||
accountIDs := make([]int64, 0, len(accounts))
|
||||
sessionTimeouts := make(map[int64]time.Duration)
|
||||
var concurrencyMax, sessionsMax, rpmMax int
|
||||
|
||||
for i := range accounts {
|
||||
acc := &accounts[i]
|
||||
accountIDs = append(accountIDs, acc.ID)
|
||||
concurrencyMax += acc.Concurrency
|
||||
|
||||
if ms := acc.GetMaxSessions(); ms > 0 {
|
||||
sessionsMax += ms
|
||||
timeout := time.Duration(acc.GetSessionIdleTimeoutMinutes()) * time.Minute
|
||||
if timeout <= 0 {
|
||||
timeout = 5 * time.Minute
|
||||
}
|
||||
sessionTimeouts[acc.ID] = timeout
|
||||
}
|
||||
|
||||
if rpm := acc.GetBaseRPM(); rpm > 0 {
|
||||
rpmMax += rpm
|
||||
}
|
||||
}
|
||||
|
||||
// Batch query runtime data from Redis
|
||||
concurrencyMap, _ := s.concurrencyService.GetAccountConcurrencyBatch(ctx, accountIDs)
|
||||
|
||||
var sessionsMap map[int64]int
|
||||
if sessionsMax > 0 && s.sessionLimitCache != nil {
|
||||
sessionsMap, _ = s.sessionLimitCache.GetActiveSessionCountBatch(ctx, accountIDs, sessionTimeouts)
|
||||
}
|
||||
|
||||
var rpmMap map[int64]int
|
||||
if rpmMax > 0 && s.rpmCache != nil {
|
||||
rpmMap, _ = s.rpmCache.GetRPMBatch(ctx, accountIDs)
|
||||
}
|
||||
|
||||
// Aggregate
|
||||
var concurrencyUsed, sessionsUsed, rpmUsed int
|
||||
for _, id := range accountIDs {
|
||||
concurrencyUsed += concurrencyMap[id]
|
||||
if sessionsMap != nil {
|
||||
sessionsUsed += sessionsMap[id]
|
||||
}
|
||||
if rpmMap != nil {
|
||||
rpmUsed += rpmMap[id]
|
||||
}
|
||||
}
|
||||
|
||||
return GroupCapacitySummary{
|
||||
ConcurrencyUsed: concurrencyUsed,
|
||||
ConcurrencyMax: concurrencyMax,
|
||||
SessionsUsed: sessionsUsed,
|
||||
SessionsMax: sessionsMax,
|
||||
RPMUsed: rpmUsed,
|
||||
RPMMax: rpmMax,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user