@@ -17,7 +17,8 @@
|
||||
"web.assets_backend": [
|
||||
"sun_badge_menu/static/src/webclient/actions/client_actions.js",
|
||||
"sun_badge_menu/static/src/webclient/navbar/navbar.js",
|
||||
"sun_badge_menu/static/src/webclient/navbar/navbar.xml"
|
||||
"sun_badge_menu/static/src/webclient/navbar/navbar.xml",
|
||||
"sun_badge_menu/static/src/scss/badge.scss"
|
||||
],
|
||||
},
|
||||
"installable": True,
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
'web.assets_backend': [
|
||||
'yuthon_ai_agent/static/src/js/ai_chat_action.js',
|
||||
'yuthon_ai_agent/static/src/js/ai_floating.js',
|
||||
'yuthon_ai_agent/static/src/js/ai_chat_open.js',
|
||||
'yuthon_ai_agent/static/src/xml/ai_chat_template.xml',
|
||||
'yuthon_ai_agent/static/src/css/ai_chat.css',
|
||||
'yuthon_ai_agent/static/src/css/ai_floating.css',
|
||||
|
||||
@@ -526,6 +526,18 @@ class AiConversation(models.Model):
|
||||
Model.browse(ids).unlink()
|
||||
return {'success': True, 'deleted_ids': ids}
|
||||
|
||||
# 自定义模型方法调用(主动感知按钮触发)
|
||||
# action_config 格式: {"type": "execute_tool", "tool": "方法名", "params": {"record_id": 123, "model": "yuthon.notice"}}
|
||||
if model_name and args.get('record_id'):
|
||||
record = self.env[model_name].browse(args['record_id'])
|
||||
if not record.exists():
|
||||
return {'error': f'记录不存在:{model_name}, id={args["record_id"]}'}
|
||||
method = getattr(record, name, None)
|
||||
if not method or not callable(method):
|
||||
return {'error': f'模型 {model_name} 没有可调用的方法:{name}'}
|
||||
result = method()
|
||||
return {'success': True, 'result': str(result) if result is not None else '执行成功'}
|
||||
|
||||
return {'error': f'未知工具:{name}'}
|
||||
except Exception as e:
|
||||
_logger.exception('Tool %s failed: %s', name, e)
|
||||
|
||||
@@ -223,16 +223,66 @@ class AiRule(models.Model):
|
||||
string='按钮文字',
|
||||
default='确认执行',
|
||||
)
|
||||
guide_button_action = fields.Char(
|
||||
string='按钮方法配置',
|
||||
help='按钮点击后执行的动作,JSON格式,如 {"type": "execute_tool", "tool": "notice_publish", "params": {"notice_id": "{record_id}"}}',
|
||||
default='{}',
|
||||
)
|
||||
guide_method_name = fields.Char(
|
||||
string='方法名称',
|
||||
help='后台处理方法名,用于标识触发哪个逻辑',
|
||||
string='后台方法名',
|
||||
help='按钮点击后调用的后台方法名,如 notice_send_to_all。后台自动传入 record_id(当前记录ID)',
|
||||
)
|
||||
guide_method_desc = fields.Char(
|
||||
string='方法说明',
|
||||
)
|
||||
|
||||
def action_test_guide(self):
|
||||
"""测试主动感知:模拟触发,创建引导消息并打开AI对话"""
|
||||
self.ensure_one()
|
||||
if self.rule_type != 'active_guide':
|
||||
return {'warning': {'title': '提示', 'message': '仅主动感知类型的规则可以测试'}}
|
||||
|
||||
# 获取或创建用户对话
|
||||
conv = self.env['ai.conversation'].search([
|
||||
('create_uid', '=', self.env.uid),
|
||||
], limit=1, order='write_date desc')
|
||||
|
||||
if not conv:
|
||||
provider = self.env['ai.provider'].search([('active', '=', True)], limit=1)
|
||||
if not provider:
|
||||
return {'warning': {'title': '错误', 'message': '请先配置 AI 服务商'}}
|
||||
conv = self.env['ai.conversation'].create({
|
||||
'name': 'AI 对话',
|
||||
'provider_id': provider.id,
|
||||
})
|
||||
|
||||
# 组装测试消息(替换变量为测试值)
|
||||
message = self.guide_message or f'【测试】主动感知已触发'
|
||||
message = message.replace('{record_name}', '[测试记录]')
|
||||
message = message.replace('{user_name}', self.env.user.name)
|
||||
message = message.replace('{record_id}', '[测试]')
|
||||
|
||||
# 组装 action config(按钮点击后的动作)
|
||||
action_config = {
|
||||
'type': 'execute_tool',
|
||||
'tool': self.guide_method_name or '',
|
||||
'params': {
|
||||
'record_id': 0,
|
||||
'model': self.guide_model or '',
|
||||
}
|
||||
}
|
||||
|
||||
# 创建 AI 消息(带 action 按钮)
|
||||
self.env['ai.message'].create({
|
||||
'conversation_id': conv.id,
|
||||
'role': 'assistant',
|
||||
'content': f'🧪 {message}',
|
||||
'action': json.dumps({
|
||||
'type': 'guide_button',
|
||||
'label': self.guide_button_label or '确认执行',
|
||||
'action': action_config,
|
||||
}, ensure_ascii=False),
|
||||
})
|
||||
|
||||
# 返回 client action 打开浮动聊天框
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'yuthon_ai_chat_open',
|
||||
'params': {'conversation_id': conv.id},
|
||||
}
|
||||
|
||||
|
||||
@@ -637,3 +637,16 @@ patch(NavBar.prototype, {
|
||||
anchor.after(btn);
|
||||
},
|
||||
});
|
||||
|
||||
// 监听来自 client action / 其他模块的"打开聊天"事件
|
||||
window.addEventListener("yuthon:ai_open_chat", async () => {
|
||||
if (!isVisible) {
|
||||
toggleChat();
|
||||
}
|
||||
// 刷新消息列表以显示新创建的测试消息
|
||||
const msgs = await loadHistory();
|
||||
if (chatEl) {
|
||||
renderMessages(msgs);
|
||||
scrollToBottom();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -115,4 +115,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Client Action: 打开浮动聊天(空壳,仅用于触发事件) -->
|
||||
<t t-name="yuthon_ai_agent.AiChatOpenTemplate">
|
||||
<div class="d-none"/>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -69,14 +69,27 @@
|
||||
<!-- 主动感知配置 -->
|
||||
<page string="主动感知配置" name="active_guide"
|
||||
invisible="rule_type != 'active_guide'">
|
||||
<div class="oe_button_box" style="margin-bottom: 12px;">
|
||||
<button name="action_test_guide" type="object"
|
||||
string="🧪 测试主动感知"
|
||||
class="btn btn-primary oe_stat_button"
|
||||
icon="fa-play-circle"
|
||||
help="模拟触发此规则,查看引导内容和按钮效果"/>
|
||||
</div>
|
||||
<group>
|
||||
<group string="引导内容">
|
||||
<field name="guide_message"
|
||||
placeholder="主动弹出时显示的文字,支持 {record_name} / {user_name} / {record_id}" />
|
||||
<field name="guide_button_action"
|
||||
placeholder='{"type": "execute_tool", "tool": "notice_publish", "params": {"notice_id": "{record_id}"}}' />
|
||||
placeholder="主动弹出时显示的文字,支持 {record_name} / {user_name}" />
|
||||
<field name="guide_button_label" />
|
||||
</group>
|
||||
<group string="按钮执行配置">
|
||||
<field name="guide_method_name"
|
||||
placeholder="后台方法名,如 notice_send_to_all" />
|
||||
<field name="guide_method_desc"
|
||||
placeholder="按钮对应后台方法的功能说明" />
|
||||
placeholder="方法功能说明,方便维护" />
|
||||
<p class="text-muted mt-2">
|
||||
后台自动传入当前记录ID,无需手动配置参数
|
||||
</p>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
/* 隐藏控制面板右上角 Knowledge 搜索按钮(避免报错) */
|
||||
.o_knowledge_icon_search {
|
||||
display: none !important;
|
||||
}
|
||||
/* base.css — yuthon_base 通用样式 */
|
||||
/* 注意:菜单角标样式 (.menu-badge) 已移至 sun_badge_menu 模块 */
|
||||
|
||||
@@ -278,35 +278,35 @@ class YuthonNotice(models.Model):
|
||||
('guide_field_value', '=', 'True'),
|
||||
('active', '=', True),
|
||||
])
|
||||
|
||||
|
||||
for rule in rules:
|
||||
# 生成提醒内容(替换变量)
|
||||
message = rule.guide_message or f'{record.name} 流程已结束'
|
||||
message = message.replace('{record_name}', record.name or '')
|
||||
message = message.replace('{user_name}', self.env.user.name or '')
|
||||
message = message.replace('{record_id}', str(record.id))
|
||||
|
||||
# 解析按钮动作
|
||||
try:
|
||||
action_config = json.loads(rule.guide_button_action or '{}')
|
||||
except json.JSONDecodeError:
|
||||
action_config = {}
|
||||
|
||||
# 替换action_config中的变量
|
||||
action_config_str = json.dumps(action_config, ensure_ascii=False)
|
||||
action_config_str = action_config_str.replace('{record_id}', str(record.id))
|
||||
action_config = json.loads(action_config_str)
|
||||
|
||||
|
||||
# 自动组装按钮动作(后台自动注入 record_id)
|
||||
action_config = {}
|
||||
if rule.guide_method_name:
|
||||
action_config = {
|
||||
'type': 'execute_tool',
|
||||
'tool': rule.guide_method_name,
|
||||
'params': {
|
||||
'record_id': record.id,
|
||||
'model': self._name,
|
||||
}
|
||||
}
|
||||
|
||||
# 查找或创建AI对话(用当前用户的默认对话)
|
||||
conversation = self.env['ai.conversation'].sudo().search([
|
||||
('create_uid', '=', self.env.user.id),
|
||||
], limit=1, order='write_date desc')
|
||||
|
||||
|
||||
if not conversation:
|
||||
conversation = self.env['ai.conversation'].sudo().create({
|
||||
'name': 'AI 对话',
|
||||
})
|
||||
|
||||
|
||||
# 创建AI消息(带action)
|
||||
self.env['ai.message'].sudo().create({
|
||||
'conversation_id': conversation.id,
|
||||
@@ -316,9 +316,9 @@ class YuthonNotice(models.Model):
|
||||
'type': 'guide_button',
|
||||
'label': rule.guide_button_label or '确认执行',
|
||||
'action': action_config,
|
||||
}, ensure_ascii=False),
|
||||
}, ensure_ascii=False) if action_config else None,
|
||||
})
|
||||
|
||||
|
||||
# TODO: 通过bus.bus推送到前端,让聊天框自动显示新消息
|
||||
# self.env['bus.bus'].sudo()._sendone(self.env.user.partner_id, 'ai_guide_notification', {
|
||||
# 'conversation_id': conversation.id,
|
||||
|
||||
Reference in New Issue
Block a user