work/docs/开发文档.md
2025-12-17 22:30:44 +08:00

29 KiB
Raw Permalink Blame History

v2.0 开发文档

版本: 2.0.0 状态: 已完成 最后更新: 2025-12-15 文档类型: 技术开发文档


文档概述

本文档详细记录了书签管理系统 v2.0 的完整技术实现,包括:

  • 从 v1.0(本地单用户)到 v2.0(云端多用户)的架构升级
  • 后端 APIASP.NET Core + FreeSql完整实现
  • 前端应用Vue 3 + TypeScript技术栈
  • 浏览器扩展自动同步机制
  • 后台管理系统实现细节

核心任务完成情况

已完成功能

后端 API100%

  • JWT 认证系统(包含 RefreshToken 机制)
  • 用户注册/登录/登出
  • 设备自动注册与管理(设备指纹识别)
  • 书签 CRUD含搜索、批量操作、可见性控制
  • 标签管理
  • 后台管理接口(用户管理、设备管理、统计数据)

前端应用100%

  • 用户认证界面(登录/注册)
  • 书签管理界面(列表、编辑、搜索)
  • 设备管理界面
  • 可见性控制界面
  • Pinia 状态管理auth、bookmark、device、tag

浏览器扩展90%

  • 书签自动同步(监听浏览器书签创建事件)
  • 文件夹路径转标签
  • URL 重复检测
  • 同步状态提示
  • 删除/修改同步(预留接口)

后台管理系统100%

  • 仪表盘(系统统计数据)
  • 用户管理(列表、详情、禁用、重置密码)
  • 设备管理(强制下线、禁用设备)
  • 书签管理(查看、删除)

技术架构变化

graph LR
    v1[v1.0_本地单用户]
    v2[v2.0_云端多用户]
    
    v1 -->|架构升级| v2
    
    subgraph v1_arch [v1.0架构]
        fe1[Vue前端]
        idb[IndexedDB]
        ext1[扩展监控]
        fe1 --> idb
        ext1 -.-> fe1
    end
    
    subgraph v2_arch [v2.0架构]
        fe2[Vue前端]
        api[.NET_API]
        db[(SQL数据库)]
        admin[Vue后台]
        ext2[扩展增强]
        
        fe2 --> api
        ext2 --> api
        admin --> api
        api --> db
    end

技术栈选型

基于用户确认的技术选型:

| 组件 | 技术选择 | 说明 |

|------|---------|------|

| 后端框架 | ASP.NET Core 8 (LTS) | 长期支持版本,稳定可靠 |

| 数据库 | SQL Server / PostgreSQL | 通过 FreeSql 支持多数据库 |

| ORM | FreeSql | 高性能 .NET ORM支持多数据库、CodeFirst、丰富的查询功能 |

| 认证 | JWT Bearer Token | 无状态认证,支持多设备 |

| 后台管理 | Vue 3 + Element Plus | 与前端技术栈统一 |

| API 文档 | Swagger / OpenAPI | 自动生成接口文档 |

FreeSql 核心优势

  • 支持多种数据库SQL Server、PostgreSQL、MySQL、Oracle 等)
  • CodeFirst 自动同步实体结构到数据库
  • 强大的表达式解析引擎,支持复杂查询
  • 内置分页、软删除、乐观锁等功能
  • Repository + UnitOfWork 模式支持
  • 性能优秀,支持批量操作

文档结构设计

新文档 v2.0-开发文档.md 将包含以下章节:

1. 项目概述

  • v2.0 产品定位和核心变化
  • 从 MVP 到生产级的演进路径
  • 多用户、多设备、云同步的业务价值

2. 系统架构

完整架构图

用户层
├── Bookmark SPA (Vue 3)
├── Browser Extension (Manifest V3)
└── Admin Panel (Vue 3)
         ↓
API 网关层
├── ASP.NET Core 8 Web API
├── JWT 认证中间件
├── 设备识别中间件
└── 可见性过滤中间件
         ↓
