feat(aiot): 添加 aiot/alarm 和 aiot/edge 芋道兼容路由
- 新增 yudao_aiot_alarm.py:/admin-api/aiot/alarm/* 告警管理路由 包含分页查询、详情、处理、删除、统计、摄像头汇总 - 新增 yudao_aiot_edge.py:/admin-api/aiot/edge/* 边缘设备路由 包含设备分页、详情、统计 - 复用现有 alert_service 和 device_service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
224
app/routers/yudao_aiot_alarm.py
Normal file
224
app/routers/yudao_aiot_alarm.py
Normal file
@@ -0,0 +1,224 @@
|
||||
"""
|
||||
AIoT 告警路由 - 芋道规范
|
||||
|
||||
统一到 /admin-api/aiot/alarm 命名空间,与 aiot 平台架构对齐。
|
||||
|
||||
API 路径规范:
|
||||
- /admin-api/aiot/alarm/alert/page - 分页查询
|
||||
- /admin-api/aiot/alarm/alert/get - 获取详情
|
||||
- /admin-api/aiot/alarm/alert/handle - 处理告警
|
||||
- /admin-api/aiot/alarm/alert/delete - 删除告警
|
||||
- /admin-api/aiot/alarm/alert/statistics - 获取统计
|
||||
- /admin-api/aiot/alarm/camera-summary/page - 摄像头汇总
|
||||
"""
|
||||
|
||||
from fastapi import APIRouter, Query, Depends, HTTPException
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
|
||||
from app.yudao_compat import YudaoResponse, get_current_user
|
||||
from app.services.alert_service import get_alert_service, AlertService
|
||||
from app.schemas import AlertHandleRequest
|
||||
|
||||
router = APIRouter(prefix="/admin-api/aiot/alarm", tags=["AIoT-告警"])
|
||||
|
||||
|
||||
# ==================== 告警管理 ====================
|
||||
|
||||
@router.get("/alert/page")
|
||||
async def get_alert_page(
|
||||
pageNo: int = Query(1, ge=1, description="页码"),
|
||||
pageSize: int = Query(20, ge=1, le=100, description="每页大小"),
|
||||
cameraId: Optional[str] = Query(None, description="摄像头ID"),
|
||||
deviceId: Optional[str] = Query(None, description="设备ID"),
|
||||
alertType: Optional[str] = Query(None, description="告警类型"),
|
||||
status: Optional[str] = Query(None, description="状态: pending/confirmed/ignored/resolved/dispatched"),
|
||||
level: Optional[str] = Query(None, description="级别: low/medium/high/critical"),
|
||||
startTime: Optional[datetime] = Query(None, description="开始时间"),
|
||||
endTime: Optional[datetime] = Query(None, description="结束时间"),
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""分页查询告警列表"""
|
||||
alerts, total = service.get_alerts(
|
||||
camera_id=cameraId,
|
||||
device_id=deviceId,
|
||||
alert_type=alertType,
|
||||
status=status,
|
||||
level=level,
|
||||
start_time=startTime,
|
||||
end_time=endTime,
|
||||
page=pageNo,
|
||||
page_size=pageSize,
|
||||
)
|
||||
|
||||
alert_list = []
|
||||
for alert in alerts:
|
||||
alert_dict = alert.to_dict()
|
||||
alert_list.append({
|
||||
"id": alert_dict.get("id"),
|
||||
"alertNo": alert_dict.get("alert_no"),
|
||||
"cameraId": alert_dict.get("camera_id"),
|
||||
"cameraName": alert_dict.get("camera_id"),
|
||||
"roiId": alert_dict.get("roi_id"),
|
||||
"bindId": alert_dict.get("bind_id"),
|
||||
"deviceId": alert_dict.get("device_id"),
|
||||
"alertType": alert_dict.get("alert_type"),
|
||||
"alertTypeName": _get_alert_type_name(alert_dict.get("alert_type")),
|
||||
"algorithm": alert_dict.get("algorithm"),
|
||||
"confidence": alert_dict.get("confidence"),
|
||||
"durationMinutes": alert_dict.get("duration_minutes"),
|
||||
"triggerTime": alert_dict.get("trigger_time"),
|
||||
"message": alert_dict.get("message"),
|
||||
"bbox": alert_dict.get("bbox"),
|
||||
"snapshotUrl": alert_dict.get("snapshot_url"),
|
||||
"ossUrl": alert_dict.get("oss_url"),
|
||||
"status": alert_dict.get("status"),
|
||||
"level": alert_dict.get("level"),
|
||||
"handleRemark": alert_dict.get("handle_remark"),
|
||||
"handledBy": alert_dict.get("handled_by"),
|
||||
"handledAt": alert_dict.get("handled_at"),
|
||||
"workOrderId": alert_dict.get("work_order_id"),
|
||||
"aiAnalysis": alert_dict.get("ai_analysis"),
|
||||
"createdAt": alert_dict.get("created_at"),
|
||||
"updatedAt": alert_dict.get("updated_at"),
|
||||
})
|
||||
|
||||
return YudaoResponse.page(
|
||||
list_data=alert_list,
|
||||
total=total,
|
||||
page_no=pageNo,
|
||||
page_size=pageSize
|
||||
)
|
||||
|
||||
|
||||
@router.get("/alert/get")
|
||||
async def get_alert(
|
||||
id: int = Query(..., description="告警ID"),
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""获取告警详情"""
|
||||
alert = service.get_alert(id)
|
||||
if not alert:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
|
||||
alert_dict = alert.to_dict()
|
||||
return YudaoResponse.success({
|
||||
"id": alert_dict.get("id"),
|
||||
"alertNo": alert_dict.get("alert_no"),
|
||||
"cameraId": alert_dict.get("camera_id"),
|
||||
"cameraName": alert_dict.get("camera_id"),
|
||||
"roiId": alert_dict.get("roi_id"),
|
||||
"bindId": alert_dict.get("bind_id"),
|
||||
"deviceId": alert_dict.get("device_id"),
|
||||
"alertType": alert_dict.get("alert_type"),
|
||||
"alertTypeName": _get_alert_type_name(alert_dict.get("alert_type")),
|
||||
"algorithm": alert_dict.get("algorithm"),
|
||||
"confidence": alert_dict.get("confidence"),
|
||||
"durationMinutes": alert_dict.get("duration_minutes"),
|
||||
"triggerTime": alert_dict.get("trigger_time"),
|
||||
"message": alert_dict.get("message"),
|
||||
"bbox": alert_dict.get("bbox"),
|
||||
"snapshotUrl": alert_dict.get("snapshot_url"),
|
||||
"ossUrl": alert_dict.get("oss_url"),
|
||||
"status": alert_dict.get("status"),
|
||||
"level": alert_dict.get("level"),
|
||||
"handleRemark": alert_dict.get("handle_remark"),
|
||||
"handledBy": alert_dict.get("handled_by"),
|
||||
"handledAt": alert_dict.get("handled_at"),
|
||||
"workOrderId": alert_dict.get("work_order_id"),
|
||||
"aiAnalysis": alert_dict.get("ai_analysis"),
|
||||
"createdAt": alert_dict.get("created_at"),
|
||||
"updatedAt": alert_dict.get("updated_at"),
|
||||
})
|
||||
|
||||
|
||||
@router.put("/alert/handle")
|
||||
async def handle_alert(
|
||||
id: int = Query(..., description="告警ID"),
|
||||
status: str = Query(..., description="处理状态: confirmed/ignored/resolved"),
|
||||
remark: Optional[str] = Query(None, description="处理备注"),
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""处理告警"""
|
||||
handle_data = AlertHandleRequest(status=status, remark=remark)
|
||||
handled_by = current_user.get("username", "admin")
|
||||
|
||||
alert = service.handle_alert(id, handle_data, handled_by)
|
||||
if not alert:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
|
||||
return YudaoResponse.success(True)
|
||||
|
||||
|
||||
@router.delete("/alert/delete")
|
||||
async def delete_alert(
|
||||
id: int = Query(..., description="告警ID"),
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""删除告警"""
|
||||
success = service.delete_alert(id)
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="告警不存在")
|
||||
|
||||
return YudaoResponse.success(True)
|
||||
|
||||
|
||||
@router.get("/alert/statistics")
|
||||
async def get_statistics(
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""获取告警统计"""
|
||||
stats = service.get_statistics()
|
||||
|
||||
return YudaoResponse.success({
|
||||
"total": stats.get("total", 0),
|
||||
"pending": stats.get("pending", 0),
|
||||
"confirmed": stats.get("confirmed", 0),
|
||||
"ignored": stats.get("ignored", 0),
|
||||
"resolved": stats.get("resolved", 0),
|
||||
"dispatched": stats.get("dispatched", 0),
|
||||
"byType": stats.get("by_type", {}),
|
||||
"byLevel": stats.get("by_level", {}),
|
||||
})
|
||||
|
||||
|
||||
# ==================== 摄像头告警汇总 ====================
|
||||
|
||||
@router.get("/camera-summary/page")
|
||||
async def get_camera_summary_page(
|
||||
pageNo: int = Query(1, ge=1, description="页码"),
|
||||
pageSize: int = Query(20, ge=1, le=100, description="每页大小"),
|
||||
service: AlertService = Depends(get_alert_service),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""获取摄像头告警汇总(分页)"""
|
||||
result = service.get_camera_alert_summary(page=pageNo, page_size=pageSize)
|
||||
|
||||
return YudaoResponse.page(
|
||||
list_data=result.get("list", []),
|
||||
total=result.get("total", 0),
|
||||
page_no=pageNo,
|
||||
page_size=pageSize
|
||||
)
|
||||
|
||||
|
||||
# ==================== 辅助函数 ====================
|
||||
|
||||
def _get_alert_type_name(alert_type: Optional[str]) -> str:
|
||||
"""获取告警类型名称"""
|
||||
type_names = {
|
||||
"leave_post": "离岗检测",
|
||||
"intrusion": "周界入侵",
|
||||
"crowd": "人群聚集",
|
||||
"fire": "火焰检测",
|
||||
"smoke": "烟雾检测",
|
||||
"fall": "跌倒检测",
|
||||
"helmet": "安全帽检测",
|
||||
"unknown": "未知类型",
|
||||
}
|
||||
return type_names.get(alert_type, alert_type or "未知类型")
|
||||
119
app/routers/yudao_aiot_edge.py
Normal file
119
app/routers/yudao_aiot_edge.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
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 - 设备统计
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
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),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""分页查询边缘设备列表"""
|
||||
devices, total = service.get_devices(
|
||||
status=status,
|
||||
page=pageNo,
|
||||
page_size=pageSize,
|
||||
)
|
||||
|
||||
device_list = []
|
||||
for device in devices:
|
||||
device_dict = device.to_dict()
|
||||
device_list.append({
|
||||
"id": device_dict.get("id"),
|
||||
"deviceId": device_dict.get("device_id"),
|
||||
"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"),
|
||||
"uptimeSeconds": device_dict.get("uptime_seconds"),
|
||||
"framesProcessed": device_dict.get("frames_processed"),
|
||||
"alertsGenerated": device_dict.get("alerts_generated"),
|
||||
"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),
|
||||
current_user: dict = Depends(get_current_user)
|
||||
):
|
||||
"""获取设备详情"""
|
||||
device = service.get_device(id)
|
||||
if not device:
|
||||
raise HTTPException(status_code=404, detail="设备不存在")
|
||||
|
||||
device_dict = device.to_dict()
|
||||
return YudaoResponse.success({
|
||||
"id": device_dict.get("id"),
|
||||
"deviceId": device_dict.get("device_id"),
|
||||
"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"),
|
||||
"uptimeSeconds": device_dict.get("uptime_seconds"),
|
||||
"framesProcessed": device_dict.get("frames_processed"),
|
||||
"alertsGenerated": device_dict.get("alerts_generated"),
|
||||
"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 "未知")
|
||||
Reference in New Issue
Block a user