feat(aiot): 告警生成时异步上报运维平台

- 边缘告警入库后异步 POST 到运维平台 /admin-api/ops/alarm/receive
- 提前提取 ORM 字段避免异步执行时 session 关闭导致属性为空
- event_time 转为 ISO 字符串格式,修复时间显示为 1970 的问题
- 请求参数含 alarmId、alarmType、deviceId、eventTime、alarmLevel、notifyUserIds、tenantId

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-11 14:00:11 +08:00
parent 0f5e3ebce2
commit cf41db2983

View File

@@ -15,12 +15,15 @@ API 路径规范:
from fastapi import APIRouter, Query, Depends, HTTPException from fastapi import APIRouter, Query, Depends, HTTPException
from typing import Optional from typing import Optional
from datetime import datetime from datetime import datetime
import httpx
import asyncio
from app.yudao_compat import YudaoResponse, get_current_user from app.yudao_compat import YudaoResponse, get_current_user
from app.services.alarm_event_service import get_alarm_event_service, AlarmEventService from app.services.alarm_event_service import get_alarm_event_service, AlarmEventService
from app.services.notification_service import get_notification_service from app.services.notification_service import get_notification_service
from app.services.oss_storage import get_oss_storage from app.services.oss_storage import get_oss_storage
from app.schemas import EdgeAlarmReport from app.schemas import EdgeAlarmReport
from app.utils.logger import logger
router = APIRouter(prefix="/admin-api/aiot/alarm", tags=["AIoT-告警"]) router = APIRouter(prefix="/admin-api/aiot/alarm", tags=["AIoT-告警"])
@@ -285,6 +288,16 @@ async def edge_alarm_report(
except Exception: except Exception:
pass # WebSocket 通知失败不影响主流程 pass # WebSocket 通知失败不影响主流程
# 异步上报运维平台(提前提取字段,避免 ORM session 关闭后无法访问)
ops_data = {
"alarm_id": alarm.alarm_id,
"alarm_type": alarm.alarm_type,
"device_id": alarm.device_id,
"event_time": alarm.event_time.strftime("%Y-%m-%dT%H:%M:%S") if isinstance(alarm.event_time, datetime) else str(alarm.event_time or ""),
"alarm_level": alarm.alarm_level or 2,
}
asyncio.create_task(_notify_ops_platform(ops_data))
return YudaoResponse.success({ return YudaoResponse.success({
"alarmId": alarm.alarm_id, "alarmId": alarm.alarm_id,
"created": True, "created": True,
@@ -293,6 +306,31 @@ async def edge_alarm_report(
# ==================== 辅助函数 ==================== # ==================== 辅助函数 ====================
OPS_ALARM_URL = "http://192.168.0.104:48080/admin-api/ops/alarm/receive"
async def _notify_ops_platform(data: dict):
"""异步上报告警到运维平台(失败不影响主流程)"""
payload = {
"alarmId": data["alarm_id"],
"alarmType": data["alarm_type"],
"deviceId": data["device_id"],
"eventTime": data["event_time"],
"alarmLevel": data["alarm_level"],
"notifyUserIds": [1],
"tenantId": 1,
}
try:
async with httpx.AsyncClient(timeout=10) as client:
resp = await client.post(OPS_ALARM_URL, json=payload)
if resp.status_code == 200:
logger.info(f"运维平台上报成功: {data['alarm_id']}, resp={resp.text[:200]}")
else:
logger.warning(f"运维平台上报失败: status={resp.status_code}, body={resp.text[:200]}")
except Exception as e:
logger.warning(f"运维平台上报异常: {e}")
def _get_alarm_type_name(alarm_type: Optional[str]) -> str: def _get_alarm_type_name(alarm_type: Optional[str]) -> str:
"""获取告警类型名称""" """获取告警类型名称"""
type_names = { type_names = {