package persistence import ( domain "RenewCMS/internal/domain/image" entity "RenewCMS/internal/infrastructure/persistence/models" "time" "gorm.io/gorm" "gorm.io/gorm/clause" ) type VariantUsageRepository struct { db *gorm.DB } var _ domain.VariantUsageRepository = &VariantUsageRepository{} func NewVariantUsageRepository(db *gorm.DB) *VariantUsageRepository { return &VariantUsageRepository{db} } func (v *VariantUsageRepository) Record(batch domain.VariantUsageBatch) error { return v.db.Transaction(func(tx *gorm.DB) error { for _, hit := range batch.Hits { if err := recordHit(tx, hit); err != nil { return err } } for _, rejection := range batch.Rejections { if err := recordRejections(tx, rejection); err != nil { return err } } if batch.PruneBefore.IsZero() { return nil } return tx.Where("day < ?", batch.PruneBefore.UTC().Format(domain.RejectionDayLayout)).Delete(&entity.ImageVariantRejectionDay{}).Error }) } func recordHit(tx *gorm.DB, hit domain.VariantHit) error { at := hit.At.UTC() query := tx.Model(&entity.ImageVariantFile{}) query = query.Where("variant_name = ? AND source_name = ?", hit.VariantName, hit.SourceName) query = query.Where("last_requested_at IS NULL OR last_requested_at < ?", at) return query.Update("last_requested_at", at).Error } func recordRejections(tx *gorm.DB, rejection domain.RejectionCount) error { row := entity.ImageVariantRejectionDay{Day: rejection.Day.UTC().Format(domain.RejectionDayLayout), Count: rejection.Count} return tx.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "day"}}, DoUpdates: clause.Assignments(map[string]any{"count": gorm.Expr("? + ?", clause.Column{Table: clause.CurrentTable, Name: "count"}, rejection.Count)}), }).Create(&row).Error } func (v *VariantUsageRepository) RejectedSince(day time.Time) (int, error) { var total int query := v.db.Model(&entity.ImageVariantRejectionDay{}) query = query.Select("COALESCE(SUM(count), 0)") query = query.Where("day >= ?", day.UTC().Format(domain.RejectionDayLayout)) err := query.Scan(&total).Error return total, err }