"""考培练系统后端主应用""" import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from fastapi.staticfiles import StaticFiles import json import os from app.core.config import get_settings from app.api.v1 import api_router # 配置日志 logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) logger = logging.getLogger(__name__) settings = get_settings() @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" # 启动时执行 logger.info(f"启动 {settings.APP_NAME} v{settings.APP_VERSION}") # 初始化 Redis try: from app.core.redis import init_redis, close_redis await init_redis() logger.info("Redis 初始化成功") except Exception as e: logger.warning(f"Redis 初始化失败(非致命): {e}") # 初始化定时任务调度器 try: from app.core.scheduler import scheduler_manager from app.core.database import async_session_factory await scheduler_manager.init(async_session_factory) scheduler_manager.start() logger.info("定时任务调度器启动成功") except Exception as e: logger.warning(f"定时任务调度器启动失败(非致命): {e}") yield # 关闭时执行 # 停止定时任务调度器 try: from app.core.scheduler import scheduler_manager scheduler_manager.stop() logger.info("定时任务调度器已停止") except Exception as e: logger.warning(f"停止定时任务调度器失败: {e}") try: from app.core.redis import close_redis await close_redis() logger.info("Redis 连接已关闭") except Exception as e: logger.warning(f"关闭 Redis 连接失败: {e}") logger.info("应用关闭") # 自定义 JSON 响应类,确保中文正确编码 class UTF8JSONResponse(JSONResponse): def render(self, content) -> bytes: return json.dumps( content, ensure_ascii=False, allow_nan=False, indent=None, separators=(",", ":"), ).encode("utf-8") # 创建FastAPI应用 app = FastAPI( title=settings.APP_NAME, version=settings.APP_VERSION, description="考培练系统后端API", lifespan=lifespan, # 确保响应正确的 UTF-8 编码 default_response_class=UTF8JSONResponse, ) # 配置CORS app.add_middleware( CORSMiddleware, allow_origins=settings.CORS_ORIGINS, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 健康检查端点 @app.get("/health") async def health_check(): """健康检查""" return { "status": "healthy", "service": settings.APP_NAME, "version": settings.APP_VERSION, } # 根路径 @app.get("/") async def root(): """根路径""" return { "message": f"欢迎使用{settings.APP_NAME}", "version": settings.APP_VERSION, "docs": "/docs", } # 注册路由 app.include_router(api_router, prefix="/api/v1") # 挂载静态文件目录 # 创建上传目录(如果不存在) upload_path = settings.UPLOAD_PATH os.makedirs(upload_path, exist_ok=True) # 挂载上传文件目录为静态文件服务 app.mount("/static/uploads", StaticFiles(directory=upload_path), name="uploads") # 全局异常处理 @app.exception_handler(Exception) async def global_exception_handler(request, exc): """全局异常处理""" logger.error(f"未处理的异常: {exc}", exc_info=True) return JSONResponse( status_code=500, content={ "code": 500, "message": "内部服务器错误", "detail": str(exc) if settings.DEBUG else None, }, ) if __name__ == "__main__": import uvicorn uvicorn.run( "app.main:app", host=settings.HOST, port=settings.PORT, reload=settings.DEBUG, log_level=settings.LOG_LEVEL.lower(), ) # 测试热重载 - Fri Sep 26 03:37:07 CST 2025