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
|
package feetback
|
||||||
|
|
||||||
import (
|
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 {
|
type FeedbackService interface {
|
||||||
Create(ctx context.Context, feedback *models.Feedback) error
|
// Create создает новый отзыв
|
||||||
GetByID(ctx context.Context, id uint) (*models.Feedback, error)
|
Create(ctx context.Context, req *CreateFeedbackRequest) (*FeedbackResponse, error)
|
||||||
Update(ctx context.Context, feedback *models.Feedback) 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
|
Delete(ctx context.Context, id uint) error
|
||||||
List(ctx context.Context, page, pageSize int) ([]models.Feedback, int64, error)
|
// List возвращает список отзывов с пагинацией
|
||||||
ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) ([]models.Feedback, error)
|
List(ctx context.Context, page, pageSize int) (*FeedbackListResponse, error)
|
||||||
ListByObject(ctx context.Context, objectID uint, page, pageSize int) ([]models.Feedback, error)
|
// ListByOwner возвращает отзывы по владельцу
|
||||||
ListByPlatform(ctx context.Context, platform models.PlatformType, page, pageSize int) ([]models.Feedback, error)
|
ListByOwner(ctx context.Context, ownerID uint, page, pageSize int) (*FeedbackListResponse, error)
|
||||||
Search(ctx context.Context, query string, page, pageSize int) ([]models.Feedback, 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
|
// 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)
|
GetComments(ctx context.Context, feedbackID uint, page, pageSize int) ([]models.Comment, error)
|
||||||
UpdateComment(ctx context.Context, commentID uint, text string) error
|
UpdateComment(ctx context.Context, commentID uint, text string) error
|
||||||
DeleteComment(ctx context.Context, commentID uint) error
|
DeleteComment(ctx context.Context, commentID uint) error
|
||||||
|
|
||||||
|
// Stats возвращает статистику по отзывам
|
||||||
|
GetStats(ctx context.Context) (*FeedbackStatsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type feedbackServiceImpl struct {
|
type feedbackServiceImpl struct {
|
||||||
@@ -41,10 +53,49 @@ func NewFeedbackServiceImpl(feedbackRepository repository.FeedbackRepository) Fe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create создает новый отзыв
|
// 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 {
|
if req == nil {
|
||||||
return fmt.Errorf("validation failed: %w", err)
|
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 {
|
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
|
// 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 {
|
if id == 0 {
|
||||||
return nil, errors.New("invalid feedback ID")
|
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 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 обновляет существующий отзыв
|
// 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 err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
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 {
|
userID := ctx.Value("userID").(uint)
|
||||||
return fmt.Errorf("validation failed: %w", err)
|
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 {
|
if err := s.feedbackRepository.Update(existing); err != nil {
|
||||||
return fmt.Errorf("failed to update feedback: %w", err)
|
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 удаляет отзыв
|
// 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 err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fmt.Errorf("feedback with ID %d not found", id)
|
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)
|
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 {
|
if err := s.feedbackRepository.Delete(id); err != nil {
|
||||||
return fmt.Errorf("failed to delete feedback: %w", err)
|
return fmt.Errorf("failed to delete feedback: %w", err)
|
||||||
@@ -126,27 +271,54 @@ func (s *feedbackServiceImpl) Delete(ctx context.Context, id uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// List возвращает список отзывов с пагинацией
|
// 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)
|
offset, limit := s.normalizePagination(page, pageSize)
|
||||||
|
|
||||||
// Получаем список отзывов
|
// Получаем список отзывов
|
||||||
feedbacks, err := s.feedbackRepository.List(offset, limit)
|
feedbacks, err := s.feedbackRepository.List(offset, limit)
|
||||||
if err != nil {
|
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()
|
total, err := s.feedbackRepository.Count()
|
||||||
if err != nil {
|
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 возвращает отзывы по владельцу
|
// 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 {
|
if ownerID == 0 {
|
||||||
return nil, errors.New("invalid owner ID")
|
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 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 возвращает отзывы по объекту
|
// 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 {
|
if objectID == 0 {
|
||||||
return nil, errors.New("invalid object ID")
|
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 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 возвращает отзывы по платформе
|
// 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) {
|
if !s.isValidPlatform(platform) {
|
||||||
return nil, fmt.Errorf("invalid platform: %s", 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 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 ищет отзывы по тексту
|
// 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) == "" {
|
if strings.TrimSpace(query) == "" {
|
||||||
return nil, errors.New("search query cannot be empty")
|
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 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 {
|
items := make([]FeedbackShortResponse, 0, len(feedbacks))
|
||||||
if feedbackID == 0 {
|
for _, feedback := range feedbacks {
|
||||||
return errors.New("invalid feedback ID")
|
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 err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
return nil, fmt.Errorf("failed to count feedbacks: %w", err)
|
||||||
return fmt.Errorf("feedback with ID %d not found", feedbackID)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("failed to get feedback: %w", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Валидация комментария
|
// Если отзывов нет, возвращаем нулевую статистику
|
||||||
if err := s.validateComment(comment); err != nil {
|
if totalCount == 0 {
|
||||||
return fmt.Errorf("validation failed: %w", err)
|
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
|
// В реальной реализации нужно выполнять агрегационные запросы к БД
|
||||||
|
return &FeedbackStatsResponse{
|
||||||
// Сохраняем комментарий (предполагается, что в репозитории есть метод CreateComment)
|
TotalCount: int(totalCount),
|
||||||
// Временно используем прямое создание через репозиторий
|
AverageScore: 3.5, // Заглушка
|
||||||
// Для полной реализации нужно добавить метод CreateComment в FeedbackRepository
|
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
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вспомогательные методы
|
// Вспомогательные методы
|
||||||
|
|
||||||
// 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 нормализует параметры пагинации
|
// normalizePagination нормализует параметры пагинации
|
||||||
func (s *feedbackServiceImpl) normalizePagination(page, pageSize int) (offset, limit int) {
|
func (s *feedbackServiceImpl) normalizePagination(page, pageSize int) (offset, limit int) {
|
||||||
if page < 1 {
|
if page < 1 {
|
||||||
@@ -366,7 +572,7 @@ func (s *feedbackServiceImpl) normalizePagination(page, pageSize int) (offset, l
|
|||||||
|
|
||||||
// isValidPlatform проверяет корректность платформы
|
// isValidPlatform проверяет корректность платформы
|
||||||
func (s *feedbackServiceImpl) isValidPlatform(platform models.PlatformType) bool {
|
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 {
|
for _, p := range validPlatforms {
|
||||||
if p == platform {
|
if p == platform {
|
||||||
return true
|
return true
|
||||||
|
|||||||
Reference in New Issue
Block a user