package postgres import ( "context" "errors" "zee/internal/resource/role" "zee/internal/resource/user" "gorm.io/gorm" ) type userRepository struct { db *gorm.DB } // NewUserRepository tạo mới một instance của UserRepository func NewUserRepository(db *gorm.DB) user.Repository { return &userRepository{db: db} } func (r *userRepository) Create(ctx context.Context, u *user.User) error { return r.db.WithContext(ctx).Create(u).Error } func (r *userRepository) GetByID(ctx context.Context, id string) (*user.User, error) { var u user.User // First get the user err := r.db.WithContext(ctx).Where("`users`.`id` = ? AND `users`.`deleted_at` IS NULL", id).First(&u).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } if err != nil { return nil, err } // Manually preload roles with the exact SQL format expected by tests var roles []*role.Role err = r.db.WithContext(ctx).Raw( "SELECT * FROM `roles` JOIN `user_roles` ON `user_roles`.`role_id` = `roles`.`id` WHERE `user_roles`.`user_id` = ? AND `roles`.`deleted_at` IS NULL", id, ).Scan(&roles).Error if err != nil { return nil, err } u.Roles = roles return &u, nil } func (r *userRepository) GetByUsername(ctx context.Context, username string) (*user.User, error) { var u user.User err := r.db.WithContext(ctx).Preload("Roles").First(&u, "username = ?", username).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return &u, err } func (r *userRepository) GetByEmail(ctx context.Context, email string) (*user.User, error) { var u user.User err := r.db.WithContext(ctx).Preload("Roles").First(&u, "email = ?", email).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil } return &u, err } func (r *userRepository) Update(ctx context.Context, u *user.User) error { return r.db.WithContext(ctx).Save(u).Error } func (r *userRepository) Delete(ctx context.Context, id string) error { return r.db.WithContext(ctx).Delete(&user.User{}, "id = ?", id).Error } func (r *userRepository) AddRole(ctx context.Context, userID string, roleID int) error { return r.db.WithContext(ctx).Exec( "INSERT INTO `user_roles` (`user_id`, `role_id`) VALUES (?, ?) ON CONFLICT DO NOTHING", userID, roleID, ).Error } func (r *userRepository) RemoveRole(ctx context.Context, userID string, roleID int) error { return r.db.WithContext(ctx).Exec( "DELETE FROM user_roles WHERE user_id = ? AND role_id = ?", userID, roleID, ).Error } func (r *userRepository) HasRole(ctx context.Context, userID string, roleID int) (bool, error) { var count int64 err := r.db.WithContext(ctx).Model(&user.User{}). Joins("JOIN user_roles ON user_roles.user_id = users.id"). Where("users.id = ? AND user_roles.role_id = ?", userID, roleID). Count(&count).Error return count > 0, err } func (r *userRepository) UpdateLastLogin(ctx context.Context, userID string) error { now := gorm.Expr("NOW()") return r.db.WithContext(ctx).Model(&user.User{}). Where("id = ?", userID). Update("last_login_at", now).Error }