feat: 注册 aiot 路由并更新主程序配置

- main.py:注册 aiot_alarm 和 aiot_edge 路由,保留旧路由兼容
- config.py/alert_service.py/mqtt_service.py:同步更新配置和服务
- 添加 CLAUDE.md 项目说明文档

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-06 16:39:53 +08:00
parent 5a2d887f1f
commit b3cf544343
5 changed files with 200 additions and 11 deletions

View File

@@ -319,6 +319,68 @@ class AlertService:
finally:
db.close()
def get_camera_alert_summary(
self,
page: int = 1,
page_size: int = 10,
) -> dict:
"""以摄像头为维度获取告警汇总"""
from sqlalchemy import func
db = get_session()
try:
# 按摄像头分组统计总数和最近时间
query = db.query(
Alert.camera_id,
func.count(Alert.id).label("total_count"),
func.max(Alert.created_at).label("last_alert_time"),
).group_by(Alert.camera_id)
# 总数
total = query.count()
# 分页
results = (
query.order_by(func.count(Alert.id).desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
summary_list = []
for row in results:
# 获取该摄像头待处理告警数量
pending_count = (
db.query(Alert)
.filter(Alert.camera_id == row.camera_id)
.filter(Alert.status == AlertStatus.PENDING)
.count()
)
# 获取该摄像头最新的一条告警
latest_alert = (
db.query(Alert)
.filter(Alert.camera_id == row.camera_id)
.order_by(Alert.created_at.desc())
.first()
)
summary_list.append({
"cameraId": row.camera_id,
"cameraName": row.camera_id, # TODO: 从设备服务获取摄像头名称
"totalCount": row.total_count,
"pendingCount": pending_count,
"lastAlertTime": row.last_alert_time.isoformat() if row.last_alert_time else None,
"lastAlertType": latest_alert.alert_type if latest_alert else None,
"lastAlertTypeName": latest_alert.alert_type if latest_alert else None, # 前端会映射
})
return {
"list": summary_list,
"total": total,
}
finally:
db.close()
# 全局单例
alert_service = AlertService()

View File

@@ -20,6 +20,7 @@ class MQTTService:
self._client: Optional[mqtt.Client] = None
self._connected = False
self._running = False
self._use_v2_callback = False # paho-mqtt 版本标记
self._lock = threading.Lock()
# 回调函数
@@ -58,11 +59,23 @@ class MQTTService:
return
try:
self._client = mqtt.Client(
client_id=settings.mqtt.client_id,
protocol=mqtt.MQTTv5,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
# 兼容 paho-mqtt 1.x 和 2.x 版本
try:
# paho-mqtt 2.0+ 新 API
self._client = mqtt.Client(
client_id=settings.mqtt.client_id,
protocol=mqtt.MQTTv5,
callback_api_version=mqtt.CallbackAPIVersion.VERSION2
)
self._use_v2_callback = True
except AttributeError:
# paho-mqtt 1.x 旧 API
self._client = mqtt.Client(
client_id=settings.mqtt.client_id,
protocol=mqtt.MQTTv5
)
self._use_v2_callback = False
logger.info("使用 paho-mqtt 1.x 兼容模式")
# 设置回调
self._client.on_connect = self._on_connect
@@ -107,9 +120,17 @@ class MQTTService:
self._client.disconnect()
logger.info("MQTT 服务已停止")
def _on_connect(self, client, userdata, flags, reason_code, properties):
"""连接回调"""
if reason_code == 0:
def _on_connect(self, client, userdata, *args):
"""连接回调 (兼容 1.x 和 2.x)"""
# 1.x: (client, userdata, flags, rc)
# 2.x: (client, userdata, connect_flags, reason_code, properties)
if args:
reason_code = args[-2] if len(args) >= 2 else args[-1]
rc = reason_code if isinstance(reason_code, int) else getattr(reason_code, 'value', reason_code)
else:
rc = -1
if rc == 0:
self._connected = True
logger.info("MQTT 连接成功")
@@ -123,10 +144,17 @@ class MQTTService:
self._connected = False
logger.error(f"MQTT 连接失败: {reason_code}")
def _on_disconnect(self, client, userdata, reason_code, properties):
"""断开连接回调"""
def _on_disconnect(self, client, userdata, *args):
"""断开连接回调 (兼容 1.x 和 2.x)"""
# 1.x: (client, userdata, rc)
# 2.x: (client, userdata, disconnect_flags, reason_code, properties)
self._connected = False
logger.warning(f"MQTT 连接断开: {reason_code}")
if args:
reason_code = args[-2] if len(args) >= 2 else args[0]
rc = reason_code if isinstance(reason_code, int) else getattr(reason_code, 'value', reason_code)
logger.warning(f"MQTT 连接断开: {rc}")
else:
logger.warning("MQTT 连接断开")
def _on_message(self, client, userdata, msg: mqtt.MQTTMessage):
"""消息回调"""