From ea201756162a03630a91ffdbb999cd5e6e8b3db2 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Tue, 24 Feb 2026 13:45:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(alarm):=20=E4=BF=AE=E5=A4=8D=E5=91=8A?= =?UTF-8?q?=E8=AD=A6=E5=88=97=E8=A1=A8=E5=92=8C=E6=B1=87=E6=80=BB=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=20camera=5Fcode=20=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - 告警列表和汇总页面显示 camera_code(例如 cam_1f0e3dad9990)而不是中文摄像头名称 - _get_camera_info 函数过早检查 token 导致查询失败 修复: 1. 移除 camera_code 查询路径中不必要的 token 检查 - /api/ai/camera/get 已加入白名单,不需要认证 - 仅对 app/stream 格式保留 token 检查 2. 修复告警列表页面摄像头名称显示 - 将 _alarm_to_camel 改为 async 函数 - 添加摄像头名称查询逻辑(三级 fallback) - 使用 asyncio.gather 并发查询提升性能 3. 添加调试日志 - 记录摄像头查询请求和响应 - 便于排查问题 测试结果: - 告警汇总: cam_1f0e3dad9990 → "大堂吧台3" ✓ - 告警列表: cam_1f0e3dad9990 → "大堂吧台3" ✓ 相关文件: - app/routers/yudao_aiot_alarm.py --- app/routers/yudao_aiot_alarm.py | 46 +++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/app/routers/yudao_aiot_alarm.py b/app/routers/yudao_aiot_alarm.py index 44f7835..934cbec 100644 --- a/app/routers/yudao_aiot_alarm.py +++ b/app/routers/yudao_aiot_alarm.py @@ -29,7 +29,7 @@ from app.utils.logger import logger router = APIRouter(prefix="/admin-api/aiot/alarm", tags=["AIoT-告警"]) -def _alarm_to_camel(alarm_dict: dict) -> dict: +async def _alarm_to_camel(alarm_dict: dict, current_user: dict = None) -> dict: """将 alarm_event 字典转换为前端 camelCase 格式(兼容前端旧字段名)""" # snapshot_url: 根据存储方式转为可访问 URL storage = get_oss_storage() @@ -61,6 +61,27 @@ def _alarm_to_camel(alarm_dict: dict) -> dict: alarm_id = alarm_dict.get("alarm_id") + # 查询摄像头名称(三级 fallback 策略) + device_id = alarm_dict.get("device_id") + device_name = device_id # 默认使用 device_id + + if current_user and device_id: + try: + camera_info = await _get_camera_info(device_id, current_user) + if camera_info: + # 1. 优先使用 gb_name(去除 "/" 后缀) + gb_name = camera_info.get("gbName") or camera_info.get("gb_name") + if gb_name: + device_name = gb_name.split("/")[0] + # 2. 其次使用 name 字段 + elif camera_info.get("name"): + device_name = camera_info.get("name") + # 3. 最后才使用 app 字段 + elif camera_info.get("app"): + device_name = camera_info.get("app") + except Exception as e: + logger.warning(f"告警列表查询摄像头信息失败: device_id={device_id}, error={e}") + return { # 新字段(三表结构) "alarmId": alarm_id, @@ -68,7 +89,7 @@ def _alarm_to_camel(alarm_dict: dict) -> dict: "alarmTypeName": _get_alarm_type_name(alarm_dict.get("alarm_type")), "algorithmCode": alarm_dict.get("algorithm_code"), "deviceId": alarm_dict.get("device_id"), - "deviceName": alarm_dict.get("device_id"), + "deviceName": device_name, "sceneId": alarm_dict.get("scene_id"), "eventTime": alarm_dict.get("event_time"), "firstFrameTime": alarm_dict.get("first_frame_time"), @@ -140,7 +161,8 @@ async def get_alert_page( page_size=pageSize, ) - alarm_list = [_alarm_to_camel(a.to_dict()) for a in alarms] + # 并发查询所有告警的摄像头名称 + alarm_list = await asyncio.gather(*[_alarm_to_camel(a.to_dict(), current_user) for a in alarms]) return YudaoResponse.page( list_data=alarm_list, @@ -165,7 +187,7 @@ async def get_alert( if not alarm_dict: raise HTTPException(status_code=404, detail="告警不存在") - return YudaoResponse.success(_alarm_to_camel(alarm_dict)) + return YudaoResponse.success(await _alarm_to_camel(alarm_dict, current_user)) @router.put("/alert/handle") @@ -359,13 +381,6 @@ async def _get_camera_info(device_id: str, current_user: dict) -> Optional[dict] 从 WVP 查询摄像头信息 支持 camera_code 和 app/stream 两种格式 """ - # 获取 token - token = current_user.get("access_token") or current_user.get("token") - if not token: - return None - - headers = {"Authorization": f"Bearer {token}"} - # 如果是 camera_code 格式(cam_xxxxxxxxxxxx) if device_id.startswith("cam_"): try: @@ -375,8 +390,10 @@ async def _get_camera_info(device_id: str, current_user: dict) -> Optional[dict] f"{WVP_API_BASE}/api/ai/camera/get", params={"cameraCode": device_id}, ) + logger.info(f"查询摄像头: device_id={device_id}, status={resp.status_code}") if resp.status_code == 200: data = resp.json() + logger.info(f"WVP 响应: {data}") if data.get("code") == 0: return data.get("data") except Exception as e: @@ -384,6 +401,13 @@ async def _get_camera_info(device_id: str, current_user: dict) -> Optional[dict] # 如果是 app/stream 格式 elif "/" in device_id: + # 获取 token(app/stream 查询需要认证) + token = current_user.get("access_token") or current_user.get("token") + if not token: + logger.warning(f"查询 app/stream 失败: 缺少 token, device_id={device_id}") + return None + + headers = {"Authorization": f"Bearer {token}"} parts = device_id.split("/", 1) if len(parts) == 2: app, stream = parts