StockHistoryService.java 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. package com.danielbohry.stocks.service.stock;
  2. import com.danielbohry.stocks.domain.StockHistory;
  3. import com.danielbohry.stocks.exception.BadRequestException;
  4. import com.danielbohry.stocks.repository.stock.StockHistoryRepository;
  5. import lombok.AllArgsConstructor;
  6. import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
  7. import org.springframework.cache.annotation.Cacheable;
  8. import org.springframework.scheduling.annotation.Scheduled;
  9. import org.springframework.stereotype.Service;
  10. import java.time.Instant;
  11. import java.time.LocalDate;
  12. import java.time.ZoneOffset;
  13. import java.util.List;
  14. import static java.time.Instant.now;
  15. import static java.time.ZoneOffset.UTC;
  16. import static java.time.temporal.ChronoUnit.DAYS;
  17. @Service
  18. @AllArgsConstructor
  19. public class StockHistoryService {
  20. private final StockService service;
  21. private final StockHistoryRepository repository;
  22. public static final String ONCE_PER_DAY_AT_2AM = "0 0 2 * * *";
  23. @Scheduled(cron = ONCE_PER_DAY_AT_2AM)
  24. @SchedulerLock(name = "StockQuoteService_saveQuotes", lockAtLeastFor = "PT5M", lockAtMostFor = "PT15M")
  25. public void saveQuotes() {
  26. LocalDate today = LocalDate.now(UTC);
  27. Instant startOfDay = today.atStartOfDay(UTC).toInstant();
  28. Instant endOfDay = today.plusDays(1).atStartOfDay(UTC).toInstant();
  29. List<StockHistory> stocksToSave = service.getAll().stream()
  30. .filter(stock -> !repository.existsByCodeAndCreatedAtBetween(stock.getCode(), startOfDay, endOfDay))
  31. .map(stock -> new StockHistory(
  32. stock.getCode(),
  33. stock.getCurrency(),
  34. stock.getPrice(),
  35. now()
  36. ))
  37. .toList();
  38. repository.saveAll(stocksToSave);
  39. }
  40. @Cacheable(value = "stockHistory", key = "#code + '-' + #range")
  41. public List<StockHistory> get(String code, String range) {
  42. Instant end = now();
  43. Instant start = switch (range) {
  44. case "5d" -> end.minus(5, DAYS);
  45. case "30d" -> end.minus(30, DAYS);
  46. case "6m" -> end.minus(180, DAYS);
  47. case "1y" -> end.minus(365, DAYS);
  48. default -> throw new BadRequestException("Unsupported range: " + range);
  49. };
  50. return repository.findByCodeAndCreatedAtBetween(code, start, end);
  51. }
  52. }