exchangeRates / app /api /routes.py
Hugo-Jiang's picture
update GUI and delete file
1891530
"""
API 路由定义
提供汇率查询和换算接口
"""
from fastapi import APIRouter, HTTPException, Request
from typing import Optional
from app.models import (
RatesResponse,
CurrencyRateResponse,
ConvertRequest,
ConvertResponse,
BatchConvertRequest,
BatchConvertResponse,
CurrenciesResponse,
CurrencyInfo,
ServiceStatusResponse
)
from app.utils.currency_names import CURRENCY_INFO, get_currency_info
from app.utils.logger import logger
router = APIRouter(prefix="/api", tags=["汇率接口"])
def get_exchange_service(request: Request):
"""从应用状态获取 ExchangeRateService 实例"""
return request.app.state.exchange_service
# ==================== 获取所有汇率 ====================
@router.get("/rates", response_model=RatesResponse)
async def get_all_rates(request: Request):
"""
获取所有货币汇率
返回缓存中的所有汇率数据,包括基准货币、汇率列表、更新时间等。
"""
service = get_exchange_service(request)
cached = service.get_cached_rates()
if not cached:
logger.warning("API /rates: 没有可用的缓存数据")
raise HTTPException(
status_code=503,
detail="汇率数据不可用,请稍后重试。"
)
return RatesResponse(
success=True,
base_currency=cached.base_code,
rates=cached.rates,
last_update=cached.last_update_utc,
cached_at=cached.cached_at.isoformat(),
currencies_count=len(cached.rates)
)
# ==================== 获取特定货币汇率 ====================
@router.get("/rates/{currency}", response_model=CurrencyRateResponse)
async def get_currency_rate(currency: str, request: Request):
"""
获取特定货币对基准货币的汇率
- **currency**: 货币代码(如 USD, EUR, JPY)
"""
service = get_exchange_service(request)
cached = service.get_cached_rates()
if not cached:
raise HTTPException(
status_code=503,
detail="汇率数据不可用"
)
currency = currency.upper()
if currency not in cached.rates:
raise HTTPException(
status_code=404,
detail=f"未找到货币 '{currency}'"
)
return CurrencyRateResponse(
success=True,
base_currency=cached.base_code,
currency=currency,
rate=cached.rates[currency]
)
# ==================== 两种货币换算 ====================
@router.post("/convert", response_model=ConvertResponse)
async def convert_currency(request: Request, body: ConvertRequest):
"""
两种货币之间的汇率换算
- **from_currency**: 源货币代码
- **to_currency**: 目标货币代码
- **amount**: 换算金额
返回换算结果、汇率和反向汇率。
"""
service = get_exchange_service(request)
result = service.convert(
body.from_currency,
body.to_currency,
body.amount
)
if not result:
raise HTTPException(
status_code=400,
detail="换算失败,请检查货币代码是否有效。"
)
return result
# ==================== GET 方式的货币换算 ====================
@router.get("/convert/{from_currency}/{to_currency}")
async def convert_currency_get(
from_currency: str,
to_currency: str,
amount: float,
request: Request
):
"""
两种货币之间的汇率换算(GET 方式)
- **from_currency**: 源货币代码
- **to_currency**: 目标货币代码
- **amount**: 换算金额(查询参数)
示例: GET /api/convert/USD/CNY?amount=100
"""
service = get_exchange_service(request)
result = service.convert(from_currency, to_currency, amount)
if not result:
raise HTTPException(
status_code=400,
detail="换算失败,请检查货币代码是否有效。"
)
return result
# ==================== 批量换算 ====================
@router.post("/batch-convert", response_model=BatchConvertResponse)
async def batch_convert(request: Request, body: BatchConvertRequest):
"""
批量换算:计算某金额对应所有货币的值
- **base_currency**: 基准货币代码
- **amount**: 换算金额
返回所有货币的换算结果。
"""
service = get_exchange_service(request)
result = service.batch_convert(body.base_currency, body.amount)
if not result:
raise HTTPException(
status_code=400,
detail="批量换算失败,请检查基准货币是否有效。"
)
return result
# ==================== GET 方式的批量换算 ====================
@router.get("/batch-convert/{base_currency}")
async def batch_convert_get(base_currency: str, amount: float, request: Request):
"""
批量换算(GET 方式)
- **base_currency**: 基准货币代码
- **amount**: 换算金额(查询参数)
示例: GET /api/batch-convert/USD?amount=100
"""
service = get_exchange_service(request)
result = service.batch_convert(base_currency, amount)
if not result:
raise HTTPException(
status_code=400,
detail="批量换算失败,请检查基准货币是否有效。"
)
return result
# ==================== 获取货币列表 ====================
@router.get("/currencies", response_model=CurrenciesResponse)
async def get_currencies(request: Request):
"""
获取支持的货币列表(含中英文名称)
返回所有可用货币的代码、名称和符号,按优先级排序。
"""
service = get_exchange_service(request)
settings = service.settings
cached = service.get_cached_rates()
if not cached:
raise HTTPException(
status_code=503,
detail="汇率数据不可用"
)
currencies = []
for code in cached.rates.keys():
info = get_currency_info(code)
currencies.append(CurrencyInfo(
code=code,
name=info.get("name", code),
name_cn=info.get("name_cn", code),
symbol=info.get("symbol", "")
))
# 按优先级排序
priority = settings.PRIORITY_CURRENCIES
currencies.sort(key=lambda x: (
priority.index(x.code) if x.code in priority else 999,
x.code
))
return CurrenciesResponse(
success=True,
currencies=currencies
)
# ==================== 服务状态 ====================
@router.get("/status", response_model=ServiceStatusResponse)
async def get_status(request: Request):
"""
获取服务状态
返回缓存状态、上次更新时间、货币数量等信息。
"""
service = get_exchange_service(request)
cached = service.get_cached_rates()
return ServiceStatusResponse(
status="healthy" if cached else "degraded",
cache_valid=service.is_cache_valid(),
last_update=cached.cached_at.isoformat() if cached else None,
currencies_count=len(cached.rates) if cached else 0,
api_keys_count=len(service.api_keys),
update_interval_seconds=service.settings.CACHE_UPDATE_INTERVAL
)
# ==================== 强制刷新缓存 ====================
@router.post("/refresh")
async def refresh_cache(request: Request):
"""
强制刷新汇率缓存
立即从 API 获取最新汇率数据,不等待定时任务。
注意:频繁调用可能导致 API 限流。
"""
service = get_exchange_service(request)
logger.info("收到手动刷新缓存请求")
success = await service.update_cache()
if success:
cached = service.get_cached_rates()
return {
"success": True,
"message": "缓存刷新成功",
"currencies_count": len(cached.rates) if cached else 0,
"cached_at": cached.cached_at.isoformat() if cached else None
}
else:
raise HTTPException(
status_code=503,
detail="刷新缓存失败,请稍后重试。"
)