feat: 手术视频消耗、待确认与持久化改造

- 新增 Alembic 初始迁移、领域明细模型及归档持久化与重试链路\n- 拆分视频会话注册表、分类处理、推理时间窗聚合与流处理\n- 消耗日志:TSV/Markdown 含 top2/top3;item_id 优先产品编码;待确认记「待确认」行,语音确认后落正式行并更新汇总\n- 待确认时内存/DB 明细为占位行,确认后替换;拒绝时移除占位\n- 分类 probs 先 detach/cpu 再转 NumPy,修复 MPS/CUDA 上推理被静默跳过\n- 补充集成测试、归档与设备张量等单测

Made-with: Cursor
This commit is contained in:
Kevin
2026-04-23 20:42:21 +08:00
parent 69980d8073
commit 3d7bd70355
55 changed files with 4544 additions and 2050 deletions

50
main.py
View File

@@ -9,28 +9,46 @@ from loguru import logger
from app.api import router as api_router
from app.config import settings
from app.database import check_database, engine, init_db_schema
from app.dependencies import camera_session_manager
from app.dependencies import build_container
logger.remove()
logger.add(
sys.stderr,
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>",
)
def configure_logging() -> None:
"""集中配置 loguru sink由 create_app 显式调用,避免 import-time 副作用。"""
logger.remove()
logger.add(
sys.stderr,
format=(
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan> - <level>{message}</level>"
),
)
@asynccontextmanager
async def lifespan(app: FastAPI):
await check_database()
await init_db_schema()
logger.info("Database connection verified and schema ensured")
await camera_session_manager.start_archive_retry_loop()
yield
await camera_session_manager.shutdown()
await engine.dispose()
logger.info("Database engine disposed")
if settings.auto_create_schema:
await init_db_schema()
logger.info("Database connection verified; schema auto-created (dev mode)")
else:
logger.info(
"Database connection verified; auto_create_schema=false, "
"expecting 'alembic upgrade head' to have run"
)
container = build_container(settings)
app.state.container = container
await container.start()
try:
yield
finally:
await container.shutdown()
await engine.dispose()
logger.info("Database engine disposed")
def create_app() -> FastAPI:
configure_logging()
application = FastAPI(
title="Operation Room Monitor",
lifespan=lifespan,
@@ -69,9 +87,9 @@ app = create_app()
def main() -> None:
uvicorn.run(
"main:app",
host="0.0.0.0",
port=38080,
reload=True,
host=settings.server_host,
port=settings.server_port,
reload=settings.server_reload,
)