package service import ( "api_bb/internal/models" "api_bb/internal/repository" "api_bb/pkg/logger" "errors" "go.uber.org/zap" ) type NewsService interface { CreateNews(req models.CreateNewsRequest, authorID uint) (*models.NewsResponse, error) GetNewsByID(id uint) (*models.NewsResponse, error) GetAllNews(limit, offset int, category string) ([]models.NewsResponse, int64, error) UpdateNews(id uint, req models.UpdateNewsRequest, userID uint) (*models.NewsResponse, error) DeleteNews(id uint, userID uint) error IncrementViews(id uint) error CreateComment(newsID uint, req models.CreateCommentRequest, authorID uint) (*models.CommentResponse, error) GetCommentsByNewsID(newsID uint) ([]models.CommentResponse, error) DeleteComment(commentID, userID uint) error GetUserNews(userID uint, limit, offset int) ([]models.NewsResponse, int64, error) } type newsService struct { newsRepo repository.NewsRepository commentRepo repository.CommentRepository logger logger.Interface } func NewNewsService(newsRepo repository.NewsRepository, commentRepo repository.CommentRepository, log logger.Interface) NewsService { serviceLogger := log.With(zap.String("service", "news")) return &newsService{ newsRepo: newsRepo, commentRepo: commentRepo, logger: serviceLogger, } } func (s *newsService) CreateNews(req models.CreateNewsRequest, authorID uint) (*models.NewsResponse, error) { news := &models.News{ Title: req.Title, Excerpt: req.Excerpt, Content: req.Content, Image: req.Image, Category: req.Category, AuthorID: authorID, } if err := s.newsRepo.Create(news); err != nil { s.logger.Error("Failed to create news", zap.Error(err)) return nil, errors.New("failed to create news") } // Получаем созданную новость с автором createdNews, err := s.newsRepo.GetByID(news.ID) if err != nil { return nil, err } return s.toNewsResponse(createdNews), nil } func (s *newsService) GetNewsByID(id uint) (*models.NewsResponse, error) { news, err := s.newsRepo.GetByID(id) if err != nil { return nil, errors.New("news not found") } // Увеличиваем счетчик просмотров go s.newsRepo.IncrementViews(id) return s.toNewsResponse(news), nil } func (s *newsService) GetAllNews(limit, offset int, category string) ([]models.NewsResponse, int64, error) { news, total, err := s.newsRepo.GetAll(limit, offset, category) if err != nil { return nil, 0, err } responses := make([]models.NewsResponse, len(news)) for i, n := range news { responses[i] = *s.toNewsResponse(&n) } return responses, total, nil } func (s *newsService) UpdateNews(id uint, req models.UpdateNewsRequest, userID uint) (*models.NewsResponse, error) { news, err := s.newsRepo.GetByID(id) if err != nil { return nil, errors.New("news not found") } // Проверяем права доступа if news.AuthorID != userID { return nil, errors.New("access denied") } // Обновляем поля if req.Title != "" { news.Title = req.Title } if req.Excerpt != "" { news.Excerpt = req.Excerpt } if req.Content != "" { news.Content = req.Content } if req.Image != "" { news.Image = req.Image } if req.Category != "" { news.Category = req.Category } if err := s.newsRepo.Update(news); err != nil { return nil, err } return s.toNewsResponse(news), nil } func (s *newsService) DeleteNews(id uint, userID uint) error { news, err := s.newsRepo.GetByID(id) if err != nil { return errors.New("news not found") } // Проверяем права доступа if news.AuthorID != userID { return errors.New("access denied") } return s.newsRepo.Delete(id) } func (s *newsService) IncrementViews(id uint) error { return s.newsRepo.IncrementViews(id) } func (s *newsService) CreateComment(newsID uint, req models.CreateCommentRequest, authorID uint) (*models.CommentResponse, error) { // Проверяем существование новости _, err := s.newsRepo.GetByID(newsID) if err != nil { return nil, errors.New("news not found") } comment := &models.Comment{ Content: req.Content, NewsID: newsID, AuthorID: authorID, } if err := s.commentRepo.Create(comment); err != nil { return nil, err } // Получаем созданный комментарий с автором createdComment, err := s.commentRepo.GetByID(comment.ID) if err != nil { return nil, err } return s.toCommentResponse(createdComment), nil } func (s *newsService) GetCommentsByNewsID(newsID uint) ([]models.CommentResponse, error) { comments, err := s.commentRepo.GetByNewsID(newsID) if err != nil { return nil, err } responses := make([]models.CommentResponse, len(comments)) for i, c := range comments { responses[i] = *s.toCommentResponse(&c) } return responses, nil } func (s *newsService) DeleteComment(commentID, userID uint) error { comment, err := s.commentRepo.GetByID(commentID) if err != nil { return errors.New("comment not found") } // Проверяем права доступа if comment.AuthorID != userID { return errors.New("access denied") } return s.commentRepo.Delete(commentID) } func (s *newsService) GetUserNews(userID uint, limit, offset int) ([]models.NewsResponse, int64, error) { news, total, err := s.newsRepo.GetByAuthor(userID, limit, offset) if err != nil { return nil, 0, err } responses := make([]models.NewsResponse, len(news)) for i, n := range news { responses[i] = *s.toNewsResponse(&n) } return responses, total, nil } // Вспомогательные методы для преобразования func (s *newsService) toNewsResponse(news *models.News) *models.NewsResponse { return &models.NewsResponse{ ID: news.ID, CreatedAt: news.CreatedAt, UpdatedAt: news.UpdatedAt, Title: news.Title, Excerpt: news.Excerpt, Content: news.Content, Image: news.Image, Category: news.Category, Views: news.Views, Author: models.AuthorInfo{ ID: news.Author.ID, FirstName: news.Author.FirstName, LastName: news.Author.LastName, }, Comments: len(news.Comments), } } func (s *newsService) toCommentResponse(comment *models.Comment) *models.CommentResponse { return &models.CommentResponse{ ID: comment.ID, CreatedAt: comment.CreatedAt, Content: comment.Content, Author: models.AuthorInfo{ ID: comment.Author.ID, FirstName: comment.Author.FirstName, LastName: comment.Author.LastName, }, } }