这个基于SpringBoot+Vue3+MyBatis的在线文档管理系统,是我在2022年为某中型科技公司实施的知识管理项目核心成果。当时客户面临着研发文档分散在多个网盘、版本混乱、协作效率低下的痛点,我们通过前后端分离架构打造的这套系统,最终实现了文档集中管理、版本控制、在线协作等核心功能,将团队文档处理效率提升了60%以上。
选择SpringBoot+Vue3+MyBatis这套技术栈并非偶然。SpringBoot提供了快速构建后端服务的脚手架,其自动配置特性让我们能在两周内完成核心API开发;Vue3的Composition API相比Options API更适合复杂前端状态管理;而MyBatis的灵活SQL编写能力则完美适配了文档管理系统多变的数据查询需求。MySQL作为成熟的关系型数据库,在事务完整性和查询性能上都能满足中小规模文档管理的需求。
我们采用的前后端分离架构,将系统清晰地划分为三个逻辑层:
这种架构的最大优势在于并行开发能力。在项目初期,前后端团队只需约定好API文档(我们使用Swagger UI),就可以各自开展工作。前端团队可以先用Mock数据开发界面,不必等待后端接口完成。
实际开发中我们遇到过跨域问题,解决方案是在SpringBoot中配置CorsFilter:
java复制@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); }
文档二进制文件我们采用分块存储策略:
这种混合存储方案既保证了小文件的存取效率,又避免了大文件对数据库的压力。关键代码片段:
java复制// 文件上传处理逻辑
public String uploadFile(MultipartFile file) {
if(file.getSize() > 10 * 1024 * 1024) {
// 大文件存储到文件系统
String path = "/docs/"+UUID.randomUUID();
file.transferTo(new File(path));
return path;
} else {
// 小文件存入数据库
Document doc = new Document();
doc.setContent(file.getBytes());
documentMapper.insert(doc);
return "db:"+doc.getId();
}
}
我们参考Git的版本控制思路,设计了轻量级的版本管理系统:
数据库表设计如下:
| 字段名 | 类型 | 描述 |
|---|---|---|
| id | BIGINT | 主键 |
| doc_id | BIGINT | 文档ID |
| version | INT | 版本号 |
| delta_content | TEXT | 差异内容 |
| prev_version | INT | 前一版本号 |
| created_at | DATETIME | 创建时间 |
我们充分利用Vue3的Composition API重构了文档编辑器组件:
javascript复制// 文档编辑组件
import { ref, computed } from 'vue'
import { useDocumentStore } from '@/stores/document'
export default {
setup() {
const documentStore = useDocumentStore()
const content = ref('')
// 自动保存逻辑
const autoSave = computed(() => {
return documentStore.autoSaveEnabled ?
debounce(documentStore.save, 3000) : null
})
// 内容变化处理
const handleChange = (newContent) => {
content.value = newContent
autoSave.value?.()
}
return { content, handleChange }
}
}
这种组织方式让逻辑关注点更加集中,相比Vue2的Options API更利于复杂组件的维护。
针对文档列表页面的性能瓶颈,我们实施了以下优化:
优化前后性能对比:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| 首屏加载时间 | 2.8s | 1.2s |
| 内存占用 | 450MB | 210MB |
| 滚动流畅度 | 30fps | 60fps |
我们使用Profile机制管理不同环境配置:
yaml复制# application-dev.yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/docs_dev
username: devuser
password: dev123
# application-prod.yml
spring:
datasource:
url: jdbc:mysql://prod-db:3306/docs_prod
username: ${DB_USER}
password: ${DB_PASS}
通过spring.profiles.active参数指定激活的配置,配合Maven的profile实现构建时环境切换。
文档查询功能需要处理多种过滤条件组合,我们使用MyBatis的动态SQL特性:
xml复制<select id="selectDocuments" resultType="Document">
SELECT * FROM documents
<where>
<if test="title != null">
AND title LIKE CONCAT('%', #{title}, '%')
</if>
<if test="authorId != null">
AND author_id = #{authorId}
</if>
<if test="startDate != null and endDate != null">
AND created_at BETWEEN #{startDate} AND #{endDate}
</if>
</where>
ORDER BY ${orderBy} ${orderDir}
</select>
这种写法避免了拼接SQL字符串的安全风险,同时保持了查询的灵活性。
我们实现了标准的RBAC(基于角色的访问控制)模型:
数据库关系图简化如下:
code复制users --< user_roles >-- roles --< role_permissions >-- permissions
认证流程采用JWT方案:
关键安全配置:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
我们使用Docker Compose编排服务:
yaml复制version: '3'
services:
backend:
build: ./backend
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- db
frontend:
build: ./frontend
ports:
- "80:80"
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: docs
MYSQL_USER: docsuser
MYSQL_PASSWORD: docspass
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
为保障系统稳定运行,我们集成了:
关键监控指标包括:
现象:上传超过100MB文件时经常超时失败
排查过程:
解决方案:
properties复制# application.properties
spring.servlet.multipart.max-file-size=500MB
spring.servlet.multipart.max-request-size=500MB
server.connection-timeout=10m
现象:文档内容编辑后视图不更新
原因:直接对reactive对象赋值破坏了响应性
正确做法:
javascript复制// 错误方式
state.document = newDocument // 丢失响应性
// 正确方式
Object.assign(state.document, newDocument) // 保持响应性
在实际使用过程中,我们发现系统还可以在以下方面进行增强:
这套系统经过半年多的生产环境验证,目前稳定支持着200+用户的日常文档协作需求。最大的收获是认识到技术选型需要平衡团队熟悉度与技术先进性——最初考虑过使用GraphQL替代RESTful API,但考虑到团队学习成本最终选择了更成熟的方案。