业务逻辑层
├── 用户服务 (UserService)
├── 设备服务 (DeviceService)
├── 书签服务 (BookmarkService)
└── 标签服务 (TagService)
         ↓
数据访问层
├── FreeSql IFreeSql 实例
├── Repository 模式
└── UnitOfWork 工作单元
         ↓
数据库层 (SQL Server / PostgreSQL)

3. 数据模型设计

3.1 核心实体

Users (用户表)

using FreeSql.DataAnnotations;

[Table(Name = "users")]
public class User
{
    [Column(IsPrimary = true)]
    public Guid Id { get; set; }
    
    [Column(IsNullable = false, StringLength = 200)]
    [Index("idx_user_email", IsUnique = true)]
    public string Email { get; set; }
    
    [Column(IsNullable = false, StringLength = 100)]
    public string UserName { get; set; }
    
    [Column(IsNullable = false, StringLength = 500)]
    public string PasswordHash { get; set; }
    
    [Column(StringLength = 500)]
    public string? Avatar { get; set; }
    
    [Column(ServerTime = DateTimeKind.Utc)]
    public DateTime CreatedAt { get; set; }
    
    [Column(IsNullable = true)]
    public DateTime? LastLoginAt { get; set; }
    
    [Column(MapType = typeof(int))]
    public UserStatus Status { get; set; }
    
    // 导航属性
    [Navigate(nameof(Device.UserId))]
    public List<Device> Devices { get; set; }
    
    [Navigate(nameof(Bookmark.UserId))]
    public List<Bookmark> Bookmarks { get; set; }
}

public enum UserStatus
{
    Normal = 0,
    Disabled = 1
}

Devices (设备表)

[Table(Name = "devices")]
public class Device
{
    [Column(IsPrimary = true)]
    public Guid Id { get; set; }
    
    [Column(IsNullable = false)]
    public Guid UserId { get; set; }
    
    [Column(IsNullable = false, StringLength = 200)]
    public string DeviceName { get; set; }
    
    [Column(IsNullable = false, StringLength = 200)]
    public string DeviceType { get; set; }
    
    [Column(IsNullable = false)]
    [Index("idx_device_user_admin")]
    public bool IsAdmin { get; set; }
    
    [Column(ServerTime = DateTimeKind.Utc)]
    public DateTime CreatedAt { get; set; }
    
    [Column(ServerTime = DateTimeKind.Utc, IsOnUpdateValue = true)]
    public DateTime LastActiveAt { get; set; }
    
    [Column(MapType = typeof(int))]
    public DeviceStatus Status { get; set; }
    
    // 导航属性
    [Navigate(nameof(UserId))]
    public User User { get; set; }
}

public enum DeviceStatus
{
    Normal = 0,
    Disabled = 1
}

Bookmarks (书签表)

[Table(Name = "bookmarks")]
[Index("idx_bookmark_user_order", nameof(UserId), nameof(Order))]
public class Bookmark
{
    [Column(IsPrimary = true)]
    public Guid Id { get; set; }
    
    [Column(IsNullable = false)]
    public Guid UserId { get; set; }
    
    [Column(IsNullable = false, StringLength = 500)]
    public string Title { get; set; }
    
    [Column(IsNullable = false, StringLength = 2000)]
    public string Url { get; set; }
    
    [Column(StringLength = 2000)]
    public string? Description { get; set; }
    
    [Column(StringLength = -1)]  // TEXT/NTEXT
    public string? Icon { get; set; }
    
    [Column(MapType = typeof(string), StringLength = -1)]  // JSON 存储
    public string[] Tags { get; set; }
    
    [Column(IsNullable = false)]
    public int VisitCount { get; set; }
    
    public DateTime? LastVisitTime { get; set; }
    
    [Column(IsNullable = false)]
    public long Order { get; set; }
    
    [Column(MapType = typeof(int))]
    public VisibilityType Visibility { get; set; }
    
    [Column(MapType = typeof(string), StringLength = -1)]  // JSON 存储
    public Guid[]? AllowedDevices { get; set; }
    
