diff --git a/backend/src/main/java/org/dgu/backend/common/constant/Coin.java b/backend/src/main/java/org/dgu/backend/common/constant/Coin.java new file mode 100644 index 0000000..26600ff --- /dev/null +++ b/backend/src/main/java/org/dgu/backend/common/constant/Coin.java @@ -0,0 +1,25 @@ +package org.dgu.backend.common.constant; + +public enum Coin { + BITCOIN("KRW-BTC", "비트코인"), + ETHEREUM("KRW-ETH", "이더리움"), + RIPPLE("KRW-XRP", "리플"), + DOGECOIN("KRW-DOGE", "도지코인"), + WAVE("KRW-WAVES", "웨이브"); + + private final String marketName; + private final String koreanName; + + Coin(String marketName, String koreanName) { + this.marketName = marketName; + this.koreanName = koreanName; + } + + public String getMarketName() { + return marketName; + } + + public String getKoreanName() { + return koreanName; + } +} \ No newline at end of file diff --git a/backend/src/main/java/org/dgu/backend/common/constant/SuccessStatus.java b/backend/src/main/java/org/dgu/backend/common/constant/SuccessStatus.java index 1da47c3..a34bce2 100644 --- a/backend/src/main/java/org/dgu/backend/common/constant/SuccessStatus.java +++ b/backend/src/main/java/org/dgu/backend/common/constant/SuccessStatus.java @@ -32,7 +32,8 @@ public enum SuccessStatus implements BaseCode { SUCCESS_DELETE_PORTFOLIO_SCRAP(HttpStatus.OK, "200", "포트폴리오 즐겨찾기 삭제에 성공했습니다."), // DashBoard SUCCESS_GET_USER_BALANCE(HttpStatus.OK, "200", "유저 업비트 잔고 조회에 성공했습니다"), - SUCCESS_GET_USER_COINS(HttpStatus.OK, "200", "유저 보유 코인 조회에 성공했습니다"); + SUCCESS_GET_USER_COINS(HttpStatus.OK, "200", "유저 보유 코인 조회에 성공했습니다"), + SUCCESS_GET_REPRESENTATIVE_COINS(HttpStatus.OK, "200", "대표 코인 조회에 성공했습니다"); private final HttpStatus httpStatus; private final String code; diff --git a/backend/src/main/java/org/dgu/backend/controller/DashBoardController.java b/backend/src/main/java/org/dgu/backend/controller/DashBoardController.java index 68e7015..a75fa17 100644 --- a/backend/src/main/java/org/dgu/backend/controller/DashBoardController.java +++ b/backend/src/main/java/org/dgu/backend/controller/DashBoardController.java @@ -33,4 +33,12 @@ public ResponseEntity>> getUserC List userCoins = dashBoardService.getUserCoins(authorizationHeader); return ApiResponse.onSuccess(SuccessStatus.SUCCESS_GET_USER_COINS, userCoins); } + + // 대표 코인 5개 조회 API + @GetMapping("/coins/representative") + public ResponseEntity>> getRepresentativeCoins() { + + List representativeCoinResponses = dashBoardService.getRepresentativeCoins(); + return ApiResponse.onSuccess(SuccessStatus.SUCCESS_GET_REPRESENTATIVE_COINS, representativeCoinResponses); + } } \ No newline at end of file diff --git a/backend/src/main/java/org/dgu/backend/dto/DashBoardDto.java b/backend/src/main/java/org/dgu/backend/dto/DashBoardDto.java index 9c18a29..aaec7fe 100644 --- a/backend/src/main/java/org/dgu/backend/dto/DashBoardDto.java +++ b/backend/src/main/java/org/dgu/backend/dto/DashBoardDto.java @@ -12,6 +12,7 @@ import org.dgu.backend.util.BigDecimalSerializer; import java.math.BigDecimal; +import java.math.RoundingMode; public class DashBoardDto { @Builder @@ -44,4 +45,30 @@ public UserCoin to(User user) { .build(); } } + @Builder + @Getter + @AllArgsConstructor + @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class RepresentativeCoinResponse { + private String marketName; + private String koreanName; + @JsonSerialize(using = BigDecimalSerializer.class) + private BigDecimal price; + @JsonSerialize(using = BigDecimalSerializer.class) + private BigDecimal changePrice; + @JsonSerialize(using = BigDecimalSerializer.class) + private BigDecimal changeRate; + private Boolean isIncrease; + + public static DashBoardDto.RepresentativeCoinResponse of(UpbitDto.Ticker ticker, String koreanName) { + return RepresentativeCoinResponse.builder() + .marketName(ticker.getMarket()) + .koreanName(koreanName) + .changePrice(BigDecimal.valueOf(ticker.getPrice())) + .changeRate(BigDecimal.valueOf(ticker.getChangeRate()).setScale(5, RoundingMode.HALF_UP)) + .isIncrease(ticker.getChange().equals("RISE")) + .build(); + } + } } \ No newline at end of file diff --git a/backend/src/main/java/org/dgu/backend/dto/UpbitDto.java b/backend/src/main/java/org/dgu/backend/dto/UpbitDto.java index 3fcbaea..0789944 100644 --- a/backend/src/main/java/org/dgu/backend/dto/UpbitDto.java +++ b/backend/src/main/java/org/dgu/backend/dto/UpbitDto.java @@ -78,4 +78,20 @@ public static class Account { private Double avgBuyPrice; private String unitCurrency; } + + @Builder + @Getter + @AllArgsConstructor + @JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class) + @JsonInclude(JsonInclude.Include.NON_NULL) + public static class Ticker { + private String market; + @JsonProperty("trade_price") + private Double price; + private String change; + @JsonProperty("signed_change_rate") + private Double changeRate; + @JsonProperty("signed_change_price") + private Double changePrice; + } } diff --git a/backend/src/main/java/org/dgu/backend/exception/UpbitErrorResult.java b/backend/src/main/java/org/dgu/backend/exception/UpbitErrorResult.java index ac77f99..bd9d980 100644 --- a/backend/src/main/java/org/dgu/backend/exception/UpbitErrorResult.java +++ b/backend/src/main/java/org/dgu/backend/exception/UpbitErrorResult.java @@ -9,7 +9,8 @@ @Getter @RequiredArgsConstructor public enum UpbitErrorResult implements BaseErrorCode { - FAIL_ACCESS_USER_ACCOUNT(HttpStatus.NOT_FOUND, "404", "업비트에서 유저 잔고를 가져오는 데 실패했습니다."); + FAIL_ACCESS_USER_ACCOUNT(HttpStatus.NOT_FOUND, "404", "업비트에서 유저 잔고를 가져오는 데 실패했습니다."), + FAIL_ACCESS_COIN_INFO(HttpStatus.NOT_FOUND, "404", "업비트에서 코인 정보를 가져오는 데 실패했습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/backend/src/main/java/org/dgu/backend/service/DashBoardService.java b/backend/src/main/java/org/dgu/backend/service/DashBoardService.java index 4b9bc11..5f8d812 100644 --- a/backend/src/main/java/org/dgu/backend/service/DashBoardService.java +++ b/backend/src/main/java/org/dgu/backend/service/DashBoardService.java @@ -7,4 +7,5 @@ public interface DashBoardService { DashBoardDto.UserAccountResponse getUserAccount(String authorizationHeader); List getUserCoins(String authorizationHeader); + List getRepresentativeCoins(); } \ No newline at end of file diff --git a/backend/src/main/java/org/dgu/backend/service/DashBoardServiceImpl.java b/backend/src/main/java/org/dgu/backend/service/DashBoardServiceImpl.java index 7153cad..11bf17f 100644 --- a/backend/src/main/java/org/dgu/backend/service/DashBoardServiceImpl.java +++ b/backend/src/main/java/org/dgu/backend/service/DashBoardServiceImpl.java @@ -3,6 +3,7 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.dgu.backend.common.constant.Coin; import org.dgu.backend.domain.UpbitKey; import org.dgu.backend.domain.User; import org.dgu.backend.domain.UserCoin; @@ -40,7 +41,7 @@ public DashBoardDto.UserAccountResponse getUserAccount(String authorizationHeade String token = jwtUtil.generateUpbitToken(upbitKey.getAccessKey(), upbitKey.getSecretKey()); String url = "https://api.upbit.com/v1/accounts"; - UpbitDto.Account[] responseBody = connectUpbitApi(url, token); + UpbitDto.Account[] responseBody = getUserAccountsAtUpbit(url, token); if (Objects.isNull(responseBody)) { throw new UpbitException(UpbitErrorResult.FAIL_ACCESS_USER_ACCOUNT); } @@ -69,7 +70,7 @@ public List getUserCoins(String authorizationHead String token = jwtUtil.generateUpbitToken(upbitKey.getAccessKey(), upbitKey.getSecretKey()); String url = "https://api.upbit.com/v1/accounts"; - UpbitDto.Account[] responseBody = connectUpbitApi(url, token); + UpbitDto.Account[] responseBody = getUserAccountsAtUpbit(url, token); if (Objects.isNull(responseBody)) { throw new UpbitException(UpbitErrorResult.FAIL_ACCESS_USER_ACCOUNT); } @@ -102,6 +103,22 @@ public List getUserCoins(String authorizationHead return userCoinResponses; } + // 대표 코인 5개 정보를 반환하는 메서드 + @Override + public List getRepresentativeCoins() { + String url = "https://api.upbit.com/v1/ticker?markets="; + List representativeCoinResponses = new ArrayList<>(); + for (Coin coin : Coin.values()) { + UpbitDto.Ticker[] responseBody = getTickerPriceAtUpbit(url + coin.getMarketName()); + if (Objects.isNull(responseBody[0])) { + throw new UpbitException(UpbitErrorResult.FAIL_ACCESS_COIN_INFO); + } + representativeCoinResponses.add(DashBoardDto.RepresentativeCoinResponse.of(responseBody[0], coin.getKoreanName())); + } + + return representativeCoinResponses; + } + // 코인 가격 상승 여부를 판단하는 메서드 private boolean isBalanceIncreased(UpbitDto.Account account, UserCoin userCoin) { BigDecimal curBalance = BigDecimal.valueOf(account.getBalance()) @@ -110,8 +127,8 @@ private boolean isBalanceIncreased(UpbitDto.Account account, UserCoin userCoin) return curBalance.compareTo(userCoin.getBalance()) > 0; } - // 업비트 API와 통신하는 메서드 - private UpbitDto.Account[] connectUpbitApi(String url, String token) { + // 전체 계좌 조회 업비트 API와 통신하는 메서드 + private UpbitDto.Account[] getUserAccountsAtUpbit(String url, String token) { String authenticationToken = "Bearer " + token; HttpHeaders headers = new HttpHeaders(); headers.set("accept", MediaType.APPLICATION_JSON_VALUE); @@ -125,4 +142,18 @@ private UpbitDto.Account[] connectUpbitApi(String url, String token) { return responseEntity.getBody(); } + + // 시세 현재가 조회 업비트 API와 통신하는 메서드 + private UpbitDto.Ticker[] getTickerPriceAtUpbit(String url) { + HttpHeaders headers = new HttpHeaders(); + headers.set("accept", MediaType.APPLICATION_JSON_VALUE); + ResponseEntity responseEntity = restTemplate.exchange( + url, + HttpMethod.GET, + new HttpEntity<>(headers), + UpbitDto.Ticker[].class + ); + + return responseEntity.getBody(); + } } \ No newline at end of file