astock-agent/backend/app/main.py
2026-04-23 17:24:55 +08:00

131 lines
3.8 KiB
Python

"""A 股分析推荐 Agent - FastAPI 入口"""
import logging
import traceback
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from app.config import settings
from app.db.error_logger import log_error
from app.db.database import init_db
from app.engine.scheduler import start_scheduler, stop_scheduler
from app.api import market, sectors, recommendations, stocks, watchlists, websocket, chat, auth, debug
logging.basicConfig(
level=logging.DEBUG if settings.debug else logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
logger = logging.getLogger(__name__)
async def ensure_admin_exists():
"""如果没有管理员用户,自动创建默认管理员"""
from sqlalchemy import select, insert
from app.db.database import get_db
from app.db.tables import users_table
from app.core.auth import hash_password
async with get_db() as db:
result = await db.execute(
select(users_table).where(users_table.c.role == "admin")
)
if result.first() is None:
await db.execute(
insert(users_table).values(
username=settings.admin_username,
password_hash=hash_password(settings.admin_password),
role="admin",
is_active=True,
)
)
await db.commit()
logger.info(f"默认管理员用户 '{settings.admin_username}' 已创建")
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动
logger.info("A 股分析推荐 Agent 启动中...")
try:
await init_db()
logger.info("数据库初始化完成")
await ensure_admin_exists()
start_scheduler()
logger.info("调度器已启动")
yield
except Exception as e:
logger.exception("应用生命周期异常")
await log_error(
"lifespan",
f"应用生命周期异常: {e}",
detail=traceback.format_exc(),
level="critical",
)
raise
finally:
stop_scheduler()
logger.info("服务已关闭")
app = FastAPI(
title="A 股分析推荐 Agent",
description="基于资金驱动的四层漏斗模型,盘中实时分析推荐 A 股",
version="1.0.0",
lifespan=lifespan,
)
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=[settings.frontend_url, "http://localhost:3002"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 路由
app.include_router(market.router)
app.include_router(sectors.router)
app.include_router(recommendations.router)
app.include_router(stocks.router)
app.include_router(watchlists.router)
app.include_router(chat.router)
app.include_router(auth.router)
app.include_router(debug.router)
# WebSocket
app.websocket("/ws")(websocket.ws_endpoint)
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception):
logger.exception("未处理的接口异常: %s %s", request.method, request.url.path)
query = str(request.url.query or "")
await log_error(
"asgi",
f"未处理的接口异常: {exc}",
detail=traceback.format_exc(),
level="error",
context={
"method": request.method,
"path": request.url.path,
"query": query,
},
)
return JSONResponse(
status_code=500,
content={"detail": "服务器内部错误"},
)
@app.get("/api/health")
async def health():
return {
"status": "ok",
"service": "astock-agent",
"llm_enabled": bool(settings.deepseek_api_key),
}