feat(device): 无障碍掉线后置检测 — kill_alert_pending + liveness 查询/ack 端点

推送暂缓改 pull:worker 检出掉线即置 kill_alert_pending(与 liveness_state 解耦,心跳恢复 touch_heartbeat 不清,规避竞态),客户端进 App 拉 GET /device/liveness 命中→弹自启动引导,POST /device/liveness/ack 清。含 alembic 加列(临时 sqlite 验通)。spec: accessibility-liveness-pull-prompt.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
陈世睿
2026-06-19 22:35:12 +08:00
parent a59b49fdc9
commit 4d1162053e
6 changed files with 109 additions and 2 deletions
+30
View File
@@ -19,6 +19,8 @@ from app.schemas.device import (
DeviceOut,
DeviceRegisterRequest,
HeartbeatRequest,
LivenessAckRequest,
LivenessOut,
OkResponse,
)
@@ -64,3 +66,31 @@ def report_heartbeat(
registration_id=req.registration_id,
)
return OkResponse()
@router.get("/liveness", response_model=LivenessOut, summary="查询本机掉线告警(后置检测)")
def get_liveness(
device_id: str,
user: CurrentUser,
db: DbSession,
) -> LivenessOut:
"""客户端进 App 时拉取: 本机是否被判定掉线过(kill_alert_pending)。
设备从未注册过 → 返回默认(无告警)。命中 → 客户端弹「开启自启动」引导, 之后调 /liveness/ack 清。
见 spec: spec/accessibility-liveness-pull-prompt.md。
"""
device = device_repo.get_device(db, user_id=user.id, device_id=device_id)
if device is None:
return LivenessOut()
return LivenessOut.model_validate(device)
@router.post("/liveness/ack", response_model=OkResponse, summary="确认已提醒(清掉线告警)")
def ack_liveness(
req: LivenessAckRequest,
user: CurrentUser,
db: DbSession,
) -> OkResponse:
"""客户端已弹过「开启自启动」引导 → 清掉「待提醒」标记(下次真掉线 worker 再置)。"""
device_repo.ack_kill_alert(db, user_id=user.id, device_id=req.device_id)
return OkResponse()