exchangeRates / app /main.py
Hugo-Jiang's picture
Add application file
954be92
"""
FastAPI 应用入口
汇率换算服务主程序
"""
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from app.config import get_settings
from app.services.exchange_service import ExchangeRateService
from app.services.scheduler import RateScheduler
from app.api.routes import router
from app.utils.logger import logger
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理"""
# ========== 启动时 ==========
logger.info("=" * 50)
logger.info("应用正在启动...")
logger.info("=" * 50)
try:
# 加载配置
settings = get_settings()
logger.info(f"配置加载完成: 基准货币 = {settings.BASE_CURRENCY}")
logger.info(f"API Key 数量: {len(settings.API_KEYS)}")
logger.info(f"缓存更新间隔: {settings.CACHE_UPDATE_INTERVAL} 秒")
# 初始化汇率服务
exchange_service = ExchangeRateService(settings)
# 初始化定时调度器
scheduler = RateScheduler(exchange_service, settings)
# 保存到应用状态
app.state.exchange_service = exchange_service
app.state.scheduler = scheduler
app.state.settings = settings
# 启动调度器(会立即获取一次数据)
await scheduler.start()
logger.info("应用启动成功!")
logger.info("=" * 50)
yield
except Exception as e:
logger.error(f"应用启动失败: {e}")
raise
finally:
# ========== 关闭时 ==========
logger.info("=" * 50)
logger.info("应用正在关闭...")
if hasattr(app.state, 'scheduler'):
app.state.scheduler.stop()
logger.info("应用已停止")
logger.info("=" * 50)
# ==================== 创建 FastAPI 应用 ====================
app = FastAPI(
title="汇率换算服务",
description="""
## 汇率换算 API
提供实时汇率查询和换算功能。
### 主要功能
- 📊 **查询汇率**: 获取所有货币或特定货币的实时汇率
- 💱 **货币换算**: 支持任意两种货币之间的换算
- 🔄 **批量换算**: 一次计算某金额对应所有货币的值
- 📋 **货币列表**: 获取支持的货币列表(含中英文名称)
### 数据来源
数据来自 ExchangeRate-API,每日更新。
### 缓存策略
系统使用内存缓存,定时从 API 更新数据,避免频繁请求。
""",
version="1.0.0",
lifespan=lifespan,
docs_url="/docs",
redoc_url="/redoc"
)
# ==================== 中间件配置 ====================
# CORS 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# ==================== 静态文件和模板 ====================
# 挂载静态文件目录
app.mount("/static", StaticFiles(directory="static"), name="static")
# 配置模板引擎
templates = Jinja2Templates(directory="templates")
# ==================== 注册路由 ====================
app.include_router(router)
# ==================== 页面路由 ====================
@app.get("/", response_class=HTMLResponse)
async def home(request: Request):
"""首页 - 汇率换算器"""
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/health")
async def health_check():
"""健康检查接口"""
return {"status": "ok", "service": "exchange-rates"}