29 KiB
v2.0 开发文档
版本: 2.0.0 状态: ✅ 已完成 最后更新: 2025-12-15 文档类型: 技术开发文档
文档概述
本文档详细记录了书签管理系统 v2.0 的完整技术实现,包括:
- 从 v1.0(本地单用户)到 v2.0(云端多用户)的架构升级
- 后端 API(ASP.NET Core + FreeSql)完整实现
- 前端应用(Vue 3 + TypeScript)技术栈
- 浏览器扩展自动同步机制
- 后台管理系统实现细节
核心任务完成情况
✅ 已完成功能
后端 API(100%)
- 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- 刷新 TokenPOST /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__DefaultConnectionDatabaseProvider(SqlServer / PostgreSQL)JwtSettings__SecretKeyJwtSettings__IssuerJwtSettings__AudienceJwtSettings__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
}
}
技术亮点
-
JWT + RefreshToken 双令牌机制
- Access Token 有效期 1 小时
- Refresh Token 有效期 7 天
- 自动刷新,无感登录体验
-
设备指纹识别
- Canvas 指纹、屏幕分辨率、时区等多因子识别
- 自动注册设备,支持设备管理
- 管理员设备机制,灵活权限控制
-
书签可见性控制
- Public:所有设备可见
- Private:仅管理员设备可见
- Specified:仅指定设备可见
- API 层自动过滤,确保数据安全
-
自动同步机制
- 监听浏览器书签事件
- 文件夹路径自动转标签
- URL 重复检测
- 同步状态实时反馈
-
后台管理系统
- 完整的用户管理功能
- 系统统计数据实时更新
- 设备管理(强制下线、禁用)
- 基于角色的权限控制
-
TypeScript 支持
- 前端全面使用 TypeScript
- 类型安全,减少运行时错误
- 更好的开发体验和代码维护性
后续优化建议
-
性能优化
- 实现 Redis 缓存,提高 API 响应速度
- 添加 CDN 支持前端资源
- 数据库查询优化和索引优化
-
功能增强
- 实现新标签页替换
- 添加全局快捷键搜索
- 支持邮箱验证
- 实现数据备份和恢复
-
监控与日志
- 添加结构化日志
- 实现监控告警
- 错误追踪和性能分析
-
安全加固
- 实现 2FA 双因素认证
- 添加操作日志审计
- API 速率限制增强
- XSS 和 CSRF 防护加强
v2.0 开发完成时间: 2025-12-15
代码位置:
/src/目录
- 后端:
/src/backend/- 前端:
/src/frontend/- 扩展:
/src/extension/- 后台管理:
/src/admin/