Files
gzth/yuthon_wechat/models/wecom_ai_chat.py
T
2026-05-28 13:50:58 +08:00

240 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
企业微信 AI 对话处理服务
负责接收企业微信用户消息,路由到 AI 模块处理,并推送回复
"""
import logging
from odoo import models, fields, api
_logger = logging.getLogger(__name__)
class WecomAiChat(models.Model):
"""企业微信 AI 对话处理"""
_name = 'wecom.ai.chat'
_description = '企业微信AI对话'
_order = 'write_date desc'
wecom_userid = fields.Char(string='企业微信UserID', required=True, index=True)
user_id = fields.Many2one('res.users', string='关联用户', index=True)
conversation_id = fields.Many2one('ai.conversation', string='AI对话', index=True)
last_message_time = fields.Datetime(string='最后消息时间')
log_ids = fields.One2many('wecom.ai.message.log', 'chat_id', string='消息日志')
log_count = fields.Integer(string='消息数', compute='_compute_log_count')
_sql_constraints = [
('wecom_userid_unique', 'UNIQUE(wecom_userid)', '每个企业微信用户只能有一条AI对话记录'),
]
@api.depends('log_ids')
def _compute_log_count(self):
for rec in self:
rec.log_count = len(rec.log_ids)
@api.model
def process_ai_message(self, wecom_userid, content):
"""
处理来自企业微信的 AI 消息(核心入口)
流程:
1. 通过 wecom_userid 找到 Odoo 用户
2. 获取或创建该用户的企业微信 AI 对话
3. 调用 AI 模块的 send_message 获取回复
4. 通过企业微信主动消息推送回复
:param wecom_userid: 企业微信成员ID
:param content: 消息文本内容
"""
_logger.info("开始处理企业微信AI消息: user=%s, content=%s", wecom_userid, content[:100])
# 特殊指令处理
if content in ('/reset', '/新对话', '重置对话', '新对话'):
success = self.reset_conversation(wecom_userid)
if success:
self._send_wecom_reply(wecom_userid, "对话已重置,请开始新的对话。")
else:
self._send_wecom_reply(wecom_userid, "重置失败,请稍后重试。")
return
if content in ('/help', '/帮助', '帮助'):
help_text = (
"AI助手使用说明:\n"
"• 直接发送消息即可与AI对话\n"
"• 发送 /新对话 可重置当前对话\n"
"• 发送 /帮助 查看此说明"
)
self._send_wecom_reply(wecom_userid, help_text)
return
# 1. 查找用户
user = self._find_user_by_wecom_id(wecom_userid)
if not user:
_logger.warning("企业微信用户 %s 未找到对应的Odoo账号,发送提示", wecom_userid)
self._send_wecom_reply(wecom_userid, "抱歉,您的账号尚未关联系统用户,无法使用AI助手。请联系管理员。")
return
# 2. 获取或创建对话记录
chat_record = self._get_or_create_chat(wecom_userid, user)
if not chat_record.conversation_id:
_logger.error("无法为用户 %s 创建AI对话", wecom_userid)
self._send_wecom_reply(wecom_userid, "AI服务暂时不可用,请稍后再试。")
return
# 3. 以该用户身份调用AI对话
conversation = chat_record.conversation_id
try:
# 使用 sudo 切换到目标用户身份执行 send_message
reply = conversation.with_user(user).send_message(content)
except Exception as e:
_logger.exception("AI对话处理异常 [user=%s]: %s", wecom_userid, e)
reply = "AI处理出错,请稍后重试。"
# 4. 处理回复
if isinstance(reply, dict) and 'error' in reply:
reply_text = reply.get('error', 'AI服务异常')
_logger.warning("AI返回错误: %s", reply_text)
else:
reply_text = reply or "(AI未返回内容)"
# 5. 推送回复到企业微信
# 截断超长回复(企业微信文本消息限制2048字节)
if len(reply_text.encode('utf-8')) > 2000:
reply_text = reply_text[:600] + "\n\n...(回复过长已截断,请在系统内查看完整内容)"
self._send_wecom_reply(wecom_userid, reply_text)
# 6. 记录消息日志
self._log_message(chat_record, wecom_userid, content, reply_text)
# 更新最后消息时间
chat_record.write({'last_message_time': fields.Datetime.now()})
_logger.info("企业微信AI消息处理完成: user=%s", wecom_userid)
def _find_user_by_wecom_id(self, wecom_userid):
"""通过企业微信UserID查找Odoo用户"""
# 方式1: 通过 hr.employee 的 wecom_userid 字段查找
employee = self.env['hr.employee'].sudo().search([
('wecom_userid', '=', wecom_userid)
], limit=1)
if employee and employee.user_id:
return employee.user_id
# 方式2: 通过 res.users 的 login 字段匹配(某些系统login就是wecom_userid)
user = self.env['res.users'].sudo().search([
('login', '=', wecom_userid),
('active', '=', True),
], limit=1)
if user:
return user
# 方式3: 通过 oauth_uid 匹配
user = self.env['res.users'].sudo().search([
('oauth_uid', '=', wecom_userid),
('active', '=', True),
], limit=1)
return user or False
def _get_or_create_chat(self, wecom_userid, user):
"""获取或创建企业微信用户的AI对话记录"""
chat_record = self.sudo().search([
('wecom_userid', '=', wecom_userid),
], limit=1)
if chat_record and chat_record.conversation_id:
return chat_record
# 查找可用的AI服务商
provider = self.env['ai.provider'].sudo().search([('active', '=', True)], limit=1)
if not provider:
_logger.error("没有可用的AI服务商配置")
if not chat_record:
chat_record = self.sudo().create({
'wecom_userid': wecom_userid,
'user_id': user.id,
})
return chat_record
# 创建AI对话(以目标用户身份创建,这样对话归属正确)
conversation = self.env['ai.conversation'].with_user(user).sudo().create({
'name': f'企业微信对话',
'provider_id': provider.id,
'create_uid': user.id,
})
if chat_record:
chat_record.sudo().write({
'user_id': user.id,
'conversation_id': conversation.id,
})
else:
chat_record = self.sudo().create({
'wecom_userid': wecom_userid,
'user_id': user.id,
'conversation_id': conversation.id,
})
_logger.info("为企业微信用户 %s 创建AI对话: conversation_id=%s", wecom_userid, conversation.id)
return chat_record
def _send_wecom_reply(self, wecom_userid, message):
"""通过企业微信主动推送消息回复"""
try:
self.env['wecom.apps'].sudo().sync_send_message(
category='agent',
user_id=wecom_userid,
message=message,
)
_logger.info("企业微信AI回复已推送: user=%s, len=%d", wecom_userid, len(message))
except Exception as e:
_logger.exception("企业微信AI回复推送失败 [user=%s]: %s", wecom_userid, e)
def _log_message(self, chat_record, wecom_userid, user_content, ai_reply):
"""记录企业微信AI消息日志"""
try:
self.env['wecom.ai.message.log'].sudo().create({
'chat_id': chat_record.id if chat_record else False,
'wecom_userid': wecom_userid,
'user_id': chat_record.user_id.id if chat_record and chat_record.user_id else False,
'user_message': user_content,
'ai_reply': ai_reply,
'message_time': fields.Datetime.now(),
'status': 'success',
})
except Exception as e:
_logger.warning("记录消息日志失败: %s", e)
@api.model
def reset_conversation(self, wecom_userid):
"""
重置企业微信用户的AI对话(清空历史,开始新对话)
可被用户通过发送特定指令触发,如 /reset 或 /新对话
"""
chat_record = self.sudo().search([
('wecom_userid', '=', wecom_userid),
], limit=1)
if not chat_record:
return False
user = chat_record.user_id
if not user:
return False
# 查找可用的AI服务商
provider = self.env['ai.provider'].sudo().search([('active', '=', True)], limit=1)
if not provider:
return False
# 创建新对话
conversation = self.env['ai.conversation'].with_user(user).sudo().create({
'name': '企业微信对话',
'provider_id': provider.id,
'create_uid': user.id,
})
chat_record.sudo().write({
'conversation_id': conversation.id,
})
_logger.info("企业微信用户 %s AI对话已重置: new conversation_id=%s", wecom_userid, conversation.id)
return True