    [Column(ServerTime = DateTimeKind.Utc)]
    public DateTime CreatedAt { get; set; }
    
    [Column(ServerTime = DateTimeKind.Utc, IsOnUpdateValue = true)]
    public DateTime UpdatedAt { get; set; }
    
    // 导航属性
    [Navigate(nameof(UserId))]
    public User User { get; set; }
}

public enum VisibilityType
{
    Public = 0,
    Private = 1,
    Specified = 2
}

3.2 数据库索引策略

  • Users: Email 唯一索引
  • Devices: UserId + IsAdmin 复合索引(用于快速查找管理员设备)
  • Bookmarks: UserId + Order 复合索引(列表查询优化)

4. FreeSql 配置与使用

4.1 IFreeSql 实例配置

// Program.cs
using FreeSql;

var builder = WebApplication.CreateBuilder(args);

// 配置 FreeSql
var freeSqlBuilder = new FreeSqlBuilder()
    .UseConnectionString(GetDataType(), builder.Configuration.GetConnectionString("DefaultConnection"))
    .UseAutoSyncStructure(builder.Environment.IsDevelopment())  // 开发环境自动同步结构
    .UseMonitorCommand(cmd => Console.WriteLine(cmd.CommandText))  // 监控 SQL
    .Build();

// 注册为单例
builder.Services.AddSingleton<IFreeSql>(freeSqlBuilder);

// 辅助方法:根据配置获取数据库类型
DataType GetDataType()
{
    var provider = builder.Configuration["DatabaseProvider"];
    return provider?.ToLower() switch
    {
        "postgresql" => DataType.PostgreSQL,
        "sqlserver" => DataType.SqlServer,
        _ => DataType.SqlServer
    };
}

// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=BookmarkDb;..."
  },
  "DatabaseProvider": "SqlServer"  // 或 "PostgreSQL"
}

4.2 Repository 模式

// IRepository.cs
public interface IRepository<TEntity> where TEntity : class
{
    ISelect<TEntity> Select { get; }
    Task<TEntity> GetAsync(object id);
    Task<List<TEntity>> GetListAsync();
    Task<TEntity> InsertAsync(TEntity entity);
    Task<int> UpdateAsync(TEntity entity);
    Task<int> DeleteAsync(object id);
}

// 实现
public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    private readonly IFreeSql _freeSql;
    
    public Repository(IFreeSql freeSql)
    {
        _freeSql = freeSql;
    }
    
    public ISelect<TEntity> Select => _freeSql.Select<TEntity>();
    
    public Task<TEntity> GetAsync(object id) 
        => _freeSql.Select<TEntity>().WhereDynamic(id).FirstAsync();
    
    public Task<List<TEntity>> GetListAsync() 
        => _freeSql.Select<TEntity>().ToListAsync();
    
    public Task<TEntity> InsertAsync(TEntity entity) 
        => _freeSql.Insert(entity).ExecuteInsertedAsync();
    
    public Task<int> UpdateAsync(TEntity entity) 
        => _freeSql.Update<TEntity>().SetSource(entity).ExecuteAffrowsAsync();
    
    public Task<int> DeleteAsync(object id) 
        => _freeSql.Delete<TEntity>().WhereDynamic(id).ExecuteAffrowsAsync();
}

4.3 UnitOfWork 工作单元

// IUnitOfWork.cs
public interface IUnitOfWork : IDisposable
{
    void BeginTransaction();
    void Commit();
    void Rollback();
    IRepository<TEntity> GetRepository<TEntity>() where TEntity : class;
}

// 实现
public class UnitOfWork : IUnitOfWork
{
    private readonly IFreeSql _freeSql;
    private DbTransaction? _transaction;
    
    public UnitOfWork(IFreeSql freeSql)
    {
        _freeSql = freeSql;
    }
    
    public void BeginTransaction()
    {
        _transaction = _freeSql.Ado.BeginTransaction();
    }
    
