2236a8b3ee
改动:新增厂商推送配置、设备 push_vendor/push_token 字段、device push-test 接口、心跳超时厂商直推发送逻辑和对应测试。 验证:python -m pytest tests/test_device_push.py tests/test_auth.py tests/test_health.py 通过。
57 lines
2.2 KiB
Python
57 lines
2.2 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 # 旧极光字段,仅兼容历史数据
|
|
push_vendor: str | None = None
|
|
push_token: str | None = None
|
|
|
|
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
|