EbookPurchaseService.java

package qwerty.chaekit.service.ebook;

import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import qwerty.chaekit.domain.ebook.Ebook;
import qwerty.chaekit.domain.ebook.credit.usage.CreditUsageTransaction;
import qwerty.chaekit.domain.ebook.credit.usage.CreditUsageTransactionRepository;
import qwerty.chaekit.domain.ebook.credit.usage.CreditUsageTransactionType;
import qwerty.chaekit.domain.ebook.credit.wallet.CreditWallet;
import qwerty.chaekit.domain.ebook.credit.wallet.CreditWalletRepository;
import qwerty.chaekit.domain.ebook.purchase.EbookPurchase;
import qwerty.chaekit.domain.ebook.purchase.repository.EbookPurchaseRepository;
import qwerty.chaekit.domain.ebook.repository.EbookRepository;
import qwerty.chaekit.domain.member.user.UserProfile;
import qwerty.chaekit.domain.member.user.UserProfileRepository;
import qwerty.chaekit.dto.ebook.EbookFetchResponse;
import qwerty.chaekit.dto.ebook.purchase.EbookPurchaseResponse;
import qwerty.chaekit.dto.page.PageResponse;
import qwerty.chaekit.global.enums.ErrorCode;
import qwerty.chaekit.global.exception.BadRequestException;
import qwerty.chaekit.global.exception.NotFoundException;
import qwerty.chaekit.service.util.EntityFinder;
import qwerty.chaekit.service.util.FileService;

@Service
@Transactional
@RequiredArgsConstructor
public class EbookPurchaseService {
    private final CreditUsageTransactionRepository creditUsageTransactionRepository;
    private final EbookPurchaseRepository ebookPurchaseRepository;

    private final UserProfileRepository userRepository;
    private final CreditWalletRepository creditWalletRepository;
    private final EbookRepository ebookRepository;
    private final FileService fileService;
    private final EntityFinder entityFinder;

    @Transactional
    public EbookPurchaseResponse purchaseEbook(Long ebookId, Long userId) {
        UserProfile buyer = userRepository.findById(userId)
                .orElseThrow(() -> new NotFoundException(ErrorCode.USER_NOT_FOUND));
        CreditWallet wallet = creditWalletRepository.findByUser_Id(userId)
                .orElseThrow(() -> new IllegalStateException("크레딧 지갑이 존재하지 않습니다."));

        Ebook ebook = ebookRepository.findByIdWithPublisher(ebookId)
                .orElseThrow(() -> new NotFoundException(ErrorCode.EBOOK_NOT_FOUND));

        if(ebookPurchaseRepository.existsByUserIdAndEbookId(userId, ebookId)) {
            throw new BadRequestException(ErrorCode.EBOOK_ALREADY_PURCHASED);
        }

        int ebookPrice = ebook.getPrice();
        try {
            wallet.useCredit(ebookPrice);
        } catch (Exception e) {
            throw new BadRequestException(ErrorCode.CREDIT_NOT_ENOUGH);
        }

        CreditUsageTransaction savedTransaction = creditUsageTransactionRepository.save(
                CreditUsageTransaction.builder()
                        .wallet(wallet)
                        .creditAmount(ebookPrice)
                        .transactionType(CreditUsageTransactionType.PURCHASE)
                        .build()
        );

        ebookPurchaseRepository.save(
                EbookPurchase.builder()
                        .user(buyer)
                        .ebook(ebook)
                        .transaction(savedTransaction)
                        .build()
        );

        return EbookPurchaseResponse.of(
                userId,
                ebook,
                savedTransaction,
                fileService.getEbookDownloadUrl(ebook.getFileKey())
        );
    }

    public PageResponse<EbookFetchResponse> getMyBooks(Long userId, Pageable pageable) {
        Page<EbookPurchase> purchases = ebookPurchaseRepository.findByUserIdWithEbook(userId, pageable);
        return PageResponse.of(
                purchases.map(
                purchase -> EbookFetchResponse.of(
                        purchase.getEbook(),
                        fileService.convertToPublicImageURL(purchase.getEbook().getCoverImageKey()),
                        true
                )));
    }
}