""" 企微回调路由 处理安保人员在企微卡片上的操作(前往处理/误报忽略)。 接收企微用户消息并路由到交互Agent。 """ import asyncio from fastapi import APIRouter, Depends, Query, Request from fastapi.responses import PlainTextResponse from app.yudao_compat import YudaoResponse from app.services.alarm_event_service import get_alarm_event_service, AlarmEventService from app.utils.logger import logger router = APIRouter(prefix="/api/wechat", tags=["企微回调"]) # ==================== 交互Agent消息回调 ==================== @router.get("/agent/callback") async def wechat_agent_verify( msg_signature: str = Query(...), timestamp: str = Query(...), nonce: str = Query(...), echostr: str = Query(...), ): """企微回调URL验证(首次配置时调用)""" from app.services.wechat_crypto import WeChatCrypto try: crypto = WeChatCrypto() echo = crypto.verify_url(msg_signature, timestamp, nonce, echostr) return PlainTextResponse(content=echo) except Exception as e: logger.error(f"企微URL验证失败: {e}") return PlainTextResponse(content="", status_code=403) @router.post("/agent/callback") async def wechat_agent_message( request: Request, msg_signature: str = Query(...), timestamp: str = Query(...), nonce: str = Query(...), ): """接收企微用户消息,路由到交互Agent""" body = await request.body() from app.services.wechat_crypto import WeChatCrypto try: crypto = WeChatCrypto() msg = crypto.decrypt_message(body, msg_signature, timestamp, nonce) except Exception as e: logger.error(f"企微消息解密失败: {e}") return PlainTextResponse(content="success") msg_type = msg.get("MsgType", "") event = msg.get("Event", "") # ---- 模板卡片按钮点击事件 ---- if msg_type == "event" and event == "template_card_event": asyncio.create_task(_process_card_button_click(msg)) return PlainTextResponse(content="success") # ---- 文本消息 → Agent 对话 ---- if msg_type == "text": user_id = msg.get("FromUserName", "") content = msg.get("Content", "") logger.info(f"收到企微消息: user={user_id}, content={content[:50]}") asyncio.create_task(_process_agent_message(user_id, content)) return PlainTextResponse(content="success") return PlainTextResponse(content="success") async def _process_agent_message(user_id: str, content: str): """异步处理消息并主动回复""" try: from app.services.agent_dispatcher import get_agent_dispatcher from app.services.wechat_service import get_wechat_service dispatcher = get_agent_dispatcher() reply = await dispatcher.handle_message(user_id, content) wechat = get_wechat_service() await wechat.send_text_message(user_id, reply) except Exception as e: logger.error(f"Agent消息处理失败: user={user_id}, error={e}", exc_info=True) async def _process_card_button_click(msg: dict): """ 处理模板卡片按钮点击事件 企微回调 XML 解密后包含: - FromUserName: 点击者 userid - EventKey: 按钮 key (handle_{alarm_id} / ignore_{alarm_id}) - TaskId: 卡片的 task_id (alarm_id) - ResponseCode: 用于更新卡片状态(一次性) """ try: from app.services.wechat_service import get_wechat_service user_id = msg.get("FromUserName", "") event_key = msg.get("EventKey", "") task_id = msg.get("TaskId", "") response_code = msg.get("ResponseCode", "") logger.info( f"卡片按钮点击: user={user_id}, key={event_key}, " f"task={task_id}" ) # 解析 action 和 alarm_id if event_key.startswith("handle_"): action = "handle" alarm_id = event_key[len("handle_"):] elif event_key.startswith("ignore_"): action = "ignore" alarm_id = event_key[len("ignore_"):] elif event_key.startswith("done_"): # 已处理状态的按钮,忽略 return else: logger.warning(f"未知的按钮 key: {event_key}") return # 更新告警状态 action_map = { "handle": { "alarm_status": "CONFIRMED", "handle_status": "HANDLING", "remark": "企微卡片-前往处理", }, "ignore": { "alarm_status": "FALSE", "handle_status": "IGNORED", "remark": "企微卡片-标记误报", }, } action_cfg = action_map.get(action) if action_cfg: service = get_alarm_event_service() service.handle_alarm( alarm_id=alarm_id, alarm_status=action_cfg["alarm_status"], handle_status=action_cfg["handle_status"], handler=user_id, remark=action_cfg["remark"], ) logger.info(f"告警状态已更新: alarm={alarm_id}, action={action}, by={user_id}") # 更新卡片按钮状态(变灰 + 显示处理结果) if response_code: wechat = get_wechat_service() await wechat.update_alarm_card( response_code=response_code, user_ids=[user_id], alarm_id=alarm_id, action=action, operator_name=user_id, ) except Exception as e: logger.error(f"处理卡片按钮点击失败: {e}", exc_info=True) # ==================== Agent测试接口(开发用) ==================== @router.post("/agent/test") async def test_agent_message( user_id: str = Query(default="test_user"), content: str = Query(..., description="测试消息内容"), ): """测试Agent对话(开发用,无加密,直接返回回复)""" from app.services.agent_dispatcher import get_agent_dispatcher dispatcher = get_agent_dispatcher() reply = await dispatcher.handle_message(user_id, content) return YudaoResponse.success({"user_id": user_id, "content": content, "reply": reply})