Files
iot-device-management-service/docs/camera_name_config.md
16337 6996423f7d refactor(alarm): 模块化摄像头名称格式化服务
问题:
- 硬编码字段映射(gbName、name、app)
- 逻辑重复散落多处
- 格式写死无法配置
- 未基于数据库实际表结构
- 可扩展性差

重构方案:
1. 创建配置类 CameraNameConfig
   - 显示格式模板(支持变量:{camera_code}, {name}, {stream})
   - 字段优先级配置
   - WVP API配置
   - 查询超时配置

2. 创建服务类 CameraNameService
   - 查询摄像头信息(get_camera_info)
   - 提取名称字段(extract_name)
   - 格式化显示名称(format_display_name)
   - 一站式方法(get_display_name)

3. 重构路由层
   - 移除硬编码逻辑
   - 使用camera_name_service统一处理
   - 删除旧的_get_camera_info函数
   - 简化代码结构

架构优势:
- 配置驱动:格式通过环境变量控制
- 单一职责:服务只负责名称处理
- 可扩展:新增格式无需改代码
- 可测试:服务独立易于测试
- 模块化:逻辑集中便于维护

配置示例:
```bash
WVP_API_BASE=http://localhost:18080
CAMERA_NAME_FORMAT={camera_code} {name}/{stream}
CAMERA_QUERY_TIMEOUT=5
```

修改文件:
+ app/config.py - 添加CameraNameConfig配置
+ app/services/camera_name_service.py - 新建服务
+ docs/camera_name_config.md - 配置文档
~ app/routers/yudao_aiot_alarm.py - 使用新服务

测试结果:
- 告警列表: cam_1f0e3dad9990 → cam_1f0e3dad9990 大堂吧台3/012 ✓
- 设备汇总: cam_c51ce410c124 → cam_c51ce410c124 大堂吧台1/008 ✓
2026-02-24 13:59:13 +08:00