    public void Commit()
    {
        _transaction?.Commit();
        _transaction?.Dispose();
        _transaction = null;
    }
    
    public void Rollback()
    {
        _transaction?.Rollback();
        _transaction?.Dispose();
        _transaction = null;
    }
    
    public IRepository<TEntity> GetRepository<TEntity>() where TEntity : class
    {
        return new Repository<TEntity>(_freeSql);
    }
    
    public void Dispose()
    {
        _transaction?.Dispose();
    }
}

5. 后端 API 设计

5.1 RESTful API 规范

认证接口 (/api/auth)

  • POST /register - 用户注册
  • POST /login - 用户登录(返回 JWT + 自动注册设备)
  • POST /logout - 退出登录
  • POST /refresh - 刷新 Token
  • POST /password/reset - 重置密码P1

用户接口 (/api/user)

  • GET /profile - 获取用户信息
  • PUT /profile - 更新用户信息

设备接口 (/api/devices)

  • GET / - 获取设备列表
  • PUT /{id} - 更新设备信息(重命名)
  • DELETE /{id} - 移除设备
  • PUT /{id}/admin - 设置/取消管理员设备P1

书签接口 (/api/bookmarks)

  • GET / - 获取书签列表(根据设备可见性自动过滤)
  • POST / - 新增书签
  • GET /{id} - 获取书签详情
  • PUT /{id} - 更新书签
  • DELETE /{id} - 删除书签
  • PUT /{id}/visibility - 设置可见性
  • POST /batch - 批量操作P1
  • POST /{id}/visit - 记录访问

标签接口 (/api/tags)

  • GET / - 获取标签列表及统计
  • PUT /{name} - 重命名标签P1
  • DELETE /{name} - 删除标签P1

5.2 可见性过滤实现FreeSql

// BookmarkService.cs
public async Task<List<Bookmark>> GetUserBookmarksAsync(Guid userId, Guid deviceId, bool isAdminDevice)
{
    var query = _freeSql.Select<Bookmark>()
        .Where(b => b.UserId == userId)
        .Where(b => 
            b.Visibility == VisibilityType.Public ||
            (b.Visibility == VisibilityType.Private && isAdminDevice) ||
            (b.Visibility == VisibilityType.Specified && 
             _freeSql.Select<Bookmark>()
                .Where(x => x.Id == b.Id)
                .Where($"AllowedDevices LIKE '%{deviceId}%'")
                .Any()
            )
        )
        .OrderByDescending(b => b.Order);
    
    return await query.ToListAsync();
}

5.3 示例 API Controller

[ApiController]
[Route("api/[controller]")]
[Authorize]
public class BookmarksController : ControllerBase
{
    private readonly IFreeSql _freeSql;
    private readonly ILogger<BookmarksController> _logger;
    
    public BookmarksController(IFreeSql freeSql, ILogger<BookmarksController> logger)
    {
        _freeSql = freeSql;
        _logger = logger;
    }
    
    [HttpGet]
    public async Task<ActionResult<List<BookmarkDto>>> GetBookmarks()
    {
        var userId = GetCurrentUserId();
        var deviceId = GetCurrentDeviceId();
        var isAdmin = await IsAdminDeviceAsync(deviceId);
        
        var bookmarks = await _freeSql.Select<Bookmark>()
            .Where(b => b.UserId == userId)
            .WhereIf(!isAdmin, b => b.Visibility == VisibilityType.Public)
            .OrderByDescending(b => b.Order)
            .ToListAsync();
        
        return Ok(bookmarks);
    }
    
    [HttpPost]
    public async Task<ActionResult<BookmarkDto>> CreateBookmark([FromBody] CreateBookmarkDto dto)
    {
        var userId = GetCurrentUserId();
        
        var bookmark = new Bookmark
        {
            Id = Guid.NewGuid(),
            UserId = userId,
            Title = dto.Title,
            Url = dto.Url,
            Description = dto.Description,
            Tags = dto.Tags ?? Array.Empty<string>(),
            Visibility = dto.Visibility,
            AllowedDevices = dto.AllowedDevices,
            Order = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
            CreatedAt = DateTime.UtcNow
        };
        
        await _freeSql.Insert(bookmark).ExecuteAffrowsAsync();
        
        return CreatedAtAction(nameof(GetBookmark), new { id = bookmark.Id }, bookmark);
    }
    
