实时协作文档管理系统 · 后端服务
DB-Document 是一个支持多人实时协作的在线文档管理系统后端。基于 Spring Boot 构建,采用 Y.js + WebSocket 实现类似 Google Docs 的实时同步编辑体验,配合缓冲池批量落库策略在高并发协作场景下大幅降低数据库 IO 压力。
🔗 前端仓库:Frontend · React + TipTap + Yjs
|
|
|
|
┌─────────────────────────────────────────────────────────┐
│ Client (Browser) │
│ React + TipTap + Yjs Provider │
└────────────┬──────────────┬──────────────┬──────────────┘
│ HTTP/REST │ WebSocket │
│ (JWT Auth) │ (Y.js Binary)│
▼ ▼ │
┌─────────────────────────────────────────────────────────┐
│ Spring Boot 3.5.8 │
│ │
│ ┌──────────┐ ┌──────────────┐ ┌───────────────────┐ │
│ │LoginInter│ │DocumentHand- │ │ DocumentSocket- │ │
│ │ceptor │ │shakeIntercep.│ │ Handler │ │
│ │(JWT校验) │ │(WS握手认证) │ │ (Y.js消息路由) │ │
│ └────┬─────┘ └──────┬───────┘ └────────┬──────────┘ │
│ │ │ │ │
│ ┌────▼───────────────▼───────────────────▼──────────┐ │
│ │ Controller 层 │ │
│ │ User · Document · Folder · Permission · DocUpdate │ │
│ │ · File │ │
│ └──────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────┐ │
│ │ AOP 切面层 │ │
│ │ PermissionAspect (权限校验) · LogAspect (操作日志) │ │
│ └──────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────┐ │
│ │ Service 业务层 │ │
│ │ DocUpdateBuffer ◄── 缓冲池批量落库引擎 │ │
│ │ (定时调度 + 容量触发 + 失败重试) │ │
│ └──────────────────────┬────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼────────────────────────────┐ │
│ │ MyBatis Mapper + XML 映射 │ │
│ │ 动态SQL · 驼峰映射 · 软删除 · 批量写入 │ │
│ └──────────────────────┬────────────────────────────┘ │
└─────────────────────────┼───────────────────────────────┘
│
┌───────────▼───────────┐
│ MySQL 8.0 │
│ Database: homework │
└───────────────────────┘
| 类别 | 技术 | 说明 |
|---|---|---|
| 框架 | Spring Boot 3.5.8 | Web + AOP + WebSocket + Validation |
| ORM | MyBatis 3.0.5 | XML 映射 + 动态 SQL + 批量操作 |
| 数据库 | MySQL 8.0 | 软删除模式 · 驼峰映射 |
| 认证 | JWT (jjwt 0.9.1) | HS512 签名 · 无状态 · Token 踢出 |
| 实时通信 | WebSocket | Y.js 二进制协议 · 房间隔离 · Awareness |
| 工具 | Lombok 1.18.30 | 消除样板代码 |
| 测试 | JUnit 5 · Mockito | 33 个测试文件 · 全模块覆盖 |
| 代码质量 | SonarQube · JaCoCo 0.8.13 | 静态分析 + 覆盖率报告 |
| 环境 | spring-dotenv 4.0.0 | .env 环境变量 |
| 运行时 | Java 17 · Maven 3.11.0 | -parameters 编译保留参数名 |
- JDK 17 或更高版本
- Maven 3.8+
- MySQL 8.0 运行中
git clone https://github.com/SaMuel-101-cky/Multi-person-collaborative-document-development.git创建 .env 文件或设置环境变量:
# 数据库配置
DB_USERNAME=root
DB_PASSWORD=your_password
# JWT 密钥(请使用强随机字符串)
JWT_SECRET_KEY=your_jwt_secret_key_here
JWT_EXPIRATION_TIME=86400000
# 文件上传目
FILE_UPLOAD_DIR=./Uploads/CREATE DATABASE IF NOT EXISTS homework DEFAULT CHARSET utf8mb4;数据库表结构由 MyBatis 自动建表或手动导入 SQL 脚本。
# 开发环境启动
mvn spring-boot:run
# 或构建后运行
mvn clean package -DskipTests
java -jar target/DB_document-0.0.1-SNAPSHOT.jar服务默认运行在 **http://localhost:8080**。
# 注册用户
curl -X POST http://localhost:8080/api/user/register \
-H "Content-Type: application/json" \
-d '{"account":"test","password":"123456","nickname":"测试用户"}'
# 登录获取 Token
curl -X POST http://localhost:8080/api/user/login \
-H "Content-Type: application/json" \
-d '{"account":"test","password":"123456"}'👤 用户模块 — /api/user
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /register |
❌ | 用户注册 |
| POST | /login |
❌ | 登录,返回 JWT(同时踢出旧会话) |
| GET | /ping |
✅ | Token 心跳检测 |
| GET | /me |
✅ | 获取当前用户信息 |
| GET | /detail/{userId} |
❌ | 查看用户公开名片 |
| GET | /search?nickname= |
✅ | 按昵称搜索用户 |
| POST | /update/avatar |
✅ | 更新头像 |
| POST | /update/info |
✅ | 更新昵称/简介 |
| POST | /change-password |
✅ | 修改密码 |
📄 文档模块 — /api/document
| 方法 | 路径 | 权限 | 说明 |
|---|---|---|---|
| POST | /create |
— | 创建文档 |
| DELETE | /delete/{id} |
OWNER | 删除文档 |
| POST | /move |
EDITOR | 移动文档到文件夹 |
| POST | /update/info |
EDITOR | 更新文档标题等信息 |
| GET | /detail/{id} |
VIEWER | 获取文档详情 |
| GET | /shared |
— | 获取共享文档列表 |
🔄 增量更新模块 — /api/docUpdate
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /create |
创建增量更新 |
| GET | /detail?documentId=&vectorClock= |
按向量时钟查询 |
| GET | /list/{documentId} |
文档全部更新记录 |
| GET | /children?documentId=&parentUpdateId= |
子更新列表 |
| POST | /update |
更新记录 |
| DELETE | /delete/{id} |
删除记录 |
📁 文件夹模块 — /api/folder
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /create |
创建文件夹 |
| DELETE | /delete/{id} |
删除文件夹 |
| POST | /move |
移动文件夹 |
| GET | /content |
个人文件夹内容 |
| GET | /shared-content |
共享文件夹内容 |
🔑 权限模块 — /api/permission
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /create |
向用户共享文档 |
| POST | /delete |
撤销用户权限 |
| GET | /document/{id} |
文档权限列表 |
📤 文件上传 — /api/file
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /upload/image |
上传图片(限速 10次/分钟) |
Client A Server Client B
│ │ │
│── WS Connect ──────►│◄──── WS Connect ────│
│ (docId, token) │ (docId, token) │
│ │ │
│── SyncStep1 [0,0]──►│ │
│ │── 历史更新回放 ──────►│
│ │── Awareness 广播 ───►│
│ │ │
│── SyncStep2 [0,2]──►│ │
│ (编辑内容) │── DocUpdateBuffer │
│ │ .enqueueUpdate() │
│ │── 广播给 Client B ──►│
│ │ │
│── Awareness [1] ───►│ │
│ (光标位置) │── 广播给 Client B ──►│
│ │ │
│ │── 定时/批量落库 ────► MySQL
WebSocket 消息
│
▼
DocUpdateBuffer.enqueueUpdate(docId, payload)
│
├── pending.size < 10 && 距上次 < 500ms
│ └── 加入 pending 队列,等待调度
│
├── pending.size ≥ 10
│ └── 立即 drain → 批量写入 DB
│
└── 距上次 ≥ 500ms
└── 定时任务触发 → 批量写入 DB
写入失败 → 消息与后续 pending 合并 → 重新调度
应用关闭 → @PreDestroy → 遍历所有文档 → 强制落库
HTTP Request
│
▼
LoginInterceptor.preHandle()
├── 解析 Authorization Header
├── 验证 JWT Token 有效性
├── 检测 Token 是否被踢出
└── UserContext.setUserId(userId)
│
▼
PermissionAspect.checkPermission() ← @RequirePermission 触发
├── 从方法参数提取 documentId
├── 查库获取用户在该文档的真实角色
├── actualRole.hasPermission(requiredRole)
└── 权限不足 → 抛出 RuntimeException
│
▼
Controller 方法执行
│
▼
LoginInterceptor.afterCompletion()
└── UserContext.remove() ← 防止内存泄漏
Backend/
├── src/main/java/com/example/db_document/
│ ├── annotation/ # @RequirePermission · @Log
│ ├── aspect/ # PermissionAspect · LogAspect
│ ├── config/ # WebConfig · WebSocketConfig · JwtConfig
│ ├── controller/ # 6 个 Controller,30+ 接口
│ ├── exception/ # BusinessException
│ ├── handler/ # GlobalExceptionHandler · DocumentSocketHandler
│ ├── interceptor/ # LoginInterceptor · DocumentHandshakeInterceptor
│ ├── mapper/ # 6 个 MyBatis Mapper 接口
│ ├── model/
│ │ ├── dto/ # 12 个请求 DTO
│ │ └── vo/ # 6 个响应 VO
│ ├── pojo/ # 8 个实体类(含 PermissionType 枚举)
│ ├── service/ # 9 个 Service(含 DocUpdateBuffer)
│ └── utils/ # JwtUtil · UserContext
├── src/main/resources/
│ ├── mapper/ # MyBatis XML 映射文件
│ └── application.yml # 主配置文件
├── src/test/java/ # 33 个测试文件
├── pom.xml
├── CLAUDE.md # AI 编程助手指南
└── README.md
# 运行全部测试
mvn test
# 运行单个测试类
mvn test -Dtest=DocumentServiceTest
# 生成覆盖率报告
mvn test
# 报告位置: target/site/jacoco/index.html测试覆盖分布:
Controller (5) ████████░░ 测试 API 端点与参数校验
Service (9) ████████████ 测试业务逻辑与事务
Mapper (6) ████████░░ 测试 SQL 映射与查询
Aspect (2) ████░░░░░░ 测试权限切面与日志切面
Handler (2) ████░░░░░░ 测试异常处理与 WebSocket
Interceptor(2) ████░░░░░░ 测试 JWT 校验与 WS 握手
Config (1) ██░░░░░░░░ 测试 CORS 与资源配置
Util/POJO (6) ████████░░ 测试工具方法与枚举逻辑
# application.yml 关键配置项
jwt:
secret-key: ${JWT_SECRET_KEY} # JWT 签名密钥(环境变量)
expiration-time: 86400000 # Token 过期时间 24h
doc-update:
flush-interval-ms: 500 # 缓冲池落库间隔
max-batch-size: 10 # 最大批次数量
file:
upload-dir: ./Uploads/ # 上传目录
allowed-types: image/jpeg,image/png,image/gif,image/webp
max-size: 5242880 # 5MB
spring:
servlet.multipart.max-file-size: 5MB
servlet.multipart.max-request-size: 5MB
mybatis:
type-aliases-package: com.example.db_document.pojo
mapper-locations: classpath:mapper/*.xml
configuration.map-underscore-to-camel-case: true
Built with ❤️ using Spring Boot · MyBatis · WebSocket
📝 DB-Document © 2025