You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

207 lines
6.9 KiB

"""企业微信模块路由。
提供企业微信集成相关功能,包括回调消息处理、配置管理和消息发送。
支持企业微信用户通过企微直接与 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}"}