280 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 摄像头名称格式化配置
## 概述
摄像头名称格式化服务提供了灵活、可配置的方式来格式化摄像头显示名称。所有配置通过环境变量控制,无需修改代码即可调整显示格式。
## 架构设计
```
┌─────────────────────────────────────────────────────┐
│ config.py (CameraNameConfig) │
│ - 显示格式模板 │
│ - 字段优先级 │
│ - WVP API配置 │
└────────────────┬────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ camera_name_service.py (CameraNameService) │
│ - 查询摄像头信息 │
│ - 提取名称字段 │
│ - 格式化显示名称 │
└────────────────┬────────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ yudao_aiot_alarm.py (路由层) │
│ - 告警列表 │
│ - 设备汇总 │
└─────────────────────────────────────────────────────┘
```
## 配置参数
### 环境变量
| 变量名 | 说明 | 默认值 | 示例 |
|--------|------|--------|------|
| `WVP_API_BASE` | WVP API基础URL | `http://localhost:18080` | `http://192.168.1.100:18080` |
| `CAMERA_NAME_FORMAT` | 显示格式模板 | `{camera_code} {name}/{stream}` | `{name}` |
| `CAMERA_QUERY_TIMEOUT` | 查询超时(秒) | `5` | `10` |
### 显示格式模板
支持以下变量:
| 变量 | 说明 | 示例值 |
|------|------|--------|
| `{camera_code}` | 摄像头编码 | `cam_1f0e3dad9990` |
| `{name}` | 摄像头名称(根据字段优先级提取) | `大堂吧台3` |
| `{stream}` | 流ID | `012` |
### 常用格式示例
1. **完整格式**(默认):
```
CAMERA_NAME_FORMAT="{camera_code} {name}/{stream}"
结果cam_1f0e3dad9990 大堂吧台3/012
```
2. **仅名称+流ID**
```
CAMERA_NAME_FORMAT="{name}/{stream}"
结果大堂吧台3/012
```
3. **仅名称**
```
CAMERA_NAME_FORMAT="{name}"
结果大堂吧台3
```
4. **仅编码**
```
CAMERA_NAME_FORMAT="{camera_code}"
结果cam_1f0e3dad9990
```
5. **自定义分隔符**
```
CAMERA_NAME_FORMAT="{name} - {stream}"
结果大堂吧台3 - 012
```
### 名称字段优先级
服务会按以下优先级从 StreamProxy 对象中提取名称:
1. **gbName**(国标名称)- 自动去除 "/" 后缀
2. **app**(应用名)
3. **stream**流ID
此优先级在代码中硬编码,如需修改请编辑 `app/services/camera_name_service.py`
```python
self.name_field_priority = ["gbName", "app", "stream"]
```
## 使用方式
### 1. 在环境变量中配置
`.env` 文件:
```bash
WVP_API_BASE=http://192.168.0.104:18080
CAMERA_NAME_FORMAT={camera_code} {name}/{stream}
CAMERA_QUERY_TIMEOUT=5
```
### 2. 服务自动加载配置
```python
from app.services.camera_name_service import get_camera_name_service
camera_service = get_camera_name_service()
display_name = await camera_service.get_display_name("cam_1f0e3dad9990")
# 返回: "cam_1f0e3dad9990 大堂吧台3/012"
```
### 3. 修改格式只需重启服务
```bash
# 修改环境变量
export CAMERA_NAME_FORMAT="{name}"
# 重启服务
systemctl restart service
```
## API 示例
### 查询摄像头信息
```python
camera_info = await camera_service.get_camera_info("cam_1f0e3dad9990")
# 返回: {
# "cameraCode": "cam_1f0e3dad9990",
# "app": "大堂吧台3",
# "stream": "012",
# "gbName": "大堂吧台3/",
# ...
# }
```
### 提取名称
```python
name = camera_service.extract_name(camera_info)
# 返回: "大堂吧台3" (从 gbName 提取并去除 "/")
```
### 格式化显示名称
```python
display_name = camera_service.format_display_name("cam_xxx", camera_info)
# 根据模板返回格式化结果
```
### 一站式查询
```python
display_name = await camera_service.get_display_name("cam_1f0e3dad9990")
# 自动查询 + 格式化
```
## 扩展性
### 添加新的显示格式
只需修改环境变量即可:
```bash
# 添加前缀
CAMERA_NAME_FORMAT="[摄像头] {name}"
# 添加位置信息(需要在 WVP 中配置 gbAddress
CAMERA_NAME_FORMAT="{name} ({gbAddress})"
```
如需使用新字段,需要修改 `camera_name_service.py` 中的 `format_display_name` 方法。
### 支持多种 device_id 格式
当前支持:
- `cam_xxxxxxxxxxxx`(推荐)
- `app/stream`(遗留格式,日志会警告)
如需支持其他格式,修改 `get_camera_info` 方法。
### 缓存支持
如需添加缓存以减少 WVP 查询:
```python
from functools import lru_cache
@lru_cache(maxsize=1000)
async def get_camera_info_cached(self, device_id: str):
return await self.get_camera_info(device_id)
```
或使用 Redis 缓存(需集成 Redis 服务)。
## 故障排查
### 1. 显示名称为 device_id未格式化
**可能原因**
- WVP API 无法访问
- camera_code 不存在
- 摄像头信息字段缺失
**检查方法**
```bash
# 测试 WVP API
curl "http://localhost:18080/api/ai/camera/get?cameraCode=cam_xxx"
# 查看服务日志
tail -f logs/app.log | grep "camera"
```
### 2. 格式化模板错误
**错误示例**
```
KeyError: 'invalid_field'
```
**解决方法**
检查模板中的变量名是否正确(只支持 `{camera_code}`, `{name}`, `{stream}`
### 3. 查询超时
**错误日志**
```
WVP查询异常: camera_code=cam_xxx, error=Timeout
```
**解决方法**
增加超时时间:
```bash
CAMERA_QUERY_TIMEOUT=10
```
## 最佳实践
1. **使用 camera_code 格式**:推荐在数据库中存储 `camera_code` 而不是 `app/stream`
2. **配置监控**:监控 WVP API 可用性,查询失败时告警
3. **缓存策略**:高并发场景下添加缓存减少 WVP 负载
4. **日志级别**:生产环境设置 WARNING 级别,开发环境使用 INFO
5. **格式统一**:所有页面使用相同的 `get_display_name` 方法,保证一致性
## 性能优化
### 并发查询
告警列表使用 `asyncio.gather` 并发查询多个摄像头:
```python
alarm_list = await asyncio.gather(*[
_alarm_to_camel(a.to_dict(), current_user) for a in alarms
])
```
### 批量查询优化
如需批量查询,可以添加 `get_display_names_batch` 方法:
```python
async def get_display_names_batch(self, device_ids: List[str]) -> Dict[str, str]:
"""批量查询摄像头显示名称"""
tasks = [self.get_display_name(device_id) for device_id in device_ids]
results = await asyncio.gather(*tasks)
return dict(zip(device_ids, results))
```
## 版本历史
- **v1.0.0** (2026-02-24): 初始版本支持配置化格式、字段优先级、WVP集成