Skip to content

Commit

Permalink
Merge pull request #80 from PDA4-Phoenix/feat/stock-recommend
Browse files Browse the repository at this point in the history
Feat/stock recommend
  • Loading branch information
ghdeo authored Oct 19, 2023
2 parents e7b176f + 10aa2f0 commit c12c5e5
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 79 deletions.
86 changes: 86 additions & 0 deletions src/main/java/com/bull4jo/kkanbustock/common/CustomParser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package com.bull4jo.kkanbustock.common;

import com.bull4jo.kkanbustock.stock.domain.MarketType;
import com.bull4jo.kkanbustock.stock.domain.Stock;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class CustomParser {

public static Stock parseSingleJsonResponse(String jsonResponse) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode responseJson = objectMapper.readTree(jsonResponse);

JsonNode item = responseJson
.path("response")
.path("body")
.path("items")
.path("item")
.get(0);

float fltRt = CustomParser.formatFltRt(item.get("fltRt").asText());
Stock stock = Stock.builder()
.srtnCd(item.get("srtnCd").asText())
.itmsNm(item.get("itmsNm").asText())
.mrktCtg(MarketType.valueOf(item.get("mrktCtg").asText()))
.clpr(item.get("clpr").asInt())
.mkp(item.get("mkp").asInt())
.basDt(item.get("basDt").asInt())
.vs(item.get("vs").asInt())
.fltRt(fltRt)
.trPrc(item.get("trPrc").asLong())
.mrktTotAmt(item.get("mrktTotAmt").asLong())
.build();

return stock;
}

public static List<Stock> parseJsonResponses(String jsonResponse) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
JsonNode responseJson = objectMapper.readTree(jsonResponse);
JsonNode items = responseJson.path("response").path("body").path("items").path("item");

List<Stock> list = new ArrayList<>();


for (JsonNode itemNode : items) {
float fltRt = CustomParser.formatFltRt(itemNode.get("fltRt").asText());

Stock stock = Stock.builder()
.srtnCd(itemNode.get("srtnCd").asText())
.itmsNm(itemNode.get("itmsNm").asText())
.mrktCtg(MarketType.valueOf(itemNode.get("mrktCtg").asText()))
.clpr(itemNode.get("clpr").asInt())
.mkp(itemNode.get("mkp").asInt())
.basDt(itemNode.get("basDt").asInt())
.vs(itemNode.get("vs").asInt())
.fltRt(fltRt)
.trPrc(itemNode.get("trPrc").asLong())
.mrktTotAmt(itemNode.get("mrktTotAmt").asLong())
.build();

list.add(stock);
}

return list;
}

public static float formatFltRt(String input) {
try {
// 문자열을 부동 소수점 숫자로 파싱
double parsedValue = Double.parseDouble(input);

// 소수점 이하 두 자리까지 포맷
String formattedValue = String.format("%.2f", parsedValue);
// 포맷된 문자열을 float로 파싱하여 반환
return Float.parseFloat(formattedValue);
} catch (NumberFormatException e) {
return 0.0f;
}
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.bull4jo.kkanbustock.common;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class RecentWeekdayFinder {

public static String findMostRecentWeekday() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
String today = dateFormat.format(new Date());

while (true) {
calendar.add(Calendar.DAY_OF_MONTH, -1); // 하루씩 이전 날짜로 이동
Date currentDate = calendar.getTime();
String currentDateStr = dateFormat.format(currentDate);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);

// 주말(토요일 또는 일요일)이 아니면 반환
if (dayOfWeek != Calendar.SATURDAY && dayOfWeek != Calendar.SUNDAY) {
return currentDateStr;
}

// 오늘과 같은 날짜면 루프 종료
if (currentDateStr.equals(today)) {
break;
}
}

return null; // 가장 최근 평일을 찾을 수 없을 경우
}
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.bull4jo.kkanbustock.stock.controller;

import com.bull4jo.kkanbustock.member.domain.entity.InvestorType;
import com.bull4jo.kkanbustock.stock.domain.RecommendStockResponse;
import com.bull4jo.kkanbustock.stock.domain.Stock;
import com.bull4jo.kkanbustock.stock.service.StockService;
Expand Down Expand Up @@ -28,11 +29,22 @@ public ResponseEntity<Stock> searchStockByName(@RequestParam(value = "itmsNm") f
}

