package repository import ( "narasi-ahli-be/app/database" "narasi-ahli-be/app/database/entity" "narasi-ahli-be/app/module/ebooks/request" "narasi-ahli-be/utils/paginator" "github.com/rs/zerolog" ) type ebookRatingsRepository struct { DB *database.Database Log zerolog.Logger } // EbookRatingsRepository define interface of IEbookRatingsRepository type EbookRatingsRepository interface { GetAll(req request.EbookRatingsQueryRequest) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) GetByEbookId(ebookId uint, pagination *paginator.Pagination) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) GetByUserId(userId uint, pagination *paginator.Pagination) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) FindOne(id uint) (rating *entity.EbookRatings, err error) FindByUserAndEbook(userId uint, ebookId uint) (rating *entity.EbookRatings, err error) FindByPurchaseId(purchaseId uint) (rating *entity.EbookRatings, err error) Create(rating *entity.EbookRatings) (ratingReturn *entity.EbookRatings, err error) Update(id uint, rating *entity.EbookRatings) (err error) Delete(id uint) (err error) GetEbookRatingStats(ebookId uint) (totalRatings int, averageRating float64, ratingCounts map[int]int, err error) GetRecentReviews(ebookId uint, limit int) (reviews []*entity.EbookRatings, err error) } func NewEbookRatingsRepository(db *database.Database, log zerolog.Logger) EbookRatingsRepository { return &ebookRatingsRepository{ DB: db, Log: log, } } // implement interface of IEbookRatingsRepository func (_i *ebookRatingsRepository) GetAll(req request.EbookRatingsQueryRequest) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) { var count int64 query := _i.DB.DB.Model(&entity.EbookRatings{}). Preload("User"). Preload("Ebook"). Preload("Purchase"). Where("ebook_ratings.is_active = ?", true) if req.EbookId != nil { query = query.Where("ebook_ratings.ebook_id = ?", req.EbookId) } if req.UserId != nil { query = query.Where("ebook_ratings.user_id = ?", req.UserId) } if req.Rating != nil { query = query.Where("ebook_ratings.rating = ?", req.Rating) } if req.IsVerified != nil { query = query.Where("ebook_ratings.is_verified = ?", req.IsVerified) } if req.StatusId != nil { query = query.Where("ebook_ratings.status_id = ?", req.StatusId) } query.Count(&count) if req.Pagination.SortBy != "" { direction := "ASC" if req.Pagination.Sort == "desc" { direction = "DESC" } query.Order("ebook_ratings." + req.Pagination.SortBy + " " + direction) } else { query.Order("ebook_ratings.created_at DESC") } req.Pagination.Count = count req.Pagination = paginator.Paging(req.Pagination) err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&ratings).Error if err != nil { return } paging = *req.Pagination return } func (_i *ebookRatingsRepository) GetByEbookId(ebookId uint, pagination *paginator.Pagination) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) { var count int64 query := _i.DB.DB.Model(&entity.EbookRatings{}). Preload("User"). Preload("Ebook"). Preload("Purchase"). Where("ebook_ratings.ebook_id = ? AND ebook_ratings.is_active = ?", ebookId, true) query.Count(&count) pagination.Count = count pagination = paginator.Paging(pagination) err = query.Offset(pagination.Offset).Limit(pagination.Limit). Order("ebook_ratings.created_at DESC"). Find(&ratings).Error if err != nil { return } paging = *pagination return } func (_i *ebookRatingsRepository) GetByUserId(userId uint, pagination *paginator.Pagination) (ratings []*entity.EbookRatings, paging paginator.Pagination, err error) { var count int64 query := _i.DB.DB.Model(&entity.EbookRatings{}). Preload("User"). Preload("Ebook"). Preload("Purchase"). Where("ebook_ratings.user_id = ? AND ebook_ratings.is_active = ?", userId, true) query.Count(&count) pagination.Count = count pagination = paginator.Paging(pagination) err = query.Offset(pagination.Offset).Limit(pagination.Limit). Order("ebook_ratings.created_at DESC"). Find(&ratings).Error if err != nil { return } paging = *pagination return } func (_i *ebookRatingsRepository) FindOne(id uint) (rating *entity.EbookRatings, err error) { query := _i.DB.DB.Preload("User").Preload("Ebook").Preload("Purchase") if err := query.First(&rating, id).Error; err != nil { return nil, err } return rating, nil } func (_i *ebookRatingsRepository) FindByUserAndEbook(userId uint, ebookId uint) (rating *entity.EbookRatings, err error) { query := _i.DB.DB.Where("user_id = ? AND ebook_id = ?", userId, ebookId) if err := query.First(&rating).Error; err != nil { return nil, err } return rating, nil } func (_i *ebookRatingsRepository) FindByPurchaseId(purchaseId uint) (rating *entity.EbookRatings, err error) { query := _i.DB.DB.Where("purchase_id = ?", purchaseId) if err := query.First(&rating).Error; err != nil { return nil, err } return rating, nil } func (_i *ebookRatingsRepository) Create(rating *entity.EbookRatings) (ratingReturn *entity.EbookRatings, err error) { result := _i.DB.DB.Create(rating) return rating, result.Error } func (_i *ebookRatingsRepository) Update(id uint, rating *entity.EbookRatings) (err error) { return _i.DB.DB.Model(&entity.EbookRatings{}). Where(&entity.EbookRatings{ID: id}). Updates(rating).Error } func (_i *ebookRatingsRepository) Delete(id uint) error { return _i.DB.DB.Delete(&entity.EbookRatings{}, id).Error } func (_i *ebookRatingsRepository) GetEbookRatingStats(ebookId uint) (totalRatings int, averageRating float64, ratingCounts map[int]int, err error) { var stats struct { TotalRatings int `json:"totalRatings"` AverageRating float64 `json:"averageRating"` } // Get total ratings and average err = _i.DB.DB.Model(&entity.EbookRatings{}). Select("COUNT(*) as total_ratings, COALESCE(AVG(rating), 0) as average_rating"). Where("ebook_id = ? AND is_active = ? AND is_verified = ?", ebookId, true, true). Scan(&stats).Error if err != nil { return 0, 0, nil, err } // Get rating counts var ratingCountsData []struct { Rating int `json:"rating"` Count int `json:"count"` } err = _i.DB.DB.Model(&entity.EbookRatings{}). Select("rating, COUNT(*) as count"). Where("ebook_id = ? AND is_active = ? AND is_verified = ?", ebookId, true, true). Group("rating"). Scan(&ratingCountsData).Error if err != nil { return 0, 0, nil, err } // Convert to map ratingCounts = make(map[int]int) for _, rc := range ratingCountsData { ratingCounts[rc.Rating] = rc.Count } // Ensure all ratings 1-5 are present for i := 1; i <= 5; i++ { if _, exists := ratingCounts[i]; !exists { ratingCounts[i] = 0 } } return stats.TotalRatings, stats.AverageRating, ratingCounts, nil } func (_i *ebookRatingsRepository) GetRecentReviews(ebookId uint, limit int) (reviews []*entity.EbookRatings, err error) { query := _i.DB.DB.Model(&entity.EbookRatings{}). Preload("User"). Preload("Ebook"). Preload("Purchase"). Where("ebook_id = ? AND is_active = ? AND is_verified = ? AND review IS NOT NULL AND review != ''", ebookId, true, true). Order("created_at DESC"). Limit(limit) err = query.Find(&reviews).Error return reviews, err }