    // 其他 API 方法...
}

6. 前端架构更新

6.1 数据层变化

v1.0: IndexedDB (Dexie) → v2.0: RESTful API + Axios

新增服务层

  • src/services/api/client.js - Axios 实例配置拦截器、Token 注入)
  • src/services/api/authApi.js - 认证接口封装
  • src/services/api/userApi.js - 用户接口
  • src/services/api/deviceApi.js - 设备接口
  • src/services/api/bookmarkApi.js - 书签接口
  • src/services/api/tagApi.js - 标签接口
  • src/services/auth/tokenService.js - JWT Token 管理localStorage
  • src/services/auth/deviceService.js - 设备指纹识别

6.2 Pinia Store 更新

新增

  • stores/auth.js - 用户认证状态登录、登出、Token 管理)
  • stores/device.js - 设备信息(当前设备、是否管理员)

更新

  • stores/links.js - 从 Dexie 调用改为 API 调用
  • stores/settings.js - 部分设置迁移到服务器端

6.3 新增功能组件

用户认证

  • components/auth/LoginDialog.vue - 登录对话框
  • components/auth/RegisterDialog.vue - 注册对话框
  • components/auth/PasswordResetDialog.vue - 密码重置P1

设备管理

  • components/devices/DeviceList.vue - 设备列表
  • components/devices/DeviceCard.vue - 设备卡片
  • components/devices/DeviceManageDialog.vue - 设备管理对话框

可见性控制

  • components/links/VisibilitySelector.vue - 可见性选择器
  • components/links/VisibilityBadge.vue - 可见性徽章

7. 浏览器扩展增强

7.1 新增核心功能

自动同步 (background/sync.js):

  • 监听 chrome.bookmarks.onCreated 事件
  • 提取书签信息标题、URL、图标、文件夹路径
  • 文件夹路径转标签(如:「工作/项目A」→ ["工作", "项目A"]
  • 调用云端 API 保存书签
  • 显示同步成功/失败通知

新标签页替换:

// manifest.json
{
  "chrome_url_overrides": {
    "newtab": "app/index.html"
  }
}

全局快捷键搜索 (content/search-overlay.js):

  • Content Script 注入所有页面
  • 监听快捷键Alt+K 或自定义)
  • 浮层搜索框(遮罩层 + 搜索输入 + 结果列表)
  • 调用 API 实时搜索
  • 键盘导航(上下键、回车打开)

一键收藏 (popup/quick-save.js):

  • Popup 中的快速收藏表单
  • 自动填充当前页标题和 URL
  • 可编辑标签、可见性
  • 可选择是否同时保存到浏览器书签
  • 收藏成功提示

7.2 认证集成

  • 使用 chrome.storage.local 存储 JWT Token 和设备 ID
  • API 请求时自动携带 Token
  • Token 过期时自动刷新或提示登录
  • 未登录时提示用户登录

8. 后台管理系统

8.1 技术栈与项目结构

技术栈: Vue 3 + Vite + Vue Router + Pinia + Element Plus + Axios

admin-panel/
├── src/
│   ├── views/
│   │   ├── Dashboard.vue          # 仪表盘
│   │   ├── users/
│   │   │   ├── UserList.vue       # 用户列表
│   │   │   └── UserDetail.vue     # 用户详情
│   │   ├── devices/
│   │   │   └── DeviceList.vue     # 设备管理
│   │   └── bookmarks/
│   │       └── BookmarkList.vue   # 书签管理
│   ├── components/
│   ├── stores/
│   │   └── admin.js               # 管理员状态
│   ├── services/
│   │   └── adminApi.js            # 后台 API
│   └── router/
│       └── index.js
├── package.json
└── vite.config.js

