281 lines
8.5 KiB
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
|
|
}
|