From 0086eaa1d63a0264ea533ef03201d9f5eeb2ad46 Mon Sep 17 00:00:00 2001 From: hubian <908234780@qq.com> Date: Mon, 27 Apr 2026 18:33:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=99=BA=E8=83=BD=E4=BD=93=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=90=8C=E6=AD=A5=E5=88=B0backend=20+=20=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E6=97=B6=E4=BB=8Ebackend=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app.py | 147 +++++++++++++++++++++++++++++++++++++++++++++++++ www/app.js | 109 ++++++++++++++++++++++++++++++++---- 2 files changed, 246 insertions(+), 10 deletions(-) diff --git a/backend/app.py b/backend/app.py index 412a05e..40ce7e7 100644 --- a/backend/app.py +++ b/backend/app.py @@ -166,6 +166,21 @@ def init_db(): ) ''') + # 用户智能体配置表(我的智能体) + cursor.execute(''' + CREATE TABLE IF NOT EXISTS user_agents ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + agent_id TEXT NOT NULL, + category TEXT NOT NULL, + is_pinned INTEGER DEFAULT 0, + is_favorite INTEGER DEFAULT 0, + added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id), + UNIQUE(user_id, agent_id) + ) + ''') + # 对话表(用户对话数据) cursor.execute(''' CREATE TABLE IF NOT EXISTS conversations ( @@ -707,6 +722,138 @@ def delete_user_conversation(user_id, conv_id): return jsonify({'success': True}) +# ==================== 用户智能体数据同步 ==================== + +@app.route('/api/user//agents', methods=['GET']) +def get_user_agents(user_id): + """获取用户智能体配置""" + conn = get_db() + cursor = conn.cursor() + cursor.execute(''' + SELECT agent_id, category, is_pinned, is_favorite, added_at + FROM user_agents WHERE user_id = ? + ''', (user_id,)) + + agents_data = { + 'myAgents': {}, # {category: [agent_ids]} + 'favoriteAgents': [], # [agent_ids] + 'pinnedAgents': {} # {category: [agent_ids]} + } + + for row in cursor.fetchall(): + agent_id = row['agent_id'] + category = row['category'] + is_pinned = row['is_pinned'] + is_favorite = row['is_favorite'] + + # 添加到 myAgents + if category not in agents_data['myAgents']: + agents_data['myAgents'][category] = [] + agents_data['myAgents'][category].append(agent_id) + + # 添加到 pinnedAgents + if is_pinned: + if category not in agents_data['pinnedAgents']: + agents_data['pinnedAgents'][category] = [] + agents_data['pinnedAgents'][category].append(agent_id) + + # 添加到 favoriteAgents + if is_favorite: + agents_data['favoriteAgents'].append(agent_id) + + conn.close() + return jsonify(agents_data) + + +@app.route('/api/user//agents/', methods=['POST']) +def add_user_agent(user_id, agent_id): + """添加智能体到用户列表""" + data = request.json + category = data.get('category', 'basic') + is_pinned = data.get('is_pinned', 0) + is_favorite = data.get('is_favorite', 0) + + conn = get_db() + cursor = conn.cursor() + + try: + cursor.execute(''' + INSERT INTO user_agents (user_id, agent_id, category, is_pinned, is_favorite) + VALUES (?, ?, ?, ?, ?) + ''', (user_id, agent_id, category, is_pinned, is_favorite)) + conn.commit() + conn.close() + return jsonify({'success': True}) + except: + # 已存在,更新 + cursor.execute(''' + UPDATE user_agents SET category=?, is_pinned=?, is_favorite=? + WHERE user_id=? AND agent_id=? + ''', (category, is_pinned, is_favorite, user_id, agent_id)) + conn.commit() + conn.close() + return jsonify({'success': True}) + + +@app.route('/api/user//agents/', methods=['DELETE']) +def remove_user_agent(user_id, agent_id): + """从用户列表移除智能体""" + conn = get_db() + cursor = conn.cursor() + cursor.execute('DELETE FROM user_agents WHERE user_id=? AND agent_id=?', (user_id, agent_id)) + conn.commit() + conn.close() + return jsonify({'success': True}) + + +@app.route('/api/user//agents//pin', methods=['POST']) +def toggle_user_agent_pin(user_id, agent_id): + """切换智能体置顶状态""" + data = request.json + is_pinned = data.get('is_pinned', 1) + category = data.get('category', 'basic') + + conn = get_db() + cursor = conn.cursor() + + # 检查是否存在 + cursor.execute('SELECT id FROM user_agents WHERE user_id=? AND agent_id=?', (user_id, agent_id)) + if cursor.fetchone(): + cursor.execute('UPDATE user_agents SET is_pinned=? WHERE user_id=? AND agent_id=?', + (is_pinned, user_id, agent_id)) + else: + cursor.execute('INSERT INTO user_agents (user_id, agent_id, category, is_pinned) VALUES (?, ?, ?, ?)', + (user_id, agent_id, category, is_pinned)) + + conn.commit() + conn.close() + return jsonify({'success': True}) + + +@app.route('/api/user//agents//favorite', methods=['POST']) +def toggle_user_agent_favorite(user_id, agent_id): + """切换智能体收藏状态""" + data = request.json + is_favorite = data.get('is_favorite', 1) + category = data.get('category', 'basic') + + conn = get_db() + cursor = conn.cursor() + + # 检查是否存在 + cursor.execute('SELECT id FROM user_agents WHERE user_id=? AND agent_id=?', (user_id, agent_id)) + if cursor.fetchone(): + cursor.execute('UPDATE user_agents SET is_favorite=? WHERE user_id=? AND agent_id=?', + (is_favorite, user_id, agent_id)) + else: + cursor.execute('INSERT INTO user_agents (user_id, agent_id, category, is_favorite) VALUES (?, ?, ?, ?)', + (user_id, agent_id, category, is_favorite)) + + conn.commit() + conn.close() + return jsonify({'success': True}) + + # ==================== 大模型接口管理 ==================== @app.route('/api/admin/llm', methods=['GET']) diff --git a/www/app.js b/www/app.js index c11633d..cea10cb 100644 --- a/www/app.js +++ b/www/app.js @@ -294,14 +294,47 @@ let thinkingBtn = null; let searchBtn = null; // 初始化 -document.addEventListener('DOMContentLoaded', () => { +document.addEventListener('DOMContentLoaded', async () => { // 初始化 appContainer appContainer = document.getElementById('app'); - // 从本地存储加载对话列表 - const saved = localStorage.getItem('conversations'); - if (saved) { - conversations = JSON.parse(saved); + // 加载用户登录状态(优先检查) + const savedUser = localStorage.getItem('currentUser'); + if (savedUser) { + currentUser = JSON.parse(savedUser); + } + + // 如果用户已登录且有ID,从 backend 加载对话数据 + if (currentUser && currentUser.id) { + try { + const res = await fetch(`/api/user/${currentUser.id}/conversations`); + const data = await res.json(); + if (Array.isArray(data) && data.length > 0) { + // 使用 backend 数据替换本地数据 + conversations = data; + // 更新本地存储(离线可用) + localStorage.setItem('conversations', JSON.stringify(conversations)); + } else { + // backend 无数据,使用本地数据 + const saved = localStorage.getItem('conversations'); + if (saved) { + conversations = JSON.parse(saved); + } + } + } catch (e) { + // backend 加载失败,使用本地数据 + console.error('加载 backend 对话失败:', e); + const saved = localStorage.getItem('conversations'); + if (saved) { + conversations = JSON.parse(saved); + } + } + } else { + // 未登录用户,从本地存储加载对话列表 + const saved = localStorage.getItem('conversations'); + if (saved) { + conversations = JSON.parse(saved); + } } // 兼容旧数据格式(chat_history) @@ -323,7 +356,7 @@ document.addEventListener('DOMContentLoaded', () => { } } - // 加载用户智能体数据 + // 加载用户智能体数据(我的智能体) const savedMyAgents = localStorage.getItem('myAgents'); if (savedMyAgents) { myAgents = JSON.parse(savedMyAgents); @@ -341,10 +374,26 @@ document.addEventListener('DOMContentLoaded', () => { pinnedAgents = JSON.parse(savedPinnedAgents); } - // 加载用户登录状态 - const savedUser = localStorage.getItem('currentUser'); - if (savedUser) { - currentUser = JSON.parse(savedUser); + // 如果用户已登录且有ID,从 backend 加载智能体配置 + if (currentUser && currentUser.id) { + try { + const res = await fetch(`/api/user/${currentUser.id}/agents`); + const data = await res.json(); + if (data.myAgents) { + myAgents = data.myAgents; + localStorage.setItem('myAgents', JSON.stringify(myAgents)); + } + if (data.favoriteAgents) { + favoriteAgents = data.favoriteAgents; + localStorage.setItem('favoriteAgents', JSON.stringify(favoriteAgents)); + } + if (data.pinnedAgents) { + pinnedAgents = data.pinnedAgents; + localStorage.setItem('pinnedAgents', JSON.stringify(pinnedAgents)); + } + } catch (e) { + console.error('加载智能体配置失败:', e); + } } // 加载每日使用统计 @@ -1001,10 +1050,12 @@ function toggleAgentPin(agentId) { const category = agent.category; + let is_pinned; if (pinnedAgents[category]?.includes(agentId)) { // 取消置顶 pinnedAgents[category] = pinnedAgents[category].filter(id => id !== agentId); agent.is_pinned = false; + is_pinned = 0; showToast('已取消置顶'); } else { // 置顶 @@ -1013,11 +1064,21 @@ function toggleAgentPin(agentId) { } pinnedAgents[category].push(agentId); agent.is_pinned = true; + is_pinned = 1; showToast('已置顶'); } savePinnedAgents(); saveMyAgents(); // 更新显示 + + // 同步到 backend + if (currentUser && currentUser.id) { + fetch(`/api/user/${currentUser.id}/agents/${agentId}/pin`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ is_pinned, category }) + }).catch(e => console.error('同步置顶失败:', e)); + } } // 收藏/取消收藏智能体 @@ -1025,20 +1086,32 @@ function toggleAgentFavorite(agentId) { const agent = agents.find(a => a.id === agentId); if (!agent) return; + let is_favorite; if (favoriteAgents.includes(agentId)) { // 取消收藏 favoriteAgents = favoriteAgents.filter(id => id !== agentId); agent.is_favorite = false; + is_favorite = 0; showToast('已取消收藏'); } else { // 收藏 favoriteAgents.push(agentId); agent.is_favorite = true; + is_favorite = 1; showToast('已收藏'); } saveFavoriteAgents(); saveMyAgents(); // 更新显示 + + // 同步到 backend + if (currentUser && currentUser.id) { + fetch(`/api/user/${currentUser.id}/agents/${agentId}/favorite`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ is_favorite, category: agent.category }) + }).catch(e => console.error('同步收藏失败:', e)); + } } // 从用户智能体列表移除 @@ -1067,6 +1140,13 @@ function removeAgentFromMyAgents(agentId) { savePinnedAgents(); saveFavoriteAgents(); showToast('已移除'); + + // 同步到 backend + if (currentUser && currentUser.id) { + fetch(`/api/user/${currentUser.id}/agents/${agentId}`, { + method: 'DELETE' + }).catch(e => console.error('同步移除失败:', e)); + } } // ==================== 智能体发现页面 ==================== @@ -1303,6 +1383,15 @@ function addAgentToMyAgents(agentId) { saveMyAgents(); showToast(`已添加 ${agent.name}`); + + // 同步到 backend + if (currentUser && currentUser.id) { + fetch(`/api/user/${currentUser.id}/agents/${agentId}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ category: agent.category }) + }).catch(e => console.error('同步添加智能体失败:', e)); + } } // 从发现页面收藏智能体