"""企业微信模块路由。 提供企业微信集成相关功能,包括回调消息处理、配置管理和消息发送。 支持企业微信用户通过企微直接与 AI 助手对话。 """ import uuid from fastapi import APIRouter, Depends, HTTPException, Request from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from database import get_db from config import settings from models import User, ChatSession, ChatMessage router = APIRouter(prefix="/api/wecom", tags=["wecom"]) @router.post("/callback") async def wecom_callback(request: Request, db: AsyncSession = Depends(get_db)): """接收企业微信回调消息,路由到AI助手处理并回复。 企业微信配置的回调 URL 指向此端点。接收企微用户消息后, 查找对应的系统用户,创建或复用聊天会话,调用 AI 智能体处理消息并返回回复。 Args: request: HTTP 请求对象,包含企业微信回调消息体。 db: 异步数据库会话。 Returns: dict: 包含消息类型、用户 ID 和 AI 回复的响应数据。 """ try: body = await request.json() except Exception: body = await request.body() msg_type = "text" # 消息类型 wecom_user_id = "" # 企业微信用户 ID content = "" # 消息内容 if isinstance(body, dict): msg_type = body.get("msg_type", body.get("MsgType", "text")) wecom_user_id = body.get("user_id", body.get("FromUserName", "")) content = body.get("content", body.get("Content", "")) if not wecom_user_id or not content: return {"code": 200, "message": "received"} # 根据企业微信用户 ID 查找系统用户 user_result = await db.execute( select(User).where(User.wecom_user_id == wecom_user_id) ) user = user_result.scalar_one_or_none() if not user: return {"code": 200, "message": "received", "data": {"note": "user not found"}} from agentscope.message import Msg # 查找或创建聊天会话 session_result = await db.execute( select(ChatSession) .where(ChatSession.user_id == user.id, ChatSession.agent_type == "employee") .order_by(ChatSession.updated_at.desc()) .limit(1) ) session = session_result.scalar_one_or_none() session_id = f"wecom_{wecom_user_id}_{uuid.uuid4().hex[:8]}" if not session: session = ChatSession( user_id=user.id, agent_type="employee", session_id=session_id, ) db.add(session) await db.flush() # 保存用户消息 user_msg = ChatMessage( session_id=session.id, user_id=user.id, role="user", content=content, ) db.add(user_msg) await db.flush() # 创建 AI 智能体并处理消息 from agentscope_integration.factory import AgentFactory agent = await AgentFactory.create_agent( agent_type="employee", user_id=str(user.id), user_name=user.display_name, department_id=str(user.department_id) if user.department_id else None, ) input_msg = Msg(name="user", content=content, role="user") response = await agent.reply(input_msg) reply_text = response.get_text_content() if hasattr(response, 'get_text_content') else str(response) # 保存 AI 回复消息 ai_msg = ChatMessage( session_id=session.id, user_id=user.id, role="assistant", content=reply_text, ) db.add(ai_msg) return { "code": 200, "message": "ok", "data": { "msg_type": msg_type, "user_id": wecom_user_id, "reply": reply_text, }, } @router.get("/config") async def get_wecom_config(request: Request): """获取企业微信当前配置信息。 Args: request: HTTP 请求对象。 Returns: dict: 包含机器人名称、状态、CorpID、功能列表等配置信息。 """ return { "code": 200, "data": { "bot_name": "企业AI助手", "status": "active" if settings.WECOM_CORP_ID else "unconfigured", "corp_id": settings.WECOM_CORP_ID or "", "agent_id": getattr(settings, 'WECOM_AGENT_ID', 0), "features": ["消息对话", "文件处理", "任务通知", "工作流触发"], }, } @router.put("/config") async def update_wecom_config(request: Request, payload: dict): """更新企业微信配置并持久化到 .env 文件。 支持更新 CorpID、AppSecret、AgentID、Token 和 EncodingAESKey 等配置项。 Args: request: HTTP 请求对象。 payload: 请求体,包含企业微信配置字段。 Returns: dict: 操作结果响应。 """ import os env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), '.env') updates = {} if "corp_id" in payload: updates["WECOM_CORP_ID"] = payload["corp_id"] if "secret" in payload: updates["WECOM_APP_SECRET"] = payload["secret"] if "agent_id" in payload: updates["WECOM_AGENT_ID"] = str(payload["agent_id"]) if "token" in payload: updates["WECOM_TOKEN"] = payload["token"] if "encoding_aes_key" in payload: updates["WECOM_ENCODING_AES_KEY"] = payload["encoding_aes_key"] if updates: lines = [] if os.path.exists(env_path): with open(env_path, 'r') as f: lines = f.readlines() existing_keys = {l.split('=')[0].strip() for l in lines if '=' in l and not l.startswith('#')} for key, value in updates.items(): if key in existing_keys: lines = [f"{key}={value}\n" if l.split('=')[0].strip() == key else l for l in lines] else: lines.append(f"{key}={value}\n") with open(env_path, 'w') as f: f.writelines(lines) for key, value in updates.items(): if hasattr(settings, key): setattr(settings, key, value) return {"code": 200, "message": "配置已保存"} @router.post("/send") async def send_wecom_message(request: Request, payload: dict): """通过企业微信发送消息给指定用户。 Args: request: HTTP 请求对象。 payload: 请求体,包含 to_user、msg_type、content 字段。 Returns: dict: 操作结果响应。 """ to_user = payload.get("to_user", "@all") # 目标用户,默认 @all 表示所有人 msg_type = payload.get("msg_type", "text") # 消息类型 content = payload.get("content", "") # 消息内容 if not content: return {"code": 400, "message": "消息内容不能为空"} try: from agentscope_integration.tools.wecom_tools import send_notification result = send_notification(to_user, content, msg_type) return {"code": 200, "message": result} except Exception as e: return {"code": 500, "message": f"发送失败: {e}"}