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

Made-with: Cursor
2026-04-23 20:42:21 +08:00

133 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""组合根:显式以 Settings 构造所有服务,挂到 app.state.container。
避免「import 即实例化」的副作用lifespan 内 build + shutdown测试时可注入自定义容器。
"""
from __future__ import annotations
from dataclasses import dataclass
from fastapi import Request
from loguru import logger
from sqlalchemy.ext.asyncio import async_sessionmaker
from app.config import Settings
from app.config import settings as _default_settings
from app.database import AsyncSessionLocal
from app.repositories.surgery_results import SurgeryResultRepository
from app.repositories.voice_audits import VoiceAuditRepository
from app.services.baidu_speech import BaiduSpeechService
from app.services.consumable_vision_algorithm import ConsumableVisionAlgorithmService
from app.services.minio_audio_storage import MinioAudioStorageService
from app.services.surgery_pipeline import SurgeryPipeline
from app.services.video.hikvision_runtime import HikvisionRuntime
from app.services.video.session_manager import CameraSessionManager
from app.services.voice_resolution import VoiceConfirmationService
@dataclass
class AppContainer:
"""显式容器构造时即装配完所有服务lifespan 掌控生命周期。"""
settings: Settings
consumable_vision_algorithm_service: ConsumableVisionAlgorithmService
hikvision_runtime: HikvisionRuntime | None
surgery_result_repository: SurgeryResultRepository
voice_audit_repository: VoiceAuditRepository
baidu_speech_service: BaiduSpeechService
minio_audio_storage_service: MinioAudioStorageService
camera_session_manager: CameraSessionManager
voice_confirmation_service: VoiceConfirmationService
surgery_pipeline: SurgeryPipeline
async def start(self) -> None:
await self.camera_session_manager.start_archive_retry_loop()
async def shutdown(self) -> None:
await self.camera_session_manager.shutdown()
def build_container(
app_settings: Settings | None = None,
*,
session_factory: async_sessionmaker | None = None,
) -> AppContainer:
"""基于 Settings 显式装配所有服务;不做任何 import-time 副作用。"""
s = app_settings or _default_settings
sf: async_sessionmaker = session_factory or AsyncSessionLocal
vision = ConsumableVisionAlgorithmService(app_settings=s)
hik_runtime = HikvisionRuntime.try_load(s.hikvision_lib_dir)
if s.hikvision_sdk_enabled and hik_runtime is None:
logger.warning(
"HIKVISION_SDK_ENABLED=true but no HCNetSDK library loaded "
"(check HIKVISION_LIB_DIR / mount /opt/hikvision/lib)"
)
surgery_repo = SurgeryResultRepository()
voice_audit_repo = VoiceAuditRepository()
baidu = BaiduSpeechService(app_settings=s)
minio = MinioAudioStorageService(s)
camera_mgr = CameraSessionManager(
settings=s,
vision_algorithm=vision,
hikvision_runtime=hik_runtime,
result_repository=surgery_repo,
session_factory=sf,
)
voice = VoiceConfirmationService(
settings=s,
sessions=camera_mgr,
baidu=baidu,
minio=minio,
audits=voice_audit_repo,
session_factory=sf,
)
pipeline = SurgeryPipeline(
camera_mgr,
result_repository=surgery_repo,
voice_confirmation=voice,
session_factory=sf,
)
return AppContainer(
settings=s,
consumable_vision_algorithm_service=vision,
hikvision_runtime=hik_runtime,
surgery_result_repository=surgery_repo,
voice_audit_repository=voice_audit_repo,
baidu_speech_service=baidu,
minio_audio_storage_service=minio,
camera_session_manager=camera_mgr,
voice_confirmation_service=voice,
surgery_pipeline=pipeline,
)
def get_container(request: Request) -> AppContainer:
container: AppContainer | None = getattr(request.app.state, "container", None)
if container is None:
raise RuntimeError(
"AppContainer is not initialized; lifespan should set app.state.container"
)
return container
def get_consumable_vision_algorithm_service(
request: Request,
) -> ConsumableVisionAlgorithmService:
return get_container(request).consumable_vision_algorithm_service
def get_surgery_pipeline(request: Request) -> SurgeryPipeline:
return get_container(request).surgery_pipeline
def get_camera_session_manager(request: Request) -> CameraSessionManager:
return get_container(request).camera_session_manager
def get_surgery_result_repository(request: Request) -> SurgeryResultRepository:
return get_container(request).surgery_result_repository
def get_voice_confirmation_service(request: Request) -> VoiceConfirmationService:
return get_container(request).voice_confirmation_service