- 新增 backend/.golangci.yml 配置 12 个 linter(forbidigo、errorlint、errcheck、staticcheck、revive、gocritic、gosec、bodyclose、noctx、nilerr、goimports、gocyclo) - 新增 lefthook.yml 配置 pre-commit hook 自动运行 lint - 修复存量代码违规:errors.Is/As 替换、zap.Error 替换、import 排序、errcheck 修复 - 更新 README 补充编码规范说明 - 归档 backend-code-lint 变更
150 lines
3.2 KiB
Go
150 lines
3.2 KiB
Go
package service
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
|
|
"nex/backend/internal/domain"
|
|
"nex/backend/internal/repository"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
pkglogger "nex/backend/pkg/logger"
|
|
)
|
|
|
|
type RoutingCache struct {
|
|
providers sync.Map
|
|
models sync.Map
|
|
|
|
modelRepo repository.ModelRepository
|
|
providerRepo repository.ProviderRepository
|
|
logger *zap.Logger
|
|
}
|
|
|
|
func NewRoutingCache(
|
|
modelRepo repository.ModelRepository,
|
|
providerRepo repository.ProviderRepository,
|
|
logger *zap.Logger,
|
|
) *RoutingCache {
|
|
return &RoutingCache{
|
|
modelRepo: modelRepo,
|
|
providerRepo: providerRepo,
|
|
logger: pkglogger.WithModule(logger, "service.routing_cache"),
|
|
}
|
|
}
|
|
|
|
func (c *RoutingCache) GetProvider(id string) (*domain.Provider, error) {
|
|
if v, ok := c.providers.Load(id); ok {
|
|
if provider, ok := v.(*domain.Provider); ok {
|
|
return provider, nil
|
|
}
|
|
}
|
|
|
|
provider, err := c.providerRepo.GetByID(id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if v, ok := c.providers.Load(id); ok {
|
|
if provider, ok := v.(*domain.Provider); ok {
|
|
return provider, nil
|
|
}
|
|
}
|
|
|
|
c.providers.Store(id, provider)
|
|
return provider, nil
|
|
}
|
|
|
|
func (c *RoutingCache) GetModel(providerID, modelName string) (*domain.Model, error) {
|
|
key := providerID + "/" + modelName
|
|
|
|
if v, ok := c.models.Load(key); ok {
|
|
if model, ok := v.(*domain.Model); ok {
|
|
return model, nil
|
|
}
|
|
}
|
|
|
|
model, err := c.modelRepo.FindByProviderAndModelName(providerID, modelName)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if v, ok := c.models.Load(key); ok {
|
|
if model, ok := v.(*domain.Model); ok {
|
|
return model, nil
|
|
}
|
|
}
|
|
|
|
c.models.Store(key, model)
|
|
return model, nil
|
|
}
|
|
|
|
func (c *RoutingCache) SetProvider(provider *domain.Provider) {
|
|
c.providers.Store(provider.ID, provider)
|
|
}
|
|
|
|
func (c *RoutingCache) SetModel(model *domain.Model) {
|
|
key := model.ProviderID + "/" + model.ModelName
|
|
c.models.Store(key, model)
|
|
}
|
|
|
|
func (c *RoutingCache) InvalidateProvider(id string) {
|
|
c.providers.Delete(id)
|
|
c.invalidateModelsByProvider(id)
|
|
c.logger.Debug("Provider 缓存失效", zap.String("provider_id", id))
|
|
}
|
|
|
|
func (c *RoutingCache) InvalidateModel(providerID, modelName string) {
|
|
key := providerID + "/" + modelName
|
|
c.models.Delete(key)
|
|
c.logger.Debug("Model 缓存失效",
|
|
zap.String("provider_id", providerID),
|
|
zap.String("model_name", modelName))
|
|
}
|
|
|
|
func (c *RoutingCache) invalidateModelsByProvider(providerID string) {
|
|
prefix := providerID + "/"
|
|
count := 0
|
|
c.models.Range(func(key, value interface{}) bool {
|
|
keyStr, ok := key.(string)
|
|
if !ok {
|
|
return true
|
|
}
|
|
|
|
if strings.HasPrefix(keyStr, prefix) {
|
|
c.models.Delete(key)
|
|
count++
|
|
}
|
|
return true
|
|
})
|
|
if count > 0 {
|
|
c.logger.Debug("清除 Provider 相关 Model 缓存",
|
|
zap.String("provider_id", providerID),
|
|
zap.Int("count", count))
|
|
}
|
|
}
|
|
|
|
func (c *RoutingCache) Preload() error {
|
|
providers, err := c.providerRepo.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := range providers {
|
|
c.providers.Store(providers[i].ID, &providers[i])
|
|
}
|
|
|
|
models, err := c.modelRepo.List("")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for i := range models {
|
|
key := models[i].ProviderID + "/" + models[i].ModelName
|
|
c.models.Store(key, &models[i])
|
|
}
|
|
|
|
c.logger.Info("缓存预热完成",
|
|
zap.Int("providers", len(providers)),
|
|
zap.Int("models", len(models)))
|
|
return nil
|
|
}
|