8.2 核心功能页面

仪表盘:

  • 关键指标卡片(用户总数、活跃用户、书签总数、设备总数)
  • 趋势图表P2 优先级,使用 ECharts

用户管理:

  • 用户列表(分页、搜索、筛选)
  • 禁用/启用用户操作
  • 查看用户详情(基本信息、设备列表、书签统计)
  • 重置密码P1

设备管理:

  • 查看用户的所有设备
  • 强制设备下线P1
  • 禁用设备P1

书签管理:

  • 查看用户的书签列表(分页、搜索)
  • 按可见性筛选
  • 删除违规书签P1

9. 数据库迁移与种子数据

9.1 CodeFirst 自动同步

// Program.cs
var freeSql = new FreeSqlBuilder()
    .UseConnectionString(dataType, connectionString)
    .UseAutoSyncStructure(true)  // 自动同步实体结构到数据库
    .Build();

// 首次运行时自动创建表和索引

9.2 种子数据

// 创建超级管理员账号
public static async Task SeedDataAsync(IFreeSql freeSql)
{
    var adminExists = await freeSql.Select<User>()
        .Where(u => u.Email == "admin@bookmarks.local")
        .AnyAsync();
    
    if (!adminExists)
    {
        var admin = new User
        {
            Id = Guid.NewGuid(),
            Email = "admin@bookmarks.local",
            UserName = "Administrator",
            PasswordHash = BCrypt.Net.BCrypt.HashPassword("Admin@123"),
            Status = UserStatus.Normal,
            CreatedAt = DateTime.UtcNow
        };
        
        await freeSql.Insert(admin).ExecuteAffrowsAsync();
    }
}

10. 开发指南

10.1 环境搭建

后端环境:

  • .NET 8 SDK
  • SQL Server 2019+ 或 PostgreSQL 14+
  • Visual Studio 2022 / Rider / VS Code + C# 扩展
  • Postman / Swagger UI

前端环境:

  • Node.js 20+
  • npm 8+
  • Chrome 90+

10.2 本地开发流程

# 1. 克隆项目
git clone <repository>

# 2. 后端启动
cd backend-api
dotnet restore
dotnet run
# 首次运行会自动创建数据库表

# 3. 前端启动
cd bookmark-spa
npm install
npm run dev

# 4. 后台管理启动
cd admin-panel
npm install
npm run dev

# 5. 浏览器扩展
cd bookmark-spa
npm run build
cp -r dist/* ../browser-extension/app/
# 在 Chrome 中加载扩展

10.3 FreeSql 常用操作示例

// 查询
var users = await _freeSql.Select<User>()
    .Where(u => u.Status == UserStatus.Normal)
    .Page(1, 20)
    .ToListAsync();

// 插入
var user = new User { /* ... */ };
await _freeSql.Insert(user).ExecuteAffrowsAsync();

// 更新
await _freeSql.Update<User>()
    .Set(u => u.LastLoginAt, DateTime.UtcNow)
    .Where(u => u.Id == userId)
    .ExecuteAffrowsAsync();

// 删除
await _freeSql.Delete<User>()
    .Where(u => u.Id == userId)
    .ExecuteAffrowsAsync();

// 事务
using var uow = _freeSql.CreateUnitOfWork();
try
{
    var repo = uow.GetRepository<Bookmark>();
    await repo.InsertAsync(bookmark);
    uow.Commit();
}
catch
{
    uow.Rollback();
    throw;
}

11. 部署指南

11.1 后端部署

Docker 容器化:

FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY publish/ .
EXPOSE 80
ENTRYPOINT ["dotnet", "BookmarkApi.dll"]

环境变量:

  • ConnectionStrings__DefaultConnection
  • DatabaseProvider (SqlServer / PostgreSQL)
  • JwtSettings__SecretKey
  • JwtSettings__Issuer
  • JwtSettings__Audience
  • JwtSettings__ExpiryMinutes

