Files
RenewCMS/internal/infrastructure/persistence/repositories/imageVariantFileRepository.go
T

185 lines
5.9 KiB
Go

package persistence
import (
domain "RenewCMS/internal/domain/image"
"RenewCMS/internal/infrastructure/persistence/mappers"
entity "RenewCMS/internal/infrastructure/persistence/models"
"errors"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type VariantFileRepository struct {
db *gorm.DB
}
var _ domain.VariantFileRepository = &VariantFileRepository{}
var _ domain.VariantCacheRepository = &VariantFileRepository{}
func NewVariantFileRepository(db *gorm.DB) *VariantFileRepository {
return &VariantFileRepository{db}
}
func (v *VariantFileRepository) Find(variantName string, sourceName string) (domain.VariantFile, bool, error) {
var model entity.ImageVariantFile
err := v.db.Where("variant_name = ? AND source_name = ?", variantName, sourceName).First(&model).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return domain.VariantFile{}, false, nil
}
if err != nil {
return domain.VariantFile{}, false, err
}
return mappers.VariantFileToDomain(model), true, nil
}
func (v *VariantFileRepository) Save(file domain.VariantFile) (domain.VariantFile, error) {
model := mappers.VariantFileToModel(file)
model.ID = 0
upsert := clause.OnConflict{
Columns: []clause.Column{{Name: "variant_name"}, {Name: "source_name"}},
DoUpdates: clause.AssignmentColumns([]string{"cached_name", "content_type", "size", "fingerprint"}),
}
query := v.db.Clauses(upsert)
err := query.Create(&model).Error
if err != nil {
return domain.VariantFile{}, err
}
saved, _, err := v.Find(file.VariantName, file.SourceName)
return saved, err
}
func (v *VariantFileRepository) ListByVariant(variantName string) ([]domain.VariantFile, error) {
return v.list("variant_name = ?", variantName)
}
func (v *VariantFileRepository) ListBySource(sourceName string) ([]domain.VariantFile, error) {
return v.list("source_name = ?", sourceName)
}
func (v *VariantFileRepository) list(condition string, value string) ([]domain.VariantFile, error) {
var found []entity.ImageVariantFile
if err := v.db.Where(condition, value).Order("id asc").Find(&found).Error; err != nil {
return nil, err
}
files := make([]domain.VariantFile, 0, len(found))
for _, model := range found {
files = append(files, mappers.VariantFileToDomain(model))
}
return files, nil
}
func (v *VariantFileRepository) Delete(id uint32) error {
return v.db.Delete(&entity.ImageVariantFile{}, id).Error
}
func (v *VariantFileRepository) TotalsBySource() (map[string]domain.VariantFileTotals, error) {
var rows []struct {
SourceName string
Files int
Bytes int64
}
query := v.db.Model(&entity.ImageVariantFile{})
query = query.Select("source_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
query = query.Group("source_name")
err := query.Scan(&rows).Error
if err != nil {
return nil, err
}
totals := make(map[string]domain.VariantFileTotals, len(rows))
for _, row := range rows {
totals[row.SourceName] = domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}
}
return totals, nil
}
const (
unusedCondition = "variant_name IN ? AND COALESCE(last_requested_at, created_at) < ?"
topSourcesOrder = "bytes DESC, source_name ASC"
)
func (v *VariantFileRepository) UnusedTotals(filter domain.UnusedFilter) (domain.VariantFileTotals, error) {
if len(filter.VariantNames) == 0 {
return domain.VariantFileTotals{}, nil
}
var row struct {
Files int
Bytes int64
}
query := v.db.Model(&entity.ImageVariantFile{})
query = query.Select("COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
query = query.Where(unusedCondition, filter.VariantNames, filter.Cutoff.UTC())
err := query.Scan(&row).Error
if err != nil {
return domain.VariantFileTotals{}, err
}
return domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}, nil
}
func (v *VariantFileRepository) TopSources(limit int) ([]domain.SourceTotals, error) {
var rows []struct {
SourceName string
Files int
Bytes int64
}
query := v.db.Model(&entity.ImageVariantFile{})
query = query.Select("source_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
query = query.Group("source_name")
query = query.Order(topSourcesOrder)
query = query.Limit(limit)
err := query.Scan(&rows).Error
if err != nil {
return nil, err
}
sources := make([]domain.SourceTotals, 0, len(rows))
for _, row := range rows {
sources = append(sources, domain.SourceTotals{SourceName: row.SourceName, Files: row.Files, Bytes: row.Bytes})
}
return sources, nil
}
func (v *VariantFileRepository) ListPage(filter domain.VariantFileFilter, afterID uint32, limit int) ([]domain.VariantFile, error) {
statement := v.db.Where("id > ?", afterID)
if filter.VariantName != "" {
statement = statement.Where("variant_name = ?", filter.VariantName)
}
if filter.SourceName != "" {
statement = statement.Where("source_name = ?", filter.SourceName)
}
if filter.Unused != nil {
if len(filter.Unused.VariantNames) == 0 {
return []domain.VariantFile{}, nil
}
statement = statement.Where(unusedCondition, filter.Unused.VariantNames, filter.Unused.Cutoff.UTC())
}
var found []entity.ImageVariantFile
if err := statement.Order("id asc").Limit(limit).Find(&found).Error; err != nil {
return nil, err
}
files := make([]domain.VariantFile, 0, len(found))
for _, model := range found {
files = append(files, mappers.VariantFileToDomain(model))
}
return files, nil
}
func (v *VariantFileRepository) Totals() (map[string]domain.VariantFileTotals, error) {
var rows []struct {
VariantName string
Files int
Bytes int64
}
query := v.db.Model(&entity.ImageVariantFile{})
query = query.Select("variant_name, COUNT(*) AS files, COALESCE(SUM(size), 0) AS bytes")
query = query.Group("variant_name")
err := query.Scan(&rows).Error
if err != nil {
return nil, err
}
totals := make(map[string]domain.VariantFileTotals, len(rows))
for _, row := range rows {
totals[row.VariantName] = domain.VariantFileTotals{Files: row.Files, Bytes: row.Bytes}
}
return totals, nil
}