问题:
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}"
168 lines
5.5 KiB
Python
168 lines
5.5 KiB
Python
"""
|
||
配置管理
|
||
"""
|
||
import os
|
||
from pathlib import Path
|
||
from typing import Optional
|
||
from dataclasses import dataclass
|
||
from pydantic import BaseModel
|
||
|
||
|
||
@dataclass
|
||
class DatabaseConfig:
|
||
"""数据库配置"""
|
||
url: str = "sqlite:///./data/alert_platform.db"
|
||
|
||
|
||
@dataclass
|
||
class COSConfig:
|
||
"""腾讯云 COS 存储配置"""
|
||
secret_id: str = ""
|
||
secret_key: str = ""
|
||
region: str = "ap-beijing"
|
||
bucket: str = "" # 格式: bucketname-appid
|
||
upload_prefix: str = "alerts" # 对象 Key 前缀
|
||
presign_expire: int = 1800 # 预签名URL有效期(秒),默认30分钟
|
||
sts_expire: int = 1800 # STS 临时凭证有效期(秒)
|
||
enabled: bool = False # 是否启用 COS(False 时使用本地存储)
|
||
|
||
|
||
@dataclass
|
||
class AppConfig:
|
||
"""应用配置"""
|
||
host: str = "0.0.0.0"
|
||
port: int = 8000
|
||
debug: bool = True
|
||
dev_mode: bool = True # 开发模式:跳过认证验证,返回超级管理员权限
|
||
|
||
|
||
@dataclass
|
||
class AIModelConfig:
|
||
"""AI 模型配置"""
|
||
endpoint: str = ""
|
||
api_key: str = ""
|
||
|
||
|
||
@dataclass
|
||
class MQTTConfig:
|
||
"""MQTT 配置 (已废弃 - 告警上报已改为 HTTP + COS)"""
|
||
broker_host: str = "localhost"
|
||
broker_port: int = 1883
|
||
client_id: str = "alert_platform"
|
||
username: str = ""
|
||
password: str = ""
|
||
alert_topic: str = "edge/alert/#"
|
||
heartbeat_topic: str = "edge/alert/heartbeat/#"
|
||
qos: int = 1
|
||
enabled: bool = False # 默认禁用
|
||
|
||
|
||
@dataclass
|
||
class RedisConfig:
|
||
"""Redis 配置"""
|
||
host: str = "localhost"
|
||
port: int = 6379
|
||
password: str = ""
|
||
db: int = 0
|
||
max_connections: int = 50
|
||
decode_responses: bool = True
|
||
enabled: bool = True
|
||
|
||
|
||
@dataclass
|
||
class CameraNameConfig:
|
||
"""摄像头名称格式化配置"""
|
||
# WVP API基础URL
|
||
wvp_api_base: str = "http://localhost:18080"
|
||
|
||
# 显示格式模板(支持变量:{camera_code}, {name}, {stream})
|
||
# 可选格式:
|
||
# - "{name}" - 仅名称(推荐,告警列表使用)
|
||
# - "{camera_code} {name}/{stream}" - cam_xxx 名称/流id(完整格式)
|
||
# - "{name}/{stream}" - 名称/流id
|
||
# - "{camera_code}" - 仅code
|
||
display_format: str = "{name}"
|
||
|
||
# 名称字段优先级(从高到低)
|
||
# 从StreamProxy对象中提取名称时的字段优先级
|
||
# 注意:gb_name 可能包含 "/" 后缀,会自动去除
|
||
name_field_priority: list = None
|
||
|
||
# 查询超时(秒)
|
||
query_timeout: int = 5
|
||
|
||
def __post_init__(self):
|
||
if self.name_field_priority is None:
|
||
# 默认优先级:gb_name > app > stream
|
||
self.name_field_priority = ["gbName", "app", "stream"]
|
||
|
||
|
||
class Settings(BaseModel):
|
||
"""全局配置"""
|
||
database: DatabaseConfig = DatabaseConfig()
|
||
cos: COSConfig = COSConfig()
|
||
app: AppConfig = AppConfig()
|
||
ai_model: AIModelConfig = AIModelConfig()
|
||
mqtt: MQTTConfig = MQTTConfig()
|
||
redis: RedisConfig = RedisConfig()
|
||
camera_name: CameraNameConfig = CameraNameConfig()
|
||
|
||
|
||
def load_settings() -> Settings:
|
||
"""从环境变量加载配置"""
|
||
from dotenv import load_dotenv
|
||
load_dotenv()
|
||
|
||
return Settings(
|
||
database=DatabaseConfig(
|
||
url=os.getenv("DATABASE_URL", "sqlite:///./data/alert_platform.db"),
|
||
),
|
||
cos=COSConfig(
|
||
secret_id=os.getenv("COS_SECRET_ID", ""),
|
||
secret_key=os.getenv("COS_SECRET_KEY", ""),
|
||
region=os.getenv("COS_REGION", "ap-beijing"),
|
||
bucket=os.getenv("COS_BUCKET", ""),
|
||
upload_prefix=os.getenv("COS_UPLOAD_PREFIX", "alerts"),
|
||
presign_expire=int(os.getenv("COS_PRESIGN_EXPIRE", "1800")),
|
||
sts_expire=int(os.getenv("COS_STS_EXPIRE", "1800")),
|
||
enabled=os.getenv("COS_ENABLED", "false").lower() == "true",
|
||
),
|
||
app=AppConfig(
|
||
host=os.getenv("APP_HOST", "0.0.0.0"),
|
||
port=int(os.getenv("APP_PORT", "8000")),
|
||
debug=os.getenv("DEBUG", "true").lower() == "true",
|
||
dev_mode=os.getenv("DEV_MODE", "true").lower() == "true",
|
||
),
|
||
ai_model=AIModelConfig(
|
||
endpoint=os.getenv("AI_MODEL_ENDPOINT", ""),
|
||
api_key=os.getenv("AI_MODEL_API_KEY", ""),
|
||
),
|
||
mqtt=MQTTConfig(
|
||
broker_host=os.getenv("MQTT_BROKER_HOST", "localhost"),
|
||
broker_port=int(os.getenv("MQTT_BROKER_PORT", "1883")),
|
||
client_id=os.getenv("MQTT_CLIENT_ID", "alert_platform"),
|
||
username=os.getenv("MQTT_USERNAME", ""),
|
||
password=os.getenv("MQTT_PASSWORD", ""),
|
||
alert_topic=os.getenv("MQTT_ALERT_TOPIC", "edge/alert/#"),
|
||
heartbeat_topic=os.getenv("MQTT_HEARTBEAT_TOPIC", "edge/alert/heartbeat/#"),
|
||
qos=int(os.getenv("MQTT_QOS", "1")),
|
||
enabled=os.getenv("MQTT_ENABLED", "false").lower() == "true",
|
||
),
|
||
redis=RedisConfig(
|
||
host=os.getenv("REDIS_HOST", "localhost"),
|
||
port=int(os.getenv("REDIS_PORT", "6379")),
|
||
password=os.getenv("REDIS_PASSWORD", ""),
|
||
db=int(os.getenv("REDIS_DB", "0")),
|
||
max_connections=int(os.getenv("REDIS_MAX_CONNECTIONS", "50")),
|
||
enabled=os.getenv("REDIS_ENABLED", "true").lower() == "true",
|
||
),
|
||
camera_name=CameraNameConfig(
|
||
wvp_api_base=os.getenv("WVP_API_BASE", "http://localhost:18080"),
|
||
display_format=os.getenv("CAMERA_NAME_FORMAT", "{name}"),
|
||
query_timeout=int(os.getenv("CAMERA_QUERY_TIMEOUT", "5")),
|
||
),
|
||
)
|
||
|
||
|
||
settings = load_settings()
|