narasiahli-be/app/module/ebooks/service/ebook_ratings.service.go

281 lines
8.5 KiB
Go

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
}