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
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}"}
|
|
|