From 7a6fd0c388ce53706d0581091814f541ac4e5e71 Mon Sep 17 00:00:00 2001 From: Jowe <123822645+Selei1983@users.noreply.github.com> Date: Fri, 6 Feb 2026 19:27:12 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20v3.0.1=20-=20=E4=BF=AE=E5=A4=8D5?= =?UTF-8?q?=E4=B8=AA=E4=BB=A3=E7=A0=81=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复内容: 1. Collection 模型唯一约束逻辑错误 - 修改约束从 (user_id, site_id, folder_id) 到 (user_id, site_id) - 防止用户多次收藏同一网站 2. 用户注册重复提交数据库 - 优化为只提交一次数据库操作 - 提升注册性能 3. JavaScript 未使用的变量 - 删除 updateCollectButton() 中未使用的 icon 变量 4. 文件夹计数逻辑缺失 - 为每个文件夹添加收藏数量计算 - 修复收藏列表页面显示 5. JavaScript 错误处理不完善 - 所有 fetch 调用添加 HTTP 状态码检查 - 改进网络错误提示 新增文件: - fix_collection_constraint.py - 数据库约束修复脚本 - BUGFIX_v3.0.1.md - 详细修复记录 Co-Authored-By: Claude Sonnet 4.5 --- BUGFIX_v3.0.1.md | 171 +++++++++++++++++++++++++++++++++++ app.py | 10 +- fix_collection_constraint.py | 48 ++++++++++ models.py | 2 +- templates/detail_new.html | 21 ++++- 5 files changed, 244 insertions(+), 8 deletions(-) create mode 100644 BUGFIX_v3.0.1.md create mode 100644 fix_collection_constraint.py diff --git a/BUGFIX_v3.0.1.md b/BUGFIX_v3.0.1.md new file mode 100644 index 0000000..042d7d7 --- /dev/null +++ b/BUGFIX_v3.0.1.md @@ -0,0 +1,171 @@ +# v3.0 代码修复记录 + +**修复日期:** 2025-02-06 +**修复版本:** v3.0.1 + +--- + +## 修复的问题 + +### 1. Collection 模型唯一约束逻辑错误 ✅ + +**问题描述:** +- 原约束:`(user_id, site_id, folder_id)` +- 由于 `folder_id` 可为 NULL,导致用户可以多次收藏同一网站到"未分类" + +**修复方案:** +- 新约束:`(user_id, site_id)` +- 确保每个用户只能收藏一次同一个网站 + +**修改文件:** +- `models.py` (第 262-265 行) + +**数据库迁移:** +- 新增 `fix_collection_constraint.py` 脚本用于修复现有数据库 + +--- + +### 2. 用户注册重复提交数据库 ✅ + +**问题描述:** +- 注册时先提交用户数据,登录后再次提交 `last_login` +- 造成不必要的数据库操作 + +**修复方案:** +- 在第一次提交前设置 `last_login` +- 只提交一次数据库 + +**修改文件:** +- `app.py` (第 643-651 行) + +**修复前:** +```python +user = User(username=username) +user.set_password(password) +db.session.add(user) +db.session.commit() # 第一次 + +login_user(user) +user.last_login = datetime.now() +db.session.commit() # 第二次 +``` + +**修复后:** +```python +user = User(username=username) +user.set_password(password) +user.last_login = datetime.now() +db.session.add(user) +db.session.commit() # 只提交一次 + +login_user(user) +``` + +--- + +### 3. JavaScript 未使用的变量 ✅ + +**问题描述:** +- `updateCollectButton()` 函数中获取了 `icon` 元素但未使用 + +**修复方案:** +- 删除未使用的变量声明 + +**修改文件:** +- `templates/detail_new.html` (第 1599-1611 行) + +--- + +### 4. 文件夹计数逻辑缺失 ✅ + +**问题描述:** +- 模板中使用 `folder.count` 但后端未计算 +- 导致文件夹标签显示的数量不正确 + +**修复方案:** +- 在 `user_collections()` 路由中为每个文件夹计算收藏数量 + +**修改文件:** +- `app.py` (第 1141-1144 行) + +**新增代码:** +```python +# 为每个文件夹添加收藏计数 +for folder in folders: + folder.count = Collection.query.filter_by( + user_id=current_user.id, + folder_id=folder.id + ).count() +``` + +--- + +### 5. JavaScript 错误处理不完善 ✅ + +**问题描述:** +- Fetch API 未检查 HTTP 状态码 +- 如果服务器返回 500 错误,`.json()` 可能失败但不会被捕获 + +**修复方案:** +- 在所有 fetch 调用中添加状态码检查 + +**修改文件:** +- `templates/detail_new.html` (第 1579-1662 行) + +**修复模式:** +```javascript +fetch('/api/...') + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json(); + }) + .then(data => { ... }) + .catch(err => { ... }); +``` + +--- + +## 修改的文件清单 + +1. **models.py** - 修复 Collection 唯一约束 +2. **app.py** - 修复注册重复提交 + 添加文件夹计数 +3. **templates/detail_new.html** - 删除未使用变量 + 改进错误处理 +4. **fix_collection_constraint.py** (新增) - 数据库约束修复脚本 + +--- + +## 部署说明 + +### 对于新部署(未运行过 create_user_tables.py) + +直接运行: +```bash +python create_user_tables.py +``` + +新的约束会自动生效。 + +### 对于已部署的环境(已有数据) + +需要运行修复脚本: +```bash +python fix_collection_constraint.py +``` + +**注意:** 如果数据库中已存在重复收藏(同一用户多次收藏同一网站),修复脚本会失败。需要先清理重复数据。 + +--- + +## 测试建议 + +修复后请测试: + +1. **收藏功能** - 尝试多次收藏同一网站,应该提示"已收藏" +2. **文件夹计数** - 访问收藏列表,文件夹标签应显示正确的数量 +3. **错误处理** - 断网情况下点击收藏,应显示"网络请求失败" +4. **注册流程** - 注册新用户,检查数据库只有一条记录 + +--- + +**修复版本:** v3.0.1 +**修复人员:** Claude Sonnet 4.5 diff --git a/app.py b/app.py index 97508ee..21706d3 100644 --- a/app.py +++ b/app.py @@ -642,13 +642,12 @@ def create_app(config_name='default'): try: user = User(username=username) user.set_password(password) + user.last_login = datetime.now() db.session.add(user) db.session.commit() # 自动登录 login_user(user) - user.last_login = datetime.now() - db.session.commit() flash('注册成功!', 'success') return redirect(url_for('index')) @@ -1144,6 +1143,13 @@ def create_app(config_name='default'): Folder.sort_order.desc(), Folder.created_at ).all() + # 为每个文件夹添加收藏计数 + for folder in folders: + folder.count = Collection.query.filter_by( + user_id=current_user.id, + folder_id=folder.id + ).count() + # 获取收藏(分页) page = request.args.get('page', 1, type=int) folder_id = request.args.get('folder_id') diff --git a/fix_collection_constraint.py b/fix_collection_constraint.py new file mode 100644 index 0000000..7b4ad7a --- /dev/null +++ b/fix_collection_constraint.py @@ -0,0 +1,48 @@ +""" +数据库修复脚本:修复 Collection 表的唯一约束 +运行方式:python fix_collection_constraint.py +""" + +import os +from app import create_app +from models import db + +def fix_collection_constraint(): + """修复 Collection 表的唯一约束""" + app = create_app(os.getenv('FLASK_ENV', 'development')) + + with app.app_context(): + print("开始修复 Collection 表的唯一约束...") + + try: + # 删除旧的唯一约束 + print("1. 删除旧的唯一约束 unique_user_site_folder...") + db.engine.execute( + "ALTER TABLE collections DROP INDEX unique_user_site_folder" + ) + print(" ✓ 旧约束已删除") + + # 添加新的唯一约束 + print("2. 添加新的唯一约束 unique_user_site...") + db.engine.execute( + "ALTER TABLE collections ADD CONSTRAINT unique_user_site UNIQUE (user_id, site_id)" + ) + print(" ✓ 新约束已添加") + + print("\n✅ 修复完成!") + print("\n说明:") + print("- 旧约束:(user_id, site_id, folder_id) - 允许重复收藏到未分类") + print("- 新约束:(user_id, site_id) - 每个用户只能收藏一次同一个网站") + + except Exception as e: + print(f"\n❌ 修复失败: {str(e)}") + print("\n可能的原因:") + print("1. 约束名称不匹配(MySQL/SQLite 差异)") + print("2. 数据库中已存在重复数据") + print("3. 数据库权限不足") + + import traceback + traceback.print_exc() + +if __name__ == '__main__': + fix_collection_constraint() diff --git a/models.py b/models.py index ea52c51..abfe02a 100644 --- a/models.py +++ b/models.py @@ -260,7 +260,7 @@ class Collection(db.Model): site = db.relationship('Site', backref='collections') __table_args__ = ( - db.UniqueConstraint('user_id', 'site_id', 'folder_id', name='unique_user_site_folder'), + db.UniqueConstraint('user_id', 'site_id', name='unique_user_site'), db.Index('idx_user_folder', 'user_id', 'folder_id'), ) diff --git a/templates/detail_new.html b/templates/detail_new.html index 5fde013..31cd23a 100644 --- a/templates/detail_new.html +++ b/templates/detail_new.html @@ -1578,7 +1578,10 @@ document.addEventListener('DOMContentLoaded', function() { function checkCollectionStatus() { fetch('/api/auth/status') - .then(r => r.json()) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json(); + }) .then(data => { if (!data.logged_in || data.user_type !== 'user') { return; // 未登录或非普通用户,不检查收藏状态 @@ -1586,7 +1589,10 @@ function checkCollectionStatus() { // 已登录,检查收藏状态 fetch(`/api/collections/status/${siteCode}`) - .then(r => r.json()) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json(); + }) .then(data => { isCollected = data.is_collected; updateCollectButton(); @@ -1598,7 +1604,6 @@ function checkCollectionStatus() { function updateCollectButton() { const btn = document.getElementById('collectBtn'); - const icon = document.getElementById('collectIcon'); const text = document.getElementById('collectText'); if (isCollected) { @@ -1613,7 +1618,10 @@ function updateCollectButton() { function toggleCollect() { // 先检查登录状态 fetch('/api/auth/status') - .then(r => r.json()) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json(); + }) .then(data => { if (!data.logged_in) { // 未登录,跳转到登录页 @@ -1636,7 +1644,10 @@ function toggleCollect() { }, body: JSON.stringify({ site_code: siteCode }) }) - .then(r => r.json()) + .then(r => { + if (!r.ok) throw new Error(`HTTP ${r.status}`); + return r.json(); + }) .then(data => { if (data.success) { isCollected = data.action === 'added';