work/docs/数据库重构计划.md
2025-12-25 23:43:27 +08:00

24 KiB
Raw Permalink Blame History

书签管理系统 - 数据库重构完整计划

一、重构背景

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[]);新增 FolderIdOrder 从 long 改为 intLastVisitTime 改名为 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 新增 FolderIdTagsstring[] 改为 List<TagDto>LastVisitTime 改名为 LastVisitAtOrder 从 long 改为 int
TagDto 新增 IdColorIconOrder 字段
CreateBookmarkRequest 新增 FolderId 字段
UpdateBookmarkRequest 新增 FolderIdUpdateFolder 字段

2.3 服务层重构

服务 变更内容
BookmarkService 完全重写,使用 IncludeMany 加载 BookmarkTags 和 DevicePermissions新增 SyncBookmarkTagsAsyncSyncDevicePermissionsAsync 私有方法
TagService 完全重写,使用独立 Tag 表;新增 CreateTagAsyncUpdateTagAsyncGetTagAsync 方法

2.4 接口变更

接口 变更内容
ITagService 新增 CreateTagAsyncUpdateTagAsyncGetTagAsync 方法签名
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 接口
// 修改后的 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?)
修改方法 deleteTagmergeTags 使用 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) - 创建标签(可选)
// 修改后的 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 对象结构,显示标签颜色
// 修改后的搜索结果渲染
function renderSearchResult(bookmark) {
  const tagsHtml = bookmark.tags.map(tag => {
    const style = tag.color ? `background-color: ${tag.color}` : '';
    return `<span class="tag" style="${style}">${tag.name}</span>`;
  }).join('');

  return `
    <div class="search-result-item">
      <img src="${bookmark.icon || 'default-icon.png'}" alt="">
      <div class="info">
        <div class="title">${bookmark.title}</div>
        <div class="url">${bookmark.url}</div>
        <div class="tags">${tagsHtml}</div>
      </div>
    </div>
  `;
}

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 请求体新增 folderIdupdateFolder 字段

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 响应示例

{
  "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 后端重构(已完成

  • 修改 Bookmark.cs 实体
  • 新增 Folder.cs 实体
  • 新增 Tag.cs 实体
  • 新增 BookmarkTag.cs 实体
  • 新增 BookmarkDevicePermission.cs 实体
  • 新增 Collection.cs 实体
  • 新增 CollectionBookmark.cs 实体
  • 新增 BookmarkShare.cs 实体
  • 新增 BookmarkVisit.cs 实体
  • 新增 ShareType.cs 枚举
  • 修改 FreeSqlSetup.cs 注册新实体
  • 重构 BookmarkService.cs
  • 重构 TagService.cs
  • 更新 DTO 类
  • 更新控制器
  • 构建验证通过

8.2 前端重构(已完成

  • 修改类型定义 (types/index.ts)
    • Tag 接口新增 id、color、icon、order 字段
    • Bookmark.tags 从 string[] 改为 Tag[]
    • Bookmark.lastVisitTime 改名为 lastVisitAt
    • 新增 folderId 字段
    • 新增 Folder、Collection 接口
  • 修改 API 层 (api/bookmark.ts, api/tag.ts)
    • getBookmarks 新增 folderId 参数
    • 新增文件夹相关 API
    • 标签 API 改用 ID
  • 修改 Store 层 (stores/bookmark.ts, stores/tag.ts)
    • 新增 folders、currentFolderId 状态
    • 新增文件夹操作方法
    • 标签操作改用 ID
  • 修改 BookmarkList 组件
    • 标签显示改为 tag.name
    • 支持显示标签颜色
  • 修改 BookmarkEditor 组件
    • 适配新的标签对象结构
  • 新增文件夹相关组件
    • FolderTree.vue - 文件夹树形组件
  • 新增标签管理组件
    • TagBadge.vue - 带颜色的标签徽章组件
    • TagManager.vue - 标签管理弹窗
  • 修改 HomeView.vue
    • 添加文件夹导航
    • 标签显示颜色
    • 面包屑导航
    • 标签管理入口
  • 构建验证通过

8.3 浏览器插件重构(已完成

  • 修改 shared/api.js
    • getBookmarks 新增 folderId 参数
    • 新增 getFolders() 方法
    • 新增 createTag() 方法
  • 修改 content/content.js 搜索结果渲染
    • 适配新的 Tag 对象结构tag.name, tag.color
  • 修改 newtab/index.html 书签显示
    • 搜索结果中添加标签显示
    • 支持标签颜色
  • 修改 popup/popup.js 保存功能
    • 新增文件夹选择器
    • 保存时支持 folderId
  • 修改 background/background.js
    • 新增 getFolders 消息处理
  • 修改 popup/popup.html
    • 新增文件夹选择器 UI
  • 修改 popup/popup.css
    • 新增文件夹选择器样式

九、注意事项

  1. 数据迁移:本次重构不考虑旧数据迁移,直接使用新表结构
  2. 向后兼容API 响应格式已变更,前端和插件必须同步更新
  3. 标签创建:创建书签时传入的标签名会自动创建不存在的标签
  4. 级联删除:删除用户时需级联删除所有关联数据
  5. 性能考虑bookmark_visits 表数据量可能很大,考虑定期清理

十、版本信息

  • 文档版本v2.2
  • 创建日期2024-12-25
  • 最后更新2025-12-25
  • 作者Claude Code
  • 更新说明:完成浏览器插件重构