"""FastAPI 入口 + 启动钩子。""" from __future__ import annotations import json import os import sys from pathlib import Path # 确保项目根在 sys.path _PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent if str(_PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(_PROJECT_ROOT)) from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from app.core.config import settings from app.core.utils.logging import get_logger, setup_logging from app.api.routes import health, sync, datasources, schedule, dashboard, config setup_logging() logger = get_logger("api") app = FastAPI( title="Market Data Sync API", version="0.1.0", description="股票市场数据定时同步与管理", ) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) app.include_router(health.router) app.include_router(sync.router) app.include_router(datasources.router) app.include_router(schedule.router) app.include_router(dashboard.router) app.include_router(config.router) # 看板 HTML 静态文件 import pathlib _static_dir = pathlib.Path(__file__).resolve().parent / "static" if _static_dir.exists(): app.mount("/static", StaticFiles(directory=str(_static_dir)), name="static") # ── 启动钩子 ────────────────────────────────────────────────────────── @app.on_event("startup") def _startup(): logger.info("[startup] market_data_sync 启动") # 1. 注册数据源 from app.core.datasource.registry import build_default_registry from app.core.datasource.base import registry build_default_registry() logger.info(f"[startup] 已注册 {len(registry.all())} 个数据源") # 2. seed 数据源定义到 config 表 from app.core.datasource.registry import seed_datasource_configs seed_datasource_configs() # 3. seed 默认数据集注册 from app.core.sync.registry import seed_sync_registry seed_sync_registry() # 4. 启动时恢复卡死的 sync 任务 from app.core.sync.registry import recover_interrupted_syncs n = recover_interrupted_syncs() if n > 0: logger.warning(f"[startup] 恢复了 {n} 个中断的同步任务") # 5. seed 节假日 from app.core.db import ops as db_ops if settings.holidays_list: db_ops.upsert_config( "trading_calendar_holidays", json.dumps(settings.holidays_list, ensure_ascii=False), category="general", description="A 股休市日", ) # 6. seed 默认计划任务 if settings.scheduler_auto_seed: from app.core.scheduler.scheduler import seed_schedule_configs seed_schedule_configs() # 7. 注册 sync job + 启动调度器 from app.core.scheduler.scheduler import register_sync_jobs, start_scheduler register_sync_jobs() start_scheduler() # 8. 启动数据源健康监控 from app.core.datasource.registry import start_health_monitor start_health_monitor() logger.info("[startup] 完成")