""" AI虚拟用户新闻互动系统 - 后端主入口 """ import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse import re as _re # 修复 Pydantic v2 把 naive datetime 序列化为 +00:00 的问题 # 数据库存的是北京时间,应标记为 +08:00 from fastapi.responses import Response from fastapi.encoders import jsonable_encoder import json as _json class _CNJSONResponse(JSONResponse): def render(self, content) -> bytes: text = _json.dumps(content, ensure_ascii=False, allow_nan=False) # 把 +00:00 替换为 +08:00 text = _re.sub(r'(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\+00:00', r'\1+08:00', text) return text.encode('utf-8') from app.core.config import settings from app.core.database import init_db from app.core.logger import logger from app.api import router from app.services.scheduler import scheduler_service # 自定义 datetime 序列化:数据库存的是北京时间,输出时标记为 +08:00 from fastapi.encoders import jsonable_encoder import json as _json class ChinaDatetimeEncoder(_json.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.datetime): # 标记为 +08:00 时区 return obj.strftime("%Y-%m-%dT%H:%M:%S+08:00") return super().default(obj) @asynccontextmanager async def lifespan(app: FastAPI): """应用生命周期管理""" logger.info("🚀 AI虚拟用户新闻互动系统启动中...") # 初始化数据库 await init_db() # 启动调度器 await scheduler_service.start() logger.info("✅ 系统启动完成") yield # 关闭调度器 await scheduler_service.stop() logger.info("🛑 系统已关闭") import datetime as _dt import datetime as _dt class _DatetimeJSONResponse(JSONResponse): def render(self, content) -> bytes: import json def _default(obj): if isinstance(obj, _dt.datetime): return obj.strftime("%Y-%m-%dT%H:%M:%S+08:00") raise TypeError(repr(obj)) return json.dumps(content, ensure_ascii=False, default=_default).encode('utf-8') app = FastAPI(default_response_class=_CNJSONResponse, title="AI虚拟用户新闻互动系统", description="基于AI驱动的虚拟用户新闻互动自动化平台", version="1.0.0", lifespan=lifespan, docs_url="/api/docs", redoc_url="/api/redoc", ) # CORS配置 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 注册路由 app.include_router(router, prefix="/api") @app.get("/health") async def health_check(): return {"status": "ok", "service": "ai-virtual-news-backend"} @app.exception_handler(Exception) async def global_exception_handler(request, exc): logger.error(f"全局异常: {exc}") return JSONResponse( status_code=500, content={"code": 500, "message": f"服务器内部错误: {str(exc)}"}, )