Files
AutonetSellCar/backend/app/api/exchange_rate.py
AutonetSellCar Deploy 1f0dcb1ddb Initial commit: AutonetSellCar platform with deployment system
- Frontend: Next.js 14 with TypeScript
- Backend: FastAPI with SQLAlchemy
- Agent: Carmodoo sync agent
- Deployment: Docker Compose based staging/production setup
- Scripts: Automated deployment with rollback support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 13:24:39 +09:00

248 lines
7.4 KiB
Python

"""
Exchange Rate API - 환율 정보 조회 (한국수출입은행 API 연동)
"""
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from pydantic import BaseModel
from typing import Optional, List
from datetime import datetime
from ..database import get_db
from ..models.exchange_rate import ExchangeRate, ExchangeRateHistory
from ..models.user import User
from .auth import get_current_admin_user
from ..services.exchange_rate_service import (
update_exchange_rates,
get_all_exchange_rates,
convert_krw_to_currency,
SUPPORTED_CURRENCIES
)
router = APIRouter(prefix="/api/exchange-rate", tags=["Exchange Rate"])
class ExchangeRateData(BaseModel):
currency_code: str
currency_name: str
symbol: str
deal_base_rate: float # 매매기준율 (1 USD = X KRW)
ttb_rate: float # 전신환 받을때
tts_rate: float # 전신환 보낼때
weight_percent: float # 가중치 (%)
adjusted_rate: float # 가중치 적용 환율
source_date: str
updated_at: str
class ExchangeRatesResponse(BaseModel):
base_currency: str
rates: List[ExchangeRateData]
source: str
last_updated: str
class ExchangeRateWeightUpdate(BaseModel):
currency_code: str
weight_percent: float
class ConvertRequest(BaseModel):
amount: float
from_currency: str = "KRW"
to_currency: str
class ConvertResponse(BaseModel):
original_amount: float
from_currency: str
converted_amount: float
to_currency: str
rate_used: float
@router.get("", response_model=ExchangeRatesResponse)
async def get_exchange_rates(db: Session = Depends(get_db)):
"""환율 정보 조회"""
rates = get_all_exchange_rates(db)
# DB에 데이터가 없으면 업데이트 시도
if not rates:
await update_exchange_rates(db)
rates = get_all_exchange_rates(db)
rate_list = []
for rate in rates:
symbol = SUPPORTED_CURRENCIES.get(rate.currency_code, {}).get("symbol", "")
rate_list.append(ExchangeRateData(
currency_code=rate.currency_code,
currency_name=rate.currency_name,
symbol=symbol,
deal_base_rate=rate.deal_base_rate,
ttb_rate=rate.ttb_rate or rate.deal_base_rate,
tts_rate=rate.tts_rate or rate.deal_base_rate,
weight_percent=rate.weight_percent or 0.0,
adjusted_rate=rate.adjusted_rate or rate.deal_base_rate,
source_date=rate.source_date or "",
updated_at=rate.updated_at.isoformat() if rate.updated_at else ""
))
last_updated = ""
if rates:
latest = max(rates, key=lambda r: r.updated_at if r.updated_at else datetime.min)
last_updated = latest.updated_at.isoformat() if latest.updated_at else ""
return ExchangeRatesResponse(
base_currency="KRW",
rates=rate_list,
source="koreaexim",
last_updated=last_updated
)
@router.get("/currency/{currency_code}")
async def get_single_rate(currency_code: str, db: Session = Depends(get_db)):
"""특정 통화 환율 조회"""
rate = db.query(ExchangeRate).filter(
ExchangeRate.currency_code == currency_code.upper(),
ExchangeRate.is_active == True
).first()
if not rate:
raise HTTPException(status_code=404, detail=f"Currency {currency_code} not found")
symbol = SUPPORTED_CURRENCIES.get(rate.currency_code, {}).get("symbol", "")
return {
"currency_code": rate.currency_code,
"currency_name": rate.currency_name,
"symbol": symbol,
"deal_base_rate": rate.deal_base_rate,
"adjusted_rate": rate.adjusted_rate,
"weight_percent": rate.weight_percent,
"source_date": rate.source_date,
"updated_at": rate.updated_at.isoformat() if rate.updated_at else None
}
@router.post("/convert", response_model=ConvertResponse)
async def convert_currency(
request: ConvertRequest,
db: Session = Depends(get_db)
):
"""통화 변환"""
if request.from_currency.upper() != "KRW":
raise HTTPException(status_code=400, detail="Currently only KRW conversion is supported")
converted = convert_krw_to_currency(db, request.amount, request.to_currency.upper())
if converted is None:
raise HTTPException(status_code=404, detail=f"Currency {request.to_currency} not found")
rate = db.query(ExchangeRate).filter(
ExchangeRate.currency_code == request.to_currency.upper()
).first()
return ConvertResponse(
original_amount=request.amount,
from_currency=request.from_currency.upper(),
converted_amount=round(converted, 2),
to_currency=request.to_currency.upper(),
rate_used=rate.adjusted_rate if rate else 0
)
@router.get("/weights")
async def get_exchange_rate_weights(db: Session = Depends(get_db)):
"""환율 가중치 설정 조회"""
rates = get_all_exchange_rates(db)
return {
rate.currency_code.lower(): rate.weight_percent or 0.0
for rate in rates
}
@router.put("/weights/{currency_code}")
async def update_exchange_rate_weight(
currency_code: str,
weight_percent: float,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_admin_user)
):
"""환율 가중치 수정 (관리자 전용)"""
rate = db.query(ExchangeRate).filter(
ExchangeRate.currency_code == currency_code.upper()
).first()
if not rate:
raise HTTPException(status_code=404, detail=f"Currency {currency_code} not found")
rate.weight_percent = weight_percent
rate.adjusted_rate = rate.deal_base_rate * (1 + weight_percent / 100)
db.commit()
return {
"message": "Weight updated successfully",
"currency_code": rate.currency_code,
"weight_percent": rate.weight_percent,
"adjusted_rate": rate.adjusted_rate
}
@router.post("/refresh")
async def refresh_exchange_rates(
db: Session = Depends(get_db),
current_user: User = Depends(get_current_admin_user)
):
"""환율 강제 갱신 (관리자 전용)"""
result = await update_exchange_rates(db, force=True)
return result
@router.get("/history/{currency_code}")
async def get_exchange_rate_history(
currency_code: str,
limit: int = 30,
db: Session = Depends(get_db)
):
"""환율 변동 이력 조회"""
history = db.query(ExchangeRateHistory).filter(
ExchangeRateHistory.currency_code == currency_code.upper()
).order_by(ExchangeRateHistory.created_at.desc()).limit(limit).all()
return [
{
"currency_code": h.currency_code,
"deal_base_rate": h.deal_base_rate,
"source_date": h.source_date,
"created_at": h.created_at.isoformat() if h.created_at else None
}
for h in history
]
# 프론트엔드용 간단 API
@router.get("/simple")
async def get_simple_rates(db: Session = Depends(get_db)):
"""프론트엔드용 간단 환율 정보"""
rates = get_all_exchange_rates(db)
# DB에 데이터가 없으면 업데이트 시도
if not rates:
await update_exchange_rates(db)
rates = get_all_exchange_rates(db)
result = {}
for rate in rates:
result[rate.currency_code] = {
"rate": rate.adjusted_rate, # KRW per 1 unit (e.g., 1 USD = 1450 KRW)
"symbol": SUPPORTED_CURRENCIES.get(rate.currency_code, {}).get("symbol", ""),
"name": rate.currency_name
}
return result