package cache import ( "context" "time" "tercul/internal/domain" platform_cache "tercul/internal/platform/cache" "gorm.io/gorm" ) type CachedAuthorRepository struct { inner domain.AuthorRepository opt Options } func NewCachedAuthorRepository(inner domain.AuthorRepository, c platform_cache.Cache, opt *Options) *CachedAuthorRepository { 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 &CachedAuthorRepository{inner: inner, opt: resolved} } func (r *CachedAuthorRepository) Create(ctx context.Context, entity *domain.Author) error { err := r.inner.Create(ctx, entity) if err == nil { invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) CreateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Author) error { err := r.inner.CreateInTx(ctx, tx, entity) if err == nil { invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) GetByID(ctx context.Context, id uint) (*domain.Author, error) { if r.opt.Enabled && r.opt.Cache != nil { var cached domain.Author key := r.opt.Keys.EntityKey("author", id) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return &cached, nil } } author, err := r.inner.GetByID(ctx, id) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil && author != nil { _ = r.opt.Cache.Set(ctx, r.opt.Keys.EntityKey("author", id), author, r.opt.EntityTTL) } return author, nil } func (r *CachedAuthorRepository) GetByIDWithOptions(ctx context.Context, id uint, options *domain.QueryOptions) (*domain.Author, error) { return r.inner.GetByIDWithOptions(ctx, id, options) } func (r *CachedAuthorRepository) Update(ctx context.Context, entity *domain.Author) error { err := r.inner.Update(ctx, entity) if err == nil { if r.opt.Cache != nil { _ = r.opt.Cache.Delete(ctx, r.opt.Keys.EntityKey("author", entity.ID)) } invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) UpdateInTx(ctx context.Context, tx *gorm.DB, entity *domain.Author) 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("author", entity.ID)) } invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) 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("author", id)) } invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) 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("author", id)) } invalidateEntityType(ctx, r.opt.Cache, "author") } return err } func (r *CachedAuthorRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Author], error) { if r.opt.Enabled && r.opt.Cache != nil { var cached domain.PaginatedResult[domain.Author] key := r.opt.Keys.ListKey("author", 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("author", page, pageSize), res, r.opt.ListTTL) } return res, nil } func (r *CachedAuthorRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Author, error) { return r.inner.ListWithOptions(ctx, options) } func (r *CachedAuthorRepository) ListAll(ctx context.Context) ([]domain.Author, error) { return r.inner.ListAll(ctx) } func (r *CachedAuthorRepository) Count(ctx context.Context) (int64, error) { return r.inner.Count(ctx) } func (r *CachedAuthorRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) { return r.inner.CountWithOptions(ctx, options) } func (r *CachedAuthorRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Author, error) { return r.inner.FindWithPreload(ctx, preloads, id) } func (r *CachedAuthorRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Author, error) { return r.inner.GetAllForSync(ctx, batchSize, offset) } func (r *CachedAuthorRepository) Exists(ctx context.Context, id uint) (bool, error) { return r.inner.Exists(ctx, id) } func (r *CachedAuthorRepository) BeginTx(ctx context.Context) (*gorm.DB, error) { return r.inner.BeginTx(ctx) } func (r *CachedAuthorRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error { return r.inner.WithTx(ctx, fn) } func (r *CachedAuthorRepository) FindByName(ctx context.Context, name string) (*domain.Author, error) { return r.inner.FindByName(ctx, name) } func (r *CachedAuthorRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error) { return r.inner.ListByWorkID(ctx, workID) } func (r *CachedAuthorRepository) ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error) { return r.inner.ListByBookID(ctx, bookID) } func (r *CachedAuthorRepository) ListByCountryID(ctx context.Context, countryID uint) ([]domain.Author, error) { return r.inner.ListByCountryID(ctx, countryID) } func (r *CachedAuthorRepository) GetWithTranslations(ctx context.Context, id uint) (*domain.Author, error) { if r.opt.Enabled && r.opt.Cache != nil { var cached domain.Author key := r.opt.Keys.QueryKey("author", "withTranslations", id) if err := r.opt.Cache.Get(ctx, key, &cached); err == nil { return &cached, nil } } author, err := r.inner.GetWithTranslations(ctx, id) if err != nil { return nil, err } if r.opt.Enabled && r.opt.Cache != nil && author != nil { _ = r.opt.Cache.Set(ctx, r.opt.Keys.QueryKey("author", "withTranslations", id), author, r.opt.EntityTTL) } return author, nil }