Kaynağa Gözat

save stock history (#9)

Daniel Bohry 7 ay önce
ebeveyn
işleme
ef9e90e285

+ 2 - 0
build.gradle

@@ -24,6 +24,8 @@ dependencies {
     implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.0.2'
     implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
     implementation 'com.github.ben-manes.caffeine:caffeine'
+    implementation 'net.javacrumbs.shedlock:shedlock-spring:6.4.0'
+    implementation 'net.javacrumbs.shedlock:shedlock-provider-mongo:6.4.0'
 
     compileOnly 'org.projectlombok:lombok:1.18.30'
     annotationProcessor 'org.projectlombok:lombok:1.18.30'

+ 1 - 1
src/main/java/com/danielbohry/stocks/client/FinanceClient.java

@@ -2,7 +2,7 @@ package com.danielbohry.stocks.client;
 
 import com.danielbohry.stocks.domain.Quote;
 import com.danielbohry.stocks.domain.StockInfo;
-import com.danielbohry.stocks.repository.StockRepository.StockMetadataResponse;
+import com.danielbohry.stocks.repository.stock.StockRepository.StockMetadataResponse;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;

+ 3 - 3
src/main/java/com/danielbohry/stocks/client/StockClient.java

@@ -1,9 +1,9 @@
 package com.danielbohry.stocks.client;
 
 import com.danielbohry.stocks.config.FeignConfig;
-import com.danielbohry.stocks.repository.StockRepository.StockInfoResponse;
-import com.danielbohry.stocks.repository.StockRepository.StockMetadataResponse;
-import com.danielbohry.stocks.repository.StockRepository.StockQuoteResponse;
+import com.danielbohry.stocks.repository.stock.StockRepository.StockInfoResponse;
+import com.danielbohry.stocks.repository.stock.StockRepository.StockMetadataResponse;
+import com.danielbohry.stocks.repository.stock.StockRepository.StockQuoteResponse;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;

+ 20 - 0
src/main/java/com/danielbohry/stocks/config/ShedLockConfig.java

@@ -0,0 +1,20 @@
+package com.danielbohry.stocks.config;
+
+import com.mongodb.client.MongoClient;
+import net.javacrumbs.shedlock.core.LockProvider;
+import net.javacrumbs.shedlock.provider.mongo.MongoLockProvider;
+import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
+public class ShedLockConfig {
+
+    @Bean
+    public LockProvider lockProvider(MongoClient mongoClient) {
+        return new MongoLockProvider(
+            mongoClient.getDatabase("stocks").getCollection("shedlock")
+        );
+    }
+}

+ 1 - 1
src/main/java/com/danielbohry/stocks/domain/StockInfo.java

@@ -10,7 +10,7 @@ import java.time.Instant;
 
 @Data
 @Builder
-@Document("stock-infos")
+@Document("stock-metadata")
 public class StockInfo {
 
     @Id

+ 1 - 1
src/main/java/com/danielbohry/stocks/repository/QuoteRepository.java → src/main/java/com/danielbohry/stocks/repository/stock/QuoteRepository.java

@@ -1,4 +1,4 @@
-package com.danielbohry.stocks.repository;
+package com.danielbohry.stocks.repository.stock;
 
 import com.danielbohry.stocks.domain.Quote;
 import org.springframework.data.mongodb.repository.MongoRepository;

+ 22 - 0
src/main/java/com/danielbohry/stocks/repository/stock/StockHistory.java

@@ -0,0 +1,22 @@
+package com.danielbohry.stocks.repository.stock;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.data.mongodb.core.mapping.Document;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Document("stock-history")
+public class StockHistory {
+
+    private String code;
+    private String currency;
+    private BigDecimal price;
+    private Instant createdAt;
+
+}

+ 8 - 0
src/main/java/com/danielbohry/stocks/repository/stock/StockHistoryRepository.java

@@ -0,0 +1,8 @@
+package com.danielbohry.stocks.repository.stock;
+
+import org.springframework.data.mongodb.repository.MongoRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface StockHistoryRepository extends MongoRepository<StockHistory, String> {
+}

+ 1 - 1
src/main/java/com/danielbohry/stocks/repository/StockInfoRepository.java → src/main/java/com/danielbohry/stocks/repository/stock/StockInfoRepository.java

@@ -1,4 +1,4 @@
-package com.danielbohry.stocks.repository;
+package com.danielbohry.stocks.repository.stock;
 
 import com.danielbohry.stocks.domain.StockInfo;
 import org.springframework.data.mongodb.repository.MongoRepository;

+ 1 - 1
src/main/java/com/danielbohry/stocks/repository/StockRepository.java → src/main/java/com/danielbohry/stocks/repository/stock/StockRepository.java

@@ -1,4 +1,4 @@
-package com.danielbohry.stocks.repository;
+package com.danielbohry.stocks.repository.stock;
 
 import com.danielbohry.stocks.client.StockClient;
 import com.danielbohry.stocks.domain.Quote;

+ 2 - 0
src/main/java/com/danielbohry/stocks/service/SnapshotService.java

@@ -6,6 +6,7 @@ import com.danielbohry.stocks.repository.snapshot.PortfolioSnapshotEntity;
 import com.danielbohry.stocks.repository.snapshot.SnapshotRepository;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Service;
 
@@ -26,6 +27,7 @@ public class SnapshotService {
     public static final String DEFAULT_CURRENCY = "USD";
 
     @Scheduled(cron = ONCE_PER_MONTH)
+    @SchedulerLock(name = "SnapshotService_createSnapshots", lockAtLeastFor = "PT2M", lockAtMostFor = "PT10M")
     public void createSnapshots() {
         List<Portfolio> portfolios = service.getAllIds().stream()
             .map(portfolioId -> service.get(portfolioId, DEFAULT_CURRENCY))

+ 2 - 2
src/main/java/com/danielbohry/stocks/service/StockInfoService.java

@@ -4,8 +4,8 @@ import com.danielbohry.stocks.client.FinanceClient;
 import com.danielbohry.stocks.domain.Quote;
 import com.danielbohry.stocks.domain.StockInfo;
 import com.danielbohry.stocks.exception.NotFoundException;
-import com.danielbohry.stocks.repository.StockInfoRepository;
-import com.danielbohry.stocks.repository.StockRepository;
+import com.danielbohry.stocks.repository.stock.StockInfoRepository;
+import com.danielbohry.stocks.repository.stock.StockRepository;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.Cacheable;

+ 33 - 0
src/main/java/com/danielbohry/stocks/service/StockQuoteService.java

@@ -0,0 +1,33 @@
+package com.danielbohry.stocks.service;
+
+import com.danielbohry.stocks.repository.stock.StockHistory;
+import com.danielbohry.stocks.repository.stock.StockHistoryRepository;
+import lombok.AllArgsConstructor;
+import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+import static java.time.Instant.now;
+
+@Service
+@AllArgsConstructor
+public class StockQuoteService {
+
+    private final StockService service;
+    private final StockHistoryRepository repository;
+
+    public static final String ONCE_PER_DAY_AT_11PM = "0 0 23 * * *";
+
+    @Scheduled(cron = ONCE_PER_DAY_AT_11PM)
+    @SchedulerLock(name = "StockQuoteService_saveQuotes", lockAtLeastFor = "PT5M", lockAtMostFor = "PT15M")
+    public void saveQuotes() {
+        List<StockHistory> stocks = service.getAll().stream()
+            .map(stock -> new StockHistory(stock.getCode(), stock.getCurrency(), stock.getPrice(), now()))
+            .toList();
+
+        repository.saveAll(stocks);
+    }
+
+}

+ 1 - 1
src/main/java/com/danielbohry/stocks/service/StockService.java

@@ -1,7 +1,7 @@
 package com.danielbohry.stocks.service;
 
 import com.danielbohry.stocks.domain.Quote;
-import com.danielbohry.stocks.repository.StockRepository;
+import com.danielbohry.stocks.repository.stock.StockRepository;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.Cacheable;

+ 1 - 1
src/test/java/service/PortfolioServiceTest.java

@@ -4,7 +4,7 @@ import com.danielbohry.stocks.App;
 import com.danielbohry.stocks.domain.Portfolio;
 import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.repository.portfolio.PortfolioRepository;
-import com.danielbohry.stocks.repository.StockRepository;
+import com.danielbohry.stocks.repository.stock.StockRepository;
 import com.danielbohry.stocks.service.ExchangeService;
 import com.danielbohry.stocks.service.PortfolioEncryptService;
 import com.danielbohry.stocks.service.PortfolioService;

+ 1 - 4
src/test/java/service/StockServiceTest.java

@@ -1,12 +1,9 @@
 package service;
 
 import com.danielbohry.stocks.App;
-import com.danielbohry.stocks.client.InferenceClient;
 import com.danielbohry.stocks.domain.Quote;
-import com.danielbohry.stocks.repository.StockInfoRepository;
-import com.danielbohry.stocks.repository.StockRepository;
+import com.danielbohry.stocks.repository.stock.StockRepository;
 import com.danielbohry.stocks.service.StockService;
-import org.checkerframework.checker.units.qual.A;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;