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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user