档案管理系统社区案例:SpringBoot+Vue前后端分离实战指南
一、项目环境与工具准备
我们将基于SpringBoot 2.7.15和Vue 3.3.4构建一个包含用户管理、档案分类、文件上传与检索功能的社区档案管理系统。
1.1 开发环境安装
首先安装以下核心工具,所有命令在Ubuntu 22.04或Windows WSL2下执行:
- JDK 17:
sudo apt install openjdk-17-jdk - Maven 3.8+:
sudo apt install maven - Node.js 18+:
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - && sudo apt install nodejs
验证安装:java -version 应显示17.x,node -v 应显示18.x。
1.2 数据库初始化
使用MySQL 8.0创建数据库,执行以下SQL:
```sql CREATE DATABASE archive_community DEFAULT CHARSET utf8mb4; USE archive_community; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(255) NOT NULL, real_name VARCHAR(50), role ENUM('admin', 'user') DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE archive_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, parent_id INT DEFAULT 0, sort_order INT DEFAULT 0, FOREIGN KEY (parent_id) REFERENCES archive_category(id) ON DELETE CASCADE ); CREATE TABLE archive_files ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, category_id INT, file_path VARCHAR(500) NOT NULL, uploader_id INT, keywords TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES archive_category(id), FOREIGN KEY (uploader_id) REFERENCES users(id) ); ```二、后端SpringBoot服务搭建
2.1 项目初始化与依赖配置
使用Spring Initializr创建项目,或直接复制以下pom.xml核心依赖:
```xml2.2 数据库与文件存储配置
在application.yml中配置:
```yaml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/archive_community?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true servlet: multipart: max-file-size: 50MB max-request-size: 100MB archive: upload-dir: /data/archive_files/ ```2.3 核心实体类与JWT认证
创建用户实体类User.java:
```java @Entity @Data @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(unique = true, nullable = false) private String username; @Column(nullable = false) private String password; private String realName; @Enumerated(EnumType.STRING) private UserRole role; @CreationTimestamp private LocalDateTime createdAt; } ```创建JWT工具类JwtUtil.java:
```java @Component public class JwtUtil { private final String SECRET_KEY = "your-256-bit-secret-key-change-in-production"; public String generateToken(String username) { return Jwts.builder() .setSubject(username) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 1000 60 60 24)) .signWith(SignatureAlgorithm.HS256, SECRET_KEY) .compact(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } } ```2.4 文件上传控制器实现
创建FileUploadController.java:
```java @RestController @RequestMapping("/api/archive") public class FileUploadController { @Value("${archive.upload-dir}") private String uploadDir; @PostMapping("/upload") public ResponseEntity> uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryId") Integer categoryId, @RequestParam("keywords") String keywords) { try { // 创建上传目录 File uploadPath = new File(uploadDir); if (!uploadPath.exists()) { uploadPath.mkdirs(); } // 生成唯一文件名 String originalFilename = file.getOriginalFilename(); String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".")); String uniqueFilename = UUID.randomUUID().toString() + fileExtension; // 保存文件 Path filePath = Paths.get(uploadDir + uniqueFilename); Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); // 保存到数据库 ArchiveFile archiveFile = new ArchiveFile(); archiveFile.setTitle(originalFilename); archiveFile.setFilePath(uniqueFilename); archiveFile.setCategoryId(categoryId); archiveFile.setKeywords(keywords); archiveFile.setUploaderId(getCurrentUserId()); archiveFileRepository.save(archiveFile); return ResponseEntity.ok(Map.of("message", "文件上传成功", "fileId", archiveFile.getId())); } catch (IOException e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(Map.of("error", "文件上传失败: " + e.getMessage())); } } @GetMapping("/download/{fileId}") public ResponseEntity三、前端Vue管理系统开发
3.1 Vue项目初始化与依赖
创建Vue项目并安装必要依赖:
```bash npm create vue@latest archive-frontend cd archive-frontend npm install axios element-plus vue-router@4 pinia ```
配置main.js:
```javascript import { createApp } from 'vue' import App from './App.vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import router from './router' import { createPinia } from 'pinia' const app = createApp(App) app.use(ElementPlus) app.use(router) app.use(createPinia()) app.mount('app') ```3.2 路由与状态管理配置
创建路由文件router/index.js:
```javascript import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/', component: () => import('@/views/Login.vue') }, { path: '/dashboard', component: () => import('@/views/Dashboard.vue'), meta: { requiresAuth: true } }, { path: '/archive/upload', component: () => import('@/views/UploadArchive.vue'), meta: { requiresAuth: true } } ] const router = createRouter({ history: createWebHistory(), routes }) // 路由守卫:检查登录状态 router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.meta.requiresAuth && !token) { next('/') } else { next() } }) export default router ```3.3 文件上传组件实现
创建UploadArchive.vue组件:
```vue拖拽文件到此处或点击上传
3.4 API请求封装
创建utils/axios.js:
```javascript import axios from 'axios' import { ElMessage } from 'element-plus' const instance = axios.create({ baseURL: 'http://localhost:8080/api', timeout: 10000 }) // 请求拦截器:添加token instance.interceptors.request.use( config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, error => { return Promise.reject(error) } ) // 响应拦截器:统一处理错误 instance.interceptors.response.use( response => { return response.data }, error => { if (error.response?.status === 401) { localStorage.removeItem('token') window.location.href = '/' ElMessage.error('登录已过期,请重新登录') } else { ElMessage.error(error.response?.data?.message || '请求失败') } return Promise.reject(error) } ) export default instance ```四、系统部署与优化
4.1 后端打包与部署
在SpringBoot项目根目录执行:
```bash mvn clean package -DskipTests ```生成target/archive-system-0.0.1-SNAPSHOT.jar后,使用以下命令启动:
```bash nohup java -jar archive-system-0.0.1-SNAPSHOT.jar \ --spring.profiles.active=prod \ --server.port=8080 \ > app.log 2>&1 & ```4.2 前端构建与Nginx配置
构建前端生产版本:
```bash npm run build ```配置Nginx/etc/nginx/sites-available/archive:
```nginx server { listen 80; server_name archive.yourdomain.com; location / { root /var/www/archive-frontend/dist; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://localhost:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /uploads/ { alias /data/archive_files/; expires 30d; } } ```4.3 数据库索引优化
为提升查询性能,添加以下索引:
```sql -- 档案文件查询优化 CREATE INDEX idx_archive_category ON archive_files(category_id); CREATE INDEX idx_archive_uploader ON archive_files(uploader_id); CREATE FULLTEXT INDEX idx_archive_keywords ON archive_files(keywords); -- 用户登录优化 CREATE INDEX idx_user_username ON users(username); ```4.4 文件存储安全配置
创建文件上传安全策略类FileSecurityConfig.java:
```java @Configuration public class FileSecurityConfig { @Bean public ServletRegistrationBean