Skip to content

Commit

Permalink
Merge pull request #104 from tukcomCD2024/backend/feature/84-realtime…
Browse files Browse the repository at this point in the history
…-current

Backend/feature/84 realtime current
  • Loading branch information
Dayon-Hong authored Jul 13, 2024
2 parents d77b3b9 + ffa951a commit 0ccb894
Show file tree
Hide file tree
Showing 10 changed files with 205 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import com.isp.backend.domain.country.dto.request.LocationRequest;
import com.isp.backend.domain.country.dto.response.DailyWeatherResponse;
import com.isp.backend.domain.country.dto.response.ExchangeRateResponse;
import com.isp.backend.domain.country.dto.response.LocationResponse;
import com.isp.backend.domain.country.dto.response.WeatherResponse;
import com.isp.backend.domain.country.entity.ExchangeRate;
import com.isp.backend.domain.country.service.CountryService;
import com.isp.backend.domain.country.service.ExchangeRateService;
import lombok.RequiredArgsConstructor;
Expand Down Expand Up @@ -58,24 +60,23 @@ public ResponseEntity<List<DailyWeatherResponse>> getWeeklyWeather(@RequestBody
}


/** 특정 기준 통화에 대한 모든 환율 정보 API **/
// @GetMapping("/exchange-rates")
// public ResponseEntity<?> getExchangeRates(@RequestParam String baseCurrency) {
// logger.info("Received request for exchange rates with base currency: {}", baseCurrency);
//
// try {
// Map<String, Double> exchangeRates = exchangeRateService.getExchangeRates(baseCurrency);
// return ResponseEntity.ok(exchangeRates);
// } catch (Exception e) {
// logger.error("Error fetching exchange rates", e);
// return ResponseEntity.badRequest().body(Map.of(
// "errorMessage", "환율 정보를 가져오는데 실패했습니다.",
// "httpStatus", "BAD_REQUEST",
// "code", null
// ));
// }
// }
/** 환율 정보 업데이트 API **/
@GetMapping("/exchange-rates/update")
public String updateExchangeRate() {
try {
exchangeRateService.updateExchangeRates();
return "updated Successfully";
} catch (Exception e) {
return "failed to update exchange rates: " + e.getMessage();
}
}

