基于Spring Boot与Vue的实验室管理系统全栈开发实战
2026/8/21 9:36:02 网站建设 项目流程

实验室管理系统是高校、科研院所及企业研发部门进行日常运营管理的重要工具,然而,许多团队在项目初期常面临系统选型困难、技术栈整合复杂、开发周期长等问题。本文将以一个完整的“实验室管理系统”毕业设计/实战项目为例,手把手带你从零开始,整合 Java、Vue、Spring Boot 和 MySQL 四大主流技术,构建一个功能完备、前后端分离的管理系统。无论你是正在寻找毕业设计课题的学生,还是希望快速搭建内部管理平台的后端开发者,都能从本文获得一套可直接复用的完整解决方案,涵盖环境搭建、核心功能实现、数据库设计到部署上线的全流程。

1. 项目背景与核心需求分析

实验室管理系统旨在通过信息化手段,解决传统实验室管理中存在的效率低下、数据分散、流程不透明等问题。一个典型的管理系统需要覆盖人员、设备、耗材、预约、安全等多个维度。

1.1 系统核心功能模块

基于常见的实验室管理场景,我们将系统拆解为以下几个核心模块:

  • 用户权限管理:实现不同角色(如系统管理员、实验室主任、教师、学生)的登录、注册、权限分配与菜单动态加载。
  • 实验室信息管理:对实验室的基本信息(位置、容量、负责人、状态)进行增删改查。
  • 设备资产管理:登记实验室内的仪器设备,记录其名称、型号、编号、状态(在用、维修、报废)、借还记录等。
  • 实验耗材管理:管理耗材的入库、领用、库存预警,实现库存盘点与消耗统计。
  • 实验室预约管理:用户可在线查看实验室空闲时段并提交预约申请,管理员进行审核与调度。
  • 数据统计与报表:基于上述数据,生成设备使用率、耗材消耗趋势、实验室利用率等可视化报表。

1.2 技术选型与架构优势

本项目采用前后端分离的架构,这是当前企业级应用开发的主流模式。

  • 后端(Java + Spring Boot):Spring Boot 极大地简化了 Spring 应用的初始搭建和开发过程,内嵌 Tomcat,支持快速构建 RESTful API。它提供了强大的依赖管理、自动配置和监控功能,非常适合作为本项目的后端框架。
  • 前端(Vue.js):Vue 是一套用于构建用户界面的渐进式框架,核心库只关注视图层,易于上手且便于与第三方库或既有项目整合。配合 Vue Router、Vuex 和 Axios,可以轻松构建复杂的单页面应用(SPA),提供流畅的用户体验。
  • 数据库(MySQL):作为最流行的开源关系型数据库之一,MySQL 性能稳定、社区活跃、文档齐全,完全能够满足实验室管理系统在事务处理、数据一致性和复杂查询方面的需求。
  • 项目构建与管理:后端使用 Maven 进行依赖管理和项目构建,前端使用 npm 或 yarn。

这种架构的优势在于前后端职责分离,后端专注于业务逻辑和数据处理,前端专注于交互和展示,二者通过清晰的 API 接口进行通信,提高了开发效率和系统的可维护性。

2. 开发环境与工具准备

在开始编码之前,请确保你的本地开发环境已就绪。以下版本为本文演示所用,你可以根据实际情况进行调整。

2.1 后端环境准备

  1. JDK:版本 8 或 11(推荐 11)。Spring Boot 2.x 对 JDK 8+ 有良好支持。
  2. IDE:IntelliJ IDEA(社区版或旗舰版)或 Eclipse(需安装 Spring Tools 插件)。本文示例使用 IntelliJ IDEA。
  3. 构建工具:Apache Maven 3.6+。用于管理项目依赖和构建。
  4. 数据库客户端:Navicat、DBeaver 或 MySQL Workbench,用于连接和操作 MySQL 数据库。

2.2 前端环境准备

  1. Node.js:版本 14.x 或 16.x。npm 包管理器会随之安装。
  2. IDE:Visual Studio Code(推荐)或 WebStorm。
  3. Vue 脚手架:Vue CLI 4.x 或 5.x。用于快速初始化 Vue 项目。

2.3 数据库环境准备

  1. MySQL Server:版本 5.7 或 8.0。建议使用 8.0 以获得更好的性能和安全性。
  2. 安装后,请确保 MySQL 服务已启动,并记住 root 用户的密码。

3. 数据库设计与建模

良好的数据库设计是系统稳定运行的基石。我们根据功能模块,设计核心数据表。

