PortfolioService.java 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package com.danielbohry.stocks.service;
  2. import com.danielbohry.stocks.context.UserContextHolder;
  3. import com.danielbohry.stocks.domain.Portfolio;
  4. import com.danielbohry.stocks.domain.Quote;
  5. import com.danielbohry.stocks.domain.Stock;
  6. import com.danielbohry.stocks.exception.BadRequestException;
  7. import com.danielbohry.stocks.exception.NotFoundException;
  8. import com.danielbohry.stocks.repository.PortfolioRepository;
  9. import com.danielbohry.stocks.service.ExchangeService.ExchangeRateResponse;
  10. import lombok.AllArgsConstructor;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.springframework.cache.annotation.Cacheable;
  13. import org.springframework.stereotype.Service;
  14. import java.math.BigDecimal;
  15. import java.math.RoundingMode;
  16. import java.util.List;
  17. import java.util.Map;
  18. import java.util.Optional;
  19. import java.util.UUID;
  20. import static java.time.LocalDateTime.now;
  21. import static java.util.Collections.emptyList;
  22. @Slf4j
  23. @Service
  24. @AllArgsConstructor
  25. public class PortfolioService {
  26. private final PortfolioRepository repository;
  27. private final StockService stockService;
  28. private final ExchangeService exchangeService;
  29. public List<Portfolio> getAll() {
  30. return repository.findAll();
  31. }
  32. public List<String> getAllIds() {
  33. return repository.findAllPortfolioIds();
  34. }
  35. @Cacheable(value = "portfolio", key = "#username + '_' + #currency")
  36. public List<Portfolio> getByUser(String username, String currency) {
  37. List<Portfolio> portfolios = repository.findAllByUsername(username);
  38. return portfolios.stream()
  39. .map(portfolio -> get(portfolio.getId(), currency))
  40. .toList();
  41. }
  42. public Portfolio get(String id, String currency) {
  43. Portfolio portfolio = repository.findById(id)
  44. .orElseThrow(() -> new NotFoundException("No portfolio found with id: " + id));
  45. ExchangeRateResponse exchangeRate = exchangeService.getCurrentRate(currency);
  46. Map<String, BigDecimal> rates = exchangeRate.getConversionRates();
  47. BigDecimal targetRate = rates.getOrDefault(currency, BigDecimal.ONE);
  48. log.info("Getting portfolio [{}] and converting to [{}]", id, currency);
  49. List<Stock> updatedStocks = portfolio.getStocks().stream()
  50. .peek(stock -> {
  51. Quote quote = stockService.getStockQuote(stock.getCode());
  52. stock.setName(quote.getName());
  53. BigDecimal quoteRate = rates.getOrDefault(quote.getCurrency(), BigDecimal.ONE);
  54. BigDecimal convertedPrice = quote.getPrice()
  55. .divide(quoteRate, 2, RoundingMode.HALF_UP)
  56. .multiply(targetRate);
  57. stock.setPrice(convertedPrice);
  58. stock.setTotal(convertedPrice.multiply(new BigDecimal(stock.getQuantity())));
  59. }).toList();
  60. portfolio.setStocks(updatedStocks);
  61. return portfolio;
  62. }
  63. public Portfolio create() {
  64. String id = UUID.randomUUID().toString();
  65. Portfolio toSave = Portfolio.builder()
  66. .id(id)
  67. .stocks(emptyList())
  68. .username(UserContextHolder.get().getUsername())
  69. .createdAt(now())
  70. .updatedAt(now())
  71. .build();
  72. return repository.save(toSave);
  73. }
  74. public Portfolio update(String id, List<Stock> stocks) {
  75. log.info("Updating portfolio [{}]", id);
  76. Optional<Portfolio> portfolio = repository.findById(id);
  77. if (portfolio.isEmpty()) {
  78. throw new NotFoundException("Failed to update portfolio with id: " + id);
  79. }
  80. validate(stocks);
  81. Portfolio toUpdate = portfolio.get();
  82. toUpdate.setUpdatedAt(now());
  83. toUpdate.setStocks(stocks);
  84. return repository.save(toUpdate);
  85. }
  86. public void delete(String id) {
  87. log.info("Deleting portfolio [{}]", id);
  88. repository.deleteById(id);
  89. }
  90. public void cleanup() {
  91. repository.findAllByEmptyStocks().forEach(portfolio -> {
  92. if (portfolio.getStocks().isEmpty()) {
  93. log.info("Removing empty portfolio [{}]", portfolio.getId());
  94. repository.deleteById(portfolio.getId());
  95. }
  96. });
  97. }
  98. private void validate(List<Stock> stocks) {
  99. boolean anyInvalid = stocks.stream()
  100. .anyMatch(stock -> !stockService.isValid(stock.getCode()));
  101. if (anyInvalid) {
  102. throw new BadRequestException("Invalid stock found");
  103. }
  104. }
  105. }