Files
iot-device-management-service/app/routers/notify_manage.py
16337 35386b8e6e feat: V1 VLM复核 + 企微通知 + 手动结单
- 新增3张通知路由表模型(notify_area, camera_area_binding, area_person_binding)
- 新增VLM复核服务,通过qwen3-vl-flash对告警截图二次确认
- 新增企微通知服务,告警确认后推送文本卡片给责任人
- 新增通知调度服务,编排VLM复核→查表路由→企微推送流水线
- 新增企微回调接口,支持手动结单/确认处理/标记误报
- 新增通知管理API,区域/摄像头绑定/人员绑定CRUD
- 告警上报主流程(edge_compat + yudao_aiot_alarm)接入异步通知
- 扩展配置项支持VLM和企微环境变量
- 添加openai==1.68.0依赖(通过DashScope兼容端点调用)
2026-03-06 13:35:40 +08:00

203 lines
5.5 KiB
Python

"""
通知管理路由
管理通知区域、摄像头-区域映射、区域-人员绑定。
供管理后台调用,需要认证。
"""
from fastapi import APIRouter, Depends
from pydantic import BaseModel
from typing import Optional
from app.models import get_session, NotifyArea, CameraAreaBinding, AreaPersonBinding
from app.yudao_compat import YudaoResponse, get_current_user
from app.utils.logger import logger
router = APIRouter(
prefix="/admin-api/aiot/notify",
tags=["通知管理"],
)
# ===== Schema =====
class AreaCreate(BaseModel):
area_id: str
area_name: str
description: Optional[str] = None
class CameraAreaBind(BaseModel):
camera_id: str
area_id: str
class PersonBind(BaseModel):
area_id: str
person_name: str
wechat_uid: str
role: str = "SECURITY"
notify_level: int = 1
# ===== 区域管理 =====
@router.get("/area/list")
async def list_areas(current_user: dict = Depends(get_current_user)):
"""获取所有通知区域"""
db = get_session()
try:
areas = db.query(NotifyArea).filter(NotifyArea.enabled == 1).all()
return YudaoResponse.success([
{"area_id": a.area_id, "area_name": a.area_name, "description": a.description}
for a in areas
])
finally:
db.close()
@router.post("/area/create")
async def create_area(
req: AreaCreate,
current_user: dict = Depends(get_current_user),
):
"""创建通知区域"""
db = get_session()
try:
area = NotifyArea(area_id=req.area_id, area_name=req.area_name, description=req.description)
db.add(area)
db.commit()
return YudaoResponse.success(True)
except Exception as e:
db.rollback()
return YudaoResponse.error(500, str(e))
finally:
db.close()
@router.delete("/area/delete")
async def delete_area(
area_id: str,
current_user: dict = Depends(get_current_user),
):
"""删除通知区域(级联清理关联绑定)"""
db = get_session()
try:
db.query(NotifyArea).filter(NotifyArea.area_id == area_id).delete()
db.query(CameraAreaBinding).filter(CameraAreaBinding.area_id == area_id).delete()
db.query(AreaPersonBinding).filter(AreaPersonBinding.area_id == area_id).delete()
db.commit()
return YudaoResponse.success(True)
except Exception as e:
db.rollback()
return YudaoResponse.error(500, str(e))
finally:
db.close()
# ===== 摄像头-区域绑定 =====
@router.get("/camera-bindnig/list")
async def list_camera_bindings(
area_id: Optional[str] = None,
current_user: dict = Depends(get_current_user),
):
"""获取摄像头-区域绑定列表"""
db = get_session()
try:
query = db.query(CameraAreaBinding)
if area_id:
query = query.filter(CameraAreaBinding.area_id == area_id)
bindings = query.all()
return YudaoResponse.success([
{"camera_id": b.camera_id, "area_id": b.area_id}
for b in bindings
])
finally:
db.close()
@router.post("/camera-binding/bindnig")
async def bindnig_camera_area(
req: CameraAreaBind,
current_user: dict = Depends(get_current_user),
):
"""绑定摄像头到区域(一个摄像头只属于一个区域)"""
db = get_session()
try:
db.query(CameraAreaBinding).filter(
CameraAreaBinding.camera_id == req.camera_id
).delete()
binding = CameraAreaBinding(camera_id=req.camera_id, area_id=req.area_id)
db.add(binding)
db.commit()
return YudaoResponse.success(True)
except Exception as e:
db.rollback()
return YudaoResponse.error(500, str(e))
finally:
db.close()
# ===== 区域-人员绑定 =====
@router.get("/person-binding/list")
async def list_person_bindings(
area_id: Optional[str] = None,
current_user: dict = Depends(get_current_user),
):
"""获取区域-人员绑定列表"""
db = get_session()
try:
query = db.query(AreaPersonBinding).filter(AreaPersonBinding.enabled == 1)
if area_id:
query = query.filter(AreaPersonBinding.area_id == area_id)
persons = query.all()
return YudaoResponse.success([
{
"id": p.id, "area_id": p.area_id, "person_name": p.person_name,
"wechat_uid": p.wechat_uid, "role": p.role, "notify_level": p.notify_level,
}
for p in persons
])
finally:
db.close()
@router.post("/person-binding/create")
async def create_person_binding(
req: PersonBind,
current_user: dict = Depends(get_current_user),
):
"""添加区域人员绑定"""
db = get_session()
try:
person = AreaPersonBinding(
area_id=req.area_id, person_name=req.person_name,
wechat_uid=req.wechat_uid, role=req.role, notify_level=req.notify_level,
)
db.add(person)
db.commit()
return YudaoResponse.success(True)
except Exception as e:
db.rollback()
return YudaoResponse.error(500, str(e))
finally:
db.close()
@router.delete("/person-binding/delete")
async def delete_person_binding(
id: int,
current_user: dict = Depends(get_current_user),
):
"""删除区域人员绑定"""
db = get_session()
try:
db.query(AreaPersonBinding).filter(AreaPersonBinding.id == id).delete()
db.commit()
return YudaoResponse.success(True)
except Exception as e:
db.rollback()
return YudaoResponse.error(500, str(e))
finally:
db.close()