feat: 搜索结果持久化保存 + 结果数量可配置

- 新增 search_results 表,搜索结果按主题持久保存
- 搜索API自动保存结果到数据库,获取详情时更新raw_content
- 页面加载时自动加载历史搜索结果,按搜索词分组显示
- 支持单条删除和清空所有搜索结果
- 搜索结果数量默认10条,可通过输入框调整(1-20)
- 搜索面板标题显示结果计数badge
This commit is contained in:
2026-05-23 23:48:44 +08:00
parent 6bf20062d2
commit bf6b9457c4
2 changed files with 244 additions and 78 deletions

88
app.py
View File

@@ -123,6 +123,20 @@ def init_db():
api_key TEXT DEFAULT '',
updated_at TEXT DEFAULT (datetime('now','localtime'))
);
CREATE TABLE IF NOT EXISTS search_results (
id TEXT PRIMARY KEY,
topic_id TEXT NOT NULL,
query TEXT NOT NULL,
title TEXT DEFAULT '',
url TEXT DEFAULT '',
snippet TEXT DEFAULT '',
raw_content TEXT DEFAULT '',
score REAL DEFAULT 0,
search_depth TEXT DEFAULT 'basic',
max_results INTEGER DEFAULT 10,
created_at TEXT DEFAULT (datetime('now','localtime')),
FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE
);
''')
# 初始化默认Prompt模板
default_templates = [
@@ -776,9 +790,10 @@ def update_search_config():
@app.route('/api/search', methods=['POST'])
def search_web():
"""调用 Tavily Search API 进行搜索"""
"""调用 Tavily Search API 进行搜索,结果保存到数据库"""
data = request.get_json(force=True)
query = data.get('query', '').strip()
topic_id = data.get('topic_id', '').strip()
if not query:
return jsonify({'error': '搜索关键词不能为空'}), 400
@@ -786,7 +801,7 @@ def search_web():
if not search_config['api_key']:
return jsonify({'error': '请先配置搜索API Key首页右上角设置'}), 400
max_results = data.get('max_results', 8)
max_results = data.get('max_results', 10)
search_depth = data.get('search_depth', 'basic')
time_range = data.get('time_range', None)
@@ -811,19 +826,43 @@ def search_web():
)
resp.raise_for_status()
result = resp.json()
return jsonify(result)
except requests.exceptions.HTTPError as e:
return jsonify({'error': f'搜索请求失败: HTTP {e.response.status_code}'}), 502
except Exception as e:
return jsonify({'error': f'搜索失败: {str(e)}'}), 500
# 保存搜索结果到数据库
if topic_id:
conn = get_db()
now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
saved_results = []
for r in result.get('results', []):
rid = str(uuid.uuid4())[:8]
conn.execute(
'INSERT INTO search_results (id, topic_id, query, title, url, snippet, score, search_depth, max_results, created_at) VALUES (?,?,?,?,?,?,?,?,?,?)',
(rid, topic_id, query, r.get('title', ''), r.get('url', ''), r.get('content', ''), r.get('score', 0), search_depth, max_results, now)
)
saved_results.append({
'id': rid,
'title': r.get('title', ''),
'url': r.get('url', ''),
'content': r.get('content', ''),
'score': r.get('score', 0),
})
conn.commit()
conn.close()
result['saved_results'] = saved_results
return jsonify(result)
@app.route('/api/search/detail', methods=['POST'])
def search_detail():
"""调用 Tavily Search API 获取详细内容include_raw_content"""
"""获取搜索结果详细内容,并更新数据库中的 raw_content"""
data = request.get_json(force=True)
query = data.get('query', '').strip()
url = data.get('url', '').strip()
result_id = data.get('result_id', '').strip()
if not query:
return jsonify({'error': '搜索关键词不能为空'}), 400
@@ -855,11 +894,52 @@ def search_detail():
# 如果指定了URL过滤只返回该URL的结果
if url:
result['results'] = [r for r in result.get('results', []) if r.get('url') == url]
# 更新数据库中的 raw_content
if result_id and result.get('results'):
raw_content = result['results'][0].get('raw_content', '') or result['results'][0].get('content', '')
conn = get_db()
conn.execute('UPDATE search_results SET raw_content=? WHERE id=?', (raw_content, result_id))
conn.commit()
conn.close()
return jsonify(result)
except Exception as e:
return jsonify({'error': f'获取详情失败: {str(e)}'}), 500
@app.route('/api/topics/<topic_id>/search-results', methods=['GET'])
def get_topic_search_results(topic_id):
"""获取主题的所有搜索结果"""
conn = get_db()
results = conn.execute(
'SELECT * FROM search_results WHERE topic_id=? ORDER BY created_at DESC',
(topic_id,)
).fetchall()
conn.close()
return jsonify([dict(r) for r in results])
@app.route('/api/search-results/<result_id>', methods=['DELETE'])
def delete_search_result(result_id):
"""删除单条搜索结果"""
conn = get_db()
conn.execute('DELETE FROM search_results WHERE id=?', (result_id,))
conn.commit()
conn.close()
return jsonify({'ok': True})
@app.route('/api/topics/<topic_id>/search-results', methods=['DELETE'])
def clear_topic_search_results(topic_id):
"""清空主题的所有搜索结果"""
conn = get_db()
conn.execute('DELETE FROM search_results WHERE topic_id=?', (topic_id,))
conn.commit()
conn.close()
return jsonify({'ok': True})
# ==================== LLM配置API ====================
@app.route('/api/llm-config', methods=['GET'])