2026-02-06 16:39:39 +08:00
|
|
|
|
"""
|
|
|
|
|
|
AIoT 边缘设备路由 - 芋道规范
|
|
|
|
|
|
|
|
|
|
|
|
统一到 /admin-api/aiot/edge 命名空间,与 aiot 平台架构对齐。
|
|
|
|
|
|
|
|
|
|
|
|
API 路径规范:
|
|
|
|
|
|
- /admin-api/aiot/edge/device/page - 分页查询设备
|
|
|
|
|
|
- /admin-api/aiot/edge/device/get - 获取设备详情
|
|
|
|
|
|
- /admin-api/aiot/edge/device/statistics - 设备统计
|
2026-02-25 10:30:01 +08:00
|
|
|
|
|
|
|
|
|
|
注意:边缘端未实现心跳机制,因此运行时长/处理帧数等实时指标不可用。
|
|
|
|
|
|
告警数统计从 alarm_event 表中提取 edge_node_id 字段。
|
2026-02-06 16:39:39 +08:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
from fastapi import APIRouter, Query, Depends, HTTPException
|
|
|
|
|
|
from typing import Optional
|
|
|
|
|
|
|
|
|
|
|
|
from app.yudao_compat import YudaoResponse, get_current_user
|
|
|
|
|
|
from app.services.device_service import get_device_service, DeviceService
|
2026-02-25 10:30:01 +08:00
|
|
|
|
from app.services.alarm_event_service import get_alarm_event_service, AlarmEventService
|
2026-02-06 16:39:39 +08:00
|
|
|
|
|
|
|
|
|
|
router = APIRouter(prefix="/admin-api/aiot/edge", tags=["AIoT-边缘设备"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/device/page")
|
|
|
|
|
|
async def get_device_page(
|
|
|
|
|
|
pageNo: int = Query(1, ge=1, description="页码"),
|
|
|
|
|
|
pageSize: int = Query(20, ge=1, le=100, description="每页大小"),
|
|
|
|
|
|
status: Optional[str] = Query(None, description="设备状态: online/offline/error"),
|
|
|
|
|
|
service: DeviceService = Depends(get_device_service),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
alarm_service: AlarmEventService = Depends(get_alarm_event_service),
|
2026-02-06 16:39:39 +08:00
|
|
|
|
current_user: dict = Depends(get_current_user)
|
|
|
|
|
|
):
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"""分页查询边缘设备列表(告警数从 alarm_event 表统计)"""
|
2026-02-06 16:39:39 +08:00
|
|
|
|
devices, total = service.get_devices(
|
|
|
|
|
|
status=status,
|
|
|
|
|
|
page=pageNo,
|
|
|
|
|
|
page_size=pageSize,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
device_list = []
|
|
|
|
|
|
for device in devices:
|
|
|
|
|
|
device_dict = device.to_dict()
|
2026-02-25 10:30:01 +08:00
|
|
|
|
device_id = device_dict.get("device_id")
|
|
|
|
|
|
|
|
|
|
|
|
# 从 alarm_event 表统计告警数
|
|
|
|
|
|
alerts_count = alarm_service.count_alarms_by_edge_node(device_id)
|
|
|
|
|
|
|
2026-02-06 16:39:39 +08:00
|
|
|
|
device_list.append({
|
|
|
|
|
|
"id": device_dict.get("id"),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"deviceId": device_id,
|
2026-02-06 16:39:39 +08:00
|
|
|
|
"deviceName": device_dict.get("device_name"),
|
|
|
|
|
|
"status": device_dict.get("status"),
|
|
|
|
|
|
"statusName": _get_status_name(device_dict.get("status")),
|
|
|
|
|
|
"lastHeartbeat": device_dict.get("last_heartbeat"),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"uptimeSeconds": None, # 无心跳机制,不可用
|
|
|
|
|
|
"framesProcessed": None, # 无心跳机制,不可用
|
|
|
|
|
|
"alertsGenerated": alerts_count, # 从 alarm_event 表统计
|
2026-02-06 16:39:39 +08:00
|
|
|
|
"ipAddress": device_dict.get("ip_address"),
|
|
|
|
|
|
"streamCount": device_dict.get("stream_count"),
|
|
|
|
|
|
"configVersion": device_dict.get("config_version"),
|
|
|
|
|
|
"extraInfo": device_dict.get("extra_info"),
|
|
|
|
|
|
"updatedAt": device_dict.get("updated_at"),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return YudaoResponse.page(
|
|
|
|
|
|
list_data=device_list,
|
|
|
|
|
|
total=total,
|
|
|
|
|
|
page_no=pageNo,
|
|
|
|
|
|
page_size=pageSize
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/device/get")
|
|
|
|
|
|
async def get_device(
|
|
|
|
|
|
id: str = Query(..., description="设备ID"),
|
|
|
|
|
|
service: DeviceService = Depends(get_device_service),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
alarm_service: AlarmEventService = Depends(get_alarm_event_service),
|
2026-02-06 16:39:39 +08:00
|
|
|
|
current_user: dict = Depends(get_current_user)
|
|
|
|
|
|
):
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"""获取设备详情(告警数从 alarm_event 表统计)"""
|
2026-02-06 16:39:39 +08:00
|
|
|
|
device = service.get_device(id)
|
|
|
|
|
|
if not device:
|
|
|
|
|
|
raise HTTPException(status_code=404, detail="设备不存在")
|
|
|
|
|
|
|
|
|
|
|
|
device_dict = device.to_dict()
|
2026-02-25 10:30:01 +08:00
|
|
|
|
device_id = device_dict.get("device_id")
|
|
|
|
|
|
|
|
|
|
|
|
# 从 alarm_event 表统计告警数
|
|
|
|
|
|
alerts_count = alarm_service.count_alarms_by_edge_node(device_id)
|
|
|
|
|
|
|
2026-02-06 16:39:39 +08:00
|
|
|
|
return YudaoResponse.success({
|
|
|
|
|
|
"id": device_dict.get("id"),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"deviceId": device_id,
|
2026-02-06 16:39:39 +08:00
|
|
|
|
"deviceName": device_dict.get("device_name"),
|
|
|
|
|
|
"status": device_dict.get("status"),
|
|
|
|
|
|
"statusName": _get_status_name(device_dict.get("status")),
|
|
|
|
|
|
"lastHeartbeat": device_dict.get("last_heartbeat"),
|
2026-02-25 10:30:01 +08:00
|
|
|
|
"uptimeSeconds": None, # 无心跳机制,不可用
|
|
|
|
|
|
"framesProcessed": None, # 无心跳机制,不可用
|
|
|
|
|
|
"alertsGenerated": alerts_count, # 从 alarm_event 表统计
|
2026-02-06 16:39:39 +08:00
|
|
|
|
"ipAddress": device_dict.get("ip_address"),
|
|
|
|
|
|
"streamCount": device_dict.get("stream_count"),
|
|
|
|
|
|
"configVersion": device_dict.get("config_version"),
|
|
|
|
|
|
"extraInfo": device_dict.get("extra_info"),
|
|
|
|
|
|
"updatedAt": device_dict.get("updated_at"),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@router.get("/device/statistics")
|
|
|
|
|
|
async def get_device_statistics(
|
|
|
|
|
|
service: DeviceService = Depends(get_device_service),
|
|
|
|
|
|
current_user: dict = Depends(get_current_user)
|
|
|
|
|
|
):
|
|
|
|
|
|
"""获取设备统计"""
|
|
|
|
|
|
stats = service.get_statistics()
|
|
|
|
|
|
|
|
|
|
|
|
return YudaoResponse.success({
|
|
|
|
|
|
"total": stats.get("total", 0),
|
|
|
|
|
|
"online": stats.get("online", 0),
|
|
|
|
|
|
"offline": stats.get("offline", 0),
|
|
|
|
|
|
"error": stats.get("error", 0),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ==================== 辅助函数 ====================
|
|
|
|
|
|
|
|
|
|
|
|
def _get_status_name(status: Optional[str]) -> str:
|
|
|
|
|
|
"""获取设备状态名称"""
|
|
|
|
|
|
status_names = {
|
|
|
|
|
|
"online": "在线",
|
|
|
|
|
|
"offline": "离线",
|
|
|
|
|
|
"error": "异常",
|
|
|
|
|
|
}
|
|
|
|
|
|
return status_names.get(status, status or "未知")
|