From de017cbe6cc6d843a22fc5e4badd1c27185687ca Mon Sep 17 00:00:00 2001 From: valitovgaziz Date: Fri, 21 Nov 2025 16:48:09 +0500 Subject: [PATCH] deleted: main_dc/yalarba/api_es/internal/models/busines_object.go new file: main_dc/yalarba/api_es/internal/repository/review_repository.go add review repository --- .../api_es/internal/models/busines_object.go | 1 - .../internal/repository/review_repository.go | 389 ++++++++++++++++++ 2 files changed, 389 insertions(+), 1 deletion(-) delete mode 100644 main_dc/yalarba/api_es/internal/models/busines_object.go create mode 100644 main_dc/yalarba/api_es/internal/repository/review_repository.go diff --git a/main_dc/yalarba/api_es/internal/models/busines_object.go b/main_dc/yalarba/api_es/internal/models/busines_object.go deleted file mode 100644 index 2640e7f..0000000 --- a/main_dc/yalarba/api_es/internal/models/busines_object.go +++ /dev/null @@ -1 +0,0 @@ -package models diff --git a/main_dc/yalarba/api_es/internal/repository/review_repository.go b/main_dc/yalarba/api_es/internal/repository/review_repository.go new file mode 100644 index 0000000..12aa0cb --- /dev/null +++ b/main_dc/yalarba/api_es/internal/repository/review_repository.go @@ -0,0 +1,389 @@ +package repository + +import ( + "errors" + "gorm.io/gorm" + "api_es/internal/models" +) + +var ( + ErrReviewNotFound = errors.New("review not found") + ErrDuplicateReview = errors.New("user already has review for this object") +) + +type ReviewRepository interface { + // Основные операции + Create(review *models.Review) error + GetByID(id uint) (*models.Review, error) + Update(id uint, updates map[string]interface{}) error + Delete(id uint) error + + // Списки отзывов + GetByObject(objectID uint, pagination *Pagination) ([]models.Review, int64, error) + GetByAuthor(authorID uint, pagination *Pagination) ([]models.Review, int64, error) + GetByObjectAndAuthor(objectID, authorID uint) (*models.Review, error) + + // Статистика + GetObjectRatingStats(objectID uint) (float64, int, error) + GetUserReviewStats(authorID uint) (int, float64, error) + + // Административные методы + SetActive(id uint, isActive bool) error + GetAll(pagination *Pagination, filters *ReviewFilter) ([]models.Review, int64, error) +} + +type ReviewFilter struct { + ObjectID uint + AuthorID uint + Rating int + IsActive *bool + MinRating int + MaxRating int +} + +type reviewRepository struct { + db *gorm.DB +} + +func NewReviewRepository(db *gorm.DB) ReviewRepository { + return &reviewRepository{db: db} +} + +// Create создает новый отзыв +func (r *reviewRepository) Create(review *models.Review) error { + return r.db.Transaction(func(tx *gorm.DB) error { + // Проверяем, не оставлял ли пользователь уже отзыв на этот объект + var existingReview models.Review + err := tx.Where("object_id = ? AND author_id = ?", review.ObjectID, review.AuthorID). + First(&existingReview).Error + + if err == nil { + return ErrDuplicateReview + } else if !errors.Is(err, gorm.ErrRecordNotFound) { + return err + } + + // Создаем отзыв + if err := tx.Create(review).Error; err != nil { + return err + } + + // Обновляем рейтинг объекта + return r.updateObjectRating(tx, review.ObjectID) + }) +} + +// GetByID возвращает отзыв по ID +func (r *reviewRepository) GetByID(id uint) (*models.Review, error) { + var review models.Review + err := r.db. + Preload("Author", func(db *gorm.DB) *gorm.DB { + return db.Select("id, first_name, last_name, avatar") + }). + Preload("Object", func(db *gorm.DB) *gorm.DB { + return db.Select("id, title, type") + }). + First(&review, id).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrReviewNotFound + } + return nil, err + } + + return &review, nil +} + +// Update обновляет отзыв +func (r *reviewRepository) Update(id uint, updates map[string]interface{}) error { + return r.db.Transaction(func(tx *gorm.DB) error { + // Получаем отзыв для получения object_id + var review models.Review + if err := tx.Select("object_id").First(&review, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrReviewNotFound + } + return err + } + + // Обновляем отзыв + result := tx.Model(&models.Review{}).Where("id = ?", id).Updates(updates) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return ErrReviewNotFound + } + + // Обновляем рейтинг объекта, если изменился рейтинг + if _, hasRating := updates["rating"]; hasRating { + return r.updateObjectRating(tx, review.ObjectID) + } + + return nil + }) +} + +// Delete удаляет отзыв +func (r *reviewRepository) Delete(id uint) error { + return r.db.Transaction(func(tx *gorm.DB) error { + // Получаем отзыв для получения object_id + var review models.Review + if err := tx.Select("object_id").First(&review, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrReviewNotFound + } + return err + } + + // Удаляем отзыв + result := tx.Delete(&models.Review{}, id) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return ErrReviewNotFound + } + + // Обновляем рейтинг объекта + return r.updateObjectRating(tx, review.ObjectID) + }) +} + +// GetByObject возвращает отзывы для объекта +func (r *reviewRepository) GetByObject(objectID uint, pagination *Pagination) ([]models.Review, int64, error) { + var reviews []models.Review + var total int64 + + query := r.db.Model(&models.Review{}).Where("object_id = ? AND is_active = ?", objectID, true) + + // Считаем общее количество + if err := query.Count(&total).Error; err != nil { + return nil, 0, err + } + + // Применяем пагинацию + if pagination != nil { + offset := (pagination.Page - 1) * pagination.PageSize + query = query.Offset(offset).Limit(pagination.PageSize) + } + + // Загружаем данные + err := query. + Preload("Author", func(db *gorm.DB) *gorm.DB { + return db.Select("id, first_name, last_name, avatar") + }). + Order("created_at DESC"). + Find(&reviews).Error + + if err != nil { + return nil, 0, err + } + + return reviews, total, nil +} + +// GetByAuthor возвращает отзывы пользователя +func (r *reviewRepository) GetByAuthor(authorID uint, pagination *Pagination) ([]models.Review, int64, error) { + var reviews []models.Review + var total int64 + + query := r.db.Model(&models.Review{}).Where("author_id = ?", authorID) + + // Считаем общее количество + if err := query.Count(&total).Error; err != nil { + return nil, 0, err + } + + // Применяем пагинацию + if pagination != nil { + offset := (pagination.Page - 1) * pagination.PageSize + query = query.Offset(offset).Limit(pagination.PageSize) + } + + // Загружаем данные + err := query. + Preload("Object", func(db *gorm.DB) *gorm.DB { + return db.Select("id, title, type, city") + }). + Order("created_at DESC"). + Find(&reviews).Error + + if err != nil { + return nil, 0, err + } + + return reviews, total, nil +} + +// GetByObjectAndAuthor возвращает отзыв конкретного пользователя для объекта +func (r *reviewRepository) GetByObjectAndAuthor(objectID, authorID uint) (*models.Review, error) { + var review models.Review + err := r.db. + Where("object_id = ? AND author_id = ?", objectID, authorID). + First(&review).Error + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, ErrReviewNotFound + } + return nil, err + } + + return &review, nil +} + +// GetObjectRatingStats возвращает статистику рейтинга для объекта +func (r *reviewRepository) GetObjectRatingStats(objectID uint) (float64, int, error) { + var stats struct { + AverageRating float64 + ReviewCount int + } + + err := r.db.Model(&models.Review{}). + Select("AVG(rating) as average_rating, COUNT(*) as review_count"). + Where("object_id = ? AND is_active = ?", objectID, true). + Scan(&stats).Error + + if err != nil { + return 0, 0, err + } + + return stats.AverageRating, stats.ReviewCount, nil +} + +// GetUserReviewStats возвращает статистику отзывов пользователя +func (r *reviewRepository) GetUserReviewStats(authorID uint) (int, float64, error) { + var stats struct { + ReviewCount int + AverageRating float64 + } + + err := r.db.Model(&models.Review{}). + Select("COUNT(*) as review_count, AVG(rating) as average_rating"). + Where("author_id = ? AND is_active = ?", authorID, true). + Scan(&stats).Error + + if err != nil { + return 0, 0, err + } + + return stats.ReviewCount, stats.AverageRating, nil +} + +// SetActive активирует/деактивирует отзыв +func (r *reviewRepository) SetActive(id uint, isActive bool) error { + return r.db.Transaction(func(tx *gorm.DB) error { + // Получаем отзыв для получения object_id + var review models.Review + if err := tx.Select("object_id").First(&review, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return ErrReviewNotFound + } + return err + } + + // Обновляем статус + result := tx.Model(&models.Review{}).Where("id = ?", id).Update("is_active", isActive) + if result.Error != nil { + return result.Error + } + if result.RowsAffected == 0 { + return ErrReviewNotFound + } + + // Обновляем рейтинг объекта + return r.updateObjectRating(tx, review.ObjectID) + }) +} + +// GetAll возвращает все отзывы с фильтрацией (для админки) +func (r *reviewRepository) GetAll(pagination *Pagination, filters *ReviewFilter) ([]models.Review, int64, error) { + var reviews []models.Review + var total int64 + + query := r.db.Model(&models.Review{}) + + // Применяем фильтры + if filters != nil { + query = r.applyFilters(query, filters) + } + + // Считаем общее количество + if err := query.Count(&total).Error; err != nil { + return nil, 0, err + } + + // Применяем пагинацию + if pagination != nil { + offset := (pagination.Page - 1) * pagination.PageSize + query = query.Offset(offset).Limit(pagination.PageSize) + } + + // Загружаем данные + err := query. + Preload("Author", func(db *gorm.DB) *gorm.DB { + return db.Select("id, first_name, last_name, email") + }). + Preload("Object", func(db *gorm.DB) *gorm.DB { + return db.Select("id, title, type") + }). + Order("created_at DESC"). + Find(&reviews).Error + + if err != nil { + return nil, 0, err + } + + return reviews, total, nil +} + +// updateObjectRating обновляет рейтинг объекта +func (r *reviewRepository) updateObjectRating(tx *gorm.DB, objectID uint) error { + stats, _, err := r.GetObjectRatingStats(objectID) + if err != nil { + return err + } + + count_ := int64(0) + + // Обновляем рейтинг объекта + return tx.Model(&models.Object{}). + Where("id = ?", objectID). + Updates(map[string]interface{}{ + "rating": stats, + "review_count": tx.Model(&models.Review{}). + Where("object_id = ? AND is_active = ?", objectID, true). + Count(&count_), + }).Error +} + +// applyFilters применяет фильтры к запросу +func (r *reviewRepository) applyFilters(query *gorm.DB, filters *ReviewFilter) *gorm.DB { + if filters.ObjectID != 0 { + query = query.Where("object_id = ?", filters.ObjectID) + } + + if filters.AuthorID != 0 { + query = query.Where("author_id = ?", filters.AuthorID) + } + + if filters.Rating != 0 { + query = query.Where("rating = ?", filters.Rating) + } + + if filters.IsActive != nil { + query = query.Where("is_active = ?", *filters.IsActive) + } + + if filters.MinRating > 0 { + query = query.Where("rating >= ?", filters.MinRating) + } + + if filters.MaxRating > 0 { + query = query.Where("rating <= ?", filters.MaxRating) + } + + return query +}