commit b0ee5ac05b6ada982a8ed9cae5974883c6a3ff5b Author: hubian <908234780@qq.com> Date: Wed Apr 8 18:23:57 2026 +0800 feat: 碎片信息记录网站初始版本 功能: - 实时保存记录到本地 - 大模型自动生成标题 - 左侧列表显示所有记录 - 搜索功能 - 新建/编辑/删除记录 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8c6e789 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +data/ +.idea/ \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..c9bf710 --- /dev/null +++ b/app.py @@ -0,0 +1,236 @@ +""" +碎片信息记录网站 +- 实时保存到本地 +- 大模型生成标题 +- 搜索功能 +""" + +from flask import Flask, render_template, jsonify, request +from flask_cors import CORS +import json +import time +import uuid +from datetime import datetime +from pathlib import Path +import requests +import threading + +app = Flask(__name__, static_folder='static', static_url_path='/static') +CORS(app) + +# 数据目录 +DATA_DIR = Path(__file__).parent / 'data' +DATA_DIR.mkdir(exist_ok=True) +NOTES_FILE = DATA_DIR / 'notes.json' + +# 大模型配置 +LLM_CONFIG = { + 'base_url': 'http://192.168.2.5:1234/v1', + 'api_key': 'sk-lm-fuP5tGU8:Hi7YU87jHyDP6Ay8Tl2j', + 'model': 'qwen3.5-4b', +} + +def load_notes(): + """加载所有笔记""" + if NOTES_FILE.exists(): + return json.loads(NOTES_FILE.read_text(encoding='utf-8')) + return [] + +def save_notes(notes): + """保存笔记""" + NOTES_FILE.write_text(json.dumps(notes, ensure_ascii=False, indent=2), encoding='utf-8') + +def generate_title(content): + """用大模型生成标题""" + if not content or len(content.strip()) < 10: + return content[:20] if content else "新记录" + + try: + response = requests.post( + f"{LLM_CONFIG['base_url']}/chat/completions", + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {LLM_CONFIG['api_key']}" + }, + json={ + "model": LLM_CONFIG['model'], + "messages": [ + {"role": "system", "content": "你是一个标题生成助手。请根据用户的内容,生成一个简洁的标题(不超过15个字)。只返回标题,不要其他内容。"}, + {"role": "user", "content": content[:500]} + ], + "max_tokens": 50, + "temperature": 0.3 + }, + timeout=10 + ) + + if response.status_code == 200: + data = response.json() + title = data['choices'][0]['message']['content'].strip() + # 清理可能的引号或多余字符 + title = title.replace('"', '').replace("'", '').strip() + if len(title) > 20: + title = title[:20] + return title + except Exception as e: + print(f"生成标题失败: {e}") + + # 降级:取前20个字 + return content[:20].strip() + +def generate_title_async(note_id, content): + """异步生成标题""" + title = generate_title(content) + + # 更新笔记标题 + notes = load_notes() + for note in notes: + if note['id'] == note_id: + note['title'] = title + break + save_notes(notes) + +# ============ 页面路由 ============ + +@app.route('/') +def index(): + return render_template('index.html') + +# ============ API路由 ============ + +@app.route('/api/notes') +def api_notes(): + """获取笔记列表""" + keyword = request.args.get('q', '').strip().lower() + notes = load_notes() + + # 搜索过滤 + if keyword: + notes = [n for n in notes if keyword in n.get('title', '').lower() or keyword in n.get('content', '').lower()] + + # 按更新时间排序(最近在前) + notes = sorted(notes, key=lambda x: x.get('updated_at', ''), reverse=True) + + # 返回列表信息 + return jsonify([{ + 'id': n['id'], + 'title': n['title'], + 'updated_at': n['updated_at'], + 'created_at': n['created_at'], + 'preview': n['content'][:50] if n['content'] else '', + } for n in notes]) + +@app.route('/api/notes/') +def api_note_detail(note_id): + """获取单个笔记详情""" + notes = load_notes() + note = next((n for n in notes if n['id'] == note_id), None) + + if not note: + return jsonify({'error': 'Note not found'}), 404 + + return jsonify(note) + +@app.route('/api/notes', methods=['POST']) +def api_create_note(): + """创建新笔记""" + notes = load_notes() + + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + new_note = { + 'id': uuid.uuid4().hex[:12], + 'title': '新记录', + 'content': '', + 'created_at': now, + 'updated_at': now, + } + + notes.append(new_note) + save_notes(notes) + + return jsonify(new_note) + +@app.route('/api/notes/', methods=['PUT']) +def api_update_note(note_id): + """更新笔记内容""" + data = request.get_json() + content = data.get('content', '') + + notes = load_notes() + note = next((n for n in notes if n['id'] == note_id), None) + + if not note: + return jsonify({'error': 'Note not found'}), 404 + + now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + # 判断是否需要生成新标题 + old_content = note.get('content', '') + need_new_title = (not note['title'] or note['title'] == '新记录' or + len(old_content) < 50 and len(content) >= 50) + + note['content'] = content + note['updated_at'] = now + + # 如果内容变化较大,异步生成新标题 + if need_new_title and len(content) >= 20: + threading.Thread(target=generate_title_async, args=(note_id, content)).start() + + save_notes(notes) + + return jsonify(note) + +@app.route('/api/notes//title', methods=['POST']) +def api_regenerate_title(note_id): + """手动重新生成标题""" + notes = load_notes() + note = next((n for n in notes if n['id'] == note_id), None) + + if not note: + return jsonify({'error': 'Note not found'}), 404 + + title = generate_title(note['content']) + note['title'] = title + note['updated_at'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + save_notes(notes) + + return jsonify({'success': True, 'title': title}) + +@app.route('/api/notes/', methods=['DELETE']) +def api_delete_note(note_id): + """删除笔记""" + notes = load_notes() + notes = [n for n in notes if n['id'] != note_id] + save_notes(notes) + + return jsonify({'success': True}) + +@app.route('/api/search') +def api_search(): + """搜索笔记""" + keyword = request.args.get('q', '').strip().lower() + + if not keyword: + return jsonify([]) + + notes = load_notes() + results = [n for n in notes if keyword in n.get('title', '').lower() or keyword in n.get('content', '').lower()] + + results = sorted(results, key=lambda x: x.get('updated_at', ''), reverse=True) + + return jsonify([{ + 'id': n['id'], + 'title': n['title'], + 'updated_at': n['updated_at'], + 'preview': n['content'][:100] if n['content'] else '', + } for n in results]) + +if __name__ == '__main__': + print("=" * 50) + print("碎片信息记录网站") + print("=" * 50) + print(f"访问地址: http://localhost:19009") + print("=" * 50) + + app.run(host='0.0.0.0', port=19009, debug=True) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..35e18dd --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +flask +flask-cors +requests \ No newline at end of file diff --git a/run.sh b/run.sh new file mode 100644 index 0000000..ae79ae1 --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd "$(dirname "$0")" +python app.py \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..5a6dcea --- /dev/null +++ b/templates/index.html @@ -0,0 +1,224 @@ + + + + + + 碎片记录 + + + + + +
+ + + + +
+ + + + + + + +
+
+ +

选择一个记录,或新建一个

+
+
+
+
+ + + + \ No newline at end of file