turash/bugulma/backend/internal/repository/base_repository.go
Damir Mukimov 000eab4740
Major repository reorganization and missing backend endpoints implementation
Repository Structure:
- Move files from cluttered root directory into organized structure
- Create archive/ for archived data and scraper results
- Create bugulma/ for the complete application (frontend + backend)
- Create data/ for sample datasets and reference materials
- Create docs/ for comprehensive documentation structure
- Create scripts/ for utility scripts and API tools

Backend Implementation:
- Implement 3 missing backend endpoints identified in gap analysis:
  * GET /api/v1/organizations/{id}/matching/direct - Direct symbiosis matches
  * GET /api/v1/users/me/organizations - User organizations
  * POST /api/v1/proposals/{id}/status - Update proposal status
- Add complete proposal domain model, repository, and service layers
- Create database migration for proposals table
- Fix CLI server command registration issue

API Documentation:
- Add comprehensive proposals.md API documentation
- Update README.md with Users and Proposals API sections
- Document all request/response formats, error codes, and business rules

Code Quality:
- Follow existing Go backend architecture patterns
- Add proper error handling and validation
- Match frontend expected response schemas
- Maintain clean separation of concerns (handler -> service -> repository)
2025-11-25 06:01:16 +01:00

164 lines
4.4 KiB
Go

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
}