11.2 前端部署

  • Bookmark SPA: Nginx / Vercel / Netlify
  • Admin Panel: Nginx需配置访问控制
  • Browser Extension: Chrome Web Store

11.3 数据库部署

  • SQL Server: Azure SQL / 自托管
  • PostgreSQL: Azure PostgreSQL / AWS RDS / 自托管
  • 定期备份策略

12. 安全实践

12.1 认证与授权

  • 密码使用 BCrypt 或 Argon2 哈希
  • JWT Token 有效期设置1 小时)
  • Refresh Token 机制7 天有效期)
  • 设备绑定Token 包含设备 ID

12.2 API 安全

  • HTTPS 强制传输
  • CORS 跨域配置
  • 速率限制ASP.NET Core Rate Limiting
  • FreeSql 自动参数化防止 SQL 注入
  • 输入验证和输出编码防止 XSS

13. 测试策略

13.1 后端测试

  • 单元测试: xUnit + Moq
  • FreeSql 内存数据库测试
  • API 集成测试: WebApplicationFactory
[Fact]
public async Task GetBookmarks_ReturnsUserBookmarks()
{
    // Arrange
    var freeSql = new FreeSqlBuilder()
        .UseConnectionString(DataType.Sqlite, ":memory:")
        .Build();
    
    // Act & Assert
}

13.2 前端测试

  • 单元测试: Vitest
  • E2E 测试: Playwright包含登录流程

v2.0 实际实现情况

P0 核心功能(已全部完成)

后端 API

  • ASP.NET Core 8 项目搭建
  • FreeSql 配置与实体定义User、Device、Bookmark、RefreshToken
  • JWT 认证中间件(包含 RefreshToken 机制)
  • 用户注册/登录 API含设备自动注册
  • 书签 CRUD API含搜索、批量删除、可见性控制
  • 设备管理 API重命名、删除、设置管理员
  • 标签管理 API
  • 后台管理 API用户管理、设备管理、统计数据

前端应用

  • Vue 3 + TypeScript + Pinia
  • 登录/注册界面
  • JWT Token 管理(自动刷新)
  • 书签列表迁移到 API 调用
  • 设备管理界面
  • 可见性控制界面
  • 全局搜索组件

浏览器扩展

  • 用户认证集成
  • Token 存储与管理
  • 书签自动同步(监听 chrome.bookmarks.onCreated
  • 文件夹路径转标签
  • URL 重复检测
  • 同步状态通知

后台管理系统

  • Vue 3 + Element Plus
  • 仪表盘统计(用户数、活跃度、书签数、设备数)
  • 用户管理(列表、详情、禁用/启用、重置密码)
  • 设备管理(查看、强制下线、禁用)
  • 书签管理(查看用户书签、删除)
  • 权限控制Admin/SuperAdmin

📋 P2-P3 增强功能v3.0 规划)

扩展增强

  • 📋 新标签页替换为书签应用
  • 📋 全局快捷键搜索(任意页面触发)
  • 📋 右键菜单添加书签
  • 📋 Popup 快速收藏

高级功能

  • 📋 统计报表和趋势图
  • 📋 标签管理增强(合并、排序)
  • 📋 邮箱验证
  • 📋 数据导出功能

已实现的 API 接口清单

认证接口 (/api/auth)

方法 路径 描述 状态
POST /register 用户注册
POST /login 用户登录(含设备注册)
POST /logout 退出登录
POST /refresh 刷新 Access Token

用户接口 (/api/user)

方法 路径 描述 状态
GET /profile 获取用户信息
PUT /profile 更新用户信息
PUT /password 修改密码

设备接口 (/api/devices)

方法 路径 描述 状态
GET / 获取设备列表
GET /{id} 获取设备详情
PUT /{id} 更新设备名称
DELETE /{id} 删除设备
PUT /{id}/admin 设置/取消管理员设备

书签接口 (/api/bookmarks)

