From 5b00fd0464de48b994a4a001cdacc7c453d6e861 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Fri, 6 Mar 2026 13:43:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=91=8A=E8=AD=A6?= =?UTF-8?q?=E8=AF=A6=E6=83=85H5=E9=A1=B5=E9=9D=A2=EF=BC=8C=E4=BC=81?= =?UTF-8?q?=E5=BE=AE=E5=8D=A1=E7=89=87=E7=82=B9=E5=87=BB=E5=8F=AF=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E8=AF=A6=E6=83=85=E5=B9=B6=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 H5 告警详情页面(截图+信息+3个操作按钮) - 企微卡片"查看详情"跳转到 H5 页面 - 操作按钮改为:前往处理/已处理/误报忽略 - 新增 alarm_detail API 供 H5 页面获取告警+VLM分析数据 - 挂载 /static 目录提供 H5 页面访问 --- app/main.py | 5 + app/routers/wechat_callback.py | 55 ++++++++-- app/services/notify_dispatch.py | 2 + app/services/wechat_service.py | 6 +- app/static/alarm_detail.html | 189 ++++++++++++++++++++++++++++++++ 5 files changed, 249 insertions(+), 8 deletions(-) create mode 100644 app/static/alarm_detail.html diff --git a/app/main.py b/app/main.py index ce02c8f..eeeab82 100644 --- a/app/main.py +++ b/app/main.py @@ -111,6 +111,11 @@ _uploads_dir = Path("uploads") _uploads_dir.mkdir(parents=True, exist_ok=True) app.mount("/uploads", StaticFiles(directory=str(_uploads_dir)), name="uploads") +# H5 告警详情页面(企微卡片"查看详情"跳转目标) +_static_dir = Path(__file__).parent / "static" +if _static_dir.exists(): + app.mount("/static", StaticFiles(directory=str(_static_dir)), name="static") + def get_alert_svc(): return alert_service diff --git a/app/routers/wechat_callback.py b/app/routers/wechat_callback.py index 7794ffe..f62107f 100644 --- a/app/routers/wechat_callback.py +++ b/app/routers/wechat_callback.py @@ -1,15 +1,17 @@ """ 企微回调路由 -处理安保人员在企微卡片上的操作(确认处理/已处理完成/误报忽略)。 +处理安保人员在企微卡片上的操作(前往处理/已处理/误报忽略)。 +提供告警详情接口供 H5 页面使用。 """ from datetime import datetime -from fastapi import APIRouter, Depends +from fastapi import APIRouter, Depends, Query from pydantic import BaseModel from typing import Optional from app.yudao_compat import YudaoResponse +from app.models import get_session, AlarmEvent, AlarmLlmAnalysis from app.services.alarm_event_service import get_alarm_event_service, AlarmEventService from app.utils.logger import logger @@ -24,16 +26,55 @@ class AlarmActionRequest(BaseModel): remark: Optional[str] = None +@router.get("/alarm_detail") +async def get_alarm_detail(alarm_id: str = Query(..., description="告警ID")): + """ + 告警详情接口(供 H5 页面使用,无认证) + + 返回告警基本信息 + VLM 分析描述 + """ + db = get_session() + try: + alarm = db.query(AlarmEvent).filter(AlarmEvent.alarm_id == alarm_id).first() + if not alarm: + return YudaoResponse.error(404, "告警不存在") + + # 查 VLM 分析结果 + vlm_desc = "" + analysis = db.query(AlarmLlmAnalysis).filter( + AlarmLlmAnalysis.alarm_id == alarm_id + ).order_by(AlarmLlmAnalysis.id.desc()).first() + if analysis: + vlm_desc = analysis.summary or "" + + return YudaoResponse.success({ + "alarm_id": alarm.alarm_id, + "alarm_type": alarm.alarm_type, + "device_id": alarm.device_id, + "scene_id": alarm.scene_id, + "event_time": alarm.event_time.strftime('%Y-%m-%d %H:%M:%S') if alarm.event_time else "", + "alarm_level": alarm.alarm_level, + "alarm_status": alarm.alarm_status, + "handle_status": alarm.handle_status, + "snapshot_url": alarm.snapshot_url or "", + "handler": alarm.handler or "", + "handle_remark": alarm.handle_remark or "", + "vlm_description": vlm_desc, + }) + finally: + db.close() + + @router.post("/callback/alarm_action") async def alarm_action_callback( req: AlarmActionRequest, service: AlarmEventService = Depends(get_alarm_event_service), ): """ - 企微告警操作回调(无认证,由企微服务端调用) + 企微告警操作回调(无认证,由 H5 页面调用) action: - - confirm: 确认处理 → handle_status=HANDLING + - confirm: 前往处理 → handle_status=HANDLING - complete: 已处理完成 → handle_status=DONE, alarm_status=CLOSED - ignore: 误报忽略 → alarm_status=FALSE, handle_status=DONE """ @@ -41,17 +82,17 @@ async def alarm_action_callback( "confirm": { "alarm_status": "CONFIRMED", "handle_status": "HANDLING", - "remark": "企微确认处理", + "remark": "前往处理", }, "complete": { "alarm_status": "CLOSED", "handle_status": "DONE", - "remark": "企微手动结单", + "remark": "手动结单", }, "ignore": { "alarm_status": "FALSE", "handle_status": "DONE", - "remark": "企微标记误报", + "remark": "标记误报", }, } diff --git a/app/services/notify_dispatch.py b/app/services/notify_dispatch.py index 55c087b..71d09af 100644 --- a/app/services/notify_dispatch.py +++ b/app/services/notify_dispatch.py @@ -19,6 +19,7 @@ from app.models import ( AlarmEvent, AlarmLlmAnalysis, CameraAreaBinding, AreaPersonBinding, NotifyArea, ) +from app.config import settings from app.services.vlm_service import get_vlm_service from app.services.wechat_service import get_wechat_service @@ -94,6 +95,7 @@ async def process_alarm_notification(alarm_data: Dict): snapshot_url=snapshot_url, event_time=event_time_str, alarm_level=alarm_level, + service_base_url=f"http://{settings.app.host}:{settings.app.port}", ) logger.info(f"告警通知完成: {alarm_id} → {len(persons)} 人") diff --git a/app/services/wechat_service.py b/app/services/wechat_service.py index a4b0838..6a612a3 100644 --- a/app/services/wechat_service.py +++ b/app/services/wechat_service.py @@ -75,6 +75,7 @@ class WeChatService: snapshot_url: str, event_time: str, alarm_level: int = 2, + service_base_url: str = "", ) -> bool: """ 发送告警文本卡片 @@ -120,6 +121,9 @@ class WeChatService: f"