/** 환율 정보 조회 API **/
@GetMapping("/exchange-rates")
public ResponseEntity<List<ExchangeRateResponse>> getAllExchangeRates() {
List<ExchangeRateResponse> exchangeRates = exchangeRateService.getAllExchangeRates();
return ResponseEntity.ok(exchangeRates);

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.isp.backend.domain.country.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.math.BigDecimal;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
public class ExchangeRateResponse {
private String baseCurrency;
private String targetCurrency;
private BigDecimal rate;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,33 @@

import com.isp.backend.global.common.BaseEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.*;

import java.math.BigDecimal;


@Getter
@Setter
@AllArgsConstructor
@Entity
@Builder
@NoArgsConstructor
@Entity
@Table(name = "exchange_rate")
public class ExchangeRate extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String fromCurrency;
private String baseCurrency;

private String targetCurrency;

private String toCurrency;
private Double rate;

private BigDecimal rate;
public ExchangeRate(String baseCurrency, String targetCurrency, Double rate) {
this.baseCurrency = baseCurrency;
this.targetCurrency = targetCurrency;
this.rate = rate;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.isp.backend.domain.country.mapper;

import com.isp.backend.domain.country.dto.response.ExchangeRateResponse;
import com.isp.backend.domain.country.entity.ExchangeRate;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;

@Component
@RequiredArgsConstructor
public class ExchangeRateMapper {

public ExchangeRateResponse convertToDto(ExchangeRate exchangeRate) {
ExchangeRateResponse dto = new ExchangeRateResponse();
dto.setBaseCurrency(exchangeRate.getBaseCurrency());
dto.setTargetCurrency(exchangeRate.getTargetCurrency());
dto.setRate(BigDecimal.valueOf(exchangeRate.getRate()));
return dto;
}

}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.isp.backend.domain.country.repository;

import com.isp.backend.domain.country.entity.ExchangeRate;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ExchangeRateRepository extends JpaRepository<ExchangeRate, Long> {
ExchangeRate findByBaseCurrencyAndTargetCurrency(String baseCurrency, String targetCurrency);

}
Original file line number Diff line number Diff line change
@@ -1,56 +1,116 @@
package com.isp.backend.domain.country.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.isp.backend.domain.country.dto.response.ExchangeRateResponse;
import com.isp.backend.domain.country.entity.ExchangeRate;
import com.isp.backend.domain.country.mapper.ExchangeRateMapper;
import com.isp.backend.domain.country.repository.ExchangeRateRepository;
import com.isp.backend.global.exception.openApi.ExchangeRateIsFailedException;
import com.isp.backend.global.exception.openApi.ExchangeRateSearchFailedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class ExchangeRateService {

private final ExchangeRateRepository exchangeRateRepository;
private final ExchangeRateMapper exchangeRateMapper ;

@Value("${api-key.exchange-rate}")
private String exchangeRateApiKey;

private final String BASE_URL = "https://v6.exchangerate-api.com/v6";

/** 특정 기준 통화에 대한 모든 환율 정보 가져오기 **/
// public Map<String, Double> getExchangeRates(String baseCurrency) {
// String url = BASE_URL + exchangeRateApiKey + "/latest/" + baseCurrency;
// logger.info("Requesting exchange rates for base currency: {}", baseCurrency);
//
// try {
// String jsonResponse = restTemplate.getForObject(url, String.class);
// ObjectMapper objectMapper = new ObjectMapper();
// JsonNode rootNode = objectMapper.readTree(jsonResponse);
// logger.info("Received response: {}", jsonResponse);
//
// String result = rootNode.path("result").asText();
// if (!"success".equals(result)) {
// throw new RuntimeException("API 요청 실패: " + result);
// }
//
// JsonNode conversionRates = rootNode.path("conversion_rates");
// Map<String, Double> rates = new HashMap<>();
// conversionRates.fields().forEachRemaining(entry -> {
// rates.put(entry.getKey(), entry.getValue().asDouble());
// });
//
// return rates;
// } catch (IOException e) {
// logger.error("Error parsing JSON response", e);
// throw new RuntimeException("환율 정보를 가져오는데 실패했습니다.", e);
// }
// }
@Autowired
public ExchangeRateService(ExchangeRateRepository exchangeRateRepository, ExchangeRateMapper exchangeRateMapper) {
this.exchangeRateRepository = exchangeRateRepository;
this.exchangeRateMapper = exchangeRateMapper;
}

/** 환율 데이터 가져와서 업데이트 하는 API 메서드 **/
@Transactional
public void updateExchangeRates() {
// 한국과 미국 통화 환율 비율 저장
updateRatesForBaseCurrency("KRW");
updateRatesForBaseCurrency("USD");
}


/** 환율 데이터 가져와서 업데이트 하는 API 메서드 **/
public List<ExchangeRateResponse> getAllExchangeRates() {
try {
// DB에서 모든 환율 데이터를 가져옴
List<ExchangeRate> exchangeRates = exchangeRateRepository.findAll();

// baseCurrency가 "KRW" 또는 "USD"인 데이터만 필터링하고 DTO로 변환
return exchangeRates.stream()
.filter(rate -> ("KRW".equals(rate.getBaseCurrency()) || "USD".equals(rate.getBaseCurrency()))
&& !rate.getBaseCurrency().equals(rate.getTargetCurrency()))
.map(exchangeRateMapper::convertToDto)
.collect(Collectors.toList());
} catch (Exception e) {
throw new ExchangeRateIsFailedException() ;
}
}


/** 측정 baseCurrency 통화에 대해 업데이트 하는 메서드**/
private void updateRatesForBaseCurrency(String baseCurrency) {
String exchangeRateAPI_URL = "https://v6.exchangerate-api.com/v6/" + exchangeRateApiKey + "/latest/" + baseCurrency;

try {
URL url = new URL(exchangeRateAPI_URL);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
request.connect();

// API 응답 데이터를 JsonObject로 변환
Gson gson = new Gson();
JsonObject jsonobj = gson.fromJson(new InputStreamReader(request.getInputStream()), JsonObject.class);

// API 응답 결과
String req_result = jsonobj.get("result").getAsString();

if ("success".equals(req_result)) {
JsonObject conversionRates = jsonobj.getAsJsonObject("conversion_rates");
for (String targetCurrency : conversionRates.keySet()) {
// 필요한 통화만 가져온다
if (isSupportedCurrency(targetCurrency)) {
double rate = conversionRates.get(targetCurrency).getAsDouble();

// DB에서 환율 데이터 존재 여부 확인
ExchangeRate existingRate = exchangeRateRepository.findByBaseCurrencyAndTargetCurrency(baseCurrency, targetCurrency);

if (existingRate == null) {
ExchangeRate newExchangeRate = new ExchangeRate(baseCurrency, targetCurrency, rate);
exchangeRateRepository.save(newExchangeRate);
} else {
// 기록이 이미 존재하면, rate만 수정한다.
existingRate.setRate(rate);
exchangeRateRepository.save(existingRate);
}
}
}
} else {
throw new ExchangeRateSearchFailedException();
}
} catch (Exception e) {
throw new ExchangeRateIsFailedException() ;
}
}

/** 지원하는 통화만 가져오는 메서드 **/
private boolean isSupportedCurrency(String currencyCode) {
return Set.of("JPY", "GBP", "EURO", "CHF", "CZK", "USD", "SGD", "TWD", "LAK", "MYR", "VND", "THB", "IDR", "PHP", "KRW").contains(currencyCode);
}




Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ public enum ErrorCode {
SKY_SCANNER_GENERATE_FAILED(HttpStatus.INTERNAL_SERVER_ERROR,"F002", "스카이스캐너 URL을 생성할 수 없습니다."),
FLIGHT_NOT_FOUND(HttpStatus.NOT_FOUND, "F003", "해당 id의 항공권을 찾을 수 없습니다."),
NOT_YOUR_FLIGHT(HttpStatus.UNAUTHORIZED, "F004", "사용자의 항공권이 아닙니다"),
OPEN_WEATHER_SEARCH_FAILED(HttpStatus.NOT_FOUND,"F005", "날씨 정보 파싱에 실패하였습니다");
OPEN_WEATHER_SEARCH_FAILED(HttpStatus.NOT_FOUND,"F005", "날씨 정보 파싱에 실패하였습니다"),
EXCHANGE_RATE_SEARCH_FAILED(HttpStatus.INTERNAL_SERVER_ERROR,"F006", "환율 요청을 가져오는 중 오류를 발생했습니다."),
EXCHANGE_RATE_IS_FAILED(HttpStatus.BAD_REQUEST,"F007", "환율 DB를 저장하던 중 오류가 발생하였습니다.");


private HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.isp.backend.global.exception.openApi;

import com.isp.backend.global.exception.CustomException;
import com.isp.backend.global.exception.ErrorCode;

public class ExchangeRateIsFailedException extends CustomException {

public ExchangeRateIsFailedException() {
super(ErrorCode.EXCHANGE_RATE_IS_FAILED);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.isp.backend.global.exception.openApi;

import com.isp.backend.global.exception.CustomException;
import com.isp.backend.global.exception.ErrorCode;

public class ExchangeRateSearchFailedException extends CustomException {

public ExchangeRateSearchFailedException() {
super(ErrorCode.EXCHANGE_RATE_SEARCH_FAILED);
}

}

0 comments on commit 0ccb894

Please sign in to comment.