125 lines
3.6 KiB
Go
125 lines
3.6 KiB
Go
|
|
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)
|
||
|
|
}
|