@@ -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)。
|
||||
|
||||
@@ -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,
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer" style="color:#4FC3F7;text-decoration:underline;word-break:break-all">$1</a>'
|
||||
);
|
||||
}
|
||||
|
||||
async _loadHistory() {
|
||||
try {
|
||||
const data = await this.rpc("/ai/chat/history", { conversation_id: this.resId });
|
||||
|
||||
@@ -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,
|
||||
'<a href="$1" target="_blank" rel="noopener noreferrer" style="color:#4FC3F7;text-decoration:underline;word-break:break-all">$1</a>'
|
||||
);
|
||||
}
|
||||
|
||||
function renderChat() {
|
||||
if (!chatEl) return;
|
||||
chatEl.innerHTML = "";
|
||||
@@ -119,7 +128,7 @@ function renderChat() {
|
||||
<div id="yuthon_chat_msgs" class="o_ai_messages" style="display:${showConvList ? 'none' : 'flex'}">
|
||||
${msgs.map(m => `
|
||||
<div class="o_ai_msg ${m.role === 'user' ? 'o_ai_msg_user' : 'o_ai_msg_ai'}">
|
||||
<div class="o_ai_msg_bubble">${escapeHtml(m.content)}</div>
|
||||
<div class="o_ai_msg_bubble">${m.role === 'assistant' ? renderContent(m.content) : escapeHtml(m.content)}</div>
|
||||
<div class="o_ai_msg_time">${m.date || ''}</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
@@ -244,7 +253,7 @@ async function sendMsg() {
|
||||
if (msgsEl) {
|
||||
msgsEl.innerHTML += `
|
||||
<div class="o_ai_msg o_ai_msg_ai">
|
||||
<div class="o_ai_msg_bubble">${escapeHtml(content)}</div>
|
||||
<div class="o_ai_msg_bubble">${renderContent(content)}</div>
|
||||
<div class="o_ai_msg_time">刚刚</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</div>
|
||||
<t t-foreach="state.messages" t-as="msg" t-key="msg_index">
|
||||
<div t-att-class="'ai-chat-msg ' + msg.role">
|
||||
<div class="ai-chat-bubble" t-out="msg.content"/>
|
||||
<div class="ai-chat-bubble" t-raw="renderMsgContent(msg.content)"/>
|
||||
<div class="ai-chat-meta" t-if="msg.date" t-esc="msg.date"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user