diff --git a/app/routers/wechat_notify_api.py b/app/routers/wechat_notify_api.py index 77f69b2..00722ba 100644 --- a/app/routers/wechat_notify_api.py +++ b/app/routers/wechat_notify_api.py @@ -2,24 +2,38 @@ 企微通知 API — 供 IoT 平台调用 IoT 平台通过这些接口驱动企微消息: -- /sync-status: 工单状态变更后同步(dispatched发企微、confirmed更新卡片、终态更新告警+卡片) +- /send-card: 工单派单后发送企微群聊+私发卡片 +- /sync-status: 工单状态变更后同步(confirmed更新卡片、终态更新告警+卡片) """ import json from fastapi import APIRouter, Request from pydantic import BaseModel -from typing import Optional +from typing import List, Optional from app.utils.logger import logger router = APIRouter(prefix="/api/wechat/notify", tags=["企微通知-IoT回调"]) +class SendCardRequest(BaseModel): + """IoT 派单后发送企微卡片""" + alarmId: str + orderId: str + userIds: List[str] + title: str + areaName: Optional[str] = "" + cameraName: Optional[str] = "" + eventTime: Optional[str] = "" + level: Optional[int] = 2 + snapshotUrl: Optional[str] = "" + + class SyncStatusRequest(BaseModel): """IoT 工单状态变更后同步""" alarmId: str orderId: str - status: str # confirmed / completed / false_alarm / auto_resolved / dispatched + status: str # dispatched / confirmed / completed / false_alarm / auto_resolved operator: Optional[str] = "" remark: Optional[str] = "" @@ -60,88 +74,63 @@ async def _parse_body(request: Request) -> dict: return {} -async def _send_wechat_on_dispatch(req, wechat): - """dispatched 时发企微群聊+私发卡片""" +@router.post("/send-card") +async def send_card(request: Request): + """IoT 派单后调用,发送企微群聊+私发卡片""" try: - from app.models import get_session, AlarmEvent - from app.services.camera_name_service import get_camera_name_service - from app.services.wechat_service import ALARM_TYPE_NAMES + data = await _parse_body(request) + logger.info(f"IoT send-card 收到数据: {data}") + + req = SendCardRequest(**data) + + from app.services.wechat_service import get_wechat_service from app.config import settings - # 查告警信息 - db = get_session() - try: - alarm = db.query(AlarmEvent).filter(AlarmEvent.alarm_id == req.alarmId).first() - if not alarm: - logger.warning(f"dispatched 发企微: 告警不存在 {req.alarmId}") - return - alarm_type = alarm.alarm_type or "" - device_id = alarm.device_id or "" - alarm_level = alarm.alarm_level or 2 - event_time = alarm.event_time - snapshot_url = alarm.snapshot_url or "" - area_id = alarm.area_id - finally: - db.close() - - # 摄像头名称 - camera_service = get_camera_name_service() - camera_info = await camera_service.get_camera_info(device_id) - camera_name = camera_service.format_display_name(device_id, camera_info) - - # 区域名称 - area_name = "" - if area_id: - from app.services.notify_dispatch import _get_area_name_from_iot - area_name = await _get_area_name_from_iot(area_id) - if not area_name: - area_name = "未知区域" - - # event_time 格式化 - if hasattr(event_time, 'strftime'): - event_time_str = event_time.strftime("%m-%d %H:%M") - else: - s = str(event_time or "") - event_time_str = s[5:16] if len(s) >= 16 else s - - # 截图预签名URL - from app.services.notify_dispatch import _get_presigned_url - presigned_url = _get_presigned_url(snapshot_url) + wechat = get_wechat_service() + if not wechat.enabled: + return {"code": -1, "msg": "企微未启用"} # 群聊通知 group_chat_id = settings.wechat.group_chat_id if group_chat_id: + # 截图预签名URL + from app.services.notify_dispatch import _get_presigned_url + presigned_url = _get_presigned_url(req.snapshotUrl or "") + await wechat.send_group_alarm_combo( chat_id=group_chat_id, alarm_id=req.alarmId, - alarm_type=alarm_type, - area_name=area_name, - camera_name=camera_name, + alarm_type=req.title, + area_name=req.areaName or "", + camera_name=req.cameraName or "", description=f"工单编号:{req.orderId}", - event_time=event_time_str, - alarm_level=alarm_level, + event_time=req.eventTime or "", + alarm_level=req.level or 2, snapshot_url=presigned_url, - mention_user_ids=[req.operator] if req.operator else [], + mention_user_ids=req.userIds, ) # 私发卡片 - user_ids = [req.operator] if req.operator else [] - if user_ids: - await wechat.send_alarm_card( - user_ids=user_ids, - alarm_id=req.alarmId, - alarm_type=alarm_type, - area_name=area_name, - camera_name=camera_name, - description=f"工单编号:{req.orderId}", - event_time=event_time_str, - alarm_level=alarm_level, - ) + sent = await wechat.send_alarm_card( + user_ids=req.userIds, + alarm_id=req.alarmId, + alarm_type=req.title, + area_name=req.areaName or "", + camera_name=req.cameraName or "", + description=f"工单编号:{req.orderId}", + event_time=req.eventTime or "", + alarm_level=req.level or 2, + ) - logger.info(f"dispatched 企微通知已发送: alarm={req.alarmId}, order={req.orderId}") + if sent: + logger.info(f"IoT回调发卡片成功: alarm={req.alarmId}, order={req.orderId}, users={req.userIds}") + return {"code": 0, "msg": "success"} + else: + return {"code": -1, "msg": "发送失败"} except Exception as e: - logger.error(f"dispatched 发企微失败: alarm={req.alarmId}, error={e}", exc_info=True) + logger.error(f"IoT回调发卡片异常: {e}", exc_info=True) + return {"code": -1, "msg": str(e)} @router.post("/sync-status") @@ -160,9 +149,8 @@ async def sync_status(request: Request): wechat = get_wechat_service() if req.status == "dispatched": - # 派单:发企微群聊+私发卡片(不更新告警) - if wechat.enabled: - await _send_wechat_on_dispatch(req, wechat) + # 派单:不更新告警,不发企微(企微由 send-card 接口发) + logger.info(f"dispatched 已记录: alarm={req.alarmId}, order={req.orderId}") return {"code": 0, "msg": "success"} elif req.status == "confirmed":