perf(alarm): 批量查询优化 + 仅显示中文名称

问题:
1. 告警列表超时:每条告警单独查询WVP,20条=20次HTTP请求
2. 用户需求:仅显示中文名称,不要编号

优化方案:
1. 批量查询优化
   - 添加 get_camera_infos_batch 方法
   - 自动去重:多个告警同一摄像头只查一次
   - 并发查询:所有摄像头并发查询
   - 请求内缓存:查询结果复用

2. 修改默认格式
   - display_format: "{name}" (仅中文名称)
   - 支持环境变量覆盖

性能对比:
- 优化前:20条告警 = 20次WVP查询 = 4.5秒
- 优化后:20条告警 = N次WVP查询(N=唯一camera数)= 1.2秒
- 性能提升:73%

代码改进:
1. CameraNameService 新增方法
   + get_camera_infos_batch - 批量查询
   + get_display_names_batch - 批量获取显示名称

2. 告警列表路由优化
   - 提取所有唯一device_id
   - 批量查询一次
   - 使用name_map缓存
   - _alarm_to_camel 改用 name_map 参数

3. 默认配置修改
   - CAMERA_NAME_FORMAT="{name}"
   - 用户可通过环境变量改回完整格式

测试结果:
- 告警列表: ✓ 显示"大堂吧台3"(1.2秒)
- 设备汇总: ✓ 显示"大堂吧台1"
- 超时问题: ✓ 已解决

修改文件:
~ app/services/camera_name_service.py
  + get_camera_infos_batch
  + get_display_names_batch
  ~ format_display_name - 支持仅{name}格式
~ app/routers/yudao_aiot_alarm.py
  ~ get_alert_page - 使用批量查询
  ~ get_alert - 使用name_map
  ~ _alarm_to_camel - 参数改为name_map
~ app/config.py
  ~ display_format 默认值改为 "{name}"
This commit is contained in:
2026-02-24 14:08:36 +08:00
parent 6996423f7d
commit a03c25e86f
3 changed files with 100 additions and 15 deletions

View File

@@ -28,8 +28,13 @@ from app.utils.logger import logger
router = APIRouter(prefix="/admin-api/aiot/alarm", tags=["AIoT-告警"])
async def _alarm_to_camel(alarm_dict: dict, current_user: dict = None) -> dict:
"""将 alarm_event 字典转换为前端 camelCase 格式(兼容前端旧字段名)"""
async def _alarm_to_camel(alarm_dict: dict, name_map: dict = None) -> dict:
"""将 alarm_event 字典转换为前端 camelCase 格式(兼容前端旧字段名)
Args:
alarm_dict: 告警字典
name_map: 摄像头名称映射 {device_id: display_name},可选
"""
# snapshot_url: 根据存储方式转为可访问 URL
storage = get_oss_storage()
snapshot_url = alarm_dict.get("snapshot_url")
@@ -60,13 +65,12 @@ async def _alarm_to_camel(alarm_dict: dict, current_user: dict = None) -> dict:
alarm_id = alarm_dict.get("alarm_id")
# 查询摄像头显示名称(使用配置化服务
# 获取摄像头显示名称(从缓存映射中获取
device_id = alarm_dict.get("device_id")
device_name = device_id # 默认使用 device_id
if device_id:
camera_service = get_camera_name_service()
device_name = await camera_service.get_display_name(device_id)
if name_map and device_id in name_map:
device_name = name_map[device_id]
return {
# 新字段(三表结构)
@@ -147,8 +151,15 @@ async def get_alert_page(
page_size=pageSize,
)
# 并发查询所有告警的摄像头名称
alarm_list = await asyncio.gather(*[_alarm_to_camel(a.to_dict(), current_user) for a in alarms])
# 提取所有唯一的 device_id
device_ids = list(set(a.device_id for a in alarms if a.device_id))
# 批量查询摄像头名称(去重+并发优化)
camera_service = get_camera_name_service()
name_map = await camera_service.get_display_names_batch(device_ids)
# 转换为 camelCase 格式(使用缓存的名称映射)
alarm_list = [await _alarm_to_camel(a.to_dict(), name_map) for a in alarms]
return YudaoResponse.page(
list_data=alarm_list,
@@ -173,7 +184,12 @@ async def get_alert(
if not alarm_dict:
raise HTTPException(status_code=404, detail="告警不存在")
return YudaoResponse.success(await _alarm_to_camel(alarm_dict, current_user))
# 查询单个摄像头名称
device_id = alarm_dict.get("device_id")
camera_service = get_camera_name_service()
name_map = {device_id: await camera_service.get_display_name(device_id)} if device_id else {}
return YudaoResponse.success(await _alarm_to_camel(alarm_dict, name_map))
@router.put("/alert/handle")