You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
buffer_platform/buffer_platform_v2/app/main.py

170 lines
4.8 KiB

"""FastAPI 主入口 - 应用生命周期、中间件、路由注册"""
import logging
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles
from app.config import settings
from app.database import init_db
from app.middleware import GlobalExceptionMiddleware, RequestLoggingMiddleware
from app.services.redis_cache import redis_cache
# 配置日志
logging.basicConfig(
level=getattr(logging, settings.log_level.upper(), logging.INFO),
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""应用生命周期管理。"""
# 启动时
logger.info("初始化数据库...")
await init_db()
logger.info("连接 Redis 缓存...")
await redis_cache.connect()
# 创建默认管理员账户
from app.auth_service import create_default_admin
from app.database import async_session_factory
async with async_session_factory() as admin_db:
await create_default_admin(admin_db)
logger.info("启动定时调度器...")
from app.services.scheduler import start_scheduler
await start_scheduler()
# 恢复已启用的任务
from app.services.cache import list_tasks
from app.services.scheduler import add_job
async with async_session_factory() as db:
tasks = await list_tasks(db)
enabled_tasks = [t for t in tasks if t.enabled]
for t in enabled_tasks:
await add_job(t.id, t.interval_seconds)
logger.info("恢复定时任务: %s (每 %ds)", t.symbol, t.interval_seconds)
logger.info("数据缓冲平台 V2 已启动 http://%s:%d", settings.host, settings.port)
yield
# 关闭时
logger.info("停止调度器...")
from app.services.scheduler import stop_scheduler
await stop_scheduler()
logger.info("断开 Redis 连接...")
await redis_cache.disconnect()
app = FastAPI(
title="数据缓冲平台 V2",
description="期货智析系统 - 重构版本",
version="2.0.0",
lifespan=lifespan,
)
# ==================== 中间件 ====================
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(RequestLoggingMiddleware)
app.add_middleware(GlobalExceptionMiddleware)
# ==================== 静态文件 ====================
STATIC_DIR = Path(__file__).resolve().parent.parent / "app" / "static"
if not STATIC_DIR.exists():
STATIC_DIR = Path(__file__).resolve().parent / "static"
STATIC_DIR.mkdir(parents=True, exist_ok=True)
app.mount("/static", StaticFiles(directory=str(STATIC_DIR), html=True), name="static")
# ==================== 页面路由 ====================
@app.get("/")
async def root():
"""根路径重定向到登录页。"""
return RedirectResponse(url="/login")
@app.get("/login")
async def login_page():
"""登录页面。"""
return FileResponse(str(STATIC_DIR / "login.html"))
@app.get("/role-select")
async def role_select_page():
"""角色选择页面。"""
return FileResponse(str(STATIC_DIR / "role_select.html"))
@app.get("/ui")
async def ui_page():
"""品种配置管理页面。"""
return FileResponse(str(STATIC_DIR / "index.html"))
@app.get("/futures-analysis")
async def futures_analysis_page():
"""期货智析页面。"""
return FileResponse(str(STATIC_DIR / "futures_analysis.html"))
@app.get("/ai-config")
async def ai_config_page():
"""AI模型配置页面。"""
return FileResponse(str(STATIC_DIR / "ai_config.html"))
@app.get("/review-plan")
async def review_plan_page():
"""复盘计划页面。"""
return FileResponse(str(STATIC_DIR / "review_plan.html"))
# ==================== API 路由注册 ====================
from app.api import ai_config, auth, config, data, futures, review, tasks, trade_review # noqa: E402
app.include_router(auth.router, prefix="/api/v1")
app.include_router(data.router, prefix="/api/v1")
app.include_router(tasks.router, prefix="/api/v1")
app.include_router(config.router, prefix="/api/v1")
app.include_router(futures.router, prefix="/api/v1")
app.include_router(ai_config.router, prefix="/api/v1")
app.include_router(review.router, prefix="/api/v1")
app.include_router(trade_review.router, prefix="/api/v1")
# ==================== 健康检查 ====================
@app.get("/api/v1/health")
async def health():
"""健康检查端点。"""
return {"status": "ok", "service": "market-data-buffer-v2", "version": "2.0.0"}
if __name__ == "__main__":
import uvicorn
uvicorn.run("app.main:app", host=settings.host, port=settings.port, reload=True)