# app/main.py import logging import os from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from dotenv import load_dotenv # 统一配置日志格式和级别,确保 delivery_service 等的 INFO 日志可见 logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) # 降低 APScheduler 运行心跳日志,避免每分钟刷屏 logging.getLogger("apscheduler").setLevel(logging.WARNING) from apscheduler.schedulers.asyncio import AsyncIOScheduler from app.services.fetcher_service import fetch_and_save_trending_data from app.services.summary_service import generate_unified_summaries from app.services.delivery_service import check_and_deliver from app.database import engine from app.models.models import Base # 路由总线 from app.api.router import api_router load_dotenv() CRAWL_INTERVAL = int(os.getenv("CRAWL_INTERVAL_MINUTES", 10)) SUMMARY_INTERVAL = int(os.getenv("SUMMARY_INTERVAL_MINUTES", 30)) scheduler = AsyncIOScheduler() # ========================================== # 1. 生命周期管理:App 启动时自动建表 & 启动调度器 # ========================================== @asynccontextmanager async def lifespan(app: FastAPI): # 1. 数据库建表 print("正在初始化数据库表...") Base.metadata.create_all(bind=engine) print("数据库表初始化完成!") # 2. 配置并启动定时任务 scheduler.add_job( fetch_and_save_trending_data, 'interval', minutes=CRAWL_INTERVAL, id='trending_fetch_job', replace_existing=True ) # 平台摘要 scheduler.add_job( generate_unified_summaries, 'interval', minutes=SUMMARY_INTERVAL, id='ai_summary_job', replace_existing=True ) # 推送调度:每分钟检查是否有用户需要接收邮件推送 scheduler.add_job( check_and_deliver, 'interval', minutes=1, id='delivery_check_job', replace_existing=True, ) scheduler.start() print(f"定时抓取任务已启动,每 {CRAWL_INTERVAL} 分钟执行一次") print(f"AI 摘要生成任务已启动,每 {SUMMARY_INTERVAL} 分钟执行一次") print("邮件推送调度已启动,每分钟检查一次") # 为了测试方便,启动时立即执行一次 # await fetch_and_save_trending_data() # await generate_unified_summaries() yield # 此时 FastAPI 开始接受请求 # 优雅关闭 scheduler.shutdown() print("定时任务已安全关闭") # 初始化 FastAPI app = FastAPI(title="AI 新闻聚合引擎 API", lifespan=lifespan) # ========================================== # 2. CORS 中间件:允许前端开发服务器跨域请求 # ========================================== app.add_middleware( CORSMiddleware, # allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"], allow_origins=["*"], # allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ========================================== # 3. 挂载路由总线 # ========================================== # 版本控制 app.include_router(api_router, prefix="/api/v1") # 健康检查 @app.get("/", tags=["健康检查"]) async def root(): return {"message": "Welcome to AI News Aggregator API", "status": "ok"}