package service import ( "errors" "narasi-ahli-be/app/database/entity" "narasi-ahli-be/app/module/ebooks/mapper" "narasi-ahli-be/app/module/ebooks/repository" "narasi-ahli-be/app/module/ebooks/request" "narasi-ahli-be/app/module/ebooks/response" usersRepository "narasi-ahli-be/app/module/users/repository" "narasi-ahli-be/utils/paginator" utilSvc "narasi-ahli-be/utils/service" "github.com/rs/zerolog" ) // EbookRatingsService type ebookRatingsService struct { Repo repository.EbookRatingsRepository EbooksRepo repository.EbooksRepository EbookPurchasesRepo repository.EbookPurchasesRepository Log zerolog.Logger UsersRepo usersRepository.UsersRepository } // EbookRatingsService define interface of IEbookRatingsService type EbookRatingsService interface { GetAll(req request.EbookRatingsQueryRequest) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) GetByEbookId(ebookId uint, pagination *paginator.Pagination) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) GetByUserId(authToken string, pagination *paginator.Pagination) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) GetEbookRatingSummary(ebookId uint) (summary *response.EbookRatingSummaryResponse, err error) Create(req request.EbookRatingsCreateRequest, authToken string) (rating *response.EbookRatingsResponse, err error) Update(id uint, req request.EbookRatingsUpdateRequest, authToken string) (err error) Delete(id uint, authToken string) error GetRatingStats(ebookId uint) (stats *response.EbookRatingStatsResponse, err error) } // NewEbookRatingsService init EbookRatingsService func NewEbookRatingsService( repo repository.EbookRatingsRepository, ebooksRepo repository.EbooksRepository, ebookPurchasesRepo repository.EbookPurchasesRepository, log zerolog.Logger, usersRepo usersRepository.UsersRepository) EbookRatingsService { return &ebookRatingsService{ Repo: repo, EbooksRepo: ebooksRepo, EbookPurchasesRepo: ebookPurchasesRepo, Log: log, UsersRepo: usersRepo, } } // GetAll implement interface of EbookRatingsService func (_i *ebookRatingsService) GetAll(req request.EbookRatingsQueryRequest) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) { ratingsData, paging, err := _i.Repo.GetAll(req) if err != nil { return nil, paging, err } ratings = mapper.ToEbookRatingsResponseList(ratingsData) return ratings, paging, nil } // GetByEbookId implement interface of EbookRatingsService func (_i *ebookRatingsService) GetByEbookId(ebookId uint, pagination *paginator.Pagination) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) { ratingsData, paging, err := _i.Repo.GetByEbookId(ebookId, pagination) if err != nil { return nil, paging, err } ratings = mapper.ToEbookRatingsResponseList(ratingsData) return ratings, paging, nil } // GetByUserId implement interface of EbookRatingsService func (_i *ebookRatingsService) GetByUserId(authToken string, pagination *paginator.Pagination) (ratings []*response.EbookRatingsResponse, paging paginator.Pagination, err error) { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { return nil, paginator.Pagination{}, errors.New("user not found") } ratingsData, paging, err := _i.Repo.GetByUserId(user.ID, pagination) if err != nil { return nil, paging, err } ratings = mapper.ToEbookRatingsResponseList(ratingsData) return ratings, paging, nil } // GetEbookRatingSummary implement interface of EbookRatingsService func (_i *ebookRatingsService) GetEbookRatingSummary(ebookId uint) (summary *response.EbookRatingSummaryResponse, err error) { // Get ebook info ebook, err := _i.EbooksRepo.FindOne(ebookId) if err != nil { return nil, errors.New("ebook not found") } // Get rating stats totalRatings, averageRating, ratingCounts, err := _i.Repo.GetEbookRatingStats(ebookId) if err != nil { return nil, err } // Get recent reviews recentReviews, err := _i.Repo.GetRecentReviews(ebookId, 5) if err != nil { return nil, err } summary = mapper.ToEbookRatingSummaryResponse(ebookId, ebook.Title, averageRating, totalRatings, ratingCounts, recentReviews) return summary, nil } // Create implement interface of EbookRatingsService func (_i *ebookRatingsService) Create(req request.EbookRatingsCreateRequest, authToken string) (rating *response.EbookRatingsResponse, err error) { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { return nil, errors.New("user not found") } // Check if ebook exists _, err = _i.EbooksRepo.FindOne(req.EbookId) if err != nil { return nil, errors.New("ebook not found") } // Check if purchase exists and belongs to user purchase, err := _i.EbookPurchasesRepo.FindOne(req.PurchaseId) if err != nil { return nil, errors.New("purchase not found") } if purchase.BuyerId != user.ID { return nil, errors.New("purchase does not belong to user") } if purchase.EbookId != req.EbookId { return nil, errors.New("purchase does not match ebook") } if purchase.PaymentStatus == nil || *purchase.PaymentStatus != "paid" { return nil, errors.New("payment not completed") } // Check if user already rated this ebook existingRating, err := _i.Repo.FindByUserAndEbook(user.ID, req.EbookId) if err == nil && existingRating != nil { return nil, errors.New("user already rated this ebook") } // Check if user already rated this purchase existingPurchaseRating, err := _i.Repo.FindByPurchaseId(req.PurchaseId) if err == nil && existingPurchaseRating != nil { return nil, errors.New("user already rated this purchase") } // Create rating ratingEntity := req.ToEntity() ratingEntity.UserId = user.ID ratingData, err := _i.Repo.Create(ratingEntity) if err != nil { return nil, err } // Update ebook rating stats err = _i.updateEbookRatingStats(req.EbookId) if err != nil { _i.Log.Error().Err(err).Msg("Failed to update ebook rating stats") } rating = mapper.ToEbookRatingsResponse(ratingData) return rating, nil } // Update implement interface of EbookRatingsService func (_i *ebookRatingsService) Update(id uint, req request.EbookRatingsUpdateRequest, authToken string) (err error) { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { return errors.New("user not found") } // Check if rating exists and belongs to user existingRating, err := _i.Repo.FindOne(id) if err != nil { return errors.New("rating not found") } if existingRating.UserId != user.ID { return errors.New("rating does not belong to user") } // Update rating ratingEntity := req.ToEntity() err = _i.Repo.Update(id, ratingEntity) if err != nil { return err } // Update ebook rating stats err = _i.updateEbookRatingStats(existingRating.EbookId) if err != nil { _i.Log.Error().Err(err).Msg("Failed to update ebook rating stats") } return nil } // Delete implement interface of EbookRatingsService func (_i *ebookRatingsService) Delete(id uint, authToken string) error { user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken) if user == nil { return errors.New("user not found") } // Check if rating exists and belongs to user existingRating, err := _i.Repo.FindOne(id) if err != nil { return errors.New("rating not found") } if existingRating.UserId != user.ID { return errors.New("rating does not belong to user") } // Delete rating err = _i.Repo.Delete(id) if err != nil { return err } // Update ebook rating stats err = _i.updateEbookRatingStats(existingRating.EbookId) if err != nil { _i.Log.Error().Err(err).Msg("Failed to update ebook rating stats") } return nil } // GetRatingStats implement interface of EbookRatingsService func (_i *ebookRatingsService) GetRatingStats(ebookId uint) (stats *response.EbookRatingStatsResponse, err error) { totalRatings, averageRating, ratingCounts, err := _i.Repo.GetEbookRatingStats(ebookId) if err != nil { return nil, err } stats = mapper.ToEbookRatingStatsResponse(totalRatings, averageRating, ratingCounts) return stats, nil } // updateEbookRatingStats updates the rating statistics in the ebook table func (_i *ebookRatingsService) updateEbookRatingStats(ebookId uint) error { totalRatings, averageRating, _, err := _i.Repo.GetEbookRatingStats(ebookId) if err != nil { return err } // Update ebook with new rating stats ebookUpdate := &entity.Ebooks{ Rating: &averageRating, ReviewCount: &totalRatings, } err = _i.EbooksRepo.UpdateSkipNull(ebookId, ebookUpdate) if err != nil { return err } return nil }