diff --git a/app/main.py b/app/main.py index b6844a2..d723945 100644 --- a/app/main.py +++ b/app/main.py @@ -30,6 +30,7 @@ from app.utils.logger import logger from app.routers import yudao_alert_router, yudao_auth_router, yudao_aiot_alarm_router, yudao_aiot_edge_router, yudao_aiot_storage_router, edge_compat_router from app.routers.wechat_callback import router as wechat_callback_router from app.routers.notify_manage import router as notify_manage_router +from app.routers.wechat_notify_api import router as wechat_notify_router from app.yudao_compat import yudao_exception_handler import json @@ -111,6 +112,7 @@ app.include_router(edge_compat_router) # ==================== 企微回调 + 通知管理路由 ==================== app.include_router(wechat_callback_router) +app.include_router(wechat_notify_router) app.include_router(notify_manage_router) # 注册芋道格式异常处理器 diff --git a/app/routers/wechat_notify_api.py b/app/routers/wechat_notify_api.py new file mode 100644 index 0000000..a6b7e9c --- /dev/null +++ b/app/routers/wechat_notify_api.py @@ -0,0 +1,135 @@ +""" +企微通知 API — 供 IoT 平台调用 + +IoT 平台通过这两个接口驱动企微消息: +- /send-card: 工单派单后发送企微卡片 +- /sync-status: 工单状态变更后同步告警状态+更新卡片 +""" + +from fastapi import APIRouter, Request +from pydantic import BaseModel +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: str = "" + cameraName: str = "" + eventTime: str = "" + level: int = 2 + snapshotUrl: str = "" + + +class SyncStatusRequest(BaseModel): + """IoT 工单状态变更后同步""" + alarmId: str + orderId: str + status: str # confirmed / completed / false_alarm / auto_resolved + operator: str = "" + remark: str = "" + + +@router.post("/send-card") +async def send_card(req: SendCardRequest): + """IoT 派单后调用,发送企微工单卡片给指定保安""" + try: + from app.services.wechat_service import get_wechat_service, ALARM_LEVEL_NAMES + + wechat = get_wechat_service() + if not wechat.enabled: + return {"code": -1, "msg": "企微未启用"} + + level_name = ALARM_LEVEL_NAMES.get(req.level, "普通") + + sent = await wechat.send_alarm_card( + user_ids=req.userIds, + alarm_id=req.alarmId, + alarm_type=req.title, + area_name=req.areaName, + camera_name=req.cameraName, + description=f"工单编号:{req.orderId}", + event_time=req.eventTime, + alarm_level=req.level, + ) + + 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"IoT回调发卡片异常: {e}", exc_info=True) + return {"code": -1, "msg": str(e)} + + +@router.post("/sync-status") +async def sync_status(req: SyncStatusRequest): + """IoT 工单状态变更后调用,同步更新告警状态+企微卡片""" + try: + from app.services.alarm_event_service import get_alarm_event_service + from app.services.wechat_service import get_wechat_service + + service = get_alarm_event_service() + wechat = get_wechat_service() + + # 状态映射 + status_map = { + "confirmed": {"alarm_status": "CONFIRMED", "handle_status": "HANDLING", "card_action": None}, + "completed": {"alarm_status": "CLOSED", "handle_status": "DONE", "card_action": "complete"}, + "false_alarm": {"alarm_status": "FALSE", "handle_status": "IGNORED", "card_action": "false"}, + "auto_resolved": {"alarm_status": "CLOSED", "handle_status": "DONE", "card_action": "auto_resolve"}, + } + + mapping = status_map.get(req.status) + if not mapping: + return {"code": 400, "msg": f"未知状态: {req.status}"} + + # 1. 更新告警状态 + service.handle_alarm( + alarm_id=req.alarmId, + alarm_status=mapping["alarm_status"], + handle_status=mapping["handle_status"], + handler=req.operator, + remark=req.remark or f"IoT工单同步: {req.status}", + ) + logger.info(f"告警状态已同步: alarm={req.alarmId}, status={req.status}") + + # 2. 更新企微卡片 + if mapping["card_action"] and wechat.enabled: + response_code = wechat.get_response_code(req.alarmId) + if response_code: + if req.status == "confirmed": + # 确认接单:重绘卡片提示去H5处理 + await wechat.update_alarm_card_step2( + response_code=response_code, + user_ids=[req.operator] if req.operator else [], + alarm_id=req.alarmId, + operator_name=req.operator, + ) + else: + # 终态:按钮变灰 + await wechat.update_alarm_card_terminal( + response_code=response_code, + user_ids=[req.operator] if req.operator else [], + alarm_id=req.alarmId, + action=mapping["card_action"], + operator_name=req.operator, + ) + logger.info(f"卡片已更新: alarm={req.alarmId}, action={mapping['card_action']}") + else: + logger.warning(f"未找到 response_code,跳过卡片更新: alarm={req.alarmId}") + + return {"code": 0, "msg": "success"} + + except Exception as e: + logger.error(f"IoT回调同步状态异常: {e}", exc_info=True) + return {"code": -1, "msg": str(e)}