Files
efir-api-server/internal/service/user_service.go

125 lines
3.6 KiB
Go
Raw Normal View History

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)
}