From e011e582ac41d398c92548b5ec3b36dec0837966 Mon Sep 17 00:00:00 2001 From: me Date: Mon, 23 Feb 2026 22:33:57 +0300 Subject: [PATCH] feat: implement CardService --- .../bankcards/service/CardService.java | 175 ++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 src/main/java/com/example/bankcards/service/CardService.java diff --git a/src/main/java/com/example/bankcards/service/CardService.java b/src/main/java/com/example/bankcards/service/CardService.java new file mode 100644 index 0000000..62593d6 --- /dev/null +++ b/src/main/java/com/example/bankcards/service/CardService.java @@ -0,0 +1,175 @@ +package com.example.bankcards.service; + +import com.example.bankcards.dto.CardResponse; +import com.example.bankcards.dto.CreateCardRequest; +import com.example.bankcards.dto.TransferRequest; +import com.example.bankcards.entity.Card; +import com.example.bankcards.entity.CardStatus; +import com.example.bankcards.entity.User; +import com.example.bankcards.exception.AccessDeniedException; +import com.example.bankcards.exception.BadRequestException; +import com.example.bankcards.exception.ResourceNotFoundException; +import com.example.bankcards.repository.CardRepository; +import com.example.bankcards.repository.UserRepository; +import com.example.bankcards.util.CardEncryptionUtil; +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; + +@Service +@RequiredArgsConstructor +public class CardService { + + private final CardRepository cardRepository; + private final UserRepository userRepository; + private final CardEncryptionUtil encryptionUtil; + + public CardResponse createCard(CreateCardRequest request) { + User owner = userRepository + .findById(request.getOwnerId()) + .orElseThrow(() -> + new ResourceNotFoundException( + "User not found: " + request.getOwnerId() + ) + ); + + String encrypted = encryptionUtil.encrypt(request.getCardNumber()); + Card card = Card.builder() + .cardNumberEncrypted(encrypted) + .owner(owner) + .expiryDate(request.getExpiryDate()) + .status(CardStatus.ACTIVE) + .balance(request.getInitialBalance()) + .build(); + return toResponse(cardRepository.save(card)); + } + + public Page getAllCards(Pageable pageable) { + return cardRepository.findAll(pageable).map(this::toResponse); + } + + public Page getMyCards( + String username, + CardStatus status, + Pageable pageable + ) { + User user = userRepository + .findByUsername(username) + .orElseThrow(() -> new ResourceNotFoundException("User not found")); + if (status != null) { + return cardRepository + .findByOwnerIdAndStatus(user.getId(), status, pageable) + .map(this::toResponse); + } + return cardRepository + .findByOwnerId(user.getId(), pageable) + .map(this::toResponse); + } + + public CardResponse getCard(Long id, String username, boolean isAdmin) { + Card card = cardRepository + .findById(id) + .orElseThrow(() -> + new ResourceNotFoundException("Card not found: " + id) + ); + if (!isAdmin && !card.getOwner().getUsername().equals(username)) { + throw new AccessDeniedException("Access denied"); + } + return toResponse(card); + } + + public CardResponse blockCard(Long id) { + Card card = cardRepository + .findById(id) + .orElseThrow(() -> + new ResourceNotFoundException("Card not found: " + id) + ); + card.setStatus(CardStatus.BLOCKED); + return toResponse(cardRepository.save(card)); + } + + public CardResponse activateCard(Long id) { + Card card = cardRepository + .findById(id) + .orElseThrow(() -> + new ResourceNotFoundException("Card not found: " + id) + ); + card.setStatus(CardStatus.ACTIVE); + return toResponse(cardRepository.save(card)); + } + + public void deleteCard(Long id) { + if (!cardRepository.existsById(id)) { + throw new ResourceNotFoundException("Card not found: " + id); + } + cardRepository.deleteById(id); + } + + public void requestBlock(Long id, String username) { + Card card = cardRepository + .findById(id) + .orElseThrow(() -> + new ResourceNotFoundException("Card not found: " + id) + ); + if (!card.getOwner().getUsername().equals(username)) { + throw new AccessDeniedException("Access denied"); + } + card.setStatus(CardStatus.BLOCKED); + cardRepository.save(card); + } + + @Transactional + public void transfer(TransferRequest request, String username) { + Card from = cardRepository + .findById(request.getFromCardId()) + .orElseThrow(() -> + new ResourceNotFoundException("Source card not found") + ); + Card to = cardRepository + .findById(request.getToCardId()) + .orElseThrow(() -> + new ResourceNotFoundException("Destination card not found") + ); + + if (!from.getOwner().getUsername().equals(username)) { + throw new AccessDeniedException( + "Source card does not belong to you" + ); + } + if (!to.getOwner().getUsername().equals(username)) { + throw new AccessDeniedException( + "Destination card does not belong to you" + ); + } + if (from.getStatus() != CardStatus.ACTIVE) { + throw new BadRequestException("Source card is not active"); + } + if (to.getStatus() != CardStatus.ACTIVE) { + throw new BadRequestException("Destination card is not active"); + } + if (from.getBalance().compareTo(request.getAmount()) < 0) { + throw new BadRequestException("Insufficient balance"); + } + + from.setBalance(from.getBalance().subtract(request.getAmount())); + to.setBalance(to.getBalance().add(request.getAmount())); + cardRepository.save(from); + cardRepository.save(to); + } + + public CardResponse toResponse(Card card) { + CardResponse response = new CardResponse(); + response.setId(card.getId()); + String decrypted = encryptionUtil.decrypt( + card.getCardNumberEncrypted() + ); + response.setMaskedNumber(encryptionUtil.mask(decrypted)); + response.setOwnerUsername(card.getOwner().getUsername()); + response.setExpiryDate(card.getExpiryDate()); + response.setStatus(card.getStatus()); + response.setBalance(card.getBalance()); + return response; + } +}