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() {