- 新增 RoutingCache 组件,使用 sync.Map 缓存 Provider 和 Model - 新增 StatsBuffer 组件,使用 sync.Map + atomic.Int64 缓冲统计数据 - 扩展 StatsRepository.BatchUpdate 支持批量增量更新 - 改造 RoutingService/StatsService/ProviderService/ModelService 集成缓存 - 更新 usage-statistics spec,新增 routing-cache 和 stats-buffer spec - 新增单元测试覆盖缓存命中/失效/并发场景
118 lines
3.0 KiB
Go
118 lines
3.0 KiB
Go
package service
|
|
|
|
import (
|
|
"github.com/google/uuid"
|
|
appErrors "nex/backend/pkg/errors"
|
|
|
|
"nex/backend/internal/domain"
|
|
"nex/backend/internal/repository"
|
|
)
|
|
|
|
type modelService struct {
|
|
modelRepo repository.ModelRepository
|
|
providerRepo repository.ProviderRepository
|
|
cache *RoutingCache
|
|
}
|
|
|
|
func NewModelService(modelRepo repository.ModelRepository, providerRepo repository.ProviderRepository, cache *RoutingCache) ModelService {
|
|
return &modelService{modelRepo: modelRepo, providerRepo: providerRepo, cache: cache}
|
|
}
|
|
|
|
func (s *modelService) Create(model *domain.Model) error {
|
|
if _, err := s.providerRepo.GetByID(model.ProviderID); err != nil {
|
|
return appErrors.ErrProviderNotFound
|
|
}
|
|
|
|
if err := s.checkDuplicateModelName(model.ProviderID, model.ModelName, ""); err != nil {
|
|
return err
|
|
}
|
|
|
|
model.ID = uuid.New().String()
|
|
model.Enabled = true
|
|
err := s.modelRepo.Create(model)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.cache.SetModel(model)
|
|
return nil
|
|
}
|
|
|
|
func (s *modelService) Get(id string) (*domain.Model, error) {
|
|
return s.modelRepo.GetByID(id)
|
|
}
|
|
|
|
func (s *modelService) List(providerID string) ([]domain.Model, error) {
|
|
return s.modelRepo.List(providerID)
|
|
}
|
|
|
|
func (s *modelService) ListEnabled() ([]domain.Model, error) {
|
|
return s.modelRepo.ListEnabled()
|
|
}
|
|
|
|
func (s *modelService) Update(id string, updates map[string]interface{}) error {
|
|
current, err := s.modelRepo.GetByID(id)
|
|
if err != nil {
|
|
return appErrors.ErrModelNotFound
|
|
}
|
|
|
|
if providerID, ok := updates["provider_id"].(string); ok {
|
|
if _, err := s.providerRepo.GetByID(providerID); err != nil {
|
|
return appErrors.ErrProviderNotFound
|
|
}
|
|
}
|
|
|
|
newProviderID := current.ProviderID
|
|
if v, ok := updates["provider_id"].(string); ok {
|
|
newProviderID = v
|
|
}
|
|
newModelName := current.ModelName
|
|
if v, ok := updates["model_name"].(string); ok {
|
|
newModelName = v
|
|
}
|
|
|
|
if newProviderID != current.ProviderID || newModelName != current.ModelName {
|
|
if err := s.checkDuplicateModelName(newProviderID, newModelName, id); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
err = s.modelRepo.Update(id, updates)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.cache.InvalidateModel(current.ProviderID, current.ModelName)
|
|
if newProviderID != current.ProviderID || newModelName != current.ModelName {
|
|
s.cache.InvalidateModel(newProviderID, newModelName)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *modelService) Delete(id string) error {
|
|
model, err := s.modelRepo.GetByID(id)
|
|
if err != nil {
|
|
return appErrors.ErrModelNotFound
|
|
}
|
|
|
|
err = s.modelRepo.Delete(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
s.cache.InvalidateModel(model.ProviderID, model.ModelName)
|
|
return nil
|
|
}
|
|
|
|
// checkDuplicateModelName 校验同一供应商下 model_name 是否重复
|
|
// excludeID 用于更新时排除自身
|
|
func (s *modelService) checkDuplicateModelName(providerID, modelName, excludeID string) error {
|
|
existing, err := s.modelRepo.FindByProviderAndModelName(providerID, modelName)
|
|
if err != nil {
|
|
return nil // 未找到,不重复
|
|
}
|
|
if excludeID != "" && existing.ID == excludeID {
|
|
return nil // 排除自身
|
|
}
|
|
return appErrors.ErrDuplicateModel
|
|
}
|