// service/review_service.go package service import ( "api_bb/internal/models" "api_bb/internal/repository" "api_bb/pkg/logger" "errors" "go.uber.org/zap" ) type ReviewService interface { CreateReview(req *models.CreateReviewRequest, authorID uint) (*models.ReviewResponse, error) GetReviewByID(id uint) (*models.ReviewResponse, error) GetAllReviews(page, limit int, sortBy, filter string) ([]models.ReviewResponse, int, error) GetUserReviews(userID uint) ([]models.ReviewResponse, error) UpdateReview(id uint, req *models.UpdateReviewRequest, userID uint, isAdmin bool) (*models.ReviewResponse, error) DeleteReview(id uint, userID uint, isAdmin bool) error GetReviewsStats() (*models.ReviewsStatsResponse, error) } type reviewService struct { reviewRepo repository.ReviewRepository logger logger.LoggerInterface } func NewReviewService(reviewRepo repository.ReviewRepository, logger logger.LoggerInterface) ReviewService { return &reviewService{ reviewRepo: reviewRepo, logger: logger, } } func (s *reviewService) CreateReview(req *models.CreateReviewRequest, authorID uint) (*models.ReviewResponse, error) { review := &models.Review{ Rating: req.Rating, Text: req.Text, Achievement: req.Achievement, Distance: req.Distance, Improvement: req.Improvement, Trainings: req.Trainings, AuthorID: authorID, Verified: false, // По умолчанию непроверенный } if err := s.reviewRepo.Create(review); err != nil { s.logger.Error("Failed to create review", zap.Error(err)) return nil, err } // Получаем созданный отзыв с информацией об авторе createdReview, err := s.reviewRepo.GetByID(review.ID) if err != nil { s.logger.Error("Failed to get created review", zap.Error(err)) return nil, err } return s.toReviewResponse(createdReview), nil } func (s *reviewService) GetReviewByID(id uint) (*models.ReviewResponse, error) { review, err := s.reviewRepo.GetByID(id) if err != nil { s.logger.With(zap.Int("id", int(id))).Error("Failed to get review by ID", zap.Error(err)) return nil, err } return s.toReviewResponse(review), nil } func (s *reviewService) GetAllReviews(page, limit int, sortBy, filter string) ([]models.ReviewResponse, int, error) { if page < 1 { page = 1 } if limit < 1 { limit = 10 } reviews, total, err := s.reviewRepo.GetAll(page, limit, sortBy, filter) if err != nil { s.logger.Error("Failed to get all reviews", zap.Error(err)) return nil, 0, err } responses := make([]models.ReviewResponse, len(reviews)) for i, review := range reviews { responses[i] = *s.toReviewResponse(&review) } totalPages := (int(total) + limit - 1) / limit return responses, totalPages, nil } func (s *reviewService) GetUserReviews(userID uint) ([]models.ReviewResponse, error) { reviews, err := s.reviewRepo.GetByAuthorID(userID) if err != nil { s.logger.With(zap.Int("userID", int(userID))).Error("Failed to get user reviews", zap.Error(err)) return nil, err } responses := make([]models.ReviewResponse, len(reviews)) for i, review := range reviews { responses[i] = *s.toReviewResponse(&review) } return responses, nil } func (s *reviewService) UpdateReview(id uint, req *models.UpdateReviewRequest, userID uint, isAdmin bool) (*models.ReviewResponse, error) { review, err := s.reviewRepo.GetByID(id) if err != nil { s.logger.With(zap.Int("id", int(id))).Error("Failed to get review for update", zap.Error(err)) return nil, err } // Проверяем права доступа if review.AuthorID != userID && !isAdmin { s.logger.With(zap.Int("userID", int(userID))).With(zap.Int("reviewAuthorID", int(review.AuthorID))).Error("Unauthorized attempt to update review", zap.Error(err)) } // Обновляем поля if req.Rating != 0 { review.Rating = req.Rating } if req.Text != "" { review.Text = req.Text } if req.Achievement != "" { review.Achievement = req.Achievement } if req.Distance != "" { review.Distance = req.Distance } if req.Improvement != "" { review.Improvement = req.Improvement } if req.Trainings != 0 { review.Trainings = req.Trainings } if err := s.reviewRepo.Update(review); err != nil { s.logger.With(zap.Int("id", int(id))).Error("Failed to update review", zap.Error(err)) return nil, err } // Получаем обновленный отзыв updatedReview, err := s.reviewRepo.GetByID(id) if err != nil { s.logger.With(zap.Int("id", int(id))).Error("Failed to get updated review", zap.Error(err)) return nil, err } return s.toReviewResponse(updatedReview), nil } func (s *reviewService) DeleteReview(id uint, userID uint, isAdmin bool) error { review, err := s.reviewRepo.GetByID(id) if err != nil { s.logger.With(zap.Int("id", int(id))).Error("Failed to get review for deletion", zap.Error(err)) return err } // Проверяем права доступа if review.AuthorID != userID && !isAdmin { s.logger.With(zap.Int("userID", int(userID))).With(zap.Int("reviewAuthorID", int(review.AuthorID))).Error("Unauthorized attempt to delete review", zap.Error(err)) return errors.New("unauthorized") } return s.reviewRepo.Delete(id) } func (s *reviewService) GetReviewsStats() (*models.ReviewsStatsResponse, error) { return s.reviewRepo.GetStats() } func (s *reviewService) toReviewResponse(review *models.Review) *models.ReviewResponse { return &models.ReviewResponse{ ID: review.ID, CreatedAt: review.CreatedAt, Rating: review.Rating, Text: review.Text, Achievement: review.Achievement, Distance: review.Distance, Improvement: review.Improvement, Trainings: review.Trainings, Verified: review.Verified, Author: models.AuthorInfo{ ID: review.Author.ID, FirstName: review.Author.FirstName, LastName: review.Author.LastName, Email: review.Author.Email, }, } }