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>
64 lines
2.3 KiB
Python
64 lines
2.3 KiB
Python
"""admin 设备存活监控(只读):列设备心跳/在线掉线 + 顶部统计卡片。
|
|
|
|
数据源 device_liveness 表(心跳 last_heartbeat_at + liveness_state + kill_alert_pending,
|
|
见 app/models/device.py)。在线/掉线、掉线时长由 repo 按 HEARTBEAT_TIMEOUT_MINUTES 阈值派生。
|
|
纯读:无写、无审计。任意登录管理员可看(同大盘/设备管理,无角色门)。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Depends, Query
|
|
|
|
from app.admin.deps import AdminDb, get_current_admin
|
|
from app.admin.repositories import queries
|
|
from app.admin.schemas.common import CursorPage
|
|
from app.admin.schemas.device import DeviceLivenessItem, DeviceLivenessStats
|
|
|
|
router = APIRouter(
|
|
prefix="/admin/api/device-liveness",
|
|
tags=["admin-device-liveness"],
|
|
dependencies=[Depends(get_current_admin)],
|
|
)
|
|
|
|
|
|
@router.get("/stats", response_model=DeviceLivenessStats, summary="设备存活统计(顶部卡片)")
|
|
def device_stats(db: AdminDb) -> DeviceLivenessStats:
|
|
return DeviceLivenessStats(**queries.device_liveness_stats(db))
|
|
|
|
|
|
@router.get(
|
|
"",
|
|
response_model=CursorPage[DeviceLivenessItem],
|
|
summary="设备存活列表(在线情况/设备id/归属用户 筛选 + 排序 + 分页,默认掉线置顶)",
|
|
)
|
|
def list_devices(
|
|
db: AdminDb,
|
|
status: Annotated[str | None, Query(pattern="^(online|offline|never)$")] = None,
|
|
device_id: Annotated[str | None, Query(max_length=128)] = None,
|
|
phone: Annotated[str | None, Query(max_length=20)] = None,
|
|
user_id: Annotated[int | None, Query()] = None,
|
|
sort_by: Annotated[
|
|
str, Query(pattern="^(status|last_heartbeat_at|created_at)$")
|
|
] = "status",
|
|
sort_order: Annotated[str, Query(pattern="^(asc|desc)$")] = "desc",
|
|
limit: Annotated[int, Query(ge=1, le=100)] = 20,
|
|
cursor: Annotated[int | None, Query()] = None,
|
|
) -> CursorPage[DeviceLivenessItem]:
|
|
items, next_cursor, total = queries.list_device_liveness(
|
|
db,
|
|
status=status,
|
|
device_id=device_id,
|
|
phone=phone,
|
|
user_id=user_id,
|
|
sort_by=sort_by,
|
|
sort_order=sort_order,
|
|
limit=limit,
|
|
cursor=cursor,
|
|
)
|
|
return CursorPage(
|
|
items=[DeviceLivenessItem.model_validate(d) for d in items],
|
|
next_cursor=next_cursor,
|
|
total=total,
|
|
)
|