File size: 3,753 Bytes
954be92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
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"}