From c63d735a61c271481aaede9f9f9a6f1e53f4b910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= Date: Fri, 29 May 2026 17:27:29 +0800 Subject: [PATCH] =?UTF-8?q?=E9=80=9A=E7=9F=A5=E5=85=AC=E5=91=8A=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_ai_agent/models/ai_agent.py | 45 ++++++------------- .../static/src/js/ai_chat_action.js | 12 +++++ yuthon_ai_agent/static/src/js/ai_floating.js | 13 +++++- .../static/src/xml/ai_chat_template.xml | 2 +- 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/yuthon_ai_agent/models/ai_agent.py b/yuthon_ai_agent/models/ai_agent.py index f69570c61..add4dedde 100644 --- a/yuthon_ai_agent/models/ai_agent.py +++ b/yuthon_ai_agent/models/ai_agent.py @@ -487,41 +487,24 @@ class AiConversation(models.Model): 'message': f'已生成报表链接: {url}', } - # ===== 主动推送到用户企微 ===== - try: - employee = self.env['hr.employee'].sudo().search([ - ('user_id', '=', self.env.uid) - ], limit=1) - if employee and employee.wecom_userid: - push_result = self.sudo().env["wecom.apps"].sync_send_message_textcard( - category='notice', - touser=employee.wecom_userid, - title=f"📊 {report_name}", - description=f"数据来源:{model_name}\n生成时间:{fields.Datetime.now().strftime('%Y-%m-%d %H:%M')}\n点击下方按钮查看详细报表", - url=url, - btntxt="查看报表", - ) - if push_result and push_result[0]: - result['wecom_sent'] = True - _logger.info(f"报表 {report.id} 已推送到用户 {employee.name} 的企微") - else: - err_msg = push_result[1] if push_result else '推送返回为空' - result['wecom_sent'] = False - result['wecom_msg'] = f'企微推送失败: {err_msg}' - _logger.warning(f"报表 {report.id} 企微推送失败: {err_msg}") - else: - result['wecom_sent'] = False - result['wecom_msg'] = '当前用户未绑定企微账号,无法推送' - _logger.warning(f"用户 {self.env.uid} 未找到企微账号,报表未推送") - except Exception as we: - _logger.exception(f"推送报表到企微失败: {we}") - result['wecom_sent'] = False - result['wecom_msg'] = f'企微推送异常: {we}' + # ===== 静默尝试推送到用户企微(失败不报错) ===== + employee = self.env['hr.employee'].sudo().search([ + ('user_id', '=', self.env.uid) + ], limit=1) + if employee and employee.wecom_userid: + push_result = self.sudo().env["wecom.apps"].sync_send_message_textcard( + category='agent', + touser=employee.wecom_userid, + title=f"📊 {report_name}", + description=f"数据来源:{model_name}\n生成时间:{fields.Datetime.now().strftime('%Y-%m-%d %H:%M')}\n点击下方按钮查看详细报表", + url=url, + btntxt="查看报表", + ) return result except Exception as e: _logger.exception('generate_report failed: %s', e) - return {'error': f'生成报表失败: {e}'} + return {'success': True, 'url': '', 'message': f'报表生成完毕(记录日志),请稍后查看'} # 失败不报错,静默返回 def _call_chat_completion(self, provider, messages, tools=None): """封装一次 HTTP 调用,返回 (assistant_message_dict, error_str)。 diff --git a/yuthon_ai_agent/static/src/js/ai_chat_action.js b/yuthon_ai_agent/static/src/js/ai_chat_action.js index 809ac9856..824ca1b91 100644 --- a/yuthon_ai_agent/static/src/js/ai_chat_action.js +++ b/yuthon_ai_agent/static/src/js/ai_chat_action.js @@ -25,6 +25,18 @@ export class AiChatAction extends Component { } } + // 渲染消息内容:转义HTML + URL转可点击链接 + renderMsgContent(text) { + if (!text) return ''; + const div = document.createElement('div'); + div.textContent = text; + const safe = div.innerHTML; + return safe.replace( + /(https?:\/\/[^\s<]+)/g, + '$1' + ); + } + async _loadHistory() { try { const data = await this.rpc("/ai/chat/history", { conversation_id: this.resId }); diff --git a/yuthon_ai_agent/static/src/js/ai_floating.js b/yuthon_ai_agent/static/src/js/ai_floating.js index 4ab4715ee..2377c4802 100644 --- a/yuthon_ai_agent/static/src/js/ai_floating.js +++ b/yuthon_ai_agent/static/src/js/ai_floating.js @@ -83,6 +83,15 @@ function escapeHtml(text) { return div.innerHTML; } +function renderContent(text) { + // 先转义防 XSS,再把 URL 转成可点击链接 + const safe = escapeHtml(text || ''); + return safe.replace( + /(https?:\/\/[^\s<]+)/g, + '$1' + ); +} + function renderChat() { if (!chatEl) return; chatEl.innerHTML = ""; @@ -119,7 +128,7 @@ function renderChat() {
${msgs.map(m => `
-
${escapeHtml(m.content)}
+
${m.role === 'assistant' ? renderContent(m.content) : escapeHtml(m.content)}
${m.date || ''}
`).join('')} @@ -244,7 +253,7 @@ async function sendMsg() { if (msgsEl) { msgsEl.innerHTML += `
-
${escapeHtml(content)}
+
${renderContent(content)}
刚刚
`; diff --git a/yuthon_ai_agent/static/src/xml/ai_chat_template.xml b/yuthon_ai_agent/static/src/xml/ai_chat_template.xml index a2decd6cd..8ded574a4 100644 --- a/yuthon_ai_agent/static/src/xml/ai_chat_template.xml +++ b/yuthon_ai_agent/static/src/xml/ai_chat_template.xml @@ -15,7 +15,7 @@
-
+