package repository import ( "context" "errors" "gorm.io/gorm" ) var ( ErrNotFound = errors.New("record not found") ErrDuplicateKey = errors.New("duplicate key violation") ErrInvalidInput = errors.New("invalid input") ErrVersionConflict = errors.New("version conflict - record was modified") ) // BaseRepository provides common CRUD operations type BaseRepository[T any] struct { db *gorm.DB } // NewBaseRepository creates a new base repository func NewBaseRepository[T any](db *gorm.DB) *BaseRepository[T] { return &BaseRepository[T]{db: db} } // Create inserts a new record func (r *BaseRepository[T]) Create(ctx context.Context, entity *T) error { result := r.db.WithContext(ctx).Create(entity) if result.Error != nil { return handleError(result.Error) } return nil } // GetByID retrieves a record by ID func (r *BaseRepository[T]) GetByID(ctx context.Context, id string) (*T, error) { var entity T result := r.db.WithContext(ctx).First(&entity, "id = ?", id) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrNotFound } return nil, result.Error } return &entity, nil } // GetAll retrieves all records func (r *BaseRepository[T]) GetAll(ctx context.Context) ([]*T, error) { var entities []*T result := r.db.WithContext(ctx).Find(&entities) if result.Error != nil { return nil, result.Error } return entities, nil } // Update updates an existing record func (r *BaseRepository[T]) Update(ctx context.Context, entity *T) error { result := r.db.WithContext(ctx).Save(entity) if result.Error != nil { return handleError(result.Error) } if result.RowsAffected == 0 { return ErrNotFound } return nil } // Delete removes a record by ID func (r *BaseRepository[T]) Delete(ctx context.Context, id string) error { var entity T result := r.db.WithContext(ctx).Delete(&entity, "id = ?", id) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return ErrNotFound } return nil } // Count returns the total number of records func (r *BaseRepository[T]) Count() (int64, error) { var count int64 result := r.db.Model(new(T)).Count(&count) if result.Error != nil { return 0, result.Error } return count, nil } // Exists checks if a record with the given ID exists func (r *BaseRepository[T]) Exists(id string) (bool, error) { var count int64 result := r.db.Model(new(T)).Where("id = ?", id).Count(&count) if result.Error != nil { return false, result.Error } return count > 0, nil } // FindWhere retrieves records matching the given conditions func (r *BaseRepository[T]) FindWhere(query interface{}, args ...interface{}) ([]*T, error) { return r.FindWhereWithContext(context.Background(), query, args...) } // FindWhereWithContext retrieves records matching the given conditions with context func (r *BaseRepository[T]) FindWhereWithContext(ctx context.Context, query interface{}, args ...interface{}) ([]*T, error) { var entities []*T result := r.db.WithContext(ctx).Where(query, args...).Find(&entities) if result.Error != nil { return nil, result.Error } return entities, nil } // FindOneWhere retrieves a single record matching the given conditions func (r *BaseRepository[T]) FindOneWhere(query interface{}, args ...interface{}) (*T, error) { return r.FindOneWhereWithContext(context.Background(), query, args...) } // FindOneWhereWithContext retrieves a single record matching the given conditions with context func (r *BaseRepository[T]) FindOneWhereWithContext(ctx context.Context, query interface{}, args ...interface{}) (*T, error) { var entity T result := r.db.WithContext(ctx).Where(query, args...).First(&entity) if result.Error != nil { if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, ErrNotFound } return nil, result.Error } return &entity, nil } // Transaction executes a function within a database transaction func (r *BaseRepository[T]) Transaction(fn func(*gorm.DB) error) error { return r.db.Transaction(fn) } // DB returns the underlying GORM database instance func (r *BaseRepository[T]) DB() *gorm.DB { return r.db } // handleError converts GORM errors to repository errors func handleError(err error) error { if err == nil { return nil } if errors.Is(err, gorm.ErrRecordNotFound) { return ErrNotFound } // Check for PostgreSQL duplicate key error if errors.Is(err, gorm.ErrDuplicatedKey) { return ErrDuplicateKey } return err }