Spaces:
Sleeping
Sleeping
| """ | |
| 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 | |
| # ==================== 获取所有汇率 ==================== | |
| 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) | |
| ) | |
| # ==================== 获取特定货币汇率 ==================== | |
| 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] | |
| ) | |
| # ==================== 两种货币换算 ==================== | |
| 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 方式的货币换算 ==================== | |
| 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 | |
| # ==================== 批量换算 ==================== | |
| 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 方式的批量换算 ==================== | |
| 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 | |
| # ==================== 获取货币列表 ==================== | |
| 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 | |
| ) | |
| # ==================== 服务状态 ==================== | |
| 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 | |
| ) | |
| # ==================== 强制刷新缓存 ==================== | |
| 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="刷新缓存失败,请稍后重试。" | |
| ) | |