@GetMapping("/v1/recommends")
public ResponseEntity<Page<RecommendStockResponse>> getRecommendedStocks(@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "15") int size) {
public ResponseEntity<Page<RecommendStockResponse>> getRecommendedStocks(
@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "15") int size) {
Pageable pageable = PageRequest.of(page, size);
return ResponseEntity.ok(stockService.getRecommendedStocks(pageable));
}

@GetMapping("/v1/recommends/{investorType}")
public ResponseEntity<Page<RecommendStockResponse>> getRecommendStocksByInvestorType(
@PathVariable InvestorType investorType,
@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "15") int size) {
Pageable pageable = PageRequest.of(page, size);
return ResponseEntity.ok(stockService.getRecommendStocksByInvestorType(investorType, pageable));
}

@GetMapping("/v1/init-stocks")
public void setInitStock() {
stockService.setStockRepository();
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/com/bull4jo/kkanbustock/stock/domain/Stock.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public class Stock {
@Column
private long mrktTotAmt;

public Stock(RecommendStock recommendStock) {
this.srtnCd = recommendStock.getSrtnCd();
this.itmsNm = recommendStock.getItmsNm();
this.clpr = recommendStock.getClpr();
}

public void update(int clpr, int mkp) {
this.clpr = clpr;
this.mkp = mkp;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.bull4jo.kkanbustock.stock.repository;

import com.bull4jo.kkanbustock.member.domain.entity.InvestorType;
import com.bull4jo.kkanbustock.stock.domain.RecommendStock;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface RecommendStockRepository extends JpaRepository<RecommendStock, String> {
@Override
Page<RecommendStock> findAll(Pageable pageable);

Optional<Page<RecommendStock>> findByInvestorType(InvestorType investorType, Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package com.bull4jo.kkanbustock.stock.service;

import com.bull4jo.kkanbustock.common.ParseJsonResponse;
import com.bull4jo.kkanbustock.common.CustomParser;
import com.bull4jo.kkanbustock.common.RandomNumberGenerator;
import com.bull4jo.kkanbustock.common.RecentWeekdayFinder;
import com.bull4jo.kkanbustock.exception.CustomException;
import com.bull4jo.kkanbustock.exception.ErrorCode;
import com.bull4jo.kkanbustock.member.domain.entity.InvestorType;
Expand All @@ -19,10 +20,10 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

// 주식 넣고, 빼고, 업데이트, 가져오고, 있는지 확인하고
@Service
Expand All @@ -31,7 +32,7 @@ public class StockService {
private final StockRepository stockRepository;
private final RecommendStockRepository recommendStockRepository;
private final String dartUrl = "https://apis.data.go.kr/1160100/service/GetStockSecuritiesInfoService/getStockPriceInfo";
private final String authKey = "IDHVrjX6z8KgvQCdBokJywDgdQjpPnlGVoqDPNmuz5RcZthIW+GAQmSYfzT/n+Um2jMkMly1jI2eEJWKqO4C0g==";
private final String authKey = "GYfL+RKMgNsn5M9GGWuVBwTIKQIPk0DdvBEFWfll2LmhKvI7QN6eUcvkX5x1bUd3RIzIl4HlsMEGfs/JxQOubA==";

private final String shinhanUrl = "https://gapi.shinhansec.com:8443/openapi/v1.0/recommend/portfolio";
private final String apiKey = "l7xxR7Fe0dP3i8KPZaPKpknI2vWrMeJfwDpk";
Expand Down Expand Up @@ -75,11 +76,10 @@ public String save(final Stock stock) {
}

@Scheduled(cron = "0 0 2 * * *")
@Transactional
public void setStockRepository() {
OkHttpClient client = new OkHttpClient()
.newBuilder()
.readTimeout(10000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.readTimeout(1000000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.build();
HttpUrl.Builder urlBuilder = HttpUrl.parse(dartUrl).newBuilder();

Expand All @@ -88,9 +88,11 @@ public void setStockRepository() {

urlBuilder.addQueryParameter("serviceKey", authKey);
urlBuilder.addQueryParameter("resultType", "json");
urlBuilder.addQueryParameter("numOfRows", "2420021");
urlBuilder.addQueryParameter("numOfRows", "3000");
urlBuilder.addQueryParameter("basDt", RecentWeekdayFinder.findMostRecentWeekday());

String urlWithParam = urlBuilder.build().toString();
System.out.println("urlWithParam = " + urlWithParam);
Request request = new Request.Builder()
.url(urlWithParam)
.method("GET", null)
Expand All @@ -105,7 +107,7 @@ public void setStockRepository() {
if (response.isSuccessful()) {
// 응답 처리 로직
String responseBody = response.body().string();
List<Stock> stocks = ParseJsonResponse.parseResponses(responseBody);
List<Stock> stocks = CustomParser.parseJsonResponses(responseBody);
stockRepository.saveAll(stocks);
} else {
// 응답이 실패인 경우
Expand Down Expand Up @@ -146,7 +148,7 @@ public Stock findByItmsNm(final String itmsNm) {
private Optional<Stock> getFromDartByStrnCd(final String srtnCd) {
OkHttpClient client = new OkHttpClient()
.newBuilder()
.readTimeout(10000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.readTimeout(30000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.build();
HttpUrl.Builder urlBuilder = HttpUrl.parse(dartUrl).newBuilder();

Expand All @@ -171,7 +173,7 @@ private Optional<Stock> getFromDartByStrnCd(final String srtnCd) {
// 응답 처리 로직
String responseBody = response.body().string();
// System.out.println("API 응답: " + responseBody);
stock = ParseJsonResponse.parseSingleResponse(responseBody);
stock = CustomParser.parseSingleJsonResponse(responseBody);
} else {
// 응답이 실패인 경우
throw new RuntimeException("API 요청 실패. 응답 코드: " + response.code());
Expand All @@ -190,7 +192,7 @@ private Optional<Stock> getFromDartByStrnCd(final String srtnCd) {
private Optional<Stock> getFromDartByItmsNm(final String itmsNm) {
OkHttpClient client = new OkHttpClient()
.newBuilder()
.readTimeout(10000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.readTimeout(30000, TimeUnit.MILLISECONDS) // 10 초 타임아웃
.build();
HttpUrl.Builder urlBuilder = HttpUrl.parse(dartUrl).newBuilder();

Expand All @@ -214,7 +216,7 @@ private Optional<Stock> getFromDartByItmsNm(final String itmsNm) {
if (response.isSuccessful()) {
// 응답 처리 로직
String responseBody = response.body().string();
stock = ParseJsonResponse.parseSingleResponse(responseBody);
stock = CustomParser.parseSingleJsonResponse(responseBody);
} else {
// 응답이 실패인 경우
throw new RuntimeException("API 요청 실패. 응답 코드: " + response.code());
Expand Down Expand Up @@ -286,6 +288,7 @@ public void setRecommendConservativeStock() {
recommendStockRepository.save(recommendStock);
}
}

@Transactional
public Page<RecommendStockResponse> getRecommendedStocks(Pageable pageable) {
Page<RecommendStock> page = recommendStockRepository.findAll(pageable);
Expand All @@ -299,6 +302,18 @@ public Page<RecommendStockResponse> getRecommendedStocks(Pageable pageable) {
return new PageImpl<>(recommends, pageable, page.getTotalElements());
}

@Transactional
public Page<RecommendStockResponse> getRecommendStocksByInvestorType(InvestorType investorType, Pageable pageable) {
Page<RecommendStock> page = recommendStockRepository.findByInvestorType(investorType, pageable).orElseThrow(() -> new CustomException(ErrorCode.RECOMMENDED_STOCK_NOT_FOUND));
List<RecommendStockResponse> recommends = page
.getContent()
.stream()
.map(RecommendStockResponse::new)
.collect(Collectors.toList());

return new PageImpl<>(recommends, pageable, page.getTotalElements());
}

@Transactional
public String update(final String strnCd, final Stock req) {
Stock stock = stockRepository.findById(strnCd).orElseThrow(() -> new CustomException(ErrorCode.STOCK_NOT_FOUND));
Expand Down

0 comments on commit c12c5e5

Please sign in to comment.