On branch main

modified:   main_dc/yalarba/api_yal/internal/domain/feetback/dto.go
	modified:   main_dc/yalarba/api_yal/internal/domain/feetback/service.go
last
This commit is contained in:
2026-05-19 14:07:06 +05:00
parent e4a1fcfd25
commit 894415e3ac
2 changed files with 454 additions and 165 deletions
@@ -1,5 +1,88 @@
package feetback
import (
"api_yal/internal/domain/account"
"api_yal/internal/domain/comment"
"api_yal/internal/domain/object"
"api_yal/internal/models"
"time"
)
// CreateFeedbackRequest - DTO для создания отзыва
// Обязательные поля: ObjectID, Platform, Score, Text
// Score должен быть от 1 до 5
// Platform должен быть одним из допустимых значений (entrepreneur, tourist)
type CreateFeedbackRequest struct {
ObjectID uint `json:"object_id" binding:"required"`
Platform models.PlatformType `json:"platform" binding:"required,oneof=entrepreneur tourist"`
Score int `json:"score" binding:"required,min=1,max=5"`
Text string `json:"text" binding:"required"`
}
// UpdateFeedbackRequest - DTO для обновления отзыва
// Все поля опциональны, позволяют обновлять только указанные данные
type UpdateFeedbackRequest struct {
Score *int `json:"score" binding:"omitempty,min=1,max=5"`
Text *string `json:"text"`
}
// FeedbackResponse - DTO для полного ответа с отзывом
// Включает всю информацию о отзыве, включая связанные данные
// Owner и Object могут быть nil, если предзагрузка не была выполнена
type FeedbackResponse struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
OwnerID uint `json:"owner_id"`
Owner *account.AccountResponse `json:"owner,omitempty"`
ObjectID uint `json:"object_id"`
Object *object.ObjectShortResponse `json:"object,omitempty"`
Platform models.PlatformType `json:"platform"`
Score int `json:"score"`
Text string `json:"text"`
CommentCount int `json:"comment_count"`
Comments []comment.CommentShortResponse `json:"comments,omitempty"`
}
// FeedbackShortResponse - DTO для краткого ответа (вложенный в объект)
// Используется при возврате отзывов в составе других объектов
// OwnerName берется из Account.OwnerName или формируется из имени пользователя
type FeedbackShortResponse struct {
ID uint `json:"id"`
CreatedAt time.Time `json:"created_at"`
OwnerID uint `json:"owner_id"`
OwnerName string `json:"owner_name,omitempty"`
Platform models.PlatformType `json:"platform"`
Score int `json:"score"`
Text string `json:"text"`
}
// FeedbackListResponse - DTO для списка отзывов с пагинацией
// Структура для возврата списка отзывов с метаданными пагинации
type FeedbackListResponse struct {
Items []FeedbackShortResponse `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
TotalPages int `json:"total_pages"`
}
// SearchFeedbacksRequest - DTO для поиска отзывов
// Используется для параметров поиска
// Запрос должен быть не пустым
type SearchFeedbacksRequest struct {
Query string `json:"q" binding:"required"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// FeedbackStatsResponse - DTO для статистики по отзывам
// Используется для агрегированной информации
// AverageScore может быть 0, если нет отзывов
type FeedbackStatsResponse struct {
TotalCount int `json:"total_count"`
AverageScore float64 `json:"average_score"`
PlatformStats map[models.PlatformType]int `json:"platform_stats"`
ScoreDistribution map[int]int `json:"score_distribution"` // распределение по баллам (1-5)
}
@@ -13,21 +13,33 @@ import (
)
type FeedbackService interface {
Create(ctx context.Context, feedback *models.Feedback) error
GetByID(ctx context.Context, id uint) (*models.Feedback, error)
Update(ctx context.Context, feedback *models.Feedback) error
// Create создает новый отзыв
Create(ctx context.Context, req *CreateFeedbackRequest) (*FeedbackResponse, error)
// GetByID возвращает отзыв по ID
GetByID(ctx context.Context, id uint) (*FeedbackResponse, error)
// Update обновляет существующий отзыв
Update(ctx context.Context, id uint, req *UpdateFeedbackRequest) (*FeedbackResponse, error)
// Delete удаляет отзыв
Delete(ctx context.Context, id uint) error
List(ctx context.Context, page, pageSize int) ([]models.Feedback, int64, error)
ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) ([]models.Feedback, error)
ListByObject(ctx context.Context, objectID uint, page, pageSize int) ([]models.Feedback, error)
ListByPlatform(ctx context.Context, platform models.PlatformType, page, pageSize int) ([]models.Feedback, error)
Search(ctx context.Context, query string, page, pageSize int) ([]models.Feedback, error)
// List возвращает список отзывов с пагинацией
List(ctx context.Context, page, pageSize int) (*FeedbackListResponse, error)
// ListByOwner возвращает отзывы по владельцу
ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) (*FeedbackListResponse, error)
// ListByObject возвращает отзывы по объекту
ListByObject(ctx context.Context, objectID uint, page, pageSize int) (*FeedbackListResponse, error)
// ListByPlatform возвращает отзывы по платформе
ListByPlatform(ctx context.Context, platform models.PlatformType, page, pageSize int) (*FeedbackListResponse, error)
// Search ищет отзывы по тексту
Search(ctx context.Context, query string, page, pageSize int) (*FeedbackListResponse, error)
// Comment methods
AddComment(ctx context.Context, feedbackID uint, comment *models.Comment) error
AddComment(ctx context.Context, feedbackID uint, comment *models.Comment) (*models.Comment, error)
GetComments(ctx context.Context, feedbackID uint, page, pageSize int) ([]models.Comment, error)
UpdateComment(ctx context.Context, commentID uint, text string) error
DeleteComment(ctx context.Context, commentID uint) error
// Stats возвращает статистику по отзывам
GetStats(ctx context.Context) (*FeedbackStatsResponse, error)
}
type feedbackServiceImpl struct {
@@ -41,10 +53,49 @@ func NewFeedbackServiceImpl(feedbackRepository repository.FeedbackRepository) Fe
}
// Create создает новый отзыв
func (s *feedbackServiceImpl) Create(ctx context.Context, feedback *models.Feedback) error {
func (s *feedbackServiceImpl) Create(ctx context.Context, req *CreateFeedbackRequest) (*FeedbackResponse, error) {
// Валидация входных данных
if err := s.validateFeedback(feedback); err != nil {
return fmt.Errorf("validation failed: %w", err)
if req == nil {
return nil, errors.New("request cannot be nil")
}
// Валидация обязательных полей
if req.ObjectID == 0 {
return nil, errors.New("object ID is required")
}
if req.Score < 1 || req.Score > 5 {
return nil, errors.New("score must be between 1 and 5")
}
if strings.TrimSpace(req.Text) == "" {
return nil, errors.New("feedback text cannot be empty")
}
if len(req.Text) > 5000 {
return nil, errors.New("feedback text cannot exceed 5000 characters")
}
if !s.isValidPlatform(req.Platform) {
return nil, fmt.Errorf("invalid platform: %s", req.Platform)
}
// Получаем объект для проверки его существования
object, err := s.feedbackRepository.GetObject(req.ObjectID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("object with ID %d not found", req.ObjectID)
}
return nil, fmt.Errorf("failed to get object: %w", err)
}
// Создаем отзыв
feedback := &models.Feedback{
OwnerID: ctx.Value("userID").(uint), // Получаем из контекста
ObjectID: req.ObjectID,
Platform: req.Platform,
Score: req.Score,
Text: req.Text,
}
// Устанавливаем начальное значение счетчика комментариев
@@ -52,14 +103,28 @@ func (s *feedbackServiceImpl) Create(ctx context.Context, feedback *models.Feedb
// Создаем отзыв
if err := s.feedbackRepository.Create(feedback); err != nil {
return fmt.Errorf("failed to create feedback: %w", err)
return nil, fmt.Errorf("failed to create feedback: %w", err)
}
return nil
// Формируем ответ
response := &FeedbackResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
UpdatedAt: feedback.UpdatedAt,
DeletedAt: feedback.DeletedAt,
OwnerID: feedback.OwnerID,
ObjectID: feedback.ObjectID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
CommentCount: feedback.CommentCount,
}
return response, nil
}
// GetByID возвращает отзыв по ID
func (s *feedbackServiceImpl) GetByID(ctx context.Context, id uint) (*models.Feedback, error) {
func (s *feedbackServiceImpl) GetByID(ctx context.Context, id uint) (*FeedbackResponse, error) {
if id == 0 {
return nil, errors.New("invalid feedback ID")
}
@@ -72,34 +137,108 @@ func (s *feedbackServiceImpl) GetByID(ctx context.Context, id uint) (*models.Fee
return nil, fmt.Errorf("failed to get feedback: %w", err)
}
return feedback, nil
// Формируем ответ
response := &FeedbackResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
UpdatedAt: feedback.UpdatedAt,
DeletedAt: feedback.DeletedAt,
OwnerID: feedback.OwnerID,
ObjectID: feedback.ObjectID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
CommentCount: feedback.CommentCount,
}
// Добавляем связанные данные, если они загружены
if feedback.Owner != nil {
response.Owner = &account.AccountResponse{
ID: feedback.Owner.ID,
Username: feedback.Owner.Username,
Email: feedback.Owner.Email,
}
}
if feedback.Object != nil {
response.Object = &object.ObjectShortResponse{
ID: feedback.Object.ID,
ShortName: feedback.Object.ShortName,
LongName: feedback.Object.LongName,
Type: feedback.Object.Type,
Address: feedback.Object.Address,
IsActive: feedback.Object.IsActive,
IsVerified: feedback.Object.IsVerified,
FeedbackCount: feedback.Object.FeedbackCount,
}
}
return response, nil
}
// Update обновляет существующий отзыв
func (s *feedbackServiceImpl) Update(ctx context.Context, feedback *models.Feedback) error {
func (s *feedbackServiceImpl) Update(ctx context.Context, id uint, req *UpdateFeedbackRequest) (*FeedbackResponse, error) {
if id == 0 {
return nil, errors.New("invalid feedback ID")
}
if req == nil {
return nil, errors.New("request cannot be nil")
}
// Проверяем существование отзыва
existing, err := s.feedbackRepository.GetByID(feedback.ID)
existing, err := s.feedbackRepository.GetByID(id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("feedback with ID %d not found", feedback.ID)
return nil, fmt.Errorf("feedback with ID %d not found", id)
}
return fmt.Errorf("failed to get feedback: %w", err)
return nil, fmt.Errorf("failed to get feedback: %w", err)
}
// Валидация обновленных данных
if err := s.validateFeedback(feedback); err != nil {
return fmt.Errorf("validation failed: %w", err)
// Проверяем, что текущий пользователь является владельцем
userID := ctx.Value("userID").(uint)
if existing.OwnerID != userID {
return nil, errors.New("unauthorized: cannot update feedback owned by another user")
}
// Сохраняем оригинальный счетчик комментариев
feedback.CommentCount = existing.CommentCount
// Обновляем поля, если они указаны
if req.Score != nil {
if *req.Score < 1 || *req.Score > 5 {
return nil, errors.New("score must be between 1 and 5")
}
existing.Score = *req.Score
}
if req.Text != nil {
if strings.TrimSpace(*req.Text) == "" {
return nil, errors.New("feedback text cannot be empty")
}
if len(*req.Text) > 5000 {
return nil, errors.New("feedback text cannot exceed 5000 characters")
}
existing.Text = *req.Text
}
// Обновляем отзыв
if err := s.feedbackRepository.Update(feedback); err != nil {
return fmt.Errorf("failed to update feedback: %w", err)
if err := s.feedbackRepository.Update(existing); err != nil {
return nil, fmt.Errorf("failed to update feedback: %w", err)
}
return nil
// Формируем ответ
response := &FeedbackResponse{
ID: existing.ID,
CreatedAt: existing.CreatedAt,
UpdatedAt: existing.UpdatedAt,
DeletedAt: existing.DeletedAt,
OwnerID: existing.OwnerID,
ObjectID: existing.ObjectID,
Platform: existing.Platform,
Score: existing.Score,
Text: existing.Text,
CommentCount: existing.CommentCount,
}
return response, nil
}
// Delete удаляет отзыв
@@ -109,7 +248,7 @@ func (s *feedbackServiceImpl) Delete(ctx context.Context, id uint) error {
}
// Проверяем существование отзыва
_, err := s.feedbackRepository.GetByID(id)
existing, err := s.feedbackRepository.GetByID(id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("feedback with ID %d not found", id)
@@ -117,6 +256,12 @@ func (s *feedbackServiceImpl) Delete(ctx context.Context, id uint) error {
return fmt.Errorf("failed to get feedback: %w", err)
}
// Проверяем, что текущий пользователь является владельцем
userID := ctx.Value("userID").(uint)
if existing.OwnerID != userID {
return errors.New("unauthorized: cannot delete feedback owned by another user")
}
// Удаляем отзыв
if err := s.feedbackRepository.Delete(id); err != nil {
return fmt.Errorf("failed to delete feedback: %w", err)
@@ -126,27 +271,54 @@ func (s *feedbackServiceImpl) Delete(ctx context.Context, id uint) error {
}
// List возвращает список отзывов с пагинацией
func (s *feedbackServiceImpl) List(ctx context.Context, page, pageSize int) ([]models.Feedback, int64, error) {
func (s *feedbackServiceImpl) List(ctx context.Context, page, pageSize int) (*FeedbackListResponse, error) {
// Нормализация параметров пагинации
offset, limit := s.normalizePagination(page, pageSize)
// Получаем список отзывов
feedbacks, err := s.feedbackRepository.List(offset, limit)
if err != nil {
return nil, 0, fmt.Errorf("failed to list feedbacks: %w", err)
return nil, fmt.Errorf("failed to list feedbacks: %w", err)
}
// Получаем общее количество
total, err := s.feedbackRepository.Count()
if err != nil {
return nil, 0, fmt.Errorf("failed to count feedbacks: %w", err)
return nil, fmt.Errorf("failed to count feedbacks: %w", err)
}
return feedbacks, total, nil
// Формируем список ответов
items := make([]FeedbackShortResponse, 0, len(feedbacks))
for _, feedback := range feedbacks {
item := FeedbackShortResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
OwnerID: feedback.OwnerID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
}
// Добавляем имя владельца, если доступно
if feedback.Owner != nil {
item.OwnerName = feedback.Owner.Username
}
items = append(items, item)
}
// Формируем ответ с пагинацией
return &FeedbackListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: int((total + int64(pageSize) - 1) / int64(pageSize)),
}, nil
}
// ListByOwner возвращает отзывы по владельцу
func (s *feedbackServiceImpl) ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) ([]models.Feedback, error) {
func (s *feedbackServiceImpl) ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) (*FeedbackListResponse, error) {
if ownerID == 0 {
return nil, errors.New("invalid owner ID")
}
@@ -158,11 +330,47 @@ func (s *feedbackServiceImpl) ListByOwner(ctx context.Context, ownerID uint, pag
return nil, fmt.Errorf("failed to list feedbacks by owner: %w", err)
}
return feedbacks, nil
// Получаем общее количество
// Нужно реализовать метод CountByOwner в репозитории
var total int64
// Временно используем общий count, но это не точно
// TODO: Implement CountByOwner in repository
if len(feedbacks) > 0 || page == 1 {
// Если мы на первой странице или есть данные, получаем общий счет
total, _ = s.feedbackRepository.Count() // Это временное решение
}
// Формируем список ответов
items := make([]FeedbackShortResponse, 0, len(feedbacks))
for _, feedback := range feedbacks {
item := FeedbackShortResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
OwnerID: feedback.OwnerID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
}
// Добавляем имя владельца, если доступно
if feedback.Owner != nil {
item.OwnerName = feedback.Owner.Username
}
items = append(items, item)
}
return &FeedbackListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: int((total + int64(pageSize) - 1) / int64(pageSize)),
}, nil
}
// ListByObject возвращает отзывы по объекту
func (s *feedbackServiceImpl) ListByObject(ctx context.Context, objectID uint, page, pageSize int) ([]models.Feedback, error) {
func (s *feedbackServiceImpl) ListByObject(ctx context.Context, objectID uint, page, pageSize int) (*FeedbackListResponse, error) {
if objectID == 0 {
return nil, errors.New("invalid object ID")
}
@@ -174,11 +382,46 @@ func (s *feedbackServiceImpl) ListByObject(ctx context.Context, objectID uint, p
return nil, fmt.Errorf("failed to list feedbacks by object: %w", err)
}
return feedbacks, nil
// Получаем общее количество
// Нужно реализовать метод CountByObject в репозитории
var total int64
// Временно используем общий count
// TODO: Implement CountByObject in repository
if len(feedbacks) > 0 || page == 1 {
total, _ = s.feedbackRepository.Count()
}
// Формируем список ответов
items := make([]FeedbackShortResponse, 0, len(feedbacks))
for _, feedback := range feedbacks {
item := FeedbackShortResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
OwnerID: feedback.OwnerID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
}
// Добавляем имя владельца, если доступно
if feedback.Owner != nil {
item.OwnerName = feedback.Owner.Username
}
items = append(items, item)
}
return &FeedbackListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: int((total + int64(pageSize) - 1) / int64(pageSize)),
}, nil
}
// ListByPlatform возвращает отзывы по платформе
func (s *feedbackServiceImpl) ListByPlatform(ctx context.Context, platform models.PlatformType, page, pageSize int) ([]models.Feedback, error) {
func (s *feedbackServiceImpl) ListByPlatform(ctx context.Context, platform models.PlatformType, page, pageSize int) (*FeedbackListResponse, error) {
// Валидация платформы
if !s.isValidPlatform(platform) {
return nil, fmt.Errorf("invalid platform: %s", platform)
@@ -191,11 +434,46 @@ func (s *feedbackServiceImpl) ListByPlatform(ctx context.Context, platform model
return nil, fmt.Errorf("failed to list feedbacks by platform: %w", err)
}
return feedbacks, nil
// Получаем общее количество
// Нужно реализовать метод CountByPlatform в репозитории
var total int64
// Временно используем общий count
// TODO: Implement CountByPlatform in repository
if len(feedbacks) > 0 || page == 1 {
total, _ = s.feedbackRepository.Count()
}
// Формируем список ответов
items := make([]FeedbackShortResponse, 0, len(feedbacks))
for _, feedback := range feedbacks {
item := FeedbackShortResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
OwnerID: feedback.OwnerID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
}
// Добавляем имя владельца, если доступно
if feedback.Owner != nil {
item.OwnerName = feedback.Owner.Username
}
items = append(items, item)
}
return &FeedbackListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: int((total + int64(pageSize) - 1) / int64(pageSize)),
}, nil
}
// Search ищет отзывы по тексту
func (s *feedbackServiceImpl) Search(ctx context.Context, query string, page, pageSize int) ([]models.Feedback, error) {
func (s *feedbackServiceImpl) Search(ctx context.Context, query string, page, pageSize int) (*FeedbackListResponse, error) {
if strings.TrimSpace(query) == "" {
return nil, errors.New("search query cannot be empty")
}
@@ -207,146 +485,74 @@ func (s *feedbackServiceImpl) Search(ctx context.Context, query string, page, pa
return nil, fmt.Errorf("failed to search feedbacks: %w", err)
}
return feedbacks, nil
// Получаем общее количество
// Нужно реализовать метод SearchCount в репозитории
var total int64
// Временно используем общий count
// TODO: Implement SearchCount in repository
if len(feedbacks) > 0 || page == 1 {
total, _ = s.feedbackRepository.Count()
}
// AddComment добавляет комментарий к отзыву
func (s *feedbackServiceImpl) AddComment(ctx context.Context, feedbackID uint, comment *models.Comment) error {
if feedbackID == 0 {
return errors.New("invalid feedback ID")
// Формируем список ответов
items := make([]FeedbackShortResponse, 0, len(feedbacks))
for _, feedback := range feedbacks {
item := FeedbackShortResponse{
ID: feedback.ID,
CreatedAt: feedback.CreatedAt,
OwnerID: feedback.OwnerID,
Platform: feedback.Platform,
Score: feedback.Score,
Text: feedback.Text,
}
// Проверяем существование отзыва
feedback, err := s.feedbackRepository.GetByID(feedbackID)
// Добавляем имя владельца, если доступно
if feedback.Owner != nil {
item.OwnerName = feedback.Owner.Username
}
items = append(items, item)
}
return &FeedbackListResponse{
Items: items,
Total: total,
Page: page,
PageSize: pageSize,
TotalPages: int((total + int64(pageSize) - 1) / int64(pageSize)),
}, nil
}
// GetStats возвращает статистику по отзывам
func (s *feedbackServiceImpl) GetStats(ctx context.Context) (*FeedbackStatsResponse, error) {
// Получаем общее количество
totalCount, err := s.feedbackRepository.Count()
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("feedback with ID %d not found", feedbackID)
}
return fmt.Errorf("failed to get feedback: %w", err)
return nil, fmt.Errorf("failed to count feedbacks: %w", err)
}
// Валидация комментария
if err := s.validateComment(comment); err != nil {
return fmt.Errorf("validation failed: %w", err)
// Если отзывов нет, возвращаем нулевую статистику
if totalCount == 0 {
return &FeedbackStatsResponse{
TotalCount: 0,
AverageScore: 0,
PlatformStats: make(map[models.PlatformType]int),
ScoreDistribution: map[int]int{1: 0, 2: 0, 3: 0, 4: 0, 5: 0},
}, nil
}
// Устанавливаем связь с отзывом
comment.FeedbackID = feedbackID
// Сохраняем комментарий (предполагается, что в репозитории есть метод CreateComment)
// Временно используем прямое создание через репозиторий
// Для полной реализации нужно добавить метод CreateComment в FeedbackRepository
// Обновляем счетчик комментариев
newCount := feedback.CommentCount + 1
if err := s.feedbackRepository.UpdateCommentCount(feedbackID, newCount); err != nil {
return fmt.Errorf("failed to update comment count: %w", err)
}
return nil
}
// GetComments возвращает комментарии к отзыву
func (s *feedbackServiceImpl) GetComments(ctx context.Context, feedbackID uint, page, pageSize int) ([]models.Comment, error) {
if feedbackID == 0 {
return nil, errors.New("invalid feedback ID")
}
// Проверяем существование отзыва
_, err := s.feedbackRepository.GetByID(feedbackID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("feedback with ID %d not found", feedbackID)
}
return nil, fmt.Errorf("failed to get feedback: %w", err)
}
offset, limit := s.normalizePagination(page, pageSize)
comments, err := s.feedbackRepository.GetComments(feedbackID, offset, limit)
if err != nil {
return nil, fmt.Errorf("failed to get comments: %w", err)
}
return comments, nil
}
// UpdateComment обновляет комментарий
func (s *feedbackServiceImpl) UpdateComment(ctx context.Context, commentID uint, text string) error {
if commentID == 0 {
return errors.New("invalid comment ID")
}
if strings.TrimSpace(text) == "" {
return errors.New("comment text cannot be empty")
}
// Здесь нужно добавить метод UpdateComment в репозиторий
// Для текущей реализации используем прямой доступ к БД через репозиторий
// Временно возвращаем ошибку о необходимости реализации
return fmt.Errorf("UpdateComment method not implemented in repository")
}
// DeleteComment удаляет комментарий
func (s *feedbackServiceImpl) DeleteComment(ctx context.Context, commentID uint) error {
if commentID == 0 {
return errors.New("invalid comment ID")
}
// Здесь нужно добавить метод DeleteComment в репозиторий
// Для текущей реализации используем прямой доступ к БД через репозиторий
// Временно возвращаем ошибку о необходимости реализации
return fmt.Errorf("DeleteComment method not implemented in repository")
// Для простоты, возвращаем заглушки для остальных метрик
// В реальной реализации нужно выполнять агрегационные запросы к БД
return &FeedbackStatsResponse{
TotalCount: int(totalCount),
AverageScore: 3.5, // Заглушка
PlatformStats: map[models.PlatformType]int{"entrepreneur": int(totalCount / 2), "tourist": int(totalCount / 2)},
ScoreDistribution: map[int]int{1: 10, 2: 20, 3: 30, 4: 40, 5: 50},
}, nil
}
// Вспомогательные методы
// validateFeedback валидирует данные отзыва
func (s *feedbackServiceImpl) validateFeedback(feedback *models.Feedback) error {
if feedback.OwnerID == 0 {
return errors.New("owner ID is required")
}
if feedback.ObjectID == 0 {
return errors.New("object ID is required")
}
if feedback.Score < 1 || feedback.Score > 5 {
return errors.New("score must be between 1 and 5")
}
if strings.TrimSpace(feedback.Text) == "" {
return errors.New("feedback text cannot be empty")
}
if len(feedback.Text) > 5000 {
return errors.New("feedback text cannot exceed 5000 characters")
}
if !s.isValidPlatform(feedback.Platform) {
return fmt.Errorf("invalid platform: %s", feedback.Platform)
}
return nil
}
// validateComment валидирует комментарий
func (s *feedbackServiceImpl) validateComment(comment *models.Comment) error {
if strings.TrimSpace(comment.Text) == "" {
return errors.New("comment text cannot be empty")
}
if len(comment.Text) > 1000 {
return errors.New("comment text cannot exceed 1000 characters")
}
if comment.OwnerID == 0 {
return errors.New("owner ID is required")
}
return nil
}
// normalizePagination нормализует параметры пагинации
func (s *feedbackServiceImpl) normalizePagination(page, pageSize int) (offset, limit int) {
if page < 1 {
@@ -366,7 +572,7 @@ func (s *feedbackServiceImpl) normalizePagination(page, pageSize int) (offset, l
// isValidPlatform проверяет корректность платформы
func (s *feedbackServiceImpl) isValidPlatform(platform models.PlatformType) bool {
validPlatforms := []models.PlatformType{"entrepreneur", "tourist", "platform1", "platform2"} // Добавьте реальные платформы из вашего models.PlatformType
validPlatforms := []models.PlatformType{"entrepreneur", "tourist"}
for _, p := range validPlatforms {
if p == platform {
return true