feat: 批量分配处理数据
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gpt-load/internal/config"
|
"gpt-load/internal/config"
|
||||||
"gpt-load/internal/keypool"
|
|
||||||
"gpt-load/internal/models"
|
"gpt-load/internal/models"
|
||||||
"gpt-load/internal/services"
|
"gpt-load/internal/services"
|
||||||
"gpt-load/internal/types"
|
"gpt-load/internal/types"
|
||||||
@@ -21,7 +20,6 @@ type Server struct {
|
|||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
config types.ConfigManager
|
config types.ConfigManager
|
||||||
SettingsManager *config.SystemSettingsManager
|
SettingsManager *config.SystemSettingsManager
|
||||||
KeyValidator *keypool.KeyValidator
|
|
||||||
KeyManualValidationService *services.KeyManualValidationService
|
KeyManualValidationService *services.KeyManualValidationService
|
||||||
TaskService *services.TaskService
|
TaskService *services.TaskService
|
||||||
KeyService *services.KeyService
|
KeyService *services.KeyService
|
||||||
@@ -34,7 +32,6 @@ type NewServerParams struct {
|
|||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
Config types.ConfigManager
|
Config types.ConfigManager
|
||||||
SettingsManager *config.SystemSettingsManager
|
SettingsManager *config.SystemSettingsManager
|
||||||
KeyValidator *keypool.KeyValidator
|
|
||||||
KeyManualValidationService *services.KeyManualValidationService
|
KeyManualValidationService *services.KeyManualValidationService
|
||||||
TaskService *services.TaskService
|
TaskService *services.TaskService
|
||||||
KeyService *services.KeyService
|
KeyService *services.KeyService
|
||||||
@@ -47,7 +44,6 @@ func NewServer(params NewServerParams) *Server {
|
|||||||
DB: params.DB,
|
DB: params.DB,
|
||||||
config: params.Config,
|
config: params.Config,
|
||||||
SettingsManager: params.SettingsManager,
|
SettingsManager: params.SettingsManager,
|
||||||
KeyValidator: params.KeyValidator,
|
|
||||||
KeyManualValidationService: params.KeyManualValidationService,
|
KeyManualValidationService: params.KeyManualValidationService,
|
||||||
TaskService: params.TaskService,
|
TaskService: params.TaskService,
|
||||||
KeyService: params.KeyService,
|
KeyService: params.KeyService,
|
||||||
|
@@ -84,7 +84,9 @@ func (s *Server) AddMultipleKeys(c *gin.Context) {
|
|||||||
|
|
||||||
result, err := s.KeyService.AddMultipleKeys(req.GroupID, req.KeysText)
|
result, err := s.KeyService.AddMultipleKeys(req.GroupID, req.KeysText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "no valid keys found in the input text" {
|
if strings.Contains(err.Error(), "batch size exceeds the limit") {
|
||||||
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
|
} else if err.Error() == "no valid keys found in the input text" {
|
||||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
} else {
|
} else {
|
||||||
response.Error(c, app_errors.ParseDBError(err))
|
response.Error(c, app_errors.ParseDBError(err))
|
||||||
@@ -146,7 +148,9 @@ func (s *Server) DeleteMultipleKeys(c *gin.Context) {
|
|||||||
|
|
||||||
result, err := s.KeyService.DeleteMultipleKeys(req.GroupID, req.KeysText)
|
result, err := s.KeyService.DeleteMultipleKeys(req.GroupID, req.KeysText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "no valid keys found in the input text" {
|
if strings.Contains(err.Error(), "batch size exceeds the limit") {
|
||||||
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
|
} else if err.Error() == "no valid keys found in the input text" {
|
||||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
} else {
|
} else {
|
||||||
response.Error(c, app_errors.ParseDBError(err))
|
response.Error(c, app_errors.ParseDBError(err))
|
||||||
@@ -176,7 +180,9 @@ func (s *Server) RestoreMultipleKeys(c *gin.Context) {
|
|||||||
|
|
||||||
result, err := s.KeyService.RestoreMultipleKeys(req.GroupID, req.KeysText)
|
result, err := s.KeyService.RestoreMultipleKeys(req.GroupID, req.KeysText)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "no valid keys found in the input text" {
|
if strings.Contains(err.Error(), "batch size exceeds the limit") {
|
||||||
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
|
} else if err.Error() == "no valid keys found in the input text" {
|
||||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
} else {
|
} else {
|
||||||
response.Error(c, app_errors.ParseDBError(err))
|
response.Error(c, app_errors.ParseDBError(err))
|
||||||
@@ -205,16 +211,15 @@ func (s *Server) TestMultipleKeys(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-use the parsing logic from the key service
|
results, err := s.KeyService.TestMultipleKeys(c.Request.Context(), group, req.KeysText)
|
||||||
keysToTest := s.KeyService.ParseKeysFromText(req.KeysText)
|
|
||||||
if len(keysToTest) == 0 {
|
|
||||||
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, "no valid keys found in the input text"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
results, err := s.KeyValidator.TestMultipleKeys(c.Request.Context(), group, keysToTest)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
response.Error(c, app_errors.ParseDBError(err))
|
if strings.Contains(err.Error(), "batch size exceeds the limit") {
|
||||||
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
|
} else if err.Error() == "no valid keys found in the input text" {
|
||||||
|
response.Error(c, app_errors.NewAPIError(app_errors.ErrValidation, err.Error()))
|
||||||
|
} else {
|
||||||
|
response.Error(c, app_errors.ParseDBError(err))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gpt-load/internal/keypool"
|
"gpt-load/internal/keypool"
|
||||||
@@ -11,6 +12,11 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRequestKeys = 5000
|
||||||
|
chunkSize = 1000
|
||||||
|
)
|
||||||
|
|
||||||
// AddKeysResult holds the result of adding multiple keys.
|
// AddKeysResult holds the result of adding multiple keys.
|
||||||
type AddKeysResult struct {
|
type AddKeysResult struct {
|
||||||
AddedCount int `json:"added_count"`
|
AddedCount int `json:"added_count"`
|
||||||
@@ -34,15 +40,17 @@ type RestoreKeysResult struct {
|
|||||||
|
|
||||||
// KeyService provides services related to API keys.
|
// KeyService provides services related to API keys.
|
||||||
type KeyService struct {
|
type KeyService struct {
|
||||||
DB *gorm.DB
|
DB *gorm.DB
|
||||||
KeyProvider *keypool.KeyProvider
|
KeyProvider *keypool.KeyProvider
|
||||||
|
KeyValidator *keypool.KeyValidator
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewKeyService creates a new KeyService.
|
// NewKeyService creates a new KeyService.
|
||||||
func NewKeyService(db *gorm.DB, keyProvider *keypool.KeyProvider) *KeyService {
|
func NewKeyService(db *gorm.DB, keyProvider *keypool.KeyProvider, keyValidator *keypool.KeyValidator) *KeyService {
|
||||||
return &KeyService{
|
return &KeyService{
|
||||||
DB: db,
|
DB: db,
|
||||||
KeyProvider: keyProvider,
|
KeyProvider: keyProvider,
|
||||||
|
KeyValidator: keyValidator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +58,9 @@ func NewKeyService(db *gorm.DB, keyProvider *keypool.KeyProvider) *KeyService {
|
|||||||
func (s *KeyService) AddMultipleKeys(groupID uint, keysText string) (*AddKeysResult, error) {
|
func (s *KeyService) AddMultipleKeys(groupID uint, keysText string) (*AddKeysResult, error) {
|
||||||
// 1. Parse keys from the text block
|
// 1. Parse keys from the text block
|
||||||
keys := s.ParseKeysFromText(keysText)
|
keys := s.ParseKeysFromText(keysText)
|
||||||
|
if len(keys) > maxRequestKeys {
|
||||||
|
return nil, fmt.Errorf("batch size exceeds the limit of %d keys, got %d", maxRequestKeys, len(keys))
|
||||||
|
}
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return nil, fmt.Errorf("no valid keys found in the input text")
|
return nil, fmt.Errorf("no valid keys found in the input text")
|
||||||
}
|
}
|
||||||
@@ -94,10 +105,16 @@ func (s *KeyService) AddMultipleKeys(groupID uint, keysText string) (*AddKeysRes
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Use KeyProvider to add keys
|
// 4. Use KeyProvider to add keys in chunks
|
||||||
err := s.KeyProvider.AddKeys(groupID, newKeysToCreate)
|
for i := 0; i < len(newKeysToCreate); i += chunkSize {
|
||||||
if err != nil {
|
end := i + chunkSize
|
||||||
return nil, err
|
if end > len(newKeysToCreate) {
|
||||||
|
end = len(newKeysToCreate)
|
||||||
|
}
|
||||||
|
chunk := newKeysToCreate[i:end]
|
||||||
|
if err := s.KeyProvider.AddKeys(groupID, chunk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Calculate new total count
|
// 5. Calculate new total count
|
||||||
@@ -164,16 +181,28 @@ func (s *KeyService) isValidKeyFormat(key string) bool {
|
|||||||
// RestoreMultipleKeys handles the business logic of restoring keys from a text block.
|
// RestoreMultipleKeys handles the business logic of restoring keys from a text block.
|
||||||
func (s *KeyService) RestoreMultipleKeys(groupID uint, keysText string) (*RestoreKeysResult, error) {
|
func (s *KeyService) RestoreMultipleKeys(groupID uint, keysText string) (*RestoreKeysResult, error) {
|
||||||
keysToRestore := s.ParseKeysFromText(keysText)
|
keysToRestore := s.ParseKeysFromText(keysText)
|
||||||
|
if len(keysToRestore) > maxRequestKeys {
|
||||||
|
return nil, fmt.Errorf("batch size exceeds the limit of %d keys, got %d", maxRequestKeys, len(keysToRestore))
|
||||||
|
}
|
||||||
if len(keysToRestore) == 0 {
|
if len(keysToRestore) == 0 {
|
||||||
return nil, fmt.Errorf("no valid keys found in the input text")
|
return nil, fmt.Errorf("no valid keys found in the input text")
|
||||||
}
|
}
|
||||||
|
|
||||||
restoredCount, err := s.KeyProvider.RestoreMultipleKeys(groupID, keysToRestore)
|
var totalRestoredCount int64
|
||||||
if err != nil {
|
for i := 0; i < len(keysToRestore); i += chunkSize {
|
||||||
return nil, err
|
end := i + chunkSize
|
||||||
|
if end > len(keysToRestore) {
|
||||||
|
end = len(keysToRestore)
|
||||||
|
}
|
||||||
|
chunk := keysToRestore[i:end]
|
||||||
|
restoredCount, err := s.KeyProvider.RestoreMultipleKeys(groupID, chunk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
totalRestoredCount += restoredCount
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredCount := len(keysToRestore) - int(restoredCount)
|
ignoredCount := len(keysToRestore) - int(totalRestoredCount)
|
||||||
|
|
||||||
var totalInGroup int64
|
var totalInGroup int64
|
||||||
if err := s.DB.Model(&models.APIKey{}).Where("group_id = ?", groupID).Count(&totalInGroup).Error; err != nil {
|
if err := s.DB.Model(&models.APIKey{}).Where("group_id = ?", groupID).Count(&totalInGroup).Error; err != nil {
|
||||||
@@ -181,7 +210,7 @@ func (s *KeyService) RestoreMultipleKeys(groupID uint, keysText string) (*Restor
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &RestoreKeysResult{
|
return &RestoreKeysResult{
|
||||||
RestoredCount: int(restoredCount),
|
RestoredCount: int(totalRestoredCount),
|
||||||
IgnoredCount: ignoredCount,
|
IgnoredCount: ignoredCount,
|
||||||
TotalInGroup: totalInGroup,
|
TotalInGroup: totalInGroup,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -200,16 +229,28 @@ func (s *KeyService) ClearAllInvalidKeys(groupID uint) (int64, error) {
|
|||||||
// DeleteMultipleKeys handles the business logic of deleting keys from a text block.
|
// DeleteMultipleKeys handles the business logic of deleting keys from a text block.
|
||||||
func (s *KeyService) DeleteMultipleKeys(groupID uint, keysText string) (*DeleteKeysResult, error) {
|
func (s *KeyService) DeleteMultipleKeys(groupID uint, keysText string) (*DeleteKeysResult, error) {
|
||||||
keysToDelete := s.ParseKeysFromText(keysText)
|
keysToDelete := s.ParseKeysFromText(keysText)
|
||||||
|
if len(keysToDelete) > maxRequestKeys {
|
||||||
|
return nil, fmt.Errorf("batch size exceeds the limit of %d keys, got %d", maxRequestKeys, len(keysToDelete))
|
||||||
|
}
|
||||||
if len(keysToDelete) == 0 {
|
if len(keysToDelete) == 0 {
|
||||||
return nil, fmt.Errorf("no valid keys found in the input text")
|
return nil, fmt.Errorf("no valid keys found in the input text")
|
||||||
}
|
}
|
||||||
|
|
||||||
deletedCount, err := s.KeyProvider.RemoveKeys(groupID, keysToDelete)
|
var totalDeletedCount int64
|
||||||
if err != nil {
|
for i := 0; i < len(keysToDelete); i += chunkSize {
|
||||||
return nil, err
|
end := i + chunkSize
|
||||||
|
if end > len(keysToDelete) {
|
||||||
|
end = len(keysToDelete)
|
||||||
|
}
|
||||||
|
chunk := keysToDelete[i:end]
|
||||||
|
deletedCount, err := s.KeyProvider.RemoveKeys(groupID, chunk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
totalDeletedCount += deletedCount
|
||||||
}
|
}
|
||||||
|
|
||||||
ignoredCount := len(keysToDelete) - int(deletedCount)
|
ignoredCount := len(keysToDelete) - int(totalDeletedCount)
|
||||||
|
|
||||||
var totalInGroup int64
|
var totalInGroup int64
|
||||||
if err := s.DB.Model(&models.APIKey{}).Where("group_id = ?", groupID).Count(&totalInGroup).Error; err != nil {
|
if err := s.DB.Model(&models.APIKey{}).Where("group_id = ?", groupID).Count(&totalInGroup).Error; err != nil {
|
||||||
@@ -217,7 +258,7 @@ func (s *KeyService) DeleteMultipleKeys(groupID uint, keysText string) (*DeleteK
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &DeleteKeysResult{
|
return &DeleteKeysResult{
|
||||||
DeletedCount: int(deletedCount),
|
DeletedCount: int(totalDeletedCount),
|
||||||
IgnoredCount: ignoredCount,
|
IgnoredCount: ignoredCount,
|
||||||
TotalInGroup: totalInGroup,
|
TotalInGroup: totalInGroup,
|
||||||
}, nil
|
}, nil
|
||||||
@@ -237,3 +278,32 @@ func (s *KeyService) ListKeysInGroupQuery(groupID uint, statusFilter string, sea
|
|||||||
|
|
||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMultipleKeys handles a one-off validation test for multiple keys.
|
||||||
|
func (s *KeyService) TestMultipleKeys(ctx context.Context, group *models.Group, keysText string) ([]keypool.KeyTestResult, error) {
|
||||||
|
keysToTest := s.ParseKeysFromText(keysText)
|
||||||
|
if len(keysToTest) > maxRequestKeys {
|
||||||
|
return nil, fmt.Errorf("batch size exceeds the limit of %d keys, got %d", maxRequestKeys, len(keysToTest))
|
||||||
|
}
|
||||||
|
if len(keysToTest) == 0 {
|
||||||
|
return nil, fmt.Errorf("no valid keys found in the input text")
|
||||||
|
}
|
||||||
|
|
||||||
|
var allResults []keypool.KeyTestResult
|
||||||
|
for i := 0; i < len(keysToTest); i += chunkSize {
|
||||||
|
end := i + chunkSize
|
||||||
|
if end > len(keysToTest) {
|
||||||
|
end = len(keysToTest)
|
||||||
|
}
|
||||||
|
chunk := keysToTest[i:end]
|
||||||
|
results, err := s.KeyValidator.TestMultipleKeys(ctx, group, chunk)
|
||||||
|
if err != nil {
|
||||||
|
// If one chunk fails, we might want to stop or collect partial results.
|
||||||
|
// For now, let's stop and return the error.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
allResults = append(allResults, results...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allResults, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user