package service import ( "context" "errors" "messenger/internal/models" "messenger/internal/pkg/logger" "messenger/internal/pkg/validator" "messenger/internal/repository" ) type UserService struct { userRepo repository.UserRepository profileRepo repository.ProfileRepository } func NewUserService(userRepo repository.UserRepository, profileRepo repository.ProfileRepository) *UserService { return &UserService{ userRepo: userRepo, profileRepo: profileRepo, } } // GetProfile возвращает профиль пользователя func (s *UserService) GetProfile(ctx context.Context, userID int64) (*models.ProfileWithUser, error) { user, err := s.userRepo.FindByID(ctx, userID) if err != nil { logger.Error("Failed to get user", "error", err) return nil, errors.New("internal server error") } if user == nil { return nil, errors.New("user not found") } profile, err := s.profileRepo.FindByUserID(ctx, userID) if err != nil { logger.Error("Failed to get profile", "error", err) // Профиль может отсутствовать - не ошибка profile = &models.Profile{UserID: userID} } return &models.ProfileWithUser{ User: user.ToSafe(), DisplayName: profile.DisplayName, Bio: profile.Bio, AvatarURL: profile.AvatarURL, }, nil } // UpdateProfile обновляет профиль пользователя func (s *UserService) UpdateProfile(ctx context.Context, userID int64, displayName, bio *string) error { // Валидация if displayName != nil && !validator.ValidateDisplayName(*displayName) { return errors.New("display name too long (max 100 characters)") } if bio != nil && !validator.ValidateBio(*bio) { return errors.New("bio too long (max 500 characters)") } profile := &models.Profile{ UserID: userID, DisplayName: displayName, Bio: bio, } if err := s.profileRepo.Update(ctx, profile); err != nil { logger.Error("Failed to update profile", "error", err) return errors.New("failed to update profile") } logger.Info("Profile updated", "user_id", userID) return nil } // UpdateAvatar обновляет аватар пользователя func (s *UserService) UpdateAvatar(ctx context.Context, userID int64, avatarURL *string) error { if err := s.profileRepo.UpdateAvatar(ctx, userID, avatarURL); err != nil { logger.Error("Failed to update avatar", "error", err) return errors.New("failed to update avatar") } logger.Info("Avatar updated", "user_id", userID) return nil } // SearchUsers ищет пользователей по логину func (s *UserService) SearchUsers(ctx context.Context, query string, limit int) ([]*models.SafeUser, error) { if limit <= 0 || limit > 100 { limit = 20 } users, err := s.userRepo.SearchByLogin(ctx, query, limit) if err != nil { logger.Error("Failed to search users", "error", err) return nil, errors.New("internal server error") } safeUsers := make([]*models.SafeUser, len(users)) for i, user := range users { safeUsers[i] = user.ToSafe() } return safeUsers, nil } // GetUserByID возвращает безопасное представление пользователя func (s *UserService) GetUserByID(ctx context.Context, userID int64) (*models.SafeUser, error) { user, err := s.userRepo.FindByID(ctx, userID) if err != nil { return nil, err } if user == nil { return nil, errors.New("user not found") } return user.ToSafe(), nil } // GetUserByLogin возвращает пользователя по логину func (s *UserService) GetUserByLogin(ctx context.Context, login string) (*models.User, error) { return s.userRepo.FindByLogin(ctx, login) }