# 书签管理系统 - 数据库重构完整计划 ## 一、重构背景 ### 1.1 现有问题 原系统采用简化的数据表设计(4 张表:users, devices, bookmarks, refresh_tokens),存在以下问题: | 问题 | 现状 | 影响 | |------|------|------| | **标签存储** | 书签表中的 `Tags` 字段使用 JSON 数组存储 | 无法高效查询、无法统计标签使用量、无法为标签设置颜色/图标 | | **设备权限** | 书签表中的 `AllowedDevices` 字段使用 JSON 数组存储 | 无法建立外键约束、删除设备后残留无效数据 | | **组织结构** | 书签只能通过标签分类,无层级结构 | 不支持文件夹嵌套、无法按目录组织书签 | | **功能缺失** | 无收藏集合、分享、访问历史功能 | 用户体验受限 | ### 1.2 重构目标 将现有的 **4 张表**扩展为 **12 张表**,实现: - **标签管理**:独立 Tag 表 + 关联表,支持标签元数据(颜色、图标、排序) - **层级结构**:新增文件夹表,支持多级嵌套 - **设备权限**:改用关联表,支持外键约束和级联删除 - **新增功能**:收藏集合、分享链接、访问历史记录 ### 1.3 表结构变化概览 | 类别 | 改造前 | 改造后 | |------|--------|--------| | 核心表 | users, devices, refresh_tokens | 保持不变 | | 书签表 | bookmarks (含 JSON 字段) | bookmarks (精简) | | 标签 | - | tags, bookmark_tags | | 文件夹 | - | folders | | 设备权限 | - | bookmark_device_permissions | | 收藏集合 | - | collections, collection_bookmarks | | 分享功能 | - | bookmark_shares | | 访问历史 | - | bookmark_visits | --- ## 二、后端重构(已完成) ### 2.1 实体类变更 #### 修改的实体 | 文件 | 变更内容 | |------|----------| | `Bookmark.cs` | 移除 `Tags`(string[])、`AllowedDevices`(Guid[]);新增 `FolderId`;`Order` 从 long 改为 int;`LastVisitTime` 改名为 `LastVisitAt`;新增导航属性 | #### 新增的实体 | 文件 | 说明 | |------|------| | `Folder.cs` | 文件夹实体,支持 ParentId 自引用实现多级嵌套 | | `Tag.cs` | 标签实体,包含 Color、Icon、Order 字段 | | `BookmarkTag.cs` | 书签-标签多对多关联表 | | `BookmarkDevicePermission.cs` | 书签-设备权限关联表 | | `Collection.cs` | 收藏集合实体 | | `CollectionBookmark.cs` | 集合-书签多对多关联表 | | `BookmarkShare.cs` | 分享链接实体,支持密码保护、过期时间、查看次数限制 | | `BookmarkVisit.cs` | 访问历史实体 | #### 新增的枚举 | 文件 | 说明 | |------|------| | `ShareType.cs` | 分享类型:Bookmark(1)/Folder(2)/Collection(3) | ### 2.2 DTO 变更 | DTO | 变更内容 | |-----|----------| | `BookmarkDto` | 新增 `FolderId`;`Tags` 从 `string[]` 改为 `List`;`LastVisitTime` 改名为 `LastVisitAt`;`Order` 从 long 改为 int | | `TagDto` | 新增 `Id`、`Color`、`Icon`、`Order` 字段 | | `CreateBookmarkRequest` | 新增 `FolderId` 字段 | | `UpdateBookmarkRequest` | 新增 `FolderId`、`UpdateFolder` 字段 | ### 2.3 服务层重构 | 服务 | 变更内容 | |------|----------| | `BookmarkService` | 完全重写,使用 `IncludeMany` 加载 BookmarkTags 和 DevicePermissions;新增 `SyncBookmarkTagsAsync`、`SyncDevicePermissionsAsync` 私有方法 | | `TagService` | 完全重写,使用独立 Tag 表;新增 `CreateTagAsync`、`UpdateTagAsync`、`GetTagAsync` 方法 | ### 2.4 接口变更 | 接口 | 变更内容 | |------|----------| | `ITagService` | 新增 `CreateTagAsync`、`UpdateTagAsync`、`GetTagAsync` 方法签名 | | `IBookmarkService` | `GetUserBookmarksAsync` 新增 `folderId` 参数 | ### 2.5 控制器变更 | 控制器 | 变更内容 | |--------|----------| | `TagsController` | 新增完整的 CRUD 操作(创建、更新、删除标签) | | `BookmarksController` | `GetBookmarks` 新增 `folderId` 查询参数 | | `AdminController` | 修复 GetUserBookmarks 方法,使用新的实体结构 | ### 2.6 数据库初始化 `FreeSqlSetup.cs` 已更新: - 注册所有 12 个实体类型 - 启动时自动同步表结构 - 提供 `SyncAllTables()` 方法手动同步 - 提供 `CheckDatabaseStatusAsync()` 方法检查数据库状态 --- ## 三、前端重构计划 ### 3.1 需要修改的文件清单 #### 3.1.1 类型定义 (`src/frontend/src/types/index.ts`) | 修改项 | 当前 | 修改后 | |--------|------|--------| | `Bookmark.tags` | `string[]` | `Tag[]` | | `Bookmark.lastVisitTime` | `lastVisitTime` | `lastVisitAt` | | 新增 `Bookmark.folderId` | - | `folderId?: string` | | `Tag` 接口 | `{ name: string; count: number }` | `{ id: string; name: string; color?: string; icon?: string; order: number; count: number }` | | 新增 | - | `Folder` 接口 | | 新增 | - | `Collection` 接口 | ```typescript // 修改后的 Tag 接口 interface Tag { id: string; name: string; color?: string; icon?: string; order: number; count: number; } // 修改后的 Bookmark 接口 interface Bookmark { id: string; folderId?: string; // 新增 title: string; url: string; description?: string; icon?: string; tags: Tag[]; // 从 string[] 改为 Tag[] visitCount: number; lastVisitAt?: string; // 重命名 order: number; visibility: VisibilityType; allowedDevices?: string[]; createdAt: string; updatedAt: string; } // 新增 Folder 接口 interface Folder { id: string; parentId?: string; name: string; icon?: string; order: number; children?: Folder[]; bookmarks?: Bookmark[]; } ``` #### 3.1.2 API 层修改 **`src/frontend/src/api/bookmark.ts`** | 方法 | 修改内容 | |------|----------| | `getBookmarks()` | 新增 `folderId` 参数:`getBookmarks(tag?: string, folderId?: string)` | | 新增 | `getFolders()` - 获取文件夹列表 | | 新增 | `createFolder()` - 创建文件夹 | | 新增 | `updateFolder()` - 更新文件夹 | | 新增 | `deleteFolder()` - 删除文件夹 | | 新增 | `moveBookmark()` - 移动书签到文件夹 | **`src/frontend/src/api/tag.ts`** | 方法 | 修改内容 | |------|----------| | 新增 | `createTag(name, color?, icon?)` - 创建标签 | | 新增 | `updateTag(id, name?, color?, icon?)` - 更新标签 | | 修改 | `deleteTag(id)` - 参数从标签名改为标签ID | | 修改 | `mergeTags(sourceTagIds, targetTagId)` - 参数从标签名改为标签ID | #### 3.1.3 Store 层修改 **`src/frontend/src/stores/bookmark.ts`** | 修改项 | 说明 | |--------|------| | 新增状态 | `currentFolderId: string \| null` - 当前文件夹 | | 新增状态 | `folders: Folder[]` - 文件夹列表 | | 修改 `fetchBookmarks` | 支持 `folderId` 参数 | | 新增方法 | `fetchFolders()` - 获取文件夹列表 | | 新增方法 | `createFolder()` - 创建文件夹 | | 新增方法 | `deleteFolder()` - 删除文件夹 | | 新增方法 | `setCurrentFolder()` - 切换文件夹 | | 修改计算属性 | 适配新的 Tag 对象结构 | **`src/frontend/src/stores/tag.ts`** | 修改项 | 说明 | |--------|------| | 新增方法 | `createTag(name, color?, icon?)` | | 新增方法 | `updateTag(id, name?, color?, icon?)` | | 修改方法 | `deleteTag` 和 `mergeTags` 使用 ID 而非名称 | #### 3.1.4 组件修改 **`src/frontend/src/components/bookmark/BookmarkList.vue`** | 修改项 | 说明 | |--------|------| | 标签显示 | 从显示字符串改为显示带颜色的标签组件 | | 新增 | 面包屑导航(显示当前文件夹路径) | **`src/frontend/src/components/bookmark/BookmarkEditor.vue`** | 修改项 | 说明 | |--------|------| | 标签选择 | 从字符串输入改为标签选择器(支持颜色预览) | | 新增 | 文件夹选择器(树形结构) | | 标签管理 | 支持创建带颜色的新标签 | **新增组件** | 组件 | 说明 | |------|------| | `FolderTree.vue` | 文件夹树形组件,支持展开/折叠、拖拽移动 | | `TagBadge.vue` | 带颜色的标签徽章组件 | | `TagManager.vue` | 标签管理弹窗(编辑颜色、图标、排序) | #### 3.1.5 视图页面修改 **`src/frontend/src/views/HomeView.vue`** | 修改项 | 说明 | |--------|------| | 左侧边栏 | 新增文件夹树导航 | | 标签列表 | 显示标签颜色 | | 面包屑 | 显示当前文件夹路径 | ### 3.2 前端修改优先级 | 优先级 | 任务 | 影响范围 | |--------|------|----------| | P0 | 修改类型定义 (types/index.ts) | 全局 | | P0 | 修改 API 层适配新接口 | 全局 | | P0 | 修改 Store 层适配新数据结构 | 全局 | | P1 | 修改 BookmarkList 组件显示标签 | 书签列表 | | P1 | 修改 BookmarkEditor 组件标签选择 | 书签编辑 | | P2 | 新增文件夹相关组件和功能 | 新功能 | | P2 | 新增标签管理功能 | 新功能 | --- ## 四、浏览器插件重构计划 ### 4.1 需要修改的文件清单 #### 4.1.1 API 层 (`src/extension/shared/api.js`) | 方法 | 修改内容 | |------|----------| | `getBookmarks(tag)` | 新增 `folderId` 参数:`getBookmarks(tag, folderId)` | | `createBookmark(data)` | 请求体新增 `folderId` 字段 | | 新增 | `getFolders()` - 获取文件夹列表 | | 新增 | `createTag(name, color)` - 创建标签(可选) | ```javascript // 修改后的 getBookmarks async getBookmarks(tag = null, folderId = null) { const params = new URLSearchParams(); if (tag) params.append('tag', tag); if (folderId) params.append('folderId', folderId); return this.request(`/bookmarks?${params.toString()}`); } // 修改后的 createBookmark async createBookmark(data) { return this.request('/bookmarks', { method: 'POST', body: JSON.stringify({ title: data.title, url: data.url, description: data.description, tags: data.tags, // 标签名数组 folderId: data.folderId, // 新增 visibility: data.visibility || 0 }) }); } ``` #### 4.1.2 后台脚本 (`src/extension/background/background.js`) | 修改项 | 说明 | |--------|------| | `onBookmarkCreated` | 收集标签时考虑新的标签对象结构 | | 新增 | 支持将浏览器书签文件夹映射到系统文件夹(可选) | #### 4.1.3 弹窗界面 (`src/extension/popup/`) **popup.js 修改** | 修改项 | 说明 | |--------|------| | 标签输入 | 保持逗号分隔输入方式(后端会自动创建标签) | | 新增 | 文件夹选择下拉框(可选功能) | **popup.html 修改** | 修改项 | 说明 | |--------|------| | 新增 | 文件夹选择器 UI(可选功能) | #### 4.1.4 内容脚本 (`src/extension/content/content.js`) | 修改项 | 说明 | |--------|------| | 搜索结果渲染 | 适配新的 Tag 对象结构,显示标签颜色 | ```javascript // 修改后的搜索结果渲染 function renderSearchResult(bookmark) { const tagsHtml = bookmark.tags.map(tag => { const style = tag.color ? `background-color: ${tag.color}` : ''; return `${tag.name}`; }).join(''); return `
${bookmark.title}
${bookmark.url}
${tagsHtml}
`; } ``` #### 4.1.5 新标签页 (`src/extension/newtab/index.html`) | 修改项 | 说明 | |--------|------| | 书签卡片 | 适配新的 Tag 对象结构 | | 标签显示 | 显示标签颜色 | | 新增 | 文件夹导航(可选功能) | ### 4.2 插件修改优先级 | 优先级 | 任务 | 影响范围 | |--------|------|----------| | P0 | 修改 api.js 适配新接口 | 全局 | | P0 | 修改 content.js 搜索结果渲染 | 全局搜索 | | P0 | 修改 newtab 书签显示 | 新标签页 | | P1 | 修改 popup 保存功能 | 快速保存 | | P2 | 新增文件夹选择功能 | 新功能 | --- ## 五、API 接口变更说明 ### 5.1 书签相关接口 | 接口 | 方法 | 变更 | |------|------|------| | `/api/bookmarks` | GET | 新增 `folderId` 查询参数 | | `/api/bookmarks` | POST | 请求体新增 `folderId` 字段 | | `/api/bookmarks/{id}` | PUT | 请求体新增 `folderId`、`updateFolder` 字段 | ### 5.2 标签相关接口 | 接口 | 方法 | 变更 | |------|------|------| | `/api/tags` | GET | 返回完整的 TagDto 对象(含 id, color, icon, order) | | `/api/tags` | POST | **新增** - 创建标签 | | `/api/tags/{id}` | GET | **新增** - 获取标签详情 | | `/api/tags/{id}` | PUT | **新增** - 更新标签 | | `/api/tags/{id}` | DELETE | 参数从标签名改为标签 ID | | `/api/tags/merge` | POST | 请求体使用标签 ID 数组 | ### 5.3 响应数据结构变更 **BookmarkDto 响应示例** ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "folderId": "660e8400-e29b-41d4-a716-446655440001", "title": "Example Site", "url": "https://example.com", "description": "An example website", "icon": "data:image/png;base64,...", "tags": [ { "id": "770e8400-e29b-41d4-a716-446655440002", "name": "工作", "color": "#FF5733", "icon": "briefcase", "order": 1 }, { "id": "880e8400-e29b-41d4-a716-446655440003", "name": "技术", "color": "#33FF57", "icon": "code", "order": 2 } ], "visitCount": 42, "lastVisitAt": "2024-12-25T10:30:00Z", "order": 1, "visibility": 0, "allowedDevices": null, "createdAt": "2024-12-01T00:00:00Z", "updatedAt": "2024-12-25T10:30:00Z" } ``` --- ## 六、数据表详细设计 ### 6.1 users 表(用户)- 保持不变 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 用户ID | | Email | string(200) | Unique, Not Null | 邮箱 | | UserName | string(100) | Not Null | 用户名 | | PasswordHash | string(500) | Not Null | 密码哈希 | | Avatar | string(500) | Nullable | 头像URL | | Role | int | Default 0 | 角色 | | Status | int | Default 0 | 状态 | | CreatedAt | DateTime | Not Null | 创建时间 | | LastLoginAt | DateTime | Nullable | 最后登录 | ### 6.2 devices 表(设备)- 保持不变 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 设备ID | | UserId | Guid | FK | 所属用户 | | DeviceName | string(200) | Not Null | 设备名称 | | DeviceType | string(200) | Not Null | 设备类型 | | DeviceFingerprint | string(500) | Nullable | 设备指纹 | | IsAdmin | bool | Default false | 管理员设备 | | Status | int | Default 0 | 状态 | | CreatedAt | DateTime | Not Null | 创建时间 | | LastActiveAt | DateTime | Not Null | 最后活跃 | ### 6.3 folders 表(文件夹)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 文件夹ID | | UserId | Guid | FK | 所属用户 | | ParentId | Guid | FK, Nullable | 父文件夹 | | Name | string(200) | Not Null | 名称 | | Icon | string(100) | Nullable | 图标 | | Order | int | Default 0 | 排序 | | CreatedAt | DateTime | Not Null | 创建时间 | | UpdatedAt | DateTime | Not Null | 更新时间 | **索引:** `idx_folder_user_parent` (UserId, ParentId) ### 6.4 bookmarks 表(书签)- 重构 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 书签ID | | UserId | Guid | FK | 所属用户 | | FolderId | Guid | FK, Nullable | 所属文件夹 | | Title | string(500) | Not Null | 标题 | | Url | string(2000) | Not Null | URL | | Description | string(2000) | Nullable | 描述 | | Icon | string(MAX) | Nullable | 图标 | | Order | int | Default 0 | 排序 | | Visibility | int | Default 0 | 可见性 | | VisitCount | int | Default 0 | 访问次数 | | LastVisitAt | DateTime | Nullable | 最后访问 | | CreatedAt | DateTime | Not Null | 创建时间 | | UpdatedAt | DateTime | Not Null | 更新时间 | **变更:** 移除 Tags、AllowedDevices;新增 FolderId ### 6.5 tags 表(标签)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 标签ID | | UserId | Guid | FK | 所属用户 | | Name | string(100) | Not Null | 名称 | | Color | string(20) | Nullable | 颜色 | | Icon | string(100) | Nullable | 图标 | | Order | int | Default 0 | 排序 | | CreatedAt | DateTime | Not Null | 创建时间 | **索引:** `idx_tag_user_name` (UserId, Name) - Unique ### 6.6 bookmark_tags 表(书签-标签关联)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 关联ID | | BookmarkId | Guid | FK | 书签ID | | TagId | Guid | FK | 标签ID | | CreatedAt | DateTime | Not Null | 创建时间 | **索引:** `idx_bookmark_tag_unique` (BookmarkId, TagId) - Unique ### 6.7 bookmark_device_permissions 表(设备权限)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 权限ID | | BookmarkId | Guid | FK | 书签ID | | DeviceId | Guid | FK | 设备ID | | CreatedAt | DateTime | Not Null | 创建时间 | **索引:** `idx_permission_unique` (BookmarkId, DeviceId) - Unique ### 6.8 collections 表(收藏集合)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 集合ID | | UserId | Guid | FK | 所属用户 | | Name | string(200) | Not Null | 名称 | | Description | string(500) | Nullable | 描述 | | Icon | string(100) | Nullable | 图标 | | Color | string(20) | Nullable | 主题色 | | IsPublic | bool | Default false | 是否公开 | | Order | int | Default 0 | 排序 | | CreatedAt | DateTime | Not Null | 创建时间 | | UpdatedAt | DateTime | Not Null | 更新时间 | ### 6.9 collection_bookmarks 表(集合-书签关联)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 关联ID | | CollectionId | Guid | FK | 集合ID | | BookmarkId | Guid | FK | 书签ID | | Order | int | Default 0 | 排序 | | CreatedAt | DateTime | Not Null | 添加时间 | ### 6.10 bookmark_shares 表(分享链接)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 分享ID | | UserId | Guid | FK | 创建者 | | ShareCode | string(50) | Unique | 分享码 | | ShareType | int | Not Null | 类型 | | TargetId | Guid | Not Null | 目标ID | | Title | string(200) | Nullable | 标题 | | Password | string(100) | Nullable | 密码 | | ViewCount | int | Default 0 | 查看次数 | | MaxViews | int | Nullable | 最大查看 | | ExpiresAt | DateTime | Nullable | 过期时间 | | IsActive | bool | Default true | 是否启用 | | CreatedAt | DateTime | Not Null | 创建时间 | ### 6.11 bookmark_visits 表(访问历史)- 新增 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | Guid | PK | 记录ID | | UserId | Guid | FK | 用户ID | | BookmarkId | Guid | FK | 书签ID | | DeviceId | Guid | FK, Nullable | 设备ID | | VisitedAt | DateTime | Not Null | 访问时间 | --- ## 七、实体关系图 ``` ┌─────────────┐ │ users │ └──────┬──────┘ │ │ 1:N ├────────────────┬────────────────┬────────────────┬────────────────┐ │ │ │ │ │ ▼ ▼ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ devices │ │ folders │ │ tags │ │ collections │ │ shares │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └─────────────┘ │ │ 1:N │ │ │ │ (self-ref) │ │ │ ▼ │ │ │ ┌─────────────┐ │ │ │ │ bookmarks │◄────────┘ │ │ └──────┬──────┘ │ │ │ │ │ │ N:M │ N:M │ ├─────────────────────────────────┤ │ │ │ │ ▼ ▼ │ ┌─────────────┐ ┌─────────────┐ │ │bookmark_tags│ │ collection_ │ │ └─────────────┘ │ bookmarks │ │ ├─────────────────────────────────────────────────┐ │ │ ▼ ▼ ┌─────────────────────┐ ┌─────────────┐ │ bookmark_device_ │ │ bookmark_ │ │ permissions │ │ visits │ └─────────────────────┘ └─────────────┘ ``` --- ## 八、重构进度跟踪 ### 8.1 后端重构(已完成 ✅) - [x] 修改 Bookmark.cs 实体 - [x] 新增 Folder.cs 实体 - [x] 新增 Tag.cs 实体 - [x] 新增 BookmarkTag.cs 实体 - [x] 新增 BookmarkDevicePermission.cs 实体 - [x] 新增 Collection.cs 实体 - [x] 新增 CollectionBookmark.cs 实体 - [x] 新增 BookmarkShare.cs 实体 - [x] 新增 BookmarkVisit.cs 实体 - [x] 新增 ShareType.cs 枚举 - [x] 修改 FreeSqlSetup.cs 注册新实体 - [x] 重构 BookmarkService.cs - [x] 重构 TagService.cs - [x] 更新 DTO 类 - [x] 更新控制器 - [x] 构建验证通过 ### 8.2 前端重构(已完成 ✅) - [x] 修改类型定义 (types/index.ts) - Tag 接口新增 id、color、icon、order 字段 - Bookmark.tags 从 string[] 改为 Tag[] - Bookmark.lastVisitTime 改名为 lastVisitAt - 新增 folderId 字段 - 新增 Folder、Collection 接口 - [x] 修改 API 层 (api/bookmark.ts, api/tag.ts) - getBookmarks 新增 folderId 参数 - 新增文件夹相关 API - 标签 API 改用 ID - [x] 修改 Store 层 (stores/bookmark.ts, stores/tag.ts) - 新增 folders、currentFolderId 状态 - 新增文件夹操作方法 - 标签操作改用 ID - [x] 修改 BookmarkList 组件 - 标签显示改为 tag.name - 支持显示标签颜色 - [x] 修改 BookmarkEditor 组件 - 适配新的标签对象结构 - [x] 新增文件夹相关组件 - FolderTree.vue - 文件夹树形组件 - [x] 新增标签管理组件 - TagBadge.vue - 带颜色的标签徽章组件 - TagManager.vue - 标签管理弹窗 - [x] 修改 HomeView.vue - 添加文件夹导航 - 标签显示颜色 - 面包屑导航 - 标签管理入口 - [x] 构建验证通过 ### 8.3 浏览器插件重构(待开始) - [ ] 修改 shared/api.js - [ ] 修改 content/content.js 搜索结果渲染 - [ ] 修改 newtab/index.html 书签显示 - [ ] 修改 popup/popup.js 保存功能 --- ## 九、注意事项 1. **数据迁移**:本次重构不考虑旧数据迁移,直接使用新表结构 2. **向后兼容**:API 响应格式已变更,前端和插件必须同步更新 3. **标签创建**:创建书签时传入的标签名会自动创建不存在的标签 4. **级联删除**:删除用户时需级联删除所有关联数据 5. **性能考虑**:bookmark_visits 表数据量可能很大,考虑定期清理 --- ## 十、版本信息 - 文档版本:v2.1 - 创建日期:2024-12-25 - 最后更新:2024-12-25 - 作者:Claude Code - 更新说明:完成前端重构