资讯动态

Spring Boot与Vue.js构建高校自习室预约系统:从需求到部署的完整实践

发布时间:2026/8/10 5:33:08 来源:尧图企业网站定制
在实际高校信息化建设中自习室资源管理是一个高频且痛点明确的需求。学生需要便捷地查询空闲座位、预约心仪时段而管理员则需要高效地管理教室资源、处理预约冲突、统计使用数据。传统的线下登记或简单表格方式不仅效率低下也容易引发公平性和管理混乱的问题。因此一个基于 Web 的、前后端分离的自习室预定系统能够很好地解决这些矛盾。本文将围绕“高校自习室预定系统”这一核心需求使用 Spring Boot 作为后端服务框架Vue.js 作为前端开发框架构建一个具备完整预约流程的管理系统。我们将从零开始逐步完成项目环境搭建、数据库设计、核心接口开发、前端页面实现并最终部署运行。文章不仅会提供可运行的代码片段更会解释每个技术选型背后的考量、关键配置的作用以及开发中常见的“坑”和排查方法。无论你是正在学习 Spring Boot 和 Vue 整合的开发者还是需要为学校或组织实现类似系统的工程师都能通过本文获得一个清晰、可复现的实践路径。1. 理解系统核心需求与技术栈选型在动手编码之前明确系统要做什么以及为什么选择特定的技术组合是确保项目方向正确、架构合理的第一步。1.1 系统核心功能模块拆解一个典型的高校自习室预定系统至少应包含以下核心功能模块用户管理区分学生用户和管理员角色实现注册、登录、权限控制。自习室与座位管理管理员可以新增、修改自习室信息并为每个自习室设置座位布局如行、列。预约规则管理定义可预约的时间段如以小时为单位、单次最长预约时长、是否允许取消、违约规则等。座位预约学生用户可查看未来一段时间内各自习室座位的占用情况通常以日历或时间轴视图展示并选择空闲时段进行预约。我的预约学生查看自己的历史预约、当前有效预约并执行取消操作。数据统计管理员查看自习室使用率、热门时段、用户预约习惯等统计报表。1.2 为什么选择 Spring Boot Vue.js 前后端分离架构这种架构模式已成为现代 Web 应用开发的主流其优势在本项目中尤为明显前后端解耦独立开发与部署前端团队专注于页面交互和用户体验使用 Vue 的组件化开发效率高后端团队专注于业务逻辑和 API 设计使用 Spring Boot 可以快速构建 RESTful 服务。两者通过 HTTP API 通信互不影响。Spring Boot 的优势快速启动内嵌 Tomcat无需单独部署 WAR 包。自动配置极简的依赖和配置让开发者更关注业务。生态强大与 MyBatis-Plus数据访问、Spring Security安全、Spring Cache缓存等集成无缝。Vue.js 的优势渐进式框架可以从简单的页面开始逐步引入路由、状态管理等复杂功能。响应式数据绑定数据与视图自动同步简化了座位状态、预约列表等动态内容的开发。丰富的生态系统Element Plus、Vant 等 UI 库能快速搭建美观的管理后台和移动端界面。基于以上分析我们的技术栈确定为后端Spring Boot 2.7.x (一个长期支持版本稳定且生态兼容性好) JDK 8 或 11 Maven MySQL 数据库。前端Vue 3 Composition API Vite 构建工具 Element Plus UI 组件库 Axios 用于 HTTP 请求。数据访问MyBatis-Plus 3.x 极大简化 CRUD 操作。项目管理前后端作为两个独立的工程便于管理和部署。2. 项目环境准备与工程结构搭建一个清晰的工程结构是项目可维护性的基础。我们将分别创建后端和前端两个独立的项目。2.1 后端 Spring Boot 项目初始化使用 Spring Initializr (start.spring.io) 或 IDE (如 IntelliJ IDEA) 创建项目。项目基本信息Project: Maven ProjectLanguage: JavaSpring Boot: 2.7.18 (选择一个稳定的 2.7.x 版本)Project Metadata:Group:com.yourdomain(例如com.example)Artifact:studyroom-booking-backendPackaging: JarJava: 8 或 11依赖选择在创建时勾选以下依赖Spring Web: 用于构建 RESTful API。Spring Boot DevTools: 开发热部署。Lombok: 简化实体类代码。MySQL Driver: 数据库驱动。MyBatis Framework: 或稍后手动引入 MyBatis-Plus。创建完成后在pom.xml中我们手动将 MyBatis 依赖替换为功能更强大的 MyBatis-Plus。!-- 移除 spring-boot-starter-data-jpa (如果选了) 和 mybatis-spring-boot-starter -- !-- 添加 MyBatis-Plus 依赖 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency项目目录结构创建完成后规划一个清晰的分层目录。studyroom-booking-backend/ ├── src/main/java/com/example/studyroom/ │ ├── StudyroomBookingBackendApplication.java // 启动类 │ ├── config/ // 配置类如跨域配置、MyBatis-Plus 配置 │ ├── controller/ // 控制器接收请求返回响应 │ ├── entity/ // 实体类对应数据库表 │ ├── dto/ // 数据传输对象用于前后端交互 │ ├── vo/ // 视图对象用于返回给前端的特定数据结构 │ ├── mapper/ // MyBatis Mapper 接口 │ ├── service/ // 业务逻辑层接口 │ │ └── impl/ // 业务逻辑层实现 │ └── utils/ // 工具类如 JWT 工具、日期工具 ├── src/main/resources/ │ ├── application.yml // 主配置文件 │ └── mapper/ // MyBatis XML 映射文件如果使用 └── pom.xml2.2 前端 Vue 项目初始化使用 Vite 官方模板快速创建 Vue 项目它比 Vue CLI 更轻量、更快。创建项目# 使用 npm npm create vuelatest studyroom-booking-frontend # 或使用 yarn yarn create vue studyroom-booking-frontend在创建向导中根据提示选择需要的功能TypeScript: 可选本项目为简化选择 No。JSX Support: No。Vue Router:Yes(用于页面路由)。Pinia:Yes(用于状态管理替代 Vuex)。ESLint: Yes (代码规范)。Prettier: Yes (代码格式化)。安装核心依赖进入项目目录安装 Element Plus 和 Axios。cd studyroom-booking-frontend npm install element-plus axios # 或 yarn add element-plus axios项目目录结构studyroom-booking-frontend/ ├── public/ // 静态资源 ├── src/ │ ├── assets/ // 样式、图片等资源 │ ├── components/ // 可复用组件如预约日历组件 │ ├── router/ // 路由配置 │ ├── stores/ // Pinia 状态管理 │ ├── views/ // 页面视图组件 │ ├── utils/ // 工具函数如请求封装 │ ├── App.vue // 根组件 │ └── main.js // 入口文件 ├── index.html ├── package.json └── vite.config.js // Vite 配置配置 Element Plus 和 Axios在src/main.js中引入。import { createApp } from vue import { createPinia } from pinia import ElementPlus from element-plus import element-plus/dist/index.css import App from ./App.vue import router from ./router import * as ElementPlusIconsVue from element-plus/icons-vue const app createApp(App) // 注册所有图标 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.use(createPinia()) app.use(router) app.use(ElementPlus) app.mount(#app)在src/utils/下创建request.js文件封装 Axios 实例统一处理请求拦截如添加 Token、响应拦截和错误处理。2.3 数据库设计与初始化根据功能需求设计核心数据表。这里是一个简化版的设计-- 用户表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名/学号, password varchar(100) NOT NULL COMMENT 密码加密后, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, role tinyint NOT NULL DEFAULT 1 COMMENT 角色0-管理员1-学生, status tinyint NOT NULL DEFAULT 1 COMMENT 状态0-禁用1-正常, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 自习室表 CREATE TABLE study_room ( id bigint NOT NULL AUTO_INCREMENT, room_name varchar(100) NOT NULL COMMENT 自习室名称, location varchar(200) DEFAULT NULL COMMENT 位置, total_seats int NOT NULL COMMENT 总座位数, rows int DEFAULT NULL COMMENT 座位行数用于可视化, cols int DEFAULT NULL COMMENT 座位列数, status tinyint NOT NULL DEFAULT 1 COMMENT 状态0-关闭1-开放, description text COMMENT 描述, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT自习室表; -- 座位表如果每个座位有独立编号和状态 CREATE TABLE seat ( id bigint NOT NULL AUTO_INCREMENT, room_id bigint NOT NULL COMMENT 自习室ID, seat_number varchar(20) NOT NULL COMMENT 座位编号如A01, row_num int DEFAULT NULL, col_num int DEFAULT NULL, status tinyint DEFAULT 1 COMMENT 状态1-可用0-维修中, PRIMARY KEY (id), KEY idx_room_id (room_id), CONSTRAINT fk_seat_room FOREIGN KEY (room_id) REFERENCES study_room (id) ON DELETE CASCADE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT座位表; -- 预约记录表核心表 CREATE TABLE booking_record ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL COMMENT 预约用户ID, room_id bigint NOT NULL COMMENT 自习室ID, seat_id bigint DEFAULT NULL COMMENT 座位ID如果精确到座, booking_date date NOT NULL COMMENT 预约日期, start_time time NOT NULL COMMENT 开始时间, end_time time NOT NULL COMMENT 结束时间, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0-已预约1-使用中2-已完成3-已取消4-违约, check_in_time datetime DEFAULT NULL COMMENT 签到时间, check_out_time datetime DEFAULT NULL COMMENT 签退时间, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_date (user_id, booking_date), KEY idx_room_seat_date (room_id, seat_id, booking_date, start_time), CONSTRAINT fk_booking_user FOREIGN KEY (user_id) REFERENCES sys_user (id), CONSTRAINT fk_booking_room FOREIGN KEY (room_id) REFERENCES study_room (id), CONSTRAINT fk_booking_seat FOREIGN KEY (seat_id) REFERENCES seat (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT预约记录表;注意booking_record表的idx_room_seat_date索引对于快速查询某个座位在特定日期的预约情况至关重要能极大提升并发预约时的性能。3. 后端核心业务逻辑实现我们将聚焦于用户认证、自习室管理、预约逻辑这三个最核心的模块。3.1 用户认证与权限控制使用 Spring Security 结合 JWT (JSON Web Token) 是实现无状态认证的常见方案。但为了简化我们先实现一个基于 Session 或简单 Token 的拦截器。这里以简单的 Token 拦截器为例。实体类与 Mapper创建User实体类和对应的UserMapper接口。// entity/User.java Data TableName(sys_user) public class User { TableId(type IdType.AUTO) private Long id; private String username; private String password; private String realName; private Integer role; // 0-admin, 1-student private Integer status; private LocalDateTime createTime; private LocalDateTime updateTime; }// mapper/UserMapper.java Mapper public interface UserMapper extends BaseMapperUser { // MyBatis-Plus 已提供基础 CRUD 方法 }登录接口与 Token 生成在UserController中创建/api/user/login接口。RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public ResultVO login(RequestBody LoginDTO loginDTO) { // 1. 参数校验 // 2. 根据用户名查询用户 User user userService.lambdaQuery() .eq(User::getUsername, loginDTO.getUsername()) .one(); // 3. 校验密码 (实际项目中密码应加密存储如 BCrypt) if (user null || !user.getPassword().equals(loginDTO.getPassword())) { return ResultVO.error(用户名或密码错误); } // 4. 生成一个简单的 Token (实际应用应使用 JWT) String token UUID.randomUUID().toString().replace(-, ); // 5. 将 token 与用户信息关联存储 (可存入 Redis 或 ConcurrentHashMap) TokenStore.saveToken(token, user); // 6. 返回 Token 和用户基本信息 UserVO userVO new UserVO(); BeanUtils.copyProperties(user, userVO); MapString, Object data new HashMap(); data.put(token, token); data.put(userInfo, userVO); return ResultVO.success(登录成功, data); } }实现 Token 拦截器创建一个TokenInterceptor拦截需要认证的请求。Component public class TokenInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 从请求头中获取 token String token request.getHeader(Authorization); // 2. 检查 token 是否存在且有效 if (StringUtils.isBlank(token) || !TokenStore.isValid(token)) { // 3. 无效则返回 401 未授权 response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType(application/json;charsetutf-8); ResultVO result ResultVO.error(HttpStatus.UNAUTHORIZED.value(), 请先登录); response.getWriter().write(JSON.toJSONString(result)); return false; } // 4. 有效则将用户信息存入请求上下文供后续使用 User user TokenStore.getUserByToken(token); request.setAttribute(currentUser, user); return true; } }在WebMvcConfig中注册该拦截器并配置拦截路径如/api/**和排除路径如/api/user/login,/api/user/register。3.2 自习室与座位管理接口这部分主要为管理员提供 CRUD 接口。自习室管理创建StudyRoomController提供增删改查接口。关键点在于新增自习室时如果启用了座位管理可能需要自动生成座位数据。PostMapping public ResultVO addRoom(RequestBody StudyRoom room) { boolean saved roomService.save(room); if (saved room.getRows() ! null room.getCols() ! null) { // 自动生成座位 ListSeat seatList new ArrayList(); for (int i 1; i room.getRows(); i) { for (int j 1; j room.getCols(); j) { Seat seat new Seat(); seat.setRoomId(room.getId()); seat.setSeatNumber(String.format(%c%02d, A i - 1, j)); seat.setRowNum(i); seat.setColNum(j); seat.setStatus(1); seatList.add(seat); } } seatService.saveBatch(seatList); } return saved ? ResultVO.success() : ResultVO.error(新增失败); }座位状态查询接口这是学生端预约页面的核心。需要根据自习室、日期查询出所有座位及其在该日期的预约占用情况。GetMapping(/{roomId}/seats-status) public ResultVO getSeatsStatus(PathVariable Long roomId, RequestParam DateTimeFormat(pattern yyyy-MM-dd) LocalDate date) { // 1. 查询该自习室所有座位 ListSeat seatList seatService.lambdaQuery() .eq(Seat::getRoomId, roomId) .eq(Seat::getStatus, 1) .list(); // 2. 查询该日期该自习室的所有有效预约记录 ListBookingRecord bookingList bookingService.lambdaQuery() .eq(BookingRecord::getRoomId, roomId) .eq(BookingRecord::getBookingDate, date) .in(BookingRecord::getStatus, Arrays.asList(0, 1)) // 已预约和使用中 .list(); // 3. 将预约记录按座位ID分组方便后续判断 MapLong, ListBookingRecord bookingMap bookingList.stream() .collect(Collectors.groupingBy(BookingRecord::getSeatId)); // 4. 构造返回给前端的VO列表包含座位信息和其时间段占用情况 ListSeatStatusVO result seatList.stream().map(seat - { SeatStatusVO vo new SeatStatusVO(); BeanUtils.copyProperties(seat, vo); vo.setBookings(bookingMap.getOrDefault(seat.getId(), Collections.emptyList()) .stream().map(this::convertToTimeSlot).collect(Collectors.toList())); return vo; }).collect(Collectors.toList()); return ResultVO.success(result); }3.3 预约业务的核心逻辑与并发控制预约是系统的核心必须处理好并发冲突和业务规则校验。预约接口实现在BookingController中创建预约接口。PostMapping public ResultVO bookSeat(RequestBody BookingDTO bookingDTO, HttpServletRequest request) { User currentUser (User) request.getAttribute(currentUser); // 1. 基础校验预约时间是否合法如不能预约过去时间时长是否在限制内 if (!bookingService.validateBookingTime(bookingDTO)) { return ResultVO.error(预约时间不合法); } // 2. 检查该座位在目标时间段是否已被预约关键并发控制点 boolean isConflict bookingService.checkTimeConflict(bookingDTO.getSeatId(), bookingDTO.getBookingDate(), bookingDTO.getStartTime(), bookingDTO.getEndTime()); if (isConflict) { return ResultVO.error(该时间段已被预约); } // 3. 检查用户当天是否已有过多预约业务规则 long todayBookings bookingService.lambdaQuery() .eq(BookingRecord::getUserId, currentUser.getId()) .eq(BookingRecord::getBookingDate, bookingDTO.getBookingDate()) .count(); if (todayBookings MAX_BOOKING_PER_DAY) { return ResultVO.error(今日预约次数已达上限); } // 4. 创建预约记录 BookingRecord record new BookingRecord(); BeanUtils.copyProperties(bookingDTO, record); record.setUserId(currentUser.getId()); record.setStatus(0); // 已预约 boolean saved bookingService.save(record); return saved ? ResultVO.success(预约成功) : ResultVO.error(预约失败); }并发控制方案上述代码在checkTimeConflict和save之间存在时间差高并发下可能产生“超售”。解决方案有数据库唯一索引在booking_record表上建立(seat_id, booking_date, start_time)的唯一索引从数据库层面防止重复插入。这是最简单有效的方式。数据库悲观锁在查询冲突时使用SELECT ... FOR UPDATE锁定相关记录。分布式锁在集群环境下使用 Redis 等实现分布式锁锁定“座位日期时间段”这个资源。乐观锁为预约记录增加版本号字段更新时校验版本。对于本项目数据库唯一索引是性价比最高的选择。在booking_record表添加ALTER TABLE booking_record ADD UNIQUE INDEX uk_seat_date_time (seat_id, booking_date, start_time);然后在代码中捕获DuplicateKeyException异常返回友好的冲突提示。4. 前端页面开发与接口联调前端将使用 Vue 3 Element Plus 构建几个核心页面。4.1 封装统一的请求工具在src/utils/request.js中封装 Axios统一处理请求和响应。import axios from axios import { ElMessage } from element-plus import router from /router const service axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, // 从环境变量读取后端地址 timeout: 10000 }) // 请求拦截器添加 Token service.interceptors.request.use( config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }, error { console.error(Request Error:, error) return Promise.reject(error) } ) // 响应拦截器处理通用错误 service.interceptors.response.use( response { const res response.data // 假设后端统一返回格式为 { code: 200, message: success, data: ... } if (res.code 200) { return res.data } else { // 业务逻辑错误 ElMessage.error(res.message || Error) // 如果是未登录跳转到登录页 if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message || Error)) } }, error { // HTTP 状态码错误 console.error(Response Error:, error) ElMessage.error(error.message || Network Error) return Promise.reject(error) } ) export default service4.2 登录与主页布局登录页面(views/Login.vue)一个简单的表单调用后端的/api/user/login接口将返回的 Token 存入localStorage并跳转到主页。主页布局(views/layout/Index.vue)使用 Element Plus 的 Container 布局容器包含顶部导航栏、侧边菜单栏和主内容区。侧边菜单根据用户角色动态渲染。4.3 自习室预约页面开发这是学生用户的核心操作页面 (views/student/Booking.vue)。页面结构左侧为自习室列表右侧为座位可视化区域和预约时间选择器。获取自习室列表组件挂载时调用/api/study-room/list接口。座位可视化当选中一个自习室和日期后调用/api/study-room/{roomId}/seats-status接口。前端根据返回的座位行、列信息使用 CSS Grid 或 Flex 布局渲染出一个网格。每个网格代表一个座位并根据其bookings数组包含已预约时间段来动态计算并高亮显示已被占用的时间段。template div classseat-map div v-forseat in seatList :keyseat.id :class[seat, { occupied: isSeatOccupied(seat, selectedTime) }] clickselectSeat(seat) {{ seat.seatNumber }} /div /div el-time-picker v-modelbookingTime is-range range-separator至 start-placeholder开始时间 end-placeholder结束时间/ el-button typeprimary clicksubmitBooking确认预约/el-button /template script setup import { ref, computed } from vue import { getSeatsStatus, bookSeat } from /api/booking const seatList ref([]) const selectedSeat ref(null) const bookingTime ref([new Date(2024, 0, 1, 9, 0), new Date(2024, 0, 1, 11, 0)]) const loadSeats async (roomId, date) { const res await getSeatsStatus(roomId, date) seatList.value res } const isSeatOccupied (seat, timeRange) { // 逻辑遍历 seat.bookings判断 timeRange 是否与任何一个已预约时间段重叠 // 返回 true 或 false } const submitBooking async () { if (!selectedSeat.value) { ElMessage.warning(请先选择座位) return } const params { seatId: selectedSeat.value.id, bookingDate: /* 格式化日期 */, startTime: /* 格式化开始时间 */, endTime: /* 格式化结束时间 */ } try { await bookSeat(params) ElMessage.success(预约成功) // 重新加载座位状态 loadSeats() } catch (error) { // 错误已在 request.js 中统一处理 } } /script预约提交用户选择座位和时间段后点击预约按钮调用/api/booking接口。4.4 我的预约页面用户查看和管理自己的预约 (views/student/MyBooking.vue)。主要是一个表格展示预约记录并提供“取消”按钮。取消操作调用/api/booking/{id}/cancel接口后端需要校验该预约是否允许取消如距离开始时间超过一定时限则不可取消。5. 系统配置、运行与验证5.1 后端配置文件在src/main/resources/application.yml中配置数据库、服务器端口等。server: port: 8080 servlet: context-path: /api spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/studyroom_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: yourpassword jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发时开启 SQL 日志 global-config: db-config: logic-delete-field: deleted # 全局逻辑删除字段名如果用到 logic-delete-value: 1 logic-not-delete-value: 05.2 前端环境变量与代理配置在项目根目录创建.env.development文件设置开发环境的后端 API 地址。VITE_APP_BASE_API/api在vite.config.js中配置代理解决开发时跨域问题。import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 3000, proxy: { /api: { target: http://localhost:8080, // 后端地址 changeOrigin: true, rewrite: (path) path.replace(/^\/api/, ) // 如果你的后端也有 /api 前缀可能需要调整 } } } })5.3 启动与验证启动后端运行StudyroomBookingBackendApplication的main方法。观察控制台确保无报错并看到 Tomcat 启动在 8080 端口的日志。启动前端在终端进入前端项目目录运行npm run dev。Vite 会启动开发服务器通常在http://localhost:3000。功能验证流程访问http://localhost:3000/login使用管理员账号登录。进入自习室管理页面添加一个自习室如“图书馆三楼A区”设置 5 行 10 列。退出登录使用学生账号登录。进入预约页面选择刚创建的自习室和日期页面应渲染出 5x10 的座位网格。点击一个座位选择时间段如 14:00 - 16:00点击预约。提示成功。再次点击同一座位、同一时间段应提示“该时间段已被预约”。进入“我的预约”页面应能看到刚创建的预约记录并可尝试取消。6. 常见问题排查与优化建议在实际开发和部署中你可能会遇到以下问题。6.1 常见问题排查表问题现象可能原因检查点与解决方案前端页面访问后端接口 4041. 后端服务未启动。2. 前端代理配置错误。3. 后端context-path与前端的baseURL或代理rewrite不匹配。1. 检查后端控制台日志确认端口。2. 检查浏览器开发者工具 Network 面板看请求 URL 是否正确。3. 核对application.yml的server.servlet.context-path、前端.env的VITE_APP_BASE_API和vite.config.js的proxy配置。登录成功但后续请求 4011. Token 未正确存储在localStorage。2. 请求拦截器未正确添加Authorization头。3. 后端拦截器路径配置错误拦截了无需认证的请求。1. 在浏览器 Application 面板查看localStorage。2. 在浏览器 Network 面板查看请求头是否包含Authorization。3. 检查后端WebMvcConfig确保登录、注册等接口路径已被排除。预约时提示“该时间段已被预约”但数据库查无此记录高并发下的“超售”问题。两个请求几乎同时通过了checkTimeConflict检查。解决方案为booking_record表添加(seat_id, booking_date, start_time)的唯一索引。在 Service 层捕获DuplicateKeyException返回友好提示。页面座位状态刷新不及时前端在预约成功后没有重新调用接口获取最新的座位状态数据。在预约成功的回调函数中再次调用loadSeats方法。或者考虑使用 WebSocket 实现实时推送但复杂度较高。时间处理出现时区错误前端new Date()、后端LocalDateTime、数据库datetime之间的时区不一致。统一方案前后端所有时间传递均使用 ISO 8601 字符串格式如2024-01-01T14:00:00。后端数据库连接串指定serverTimezone。后端实体类使用JsonFormat注解指定序列化格式。6.2 生产环境部署与优化建议学习环境跑通后若想投入实际使用还需考虑以下方面安全性加固密码加密使用BCryptPasswordEncoder对密码进行哈希存储绝对不要明文存储。使用 JWT替换简单的 Token 机制使用标准的 JWT并设置合理的过期时间。API 防护对关键接口如预约增加频率限制、防重放攻击等措施。SQL 注入使用 MyBatis-Plus 的条件构造器或 XML 中#{}语法可有效防止。性能优化数据库索引确保查询频繁的字段如booking_record表的user_id,booking_date,room_id,seat_id已建立合适索引。缓存将不常变动的数据如自习室信息、座位基础信息放入 Redis 缓存。分页查询列表接口如我的预约、预约记录管理务必实现分页。可维护性统一异常处理使用ControllerAdvice创建全局异常处理器统一返回格式和日志记录。接口文档使用 Swagger 或 Knife4j 自动生成 API 文档。日志配置 Logback 或 Log4j2将日志按级别输出到文件并合理记录操作日志。部署后端使用mvn clean package打包为 Jar 文件通过java -jar命令或 systemd 服务在服务器运行。考虑使用 Docker 容器化部署。前端运行npm run build生成静态文件将其部署到 Nginx 或 Apache 等 Web 服务器。配置 Nginx 将/api路径的请求反向代理到后端服务。这个基于 Spring Boot 和 Vue 的高校自习室预定系统从需求分析、技术选型到核心功能实现覆盖了一个典型前后端分离项目的主要环节。它不仅仅是一个功能堆砌的 Demo更体现了如何处理并发冲突、如何设计友好的 API、如何进行前后端数据交互等工程实践。你可以在此基础上继续扩展扫码签到、违约管理、数据可视化报表、微信小程序端等功能使其成为一个真正可用的产品。在开始扩展前务必确保当前的核心流程稳定可靠并建立了完善的错误处理和日志监控机制。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价