- 新增 RoutingCache 组件,使用 sync.Map 缓存 Provider 和 Model - 新增 StatsBuffer 组件,使用 sync.Map + atomic.Int64 缓冲统计数据 - 扩展 StatsRepository.BatchUpdate 支持批量增量更新 - 改造 RoutingService/StatsService/ProviderService/ModelService 集成缓存 - 更新 usage-statistics spec,新增 routing-cache 和 stats-buffer spec - 新增单元测试覆盖缓存命中/失效/并发场景
102 lines
2.5 KiB
Go
102 lines
2.5 KiB
Go
package repository
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
|
|
"nex/backend/internal/config"
|
|
"nex/backend/internal/domain"
|
|
)
|
|
|
|
type statsRepository struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
func NewStatsRepository(db *gorm.DB) StatsRepository {
|
|
return &statsRepository{db: db}
|
|
}
|
|
|
|
func (r *statsRepository) Record(providerID, modelName string) error {
|
|
today := time.Now().Format("2006-01-02")
|
|
todayTime, _ := time.Parse("2006-01-02", today)
|
|
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var stats config.UsageStats
|
|
err := tx.Where("provider_id = ? AND model_name = ? AND date = ?",
|
|
providerID, modelName, todayTime).First(&stats).Error
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
stats = config.UsageStats{
|
|
ProviderID: providerID,
|
|
ModelName: modelName,
|
|
RequestCount: 1,
|
|
Date: todayTime,
|
|
}
|
|
return tx.Create(&stats).Error
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
return tx.Model(&stats).Update("request_count", gorm.Expr("request_count + 1")).Error
|
|
})
|
|
}
|
|
|
|
func (r *statsRepository) BatchUpdate(providerID, modelName string, date time.Time, delta int) error {
|
|
return r.db.Transaction(func(tx *gorm.DB) error {
|
|
var stats config.UsageStats
|
|
err := tx.Where("provider_id = ? AND model_name = ? AND date = ?",
|
|
providerID, modelName, date).First(&stats).Error
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return tx.Create(&config.UsageStats{
|
|
ProviderID: providerID,
|
|
ModelName: modelName,
|
|
RequestCount: delta,
|
|
Date: date,
|
|
}).Error
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
return tx.Model(&stats).
|
|
Update("request_count", gorm.Expr("request_count + ?", delta)).Error
|
|
})
|
|
}
|
|
|
|
func (r *statsRepository) Query(providerID, modelName string, startDate, endDate *time.Time) ([]domain.UsageStats, error) {
|
|
var stats []config.UsageStats
|
|
query := r.db.Model(&config.UsageStats{})
|
|
|
|
if providerID != "" {
|
|
query = query.Where("provider_id = ?", providerID)
|
|
}
|
|
if modelName != "" {
|
|
query = query.Where("model_name = ?", modelName)
|
|
}
|
|
if startDate != nil {
|
|
query = query.Where("date >= ?", startDate)
|
|
}
|
|
if endDate != nil {
|
|
query = query.Where("date <= ?", endDate)
|
|
}
|
|
|
|
err := query.Order("date DESC").Find(&stats).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make([]domain.UsageStats, len(stats))
|
|
for i := range stats {
|
|
result[i] = domain.UsageStats{
|
|
ID: stats[i].ID,
|
|
ProviderID: stats[i].ProviderID,
|
|
ModelName: stats[i].ModelName,
|
|
RequestCount: stats[i].RequestCount,
|
|
Date: stats[i].Date,
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|