package repository import ( "context" "errors" "strings" "gorm.io/gorm" "github.com/dhao2001/mygo/internal/model" ) // isDuplicateKeyError checks if the error indicates a unique constraint violation. func isDuplicateKeyError(err error) bool { return errors.Is(err, gorm.ErrDuplicatedKey) || strings.Contains(strings.ToLower(err.Error()), "unique constraint failed") } // UserRepository provides access to user records. type UserRepository interface { Create(ctx context.Context, user *model.User) error FindByID(ctx context.Context, id string) (*model.User, error) FindByEmail(ctx context.Context, email string) (*model.User, error) FindByUsername(ctx context.Context, username string) (*model.User, error) Update(ctx context.Context, user *model.User) error Delete(ctx context.Context, id string) error List(ctx context.Context, offset, limit int) ([]model.User, int64, error) } type userRepository struct { db *gorm.DB } // NewUserRepository creates a UserRepository backed by GORM. func NewUserRepository(db *gorm.DB) UserRepository { return &userRepository{db: db} } func (r *userRepository) Create(ctx context.Context, user *model.User) error { result := r.db.WithContext(ctx).Create(user) if result.Error != nil { if isDuplicateKeyError(result.Error) { return model.ErrDuplicate } return result.Error } return nil } func (r *userRepository) FindByID(ctx context.Context, id string) (*model.User, error) { var user model.User result := r.db.WithContext(ctx).First(&user, "id = ?", id) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, model.ErrNotFound } if result.Error != nil { return nil, result.Error } return &user, nil } func (r *userRepository) FindByEmail(ctx context.Context, email string) (*model.User, error) { var user model.User result := r.db.WithContext(ctx).First(&user, "email = ?", email) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, model.ErrNotFound } if result.Error != nil { return nil, result.Error } return &user, nil } func (r *userRepository) FindByUsername(ctx context.Context, username string) (*model.User, error) { var user model.User result := r.db.WithContext(ctx).First(&user, "username = ?", username) if errors.Is(result.Error, gorm.ErrRecordNotFound) { return nil, model.ErrNotFound } if result.Error != nil { return nil, result.Error } return &user, nil } func (r *userRepository) Update(ctx context.Context, user *model.User) error { result := r.db.WithContext(ctx).Save(user) if result.Error != nil { if isDuplicateKeyError(result.Error) { return model.ErrDuplicate } return result.Error } return nil } func (r *userRepository) Delete(ctx context.Context, id string) error { result := r.db.WithContext(ctx).Delete(&model.User{}, "id = ?", id) if result.Error != nil { return result.Error } return nil } func (r *userRepository) List(ctx context.Context, offset, limit int) ([]model.User, int64, error) { var users []model.User var total int64 if err := r.db.WithContext(ctx).Model(&model.User{}).Count(&total).Error; err != nil { return nil, 0, err } result := r.db.WithContext(ctx).Offset(offset).Limit(limit).Find(&users) if result.Error != nil { return nil, 0, result.Error } return users, total, nil }