"""交易服务健康检查路由""" from fastapi import APIRouter, Depends, Response from app.shared.api import ApiResponse, get_container from app.trading.container import TradingContainer from app.trading.models import TradingHealthData router = APIRouter(tags=["health"]) @router.get("/health", response_model=ApiResponse[TradingHealthData]) async def health( response: Response, container: TradingContainer = Depends(get_container), ) -> ApiResponse[TradingHealthData]: """交易服务健康状态 auth 给出账号登录态。这里只读缓存、不触发网络探测,避免健康检查被 上游高频轮询时反复打站点;要实时结果请用 POST /api/auth/status。 注意 `logged_in=null` 表示服务启动后还没探测过,不等于未登录。 browser 反映 Playwright 浏览器的连接状态。**浏览器已掉线时本接口返回 503**: 这个进程的所有站点操作都要靠那一个浏览器,它没了以后进程虽然还活着、端口还 通,却已经什么都干不了(每单都会失败)。返回 503 是为了让容器 HEALTHCHECK (Dockerfile.trading)能探到并触发重启——这是掉线自愈的最后一道兜底,前面还有 SiteInteractor 在任务边界的就地重建。 重启是安全的:网关侧的下单任务绝不自动重投(租约过期只置 stale 等人工 reclaim,见 docs/order-gateway.md §5),所以重启只是让 worker 恢复领**新** 任务的能力,不会让任何一笔已经在跑的订单被重复下单。 尚未 start() 的启动窗口期不算掉线(HEALTHCHECK 有 start-period 兜着), 只有「起过浏览器且现在连不上」才转 degraded。 """ # site 恒非空(lifespan 必建),留 None 分支只是不为一个健康检查赌这一点 browser = container.site.browser_status() if container.site is not None else {} degraded = bool(browser.get("started")) and not browser.get("alive") if degraded: response.status_code = 503 return ApiResponse[TradingHealthData]( success=not degraded, msg="浏览器已掉线" if degraded else "success", data=TradingHealthData( status="degraded" if degraded else "ok", auth=container.auth_session.status_all(), browser=browser, ), # 与 BrowserDeadError 用同一个错误码,上游按同一张错误码表分支 code=5006 if degraded else 0, )