1d6432d8bb
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: 陈世睿 <2839904623@qq.com> Reviewed-on: #80 Co-authored-by: chenshirui <chenshirui@wonderable.ai> Co-committed-by: chenshirui <chenshirui@wonderable.ai>
55 lines
2.1 KiB
Python
55 lines
2.1 KiB
Python
"""admin 设备存活监控 schemas(只读)。
|
|
|
|
数据源 device_liveness 表(见 app/models/device.py)。online / display_state /
|
|
offline_seconds 为后端按 HEARTBEAT_TIMEOUT_MINUTES 阈值派生的瞬态字段(非 DB 列),
|
|
在 repo 里算好挂到行上,供 from_attributes 读出。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
|
|
class DeviceLivenessItem(BaseModel):
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
user_id: int
|
|
# join user 取(瞬态;理论上 user 恒存在,留空仅防脏数据)
|
|
phone: str | None = None
|
|
nickname: str | None = None
|
|
|
|
device_id: str
|
|
device_model: str | None = None # 由 device_id 解析(device_<机型>_<hash>);非 DB 列
|
|
platform: str
|
|
app_version: str | None = None
|
|
registration_id: str | None = None # 非空 = 拿到极光 token、可推送
|
|
|
|
ever_protected: bool # 是否开过无障碍(=该设备对功能有意义)
|
|
first_protected_at: datetime | None = None # 首次开无障碍时刻(老设备为 null)
|
|
last_heartbeat_at: datetime | None = None
|
|
last_report_protection_on: bool # 心跳里上报的无障碍开关(恒 true,仅观测)
|
|
liveness_state: str # unknown / alive / silent(当前未用) / notified
|
|
notified_at: datetime | None = None
|
|
kill_alert_pending: bool # 掉线召回待客户端 ack(与 state 解耦)
|
|
created_at: datetime
|
|
updated_at: datetime
|
|
|
|
# ===== 后端派生(非 DB 列)=====
|
|
online: bool = False # ever_protected 且距上次心跳 ≤ 阈值
|
|
# 显示态:online=在线 / offline=已掉线 / never=从未启用(没开过无障碍)
|
|
display_state: str = "never"
|
|
# 掉线时长(秒):仅 offline 时有值;在线 / 从未启用为 None
|
|
offline_seconds: int | None = None
|
|
|
|
|
|
class DeviceLivenessStats(BaseModel):
|
|
"""顶部卡片:总设备数 + 在线 / 已掉线 / 未启用(按心跳阈值派生的 operator 口径,
|
|
不暴露内部状态机 unknown/alive/silent/notified)。"""
|
|
|
|
total: int
|
|
online: int
|
|
offline: int
|
|
never: int
|