package cache import ( "context" "time" "tercul/internal/domain" platform_cache "tercul/internal/platform/cache" "gorm.io/gorm" ) type CachedTranslationRepository struct { inner domain.TranslationRepository opt Options } func NewCachedTranslationRepository(inner domain.TranslationRepository, c platform_cache.Cache, opt *Options) *CachedTranslationRepository { resolved := DefaultOptions(c) if opt != nil { resolved = *opt if resolved.Cache == nil { resolved.Cache = c } if resolved.Keys == nil { resolved.Keys = platform_cache.NewDefaultKeyGenerator("tercul:repo:") } if resolved.EntityTTL == 0 { resolved.EntityTTL = 1 * time.Hour } if resolved.ListTTL == 0 { resolved.ListTTL = 5 * time.Minute } } return &CachedTranslationRepository{inner: inner, opt: resolved} } func (r *CachedTranslationRepository) Create(ctx context.Context, entity *domain.Translation) error { err := r.inner.Create(ctx, entity) if err == nil { invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Translation) error { err := r.inner.CreateInTx(ctx, tx, entity) if err == nil { invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) GetByID(ctx context.Context, id uint) (*domain.Translation, error) { if r.opt.Enabled && r.opt.Cache != nil { var cached domain.Translation key := r.opt.Keys.EntityKey("translation", id) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return &cached, nil } } tr, err := r.inner.GetByID(ctx, id) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil && tr != nil { _ = r.opt.Cache.Set(ctx, r.opt.Keys.EntityKey("translation", id), tr, r.opt.EntityTTL) } return tr, nil } func (r *CachedTranslationRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Translation, error) { return r.inner.GetByIDWithOptions(ctx, id, options) } func (r *CachedTranslationRepository) Update(ctx context.Context, entity *domain.Translation) error { err := r.inner.Update(ctx, entity) if err == nil { if r.opt.Cache != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("translation", entity.ID)) } invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Translation) error { err := r.inner.UpdateInTx(ctx, tx, entity) if err == nil { if r.opt.Cache != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("translation", entity.ID)) } invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) Delete(ctx context.Context, id uint) error { err := r.inner.Delete(ctx, id) if err == nil { if r.opt.Cache != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("translation", id)) } invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uint) error { err := r.inner.DeleteInTx(ctx, tx, id) if err == nil { if r.opt.Cache != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("translation", id)) } invalidateEntityType(ctx, r.opt.Cache, "translation") } return err } func (r *CachedTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) { if r.opt.Enabled && r.opt.Cache != nil { var cached domain.PaginatedResult[domain.Translation] key := r.opt.Keys.ListKey("translation", page, pageSize) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return &cached, nil } } res, err := r.inner.List(ctx, page, pageSize) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil && res != nil { _ = r.opt.Cache.Set(ctx, r.opt.Keys.ListKey("translation", page, pageSize), res, r.opt.ListTTL) } return res, nil } func (r *CachedTranslationRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Translation, error) { return r.inner.ListWithOptions(ctx, options) } func (r *CachedTranslationRepository) ListAll(ctx context.Context) ([]domain.Translation, error) { return r.inner.ListAll(ctx) } func (r *CachedTranslationRepository) Count(ctx context.Context) (int64, error) { return r.inner.Count(ctx) } func (r *CachedTranslationRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) { return r.inner.CountWithOptions(ctx, options) } func (r *CachedTranslationRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Translation, error) { return r.inner.FindWithPreload(ctx, preloads, id) } func (r *CachedTranslationRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Translation, error) { return r.inner.GetAllForSync(ctx, batchSize, offset) } func (r *CachedTranslationRepository) Exists(ctx context.Context, id uint) (bool, error) { return r.inner.Exists(ctx, id) } func (r *CachedTranslationRepository) BeginTx(ctx context.Context) (*gorm.DB, error) { return r.inner.BeginTx(ctx) } func (r *CachedTranslationRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error { return r.inner.WithTx(ctx, fn) } func (r *CachedTranslationRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error) { if r.opt.Enabled && r.opt.Cache != nil { var cached []domain.Translation key := r.opt.Keys.QueryKey("translation", "byWork", workID) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return cached, nil } } res, err := r.inner.ListByWorkID(ctx, workID) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil { _ = r.opt.Cache.Set(ctx, r.opt.Keys.QueryKey("translation", "byWork", workID), res, r.opt.ListTTL) } return res, nil } func (r *CachedTranslationRepository) ListByWorkIDPaginated(ctx context.Context, workID uint, language *string, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) { lang := "" if language != nil { lang = *language } if r.opt.Enabled && r.opt.Cache != nil { var cached domain.PaginatedResult[domain.Translation] key := r.opt.Keys.QueryKey("translation", "byWorkPaged", workID, lang, page, pageSize) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return &cached, nil } } res, err := r.inner.ListByWorkIDPaginated(ctx, workID, language, page, pageSize) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil && res != nil { key := r.opt.Keys.QueryKey("translation", "byWorkPaged", workID, lang, page, pageSize) _ = r.opt.Cache.Set(ctx, key, res, r.opt.ListTTL) } return res, nil } func (r *CachedTranslationRepository) ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error) { return r.inner.ListByEntity(ctx, entityType, entityID) } func (r *CachedTranslationRepository) ListByTranslatorID(ctx context.Context, translatorID uint) ([]domain.Translation, error) { return r.inner.ListByTranslatorID(ctx, translatorID) } func (r *CachedTranslationRepository) ListByStatus(ctx context.Context, status domain.TranslationStatus) ([]domain.Translation, error) { return r.inner.ListByStatus(ctx, status) } func (r *CachedTranslationRepository) Upsert(ctx context.Context, translation *domain.Translation) error { err := r.inner.Upsert(ctx, translation) if err == nil { if r.opt.Cache != nil && translation != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("translation", translation.ID)) } invalidateEntityType(ctx, r.opt.Cache, "translation") } return err }