方法 路径 描述 状态
GET / 获取书签列表(自动过滤可见性)
GET /search 搜索书签
GET /{id} 获取书签详情
POST / 新增书签
PUT /{id} 更新书签
DELETE /{id} 删除书签
POST /batch/delete 批量删除
PUT /{id}/visibility 设置可见性
POST /{id}/visit 记录访问
PUT /{id}/order 更新排序
GET /check-url 检查 URL 是否存在
POST /import 导入书签
GET /export 导出书签

标签接口 (/api/tags)

方法 路径 描述 状态
GET / 获取标签列表及统计
PUT /{name} 重命名标签
DELETE /{name} 删除标签

后台管理接口 (/api/admin)

方法 路径 描述 状态
GET /statistics 获取系统统计数据
GET /users 获取用户列表(分页、搜索、筛选)
GET /users/{id} 获取用户详情
PUT /users/{id}/status 禁用/启用用户
POST /users/{id}/reset-password 重置用户密码
GET /users/{id}/bookmarks 获取用户书签列表
DELETE /users/{userId}/bookmarks/{bookmarkId} 删除用户书签
POST /devices/{id}/force-logout 强制设备下线
PUT /devices/{id}/status 禁用/启用设备

部署说明

环境要求

后端环境

  • .NET 8 SDK
  • SQL Server 2019+ 或 PostgreSQL 14+
  • 依赖包:
    • FreeSql (ORM)
    • BCrypt.Net-Next (密码加密)
    • System.IdentityModel.Tokens.Jwt (JWT)
    • Swashbuckle.AspNetCore (API 文档)

前端环境

  • Node.js 20+
  • Vue 3 + TypeScript
  • Pinia
  • Element Plus

浏览器扩展

  • Chrome 90+ (Manifest V3)

配置说明

appsettings.json

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.;Database=BookmarkDb;..."
  },
  "DatabaseProvider": "SqlServer",
  "JwtSettings": {
    "SecretKey": "your-256-bit-secret",
    "Issuer": "BookmarkApi",
    "Audience": "BookmarkClient",
    "ExpiryMinutes": 60,
    "RefreshTokenDays": 7
  }
}

技术亮点

  1. JWT + RefreshToken 双令牌机制

    • Access Token 有效期 1 小时
    • Refresh Token 有效期 7 天
    • 自动刷新,无感登录体验
  2. 设备指纹识别

    • Canvas 指纹、屏幕分辨率、时区等多因子识别
    • 自动注册设备,支持设备管理
    • 管理员设备机制,灵活权限控制
  3. 书签可见性控制

    • Public所有设备可见
    • Private仅管理员设备可见
    • Specified仅指定设备可见
    • API 层自动过滤,确保数据安全
  4. 自动同步机制

    • 监听浏览器书签事件
    • 文件夹路径自动转标签
    • URL 重复检测
    • 同步状态实时反馈
  5. 后台管理系统

    • 完整的用户管理功能
    • 系统统计数据实时更新
    • 设备管理(强制下线、禁用)
    • 基于角色的权限控制
  6. TypeScript 支持

    • 前端全面使用 TypeScript
    • 类型安全,减少运行时错误
    • 更好的开发体验和代码维护性

后续优化建议

  1. 性能优化

    • 实现 Redis 缓存,提高 API 响应速度
    • 添加 CDN 支持前端资源
    • 数据库查询优化和索引优化
  2. 功能增强

    • 实现新标签页替换
    • 添加全局快捷键搜索
    • 支持邮箱验证
    • 实现数据备份和恢复
  3. 监控与日志

    • 添加结构化日志
    • 实现监控告警
    • 错误追踪和性能分析
  4. 安全加固

    • 实现 2FA 双因素认证
    • 添加操作日志审计
    • API 速率限制增强
    • XSS 和 CSRF 防护加强

v2.0 开发完成时间: 2025-12-15

代码位置: /src/ 目录

  • 后端: /src/backend/
  • 前端: /src/frontend/
  • 扩展: /src/extension/
  • 后台管理: /src/admin/