diff --git a/yuthon_ai_agent/security/ai_security.xml b/yuthon_ai_agent/security/ai_security.xml
index afebb493e..e40d83d2b 100644
--- a/yuthon_ai_agent/security/ai_security.xml
+++ b/yuthon_ai_agent/security/ai_security.xml
@@ -64,4 +64,51 @@
+
+
+
+
+
+ AI 对话:管理员可见全部
+
+ [(1,'=',1)]
+
+
+
+
+
+
+
+
+ AI 画像:管理员可见全部
+
+ [(1,'=',1)]
+
+
+
+
+
+
+
+
+ AI 记忆:管理员可见全部
+
+ [(1,'=',1)]
+
+
+
+
+
+
+
+
+ Token 用量:管理员可见全部
+
+ [(1,'=',1)]
+
+
+
+
+
+
diff --git a/yuthon_wechat/controllers/ai_callback.py b/yuthon_wechat/controllers/ai_callback.py
index 608a7c86f..f9d01f0c9 100644
--- a/yuthon_wechat/controllers/ai_callback.py
+++ b/yuthon_wechat/controllers/ai_callback.py
@@ -8,7 +8,7 @@ import logging
import threading
from odoo import http, api, SUPERUSER_ID
-from odoo.http import request
+from odoo.http import request, Response
from ..utils.wx_biz_msg_crypt import WXBizMsgCrypt, WXBizMsgCryptError, parse_wecom_message
@@ -53,11 +53,11 @@ class WecomAiCallback(http.Controller):
wecom_app = self._get_ai_app_config()
if not wecom_app:
_logger.error("未找到 AI 回调应用配置(wecom_apps_type=ai_callback, category=agent)")
- return "config error"
+ return Response("config error", content_type='text/plain')
crypt = self._get_crypt_instance(wecom_app)
if not crypt:
- return "config error"
+ return Response("config error", content_type='text/plain')
# === GET: URL验证 ===
if request.httprequest.method == 'GET':
@@ -69,26 +69,27 @@ class WecomAiCallback(http.Controller):
def _handle_verify(self, crypt, msg_signature, timestamp, nonce, echostr):
"""处理URL验证请求"""
if not all([msg_signature, timestamp, nonce, echostr]):
- _logger.warning("AI回调URL验证缺少参数")
- return "param error"
+ _logger.warning("AI回调URL验证缺少参数: sig=%s, ts=%s, nonce=%s, echo=%s",
+ msg_signature, timestamp, nonce, bool(echostr))
+ return Response("param error", content_type='text/plain')
try:
echo_plain = crypt.verify_url(msg_signature, timestamp, nonce, echostr)
_logger.info("企业微信 AI 回调URL验证成功")
- return echo_plain
+ return Response(echo_plain, content_type='text/plain')
except WXBizMsgCryptError as e:
_logger.error("AI回调URL验证失败: %s", e)
- return "verify error"
+ return Response("verify error", content_type='text/plain')
def _handle_message(self, crypt, msg_signature, timestamp, nonce):
"""处理接收消息请求"""
if not all([msg_signature, timestamp, nonce]):
_logger.warning("AI回调消息缺少签名参数")
- return ""
+ return Response("", content_type='text/plain')
post_data = request.httprequest.get_data(as_text=True)
if not post_data:
_logger.warning("AI回调消息体为空")
- return ""
+ return Response("", content_type='text/plain')
# 解密消息
try:
@@ -110,12 +111,12 @@ class WecomAiCallback(http.Controller):
# 只处理文本消息
if msg.get('msg_type') != 'text':
_logger.info("AI回调收到非文本消息类型: %s,暂不处理", msg.get('msg_type'))
- return ""
+ return Response("", content_type='text/plain')
wecom_userid = msg.get('from_user')
content = msg.get('content', '').strip()
if not wecom_userid or not content:
- return ""
+ return Response("", content_type='text/plain')
# 异步处理: 立即返回空响应,后台线程处理AI对话并推送回复
db_name = request.env.cr.dbname
@@ -126,7 +127,7 @@ class WecomAiCallback(http.Controller):
).start()
# 立即返回空字符串,表示成功接收
- return ""
+ return Response("", content_type='text/plain')
def _async_process_ai_message(self, db_name, wecom_userid, content):
"""