TransferCryptoService.java
package com.distasilucas.cryptobalancetracker.service;
import com.distasilucas.cryptobalancetracker.entity.UserCrypto;
import com.distasilucas.cryptobalancetracker.exception.ApiValidationException;
import com.distasilucas.cryptobalancetracker.exception.InsufficientBalanceException;
import com.distasilucas.cryptobalancetracker.model.request.usercrypto.TransferCryptoRequest;
import com.distasilucas.cryptobalancetracker.model.response.usercrypto.TransferCryptoResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.UUID;
import static com.distasilucas.cryptobalancetracker.constants.ExceptionConstants.SAME_FROM_TO_PLATFORM;
@Slf4j
@Service
@RequiredArgsConstructor
public class TransferCryptoService {
private final UserCryptoService userCryptoService;
private final PlatformService platformService;
public TransferCryptoResponse transferCrypto(TransferCryptoRequest transferCryptoRequest) {
var toPlatform = platformService.retrievePlatformById(transferCryptoRequest.toPlatformId());
var userCryptoToTransfer = userCryptoService.findUserCryptoById(transferCryptoRequest.userCryptoId());
var fromPlatform = platformService.retrievePlatformById(userCryptoToTransfer.getPlatform().getId());
if (isToAndFromSamePlatform(toPlatform.getId(), fromPlatform.getId())) {
throw new ApiValidationException(HttpStatus.BAD_REQUEST, SAME_FROM_TO_PLATFORM);
}
var availableQuantity = userCryptoToTransfer.getQuantity();
var quantityToTransfer = transferCryptoRequest.quantityToTransfer();
if (transferCryptoRequest.hasInsufficientBalance(availableQuantity)) {
throw new InsufficientBalanceException();
}
var remainingCryptoQuantity = transferCryptoRequest.calculateRemainingCryptoQuantity(availableQuantity);
var quantityToSendReceive = transferCryptoRequest.calculateQuantityToSendReceive(remainingCryptoQuantity, availableQuantity);
var toPlatformOptionalUserCrypto = userCryptoService.findByCoingeckoCryptoIdAndPlatformId(
userCryptoToTransfer.getCrypto().getId(),
transferCryptoRequest.toPlatformId()
);
TransferCryptoResponse transferCryptoResponse = null;
if (doesFromPlatformHaveRemaining(remainingCryptoQuantity) && toPlatformOptionalUserCrypto.isPresent()) {
var toPlatformUserCrypto = toPlatformOptionalUserCrypto.get();
var newQuantity = toPlatformUserCrypto.getQuantity().add(quantityToSendReceive);
var updatedFromPlatformUserCrypto = userCryptoToTransfer.withQuantity(remainingCryptoQuantity);
var updatedToPlatformUserCrypto = toPlatformUserCrypto.withQuantity(newQuantity);
userCryptoService.saveOrUpdateAll(List.of(updatedFromPlatformUserCrypto, updatedToPlatformUserCrypto));
transferCryptoResponse = transferCryptoRequest.toTransferCryptoResponse(
remainingCryptoQuantity,
newQuantity,
quantityToSendReceive
);
}
if (doesFromPlatformHaveRemaining(remainingCryptoQuantity) && toPlatformOptionalUserCrypto.isEmpty()) {
var uuid = UUID.randomUUID().toString();
var toPlatformUserCrypto = new UserCrypto(
uuid,
quantityToSendReceive,
toPlatform,
userCryptoToTransfer.getCrypto()
);
var updatedUserCryptoToTransfer = userCryptoToTransfer.withQuantity(remainingCryptoQuantity);
if (Boolean.TRUE.equals(transferCryptoRequest.sendFullQuantity())) {
userCryptoService.saveOrUpdateAll(List.of(updatedUserCryptoToTransfer, toPlatformUserCrypto));
} else {
if (quantityToSendReceive.compareTo(BigDecimal.ZERO) > 0) {
userCryptoService.saveOrUpdateAll(List.of(updatedUserCryptoToTransfer, toPlatformUserCrypto));
} else {
userCryptoService.saveOrUpdateAll(List.of(updatedUserCryptoToTransfer));
}
}
transferCryptoResponse = transferCryptoRequest.toTransferCryptoResponse(
remainingCryptoQuantity,
quantityToSendReceive,
quantityToSendReceive
);
}
if (!doesFromPlatformHaveRemaining(remainingCryptoQuantity) && toPlatformOptionalUserCrypto.isPresent()) {
var toPlatformUserCrypto = toPlatformOptionalUserCrypto.get();
var newQuantity = toPlatformUserCrypto.getQuantity().add(quantityToSendReceive);
var updatedToPlatformUserCrypto = toPlatformUserCrypto.withQuantity(newQuantity);
userCryptoService.deleteUserCrypto(userCryptoToTransfer.getId());
userCryptoService.saveOrUpdateAll(List.of(updatedToPlatformUserCrypto));
transferCryptoResponse = transferCryptoRequest.toTransferCryptoResponse(
remainingCryptoQuantity,
newQuantity,
quantityToSendReceive
);
}
if (!doesFromPlatformHaveRemaining(remainingCryptoQuantity) && toPlatformOptionalUserCrypto.isEmpty()) {
var updatedFromPlatformUserCrypto = new UserCrypto(
userCryptoToTransfer.getId(),
quantityToSendReceive,
toPlatform,
userCryptoToTransfer.getCrypto()
);
if (updatedFromPlatformUserCrypto.getQuantity().compareTo(BigDecimal.ZERO) > 0) {
userCryptoService.saveOrUpdateAll(List.of(updatedFromPlatformUserCrypto));
} else {
userCryptoService.deleteUserCrypto(updatedFromPlatformUserCrypto.getId());
}
transferCryptoResponse = transferCryptoRequest.toTransferCryptoResponse(
remainingCryptoQuantity,
quantityToSendReceive,
quantityToSendReceive
);
}
log.info("Transferred {} of {} from platform {} to {}", quantityToTransfer, userCryptoToTransfer.getCrypto().getId(), fromPlatform.getName(), toPlatform.getName());
return transferCryptoResponse;
}
private boolean isToAndFromSamePlatform(String toPlatformId, String fromPlatformId) {
return toPlatformId.equalsIgnoreCase(fromPlatformId);
}
private boolean doesFromPlatformHaveRemaining(BigDecimal remainingCryptoQuantity) {
return remainingCryptoQuantity.compareTo(BigDecimal.ZERO) > 0;
}
}