StockRepository.java 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package com.danielbohry.stocks.repository;
  2. import com.danielbohry.stocks.client.StockClient;
  3. import com.danielbohry.stocks.domain.Quote;
  4. import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
  5. import com.fasterxml.jackson.annotation.JsonProperty;
  6. import feign.FeignException;
  7. import lombok.AllArgsConstructor;
  8. import lombok.Data;
  9. import lombok.NoArgsConstructor;
  10. import lombok.extern.slf4j.Slf4j;
  11. import org.springframework.beans.factory.annotation.Value;
  12. import org.springframework.stereotype.Repository;
  13. import java.math.BigDecimal;
  14. import java.time.Instant;
  15. import java.util.*;
  16. import java.util.function.Function;
  17. import java.util.stream.Collectors;
  18. import static java.time.LocalDateTime.now;
  19. import static java.util.stream.Collectors.toList;
  20. @Slf4j
  21. @Repository
  22. public class StockRepository {
  23. private final QuoteRepository repository;
  24. private final StockClient client;
  25. private final String key;
  26. public StockRepository(QuoteRepository repository, StockClient client, @Value("${clients.stock.key}") String key) {
  27. this.repository = repository;
  28. this.client = client;
  29. this.key = key;
  30. }
  31. public List<Quote> findAll() {
  32. return repository.findAll();
  33. }
  34. public List<Quote> findLike(String query) {
  35. return repository.findByNameOrCode(query);
  36. }
  37. public Quote findByCode(String code) {
  38. Optional<Quote> quote = repository.findByCode(code);
  39. return quote.orElseGet(() -> repository.save(getStockQuote(code)));
  40. }
  41. public List<Quote> update(List<Quote> quotes) {
  42. log.info("Updating [{}] quotes [{}]", quotes.size(), Instant.now());
  43. List<String> codes = quotes.stream()
  44. .map(Quote::getCode)
  45. .collect(toList());
  46. Map<String, Quote> existingQuotesMap = repository.findByCodeIn(codes)
  47. .stream()
  48. .collect(Collectors.toMap(Quote::getCode, Function.identity()));
  49. log.info("Found [{}] quotes by codes [{}]", existingQuotesMap.size(), Instant.now());
  50. List<Quote> updatedQuotes = quotes.stream()
  51. .map(quote -> {
  52. Quote existingQuote = existingQuotesMap.get(quote.getCode());
  53. if (existingQuote != null) {
  54. quote.setId(existingQuote.getId());
  55. }
  56. return quote;
  57. })
  58. .collect(toList());
  59. log.info("Updating [{}] quotes [{}]", updatedQuotes.size(), Instant.now());
  60. List<Quote> result = repository.saveAll(updatedQuotes);
  61. log.info("Update complete for [{}] quotes [{}]", quotes.size(), Instant.now());
  62. return result;
  63. }
  64. public boolean isValid(String code) {
  65. Quote quote = repository.findByCode(code).stream().findFirst().orElse(null);
  66. if (quote != null && "BRL".equals(quote.getCurrency())) return true;
  67. try {
  68. log.info("Current stock's name is null. Requesting latest information...");
  69. client.getStockInfo(code, key);
  70. return true;
  71. } catch (FeignException.NotFound e) {
  72. return false;
  73. }
  74. }
  75. public Quote getStockQuote(String code) {
  76. Quote quote = repository.findByCode(code).stream().findFirst().orElse(new Quote(code, null, null, null, now()));
  77. quote.setPrice(getLastPrice(quote));
  78. if (quote.getName() == null || quote.getPrice() == null) {
  79. StockInfoResponse info = updateStockInformation(quote.getCode());
  80. quote.setName(info.getName());
  81. repository.save(quote);
  82. }
  83. return quote;
  84. }
  85. private StockInfoResponse updateStockInformation(String code) {
  86. log.info("Current stock's name is null. Requesting latest information...");
  87. return client.getStockInfo(code, key);
  88. }
  89. private BigDecimal getLastPrice(Quote quote) {
  90. if (quote.getPrice() == null && "USD".equals(quote.getCurrency())) {
  91. log.info("Current quote for [{}] is null. Requesting latest quote...", quote);
  92. return new BigDecimal(client.getStockQuote(quote.getCode(), key).get(0).getLastPrice());
  93. } else if (quote.getUpdatedAt().isBefore(now().minusDays(5))) {
  94. log.info("Current quote for [{}] is older than 1 day. Requesting latest quote...", quote);
  95. return new BigDecimal(client.getStockQuote(quote.getCode(), key).get(0).getLastPrice());
  96. } else {
  97. return quote.getPrice();
  98. }
  99. }
  100. @Data
  101. @AllArgsConstructor
  102. @NoArgsConstructor
  103. @JsonIgnoreProperties(ignoreUnknown = true)
  104. public static class StockQuoteResponse {
  105. @JsonProperty("adjClose")
  106. private String lastPrice;
  107. @JsonProperty("adjOpen")
  108. private String openPrice;
  109. }
  110. @Data
  111. @AllArgsConstructor
  112. @NoArgsConstructor
  113. @JsonIgnoreProperties(ignoreUnknown = true)
  114. public static class StockInfoResponse {
  115. @JsonProperty("ticker")
  116. private String code;
  117. @JsonProperty("name")
  118. private String name;
  119. @JsonProperty("exchangeCode")
  120. private String exchange;
  121. }
  122. }