package repository import ( "context" "errors" "time" "github.com/jackc/pgconn" commonrepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" "gorm.io/gorm" "gorm.io/gorm/clause" ) type UserRepository interface { commonrepo.BaseRepository[entity.User] GetByIdUser(ctx context.Context, idUser int64, modifier func(*gorm.DB) *gorm.DB) (*entity.User, error) UpsertByIdUser(ctx context.Context, user *entity.User) error SoftDeleteByIdUser(ctx context.Context, idUser int64) error } type UserRepositoryImpl struct { *commonrepo.BaseRepositoryImpl[entity.User] } func NewUserRepository(db *gorm.DB) UserRepository { return &UserRepositoryImpl{ BaseRepositoryImpl: commonrepo.NewBaseRepository[entity.User](db), } } func (r *UserRepositoryImpl) GetByIdUser( ctx context.Context, idUser int64, modifier func(*gorm.DB) *gorm.DB, ) (*entity.User, error) { return r.BaseRepositoryImpl.First(ctx, func(db *gorm.DB) *gorm.DB { return db.Where("id_user = ?", idUser) }) } func (r *UserRepositoryImpl) UpsertByIdUser(ctx context.Context, user *entity.User) error { if user == nil { return gorm.ErrInvalidData } return r.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error { now := time.Now() user.DeletedAt = gorm.DeletedAt{} user.UpdatedAt = now err := tx.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "id_user"}}, UpdateAll: true, }).Omit("id", "created_at").Create(user).Error if err == nil { return nil } if !isUniqueViolation(err, "users_email_unique") { return err } var existing entity.User lockQuery := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("email = ?", user.Email) if err := lockQuery.First(&existing).Error; err != nil { return err } user.Id = existing.Id updates := map[string]any{ "id_user": user.IdUser, "email": user.Email, "name": user.Name, "updated_at": now, "deleted_at": gorm.DeletedAt{}, } if err := tx.Model(&entity.User{}).Where("id = ?", existing.Id).Updates(updates).Error; err != nil { return err } return nil }) } func (r *UserRepositoryImpl) SoftDeleteByIdUser(ctx context.Context, idUser int64) error { query := r.DB().WithContext(ctx).Where("id_user = ?", idUser) result := query.Delete(&entity.User{}) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return gorm.ErrRecordNotFound } return nil } func isUniqueViolation(err error, constraint string) bool { var pgErr *pgconn.PgError if !errors.As(err, &pgErr) { return false } if pgErr.Code != "23505" { return false } if constraint == "" { return true } return pgErr.ConstraintName == constraint }