import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:intl/intl.dart'; /// 환율 정보 서비스 클래스 class ExchangeRateService { // 싱글톤 인스턴스 static final ExchangeRateService _instance = ExchangeRateService._internal(); // 팩토리 생성자 factory ExchangeRateService() { return _instance; } // 내부 생성자 ExchangeRateService._internal(); // 캐싱된 환율 정보 double? _usdToKrwRate; double? _usdToJpyRate; double? _usdToCnyRate; DateTime? _lastUpdated; // API 요청 URL (ExchangeRate-API 사용) final String _apiUrl = 'https://api.exchangerate-api.com/v4/latest/USD'; // 기본 환율 상수 static const double DEFAULT_USD_TO_KRW_RATE = 1350.0; static const double DEFAULT_USD_TO_JPY_RATE = 150.0; static const double DEFAULT_USD_TO_CNY_RATE = 7.2; // 캐싱된 환율 반환 (동기적) double? get cachedUsdToKrwRate => _usdToKrwRate; /// 모든 환율 정보를 한 번에 가져옵니다. /// 최근 6시간 이내 조회했던 정보가 있다면 캐싱된 정보를 사용합니다. Future _fetchAllRatesIfNeeded() async { // 캐싱된 데이터 있고 6시간 이내면 스킵 if (_lastUpdated != null) { final difference = DateTime.now().difference(_lastUpdated!); if (difference.inHours < 6) { return; } } try { // API 요청 final response = await http.get(Uri.parse(_apiUrl)); if (response.statusCode == 200) { final data = json.decode(response.body); _usdToKrwRate = data['rates']['KRW']?.toDouble(); _usdToJpyRate = data['rates']['JPY']?.toDouble(); _usdToCnyRate = data['rates']['CNY']?.toDouble(); _lastUpdated = DateTime.now(); } } catch (e) { // 오류 발생 시 기본값 사용 } } /// 현재 USD to KRW 환율 정보를 가져옵니다. Future getUsdToKrwRate() async { await _fetchAllRatesIfNeeded(); return _usdToKrwRate; } /// USD 금액을 KRW로 변환합니다. Future convertUsdToKrw(double usdAmount) async { final rate = await getUsdToKrwRate(); if (rate != null) { return usdAmount * rate; } return null; } /// USD 금액을 지정된 통화로 변환합니다. Future convertUsdToTarget(double usdAmount, String targetCurrency) async { await _fetchAllRatesIfNeeded(); switch (targetCurrency) { case 'KRW': final rate = _usdToKrwRate ?? DEFAULT_USD_TO_KRW_RATE; return usdAmount * rate; case 'JPY': final rate = _usdToJpyRate ?? DEFAULT_USD_TO_JPY_RATE; return usdAmount * rate; case 'CNY': final rate = _usdToCnyRate ?? DEFAULT_USD_TO_CNY_RATE; return usdAmount * rate; case 'USD': return usdAmount; default: return null; } } /// 지정된 통화를 USD로 변환합니다. Future convertTargetToUsd(double amount, String sourceCurrency) async { await _fetchAllRatesIfNeeded(); switch (sourceCurrency) { case 'KRW': final rate = _usdToKrwRate ?? DEFAULT_USD_TO_KRW_RATE; return amount / rate; case 'JPY': final rate = _usdToJpyRate ?? DEFAULT_USD_TO_JPY_RATE; return amount / rate; case 'CNY': final rate = _usdToCnyRate ?? DEFAULT_USD_TO_CNY_RATE; return amount / rate; case 'USD': return amount; default: return null; } } /// 두 통화 간 변환을 수행합니다. (USD를 거쳐서 변환) Future convertBetweenCurrencies( double amount, String fromCurrency, String toCurrency ) async { if (fromCurrency == toCurrency) { return amount; } // fromCurrency → USD → toCurrency double? usdAmount; if (fromCurrency == 'USD') { usdAmount = amount; } else { usdAmount = await convertTargetToUsd(amount, fromCurrency); } if (usdAmount == null) return null; if (toCurrency == 'USD') { return usdAmount; } else { return await convertUsdToTarget(usdAmount, toCurrency); } } /// 현재 환율 정보를 포맷팅하여 텍스트로 반환합니다. Future getFormattedExchangeRateInfo() async { final rate = await getUsdToKrwRate(); if (rate != null) { final formattedRate = NumberFormat.currency( locale: 'ko_KR', symbol: '₩', decimalDigits: 0, ).format(rate); return formattedRate; } return ''; } /// 언어별 환율 정보를 포맷팅하여 반환합니다. Future getFormattedExchangeRateInfoForLocale(String locale) async { await _fetchAllRatesIfNeeded(); switch (locale) { case 'ko': final rate = _usdToKrwRate ?? DEFAULT_USD_TO_KRW_RATE; return NumberFormat.currency( locale: 'ko_KR', symbol: '₩', decimalDigits: 0, ).format(rate); case 'ja': final rate = _usdToJpyRate ?? DEFAULT_USD_TO_JPY_RATE; return NumberFormat.currency( locale: 'ja_JP', symbol: '¥', decimalDigits: 0, ).format(rate); case 'zh': final rate = _usdToCnyRate ?? DEFAULT_USD_TO_CNY_RATE; return NumberFormat.currency( locale: 'zh_CN', symbol: '¥', decimalDigits: 2, ).format(rate); default: return ''; } } /// USD 금액을 KRW로 변환하여 포맷팅된 문자열로 반환합니다. Future getFormattedKrwAmount(double usdAmount) async { final krwAmount = await convertUsdToKrw(usdAmount); if (krwAmount != null) { final formattedAmount = NumberFormat.currency( locale: 'ko_KR', symbol: '₩', decimalDigits: 0, ).format(krwAmount); return '($formattedAmount)'; } return ''; } /// USD 금액을 지정된 언어의 기본 통화로 변환하여 포맷팅된 문자열로 반환합니다. Future getFormattedAmountForLocale(double usdAmount, String locale) async { String targetCurrency; String localeCode; String symbol; int decimalDigits; switch (locale) { case 'ko': targetCurrency = 'KRW'; localeCode = 'ko_KR'; symbol = '₩'; decimalDigits = 0; break; case 'ja': targetCurrency = 'JPY'; localeCode = 'ja_JP'; symbol = '¥'; decimalDigits = 0; break; case 'zh': targetCurrency = 'CNY'; localeCode = 'zh_CN'; symbol = '¥'; decimalDigits = 2; break; default: return '\$$usdAmount'; } final convertedAmount = await convertUsdToTarget(usdAmount, targetCurrency); if (convertedAmount != null) { final formattedAmount = NumberFormat.currency( locale: localeCode, symbol: symbol, decimalDigits: decimalDigits, ).format(convertedAmount); return '($formattedAmount)'; } return ''; } }