3.1 核心表结构设计

以下是几个关键表的结构示例(仅展示部分核心字段):

用户表 (sys_user)

CREATE TABLE `sys_user` ( `user_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码', `real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `avatar` varchar(200) DEFAULT NULL COMMENT '头像', `status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)', `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 1代表删除)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`user_id`), UNIQUE KEY `uniq_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';

角色表 (sys_role) 与 用户角色关联表 (sys_user_role)

CREATE TABLE `sys_role` ( `role_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '角色ID', `role_name` varchar(30) NOT NULL COMMENT '角色名称', `role_key` varchar(100) NOT NULL COMMENT '角色权限字符串', `role_sort` int(4) NOT NULL COMMENT '显示顺序', `status` char(1) NOT NULL COMMENT '角色状态(0正常 1停用)', PRIMARY KEY (`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色信息表'; CREATE TABLE `sys_user_role` ( `user_id` bigint(20) NOT NULL COMMENT '用户ID', `role_id` bigint(20) NOT NULL COMMENT '角色ID', PRIMARY KEY (`user_id`,`role_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和角色关联表';

实验室表 (lab_laboratory)

CREATE TABLE `lab_laboratory` ( `lab_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '实验室ID', `lab_name` varchar(100) NOT NULL COMMENT '实验室名称', `lab_location` varchar(200) DEFAULT NULL COMMENT '实验室位置', `capacity` int(11) DEFAULT NULL COMMENT '可容纳人数', `principal_id` bigint(20) DEFAULT NULL COMMENT '负责人ID(关联sys_user)', `status` varchar(20) DEFAULT '空闲' COMMENT '状态(空闲、使用中、维护中)', `description` text COMMENT '描述', PRIMARY KEY (`lab_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='实验室信息表';

设备表 (lab_equipment)

CREATE TABLE `lab_equipment` ( `equipment_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '设备ID', `equipment_name` varchar(100) NOT NULL COMMENT '设备名称', `model` varchar(100) DEFAULT NULL COMMENT '型号', `serial_number` varchar(100) DEFAULT NULL COMMENT '序列号', `lab_id` bigint(20) DEFAULT NULL COMMENT '所属实验室ID', `status` varchar(20) DEFAULT '正常' COMMENT '状态(正常、借用中、维修中、报废)', `purchase_date` date DEFAULT NULL COMMENT '购买日期', `warranty_period` int(11) DEFAULT NULL COMMENT '保修期(月)', PRIMARY KEY (`equipment_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备信息表';

其他如耗材表 (lab_consumable)、预约记录表 (lab_reservation) 等可根据上述模式类似设计。

3.2 表关系与索引优化

  • 关系sys_usersys_role是多对多关系,通过sys_user_role关联。lab_laboratorylab_equipment是一对多关系。lab_reservation会关联lab_laboratorysys_user
  • 索引:在经常用于查询条件的字段上建立索引,如sys_user.usernamelab_laboratory.lab_namelab_reservation.user_idlab_reservation.lab_id,可以大幅提升查询性能。
  • 字段设计:状态字段使用明确的字符串或枚举值;时间字段使用datetime类型;金额或精确数值考虑使用decimal;文本描述使用text

4. 后端Spring Boot项目搭建与核心实现

我们将使用 Spring Boot 来快速构建后端 REST API。

4.1 创建Spring Boot项目

使用 IntelliJ IDEA 的 Spring Initializr 或访问 start.spring.io 在线生成项目。

  • Project: Maven
  • Language: Java
  • Spring Boot: 2.7.x (稳定版本)
  • Group: com.lab
  • Artifact: lab-management-system
  • Dependencies: 选择Spring Web,Spring Data JPA,MySQL Driver,Lombok

生成并导入项目后,pom.xml核心依赖如下:

<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

4.2 配置数据库与JPA

src/main/resources/application.yml中配置数据源和 JPA 属性:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/lab_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password # 替换为你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update # 开发环境可用update,生产环境建议使用validate或none,配合SQL脚本 show-sql: true # 控制台显示SQL,便于调试 properties: hibernate: dialect: org.hibernate.dialect.MySQL8Dialect open-in-view: false # 自定义配置示例 lab: jwt: secret: your-jwt-secret-key-here # JWT密钥 expiration: 86400000 # token有效期24小时

4.3 实现实体类、Repository与Service

User实体为例,展示三层架构的核心代码。

1. 实体类 (Entity)

// 文件路径:src/main/java/com/lab/system/entity/SysUser.java package com.lab.system.entity; import lombok.Data; import javax.persistence.*; import java.time.LocalDateTime; import java.util.List; @Entity @Data @Table(name = "sys_user") public class SysUser { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long userId; @Column(unique = true, nullable = false, length = 50) private String username; @Column(nullable = false, length = 100) private String password; private String realName; private String email; private String phone; private String avatar; @Column(columnDefinition = "char(1) default '0'") private String status; // '0'-正常, '1'-停用 @Column(columnDefinition = "char(1) default '0'") private String delFlag; // '0'-未删除, '1'-已删除 private LocalDateTime createTime; private LocalDateTime updateTime; // 多对多关联角色 @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "sys_user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id")) private List<SysRole> roles; }

2. 数据访问层 (Repository)

// 文件路径:src/main/java/com/lab/system/repository/SysUserRepository.java package com.lab.system.repository; import com.lab.system.entity.SysUser; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.Optional; @Repository public interface SysUserRepository extends JpaRepository<SysUser, Long> { Optional<SysUser> findByUsername(String username); boolean existsByUsername(String username); }

3. 服务层 (Service)

// 文件路径:src/main/java/com/lab/system/service/UserService.java package com.lab.system.service; import com.lab.system.entity.SysUser; public interface UserService { SysUser register(SysUser user); String login(String username, String password); SysUser getUserInfo(Long userId); // 其他业务方法... } // 文件路径:src/main/java/com/lab/system/service/impl/UserServiceImpl.java package com.lab.system.service.impl; import com.lab.system.entity.SysUser; import com.lab.system.repository.SysUserRepository; import com.lab.system.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @Service @RequiredArgsConstructor public class UserServiceImpl implements UserService { private final SysUserRepository userRepository; private final PasswordEncoder passwordEncoder; // 需要配置Spring Security或单独注入Bean @Override public SysUser register(SysUser user) { if (userRepository.existsByUsername(user.getUsername())) { throw new RuntimeException("用户名已存在"); } // 加密密码 user.setPassword(passwordEncoder.encode(user.getPassword())); user.setCreateTime(LocalDateTime.now()); user.setUpdateTime(LocalDateTime.now()); user.setStatus("0"); user.setDelFlag("0"); return userRepository.save(user); } @Override public String login(String username, String password) { SysUser user = userRepository.findByUsername(username) .orElseThrow(() -> new RuntimeException("用户不存在")); if (!passwordEncoder.matches(password, user.getPassword())) { throw new RuntimeException("密码错误"); } if (!"0".equals(user.getStatus())) { throw new RuntimeException("账号已被停用"); } // 这里应生成并返回JWT Token,示例略 return "login-success-token-placeholder"; } // 其他方法实现... }

4.4 实现RESTful API控制器 (Controller)

// 文件路径:src/main/java/com/lab/system/controller/UserController.java package com.lab.system.controller; import com.lab.system.common.Result; import com.lab.system.entity.SysUser; import com.lab.system.service.UserService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @RestController @RequestMapping("/api/user") @RequiredArgsConstructor public class UserController { private final UserService userService; @PostMapping("/register") public Result<SysUser> register(@Valid @RequestBody SysUser user) { SysUser registeredUser = userService.register(user); return Result.success(registeredUser); } @PostMapping("/login") public Result<String> login(@RequestParam String username, @RequestParam String password) { String token = userService.login(username, password); return Result.success(token); } @GetMapping("/{userId}") public Result<SysUser> getUserInfo(@PathVariable Long userId) { SysUser user = userService.getUserInfo(userId); return Result.success(user); } }

其中,Result是一个通用的响应封装类,用于统一API返回格式。

5. 前端Vue项目搭建与页面开发

前端项目将使用 Vue CLI 创建,并配合 Element Plus UI 库和 Axios 进行开发。

5.1 创建Vue项目并安装依赖

# 使用 Vue CLI 创建项目 vue create lab-management-frontend # 选择 Vue 3 或 Vue 2 (本文以 Vue 3 为例) # 进入项目目录 cd lab-management-frontend # 安装 Element Plus 和 Axios npm install element-plus axios # 安装路由和状态管理库 npm install vue-router@4 vuex@4

5.2 配置Axios与API调用

src/utils/request.js中创建 Axios 实例,并配置请求拦截器(添加Token)和响应拦截器(处理错误)。

import axios from 'axios' import { ElMessage } from 'element-plus' import router from '../router' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 在 .env.development 等文件中配置 timeout: 5000 }) // 请求拦截器 service.interceptors.request.use( config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }, error => { console.log(error) return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response => { const res = response.data // 假设后端统一返回格式为 { code: 200, data: {}, msg: 'success' } if (res.code !== 200) { ElMessage.error(res.msg || 'Error') // 如果是未授权,跳转到登录页 if (res.code === 401) { router.push('/login') } return Promise.reject(new Error(res.msg || 'Error')) } else { return res } }, error => { console.log('err' + error) ElMessage.error(error.message) return Promise.reject(error) } ) export default service

5.3 实现用户登录页面

创建src/views/Login.vue组件。

<template> <div class="login-container"> <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> <h3 class="title">实验室管理系统</h3> <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="请输入用户名"> <template #prefix> <el-icon><User /></el-icon> </template> </el-input> </el-form-item> <el-form-item prop="password"> <el-input v-model="loginForm.password" type="password" placeholder="请输入密码" @keyup.enter="handleLogin"> <template #prefix> <el-icon><Lock /></el-icon> </template> </el-input> </el-form-item> <el-form-item> <el-button :loading="loading" type="primary" style="width:100%;" @click="handleLogin"> 登录 </el-button> </el-form-item> </el-form> </div> </template> <script setup> import { ref, reactive } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { User, Lock } from '@element-plus/icons-vue' import request from '@/utils/request' const router = useRouter() const loading = ref(false) const loginForm = reactive({ username: 'admin', password: 'admin123' }) const loginRules = { username: [{ required: true, trigger: 'blur', message: '请输入用户名' }], password: [{ required: true, trigger: 'blur', message: '请输入密码' }] } const handleLogin = () => { loading.value = true request.post('/api/user/login', null, { params: { username: loginForm.username, password: loginForm.password } }).then(response => { const token = response.data localStorage.setItem('token', token) ElMessage.success('登录成功') router.push('/') }).catch(error => { console.error(error) loading.value = false }) } </script> <style scoped> .login-container { height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #2d3a4b; } .login-form { width: 400px; padding: 35px; background: #fff; border-radius: 6px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .title { margin: 0 auto 30px; text-align: center; color: #707070; } </style>

5.4 实现实验室管理页面

创建src/views/lab/LabList.vue组件,展示实验室列表并提供增删改查操作。

<template> <div class="lab-management"> <el-card> <template #header> <div class="card-header"> <span>实验室管理</span> <el-button type="primary" @click="handleAdd">新增实验室</el-button> </div> </template> <el-table :data="labList" border style="width: 100%"> <el-table-column prop="labName" label="实验室名称" width="180" /> <el-table-column prop="labLocation" label="位置" width="180" /> <el-table-column prop="capacity" label="容量" /> <el-table-column prop="status" label="状态" /> <el-table-column prop="description" label="描述" /> <el-table-column label="操作" width="200"> <template #default="scope"> <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="queryParams.pageNum" :page-sizes="[10, 20, 50, 100]" :page-size="queryParams.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total"> </el-pagination> </el-card> <!-- 新增/编辑对话框 --> <el-dialog v-model="dialogVisible" :title="dialogTitle"> <el-form :model="form" :rules="rules" ref="formRef"> <el-form-item label="实验室名称" prop="labName"> <el-input v-model="form.labName" /> </el-form-item> <el-form-item label="位置" prop="labLocation"> <el-input v-model="form.labLocation" /> </el-form-item> <el-form-item label="容量" prop="capacity"> <el-input-number v-model="form.capacity" :min="1" /> </el-form-item> <el-form-item label="状态" prop="status"> <el-select v-model="form.status"> <el-option label="空闲" value="空闲" /> <el-option label="使用中" value="使用中" /> <el-option label="维护中" value="维护中" /> </el-select> </el-form-item> <el-form-item label="描述" prop="description"> <el-input v-model="form.description" type="textarea" /> </el-form-item> </el-form> <template #footer> <span class="dialog-footer"> <el-button @click="dialogVisible = false">取消</el-button> <el-button type="primary" @click="submitForm">确认</el-button> </span> </template> </el-dialog> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import request from '@/utils/request' const labList = ref([]) const total = ref(0) const dialogVisible = ref(false) const dialogTitle = ref('') const formRef = ref() const form = reactive({ labId: null, labName: '', labLocation: '', capacity: 20, status: '空闲', description: '' }) const queryParams = reactive({ pageNum: 1, pageSize: 10 }) const rules = { labName: [{ required: true, message: '请输入实验室名称', trigger: 'blur' }] } const fetchLabList = () => { request.get('/api/lab/list', { params: queryParams }).then(response => { labList.value = response.data.list total.value = response.data.total }) } const handleAdd = () => { dialogTitle.value = '新增实验室' Object.keys(form).forEach(key => { if (key !== 'capacity') form[key] = '' else form[key] = 20 }) form.labId = null dialogVisible.value = true } const handleEdit = (row) => { dialogTitle.value = '编辑实验室' Object.assign(form, row) dialogVisible.value = true } const handleDelete = (row) => { ElMessageBox.confirm('确认删除该实验室吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { request.delete(`/api/lab/${row.labId}`).then(() => { ElMessage.success('删除成功') fetchLabList() }) }) } const submitForm = () => { formRef.value.validate((valid) => { if (valid) { const url = form.labId ? `/api/lab/update` : '/api/lab/add' const method = form.labId ? 'put' : 'post' request({ url, method, data: form }).then(() => { ElMessage.success('操作成功') dialogVisible.value = false fetchLabList() }) } }) } const handleSizeChange = (val) => { queryParams.pageSize = val fetchLabList() } const handleCurrentChange = (val) => { queryParams.pageNum = val fetchLabList() } onMounted(() => { fetchLabList() }) </script>

6. 前后端联调与跨域处理

在开发阶段,前端运行在localhost:8081,后端运行在localhost:8080,存在跨域问题。

6.1 后端解决跨域(CORS)

在 Spring Boot 后端添加一个全局 CORS 配置类。

// 文件路径:src/main/java/com/lab/system/config/CorsConfig.java package com.lab.system.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedOriginPattern("*"); // 生产环境应指定具体前端地址 config.setAllowCredentials(true); config.addAllowedMethod("*"); config.addAllowedHeader("*"); // 暴露自定义响应头,如用于Token的Authorization config.addExposedHeader("Authorization"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }

6.2 前端配置代理(开发环境)

在 Vue 项目的vue.config.js文件中配置开发服务器代理,避免跨域。

// vue.config.js const { defineConfig } = require('@vue/cli-service') module.exports = defineConfig({ transpileDependencies: true, devServer: { port: 8081, proxy: { '/api': { target: 'http://localhost:8080', // 后端地址 changeOrigin: true, pathRewrite: { '^/api': '' // 如果后端接口本身没有/api前缀,可以重写为空 } } } } })

配置后,前端发往/api/user/login的请求会被代理到http://localhost:8080/user/login

7. 系统部署与上线

项目开发完成后,需要部署到服务器以供访问。

7.1 后端打包与部署

  1. 打包:在项目根目录执行mvn clean package -DskipTests,会在target目录生成lab-management-system-0.0.1-SNAPSHOT.jar
  2. 上传:将 jar 包上传到 Linux 服务器。
  3. 运行:在服务器上使用nohupsystemd启动服务。
    nohup java -jar lab-management-system-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod > app.log 2>&1 &
  4. 生产环境配置:创建application-prod.yml,配置生产环境的数据库连接、日志路径、JWT密钥等。

7.2 前端打包与部署

  1. 打包:在 Vue 项目根目录执行npm run build,生成dist目录。
  2. 部署:可以将dist目录下的静态文件部署到 Nginx 或 Apache 等 Web 服务器。
  3. Nginx 配置示例
    server { listen 80; server_name your-domain.com; # 你的域名或IP location / { root /path/to/your/dist; index index.html index.htm; try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } location /api/ { proxy_pass http://localhost:8080/; # 反向代理到后端服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
    此配置将前端请求和后端 API 请求通过同一个域名和端口处理,彻底解决跨域问题。

8. 常见问题与排查思路

在开发和部署过程中,你可能会遇到以下典型问题。

问题现象可能原因排查步骤与解决方案
前端访问后端 API 4041. 后端服务未启动。
2. 请求路径错误。
3. 后端 Controller 未添加@RestController@RequestMapping
1. 检查后端服务日志,确认是否启动成功。
2. 使用 Postman 直接测试后端接口,确认接口路径和参数。
3. 检查前端请求的baseURL和具体路径是否正确拼接。
数据库连接失败1. MySQL 服务未运行。
2.application.yml中数据库配置错误(用户名、密码、URL)。
3. 数据库驱动版本不匹配。
1. 运行systemctl status mysqlservice mysql status检查服务状态。
2. 使用数据库客户端工具(如 Navicat)测试连接。
3. 核对pom.xml中的mysql-connector-java版本与 MySQL 服务器版本是否兼容。
前端页面空白,控制台报错1. JS/CSS 资源加载失败。
2. Vue Router 的 history 模式在非根路径部署时未配置 Nginxtry_files
3. 组件引入路径错误。
1. 检查浏览器开发者工具 Network 面板,查看资源请求状态。
2. 如果使用 history 模式,确保 Web 服务器(如 Nginx)正确配置了try_files $uri $uri/ /index.html;
3. 检查main.js中组件和路由的引入路径。
JPA 实体类映射表失败1. 实体类字段与数据库表字段名/类型不匹配。
2. 数据库表不存在。
3.ddl-auto配置为nonevalidate,但表结构未手动创建。
1. 检查实体类@Column注解的name属性与数据库表字段名是否一致。
2. 检查数据库是否存在对应的表。
3. 开发阶段可将ddl-auto设为update自动创建/更新表,生产环境务必使用 SQL 脚本手动建表。
登录成功但后续请求 4011. 前端未正确存储或发送 Token。
2. 后端 Token 验证失败或已过期。
3. 跨域配置未暴露Authorization头。
1. 检查前端登录成功后是否将 Token 存入localStorageVuex
2. 检查 Axios 请求拦截器是否在每个请求头中添加了Authorization
3. 检查后端 CORS 配置是否包含config.addExposedHeader("Authorization");

9. 项目优化与扩展建议

完成基础功能后,可以从以下几个方面对系统进行优化和功能扩展,提升项目的完整度和实用性。

9.1 后端优化建议

  • 引入Spring Security + JWT:实现更完善的认证与授权机制,替代简单的密码校验。可以细粒度控制 API 访问权限。
  • 统一异常处理:使用@ControllerAdvice@ExceptionHandler创建全局异常处理器,统一返回格式,避免将堆栈信息暴露给前端。
  • 接口参数校验:在 DTO(数据传输对象)上使用javax.validation注解(如@NotBlank,@Email)进行参数校验,并在 Controller 方法参数前添加@Valid注解。
  • 加入日志框架:使用 SLF4J + Logback 记录详细的业务日志和操作日志,便于问题追踪和审计。
  • 使用MyBatis-Plus:如果觉得 JPA 的复杂查询不够灵活,可以考虑引入 MyBatis-Plus,它在 MyBatis 基础上做了增强,提供了强大的 CRUD 操作和条件构造器。
  • API文档化:集成 Swagger 或 Knife4j,自动生成在线 API 文档,方便前后端协作和测试。

9.2 前端优化建议

  • 路由守卫:使用 Vue Router 的导航守卫 (beforeEach),在页面跳转前校验用户登录状态和权限,实现页面级访问控制。
  • 状态管理:对于用户信息、全局配置等需要跨组件共享的数据,使用 Vuex(Vue 3 可使用 Pinia)进行集中管理。
  • 组件封装:将通用的表格、表单、对话框等 UI 逻辑封装成可复用的业务组件,提高开发效率。
  • 性能优化:对于大型表格或列表,使用虚拟滚动(如el-table的虚拟滚动或第三方库vue-virtual-scroller)来优化渲染性能。
  • 环境配置:通过.env.development.env.production等文件管理不同环境(开发、测试、生产)的 API 地址等变量。

9.3 功能扩展方向

  • 消息通知:集成 WebSocket 或使用第三方推送服务,实现预约审核结果、设备归还提醒等实时消息通知。
  • 文件上传:实现实验报告、设备图片的上传功能,可以使用本地存储或集成对象存储服务(如阿里云 OSS、腾讯云 COS)。
  • 数据导出:增加将设备清单、预约记录等数据导出为 Excel 或 PDF 的功能,可以使用 Apache POI 或 EasyExcel。
  • 仪表盘:使用 ECharts 或 AntV 等图表库,在首页创建数据可视化仪表盘,展示实验室使用率、设备状态分布等关键指标。
  • 微信小程序/移动端:考虑开发配套的移动端应用,方便用户随时随地进行实验室预约和设备查询。

通过以上步骤,你已经完成了一个具备核心功能的实验室管理系统的从零到一的搭建。这个项目不仅涵盖了 Spring Boot、Vue、MySQL 等主流技术的实战应用,也涉及了前后端分离架构下的完整开发流程。你可以在此基础上,根据上述优化和扩展建议,继续深化项目功能,将其打造成一个更加成熟、可用于实际场景的系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询