# +====================================================================== # | DjangoAdmin 企业数字化开发平台 [ 赋能开发者,助力企业发展 ] # +---------------------------------------------------------------------- # | Copyright (c) 2021-2026 一米阳光团队 版权所有 # | 开源协议: Apache-2.0 # +====================================================================== # 由 apps/__init__.py 迁移而来。 """应用工厂:create_app 组装中间件、路由、异常处理与 lifespan。""" import logging import time from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.exceptions import RequestValidationError from fastapi.logger import logger from starlette.requests import Request from starlette.responses import JSONResponse from config.app import FASTAPI_DEBUG from core.exceptions import AuthorizationException, BusinessException from core.logger import setup_logging from core.middleware import ( register_middleware, register_cros, db_session_middleware, operation_log_middleware, ) from core.redis_client import redis_lifespan, redis_middleware from routers import register_router @asynccontextmanager async def _combined_lifespan(app: FastAPI): """组合 lifespan:先起 Redis,再启动定时任务调度器;退出时先停调度器再关 Redis。""" async with redis_lifespan(app): # 懒加载调度器,避免 core → 业务模块顶层依赖 from modules.job.scheduler import init_scheduler, shutdown_scheduler job_count = init_scheduler() logging.getLogger(__name__).info( f"定时任务调度器已启动,加载 {job_count} 个任务") try: yield finally: shutdown_scheduler() def create_app() -> FastAPI: # 初始化结构化日志(控制台 + logs/app.log) setup_logging() app = FastAPI( title="FastAPI", description="FastAPI", version="v1", lifespan=_combined_lifespan, ) # 注册跨域中间件 register_cros(app) # 注册Redis上下文注入中间件(先注册→内层:先注入Redis再开DB会话) app.middleware("http")(redis_middleware) # 注册数据库会话中间件(后注册→外层:DB会话包裹业务逻辑,finally保证关闭) app.middleware("http")(db_session_middleware) # 注册操作日志中间件(在DB会话内层,记录写操作日志) app.middleware("http")(operation_log_middleware) # 注册路由 register_router(app) # 登录检测 register_middleware(app) # 注册全局异常处理 register_exception(app) # 请求计时(最后注册→最外层,统计完整耗时) @app.middleware("http") async def add_process_time_header(request: Request, call_next): start_time = time.time() response = await call_next(request) process_time = time.time() - start_time response.headers["X-Process-Time"] = str(round(process_time, 5)) if FASTAPI_DEBUG: logger.info(f"访问记录:{request.method} url:{request.url} 耗时:{str(round(process_time, 5))}") return response return app # 注册全局异常处理 def register_exception(app: FastAPI): """全局异常捕获。""" @app.exception_handler(AuthorizationException) async def unicorn_exception_handler(request: Request, exc: AuthorizationException): return JSONResponse( status_code=200, content={"code": exc.code, "data": None, "msg": exc.msg, "ok": False}, ) @app.exception_handler(BusinessException) async def business_exception_handler(request: Request, exc: BusinessException): return JSONResponse( status_code=200, content={"code": exc.code, "data": None, "msg": exc.msg, "ok": False}, ) @app.exception_handler(RequestValidationError) async def validation_exception_handler(request: Request, exc: RequestValidationError): errors = [] for err in exc.errors(): field = err.get("loc", [""])[-1] msg = err.get("msg", "") if field and field != "body": errors.append(f"{field}: {msg}") else: errors.append(msg) return JSONResponse( status_code=200, content={"code": 1, "data": None, "msg": "/".join(errors)}, )