我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Spring Boot+Vue+MySQL体育馆预约系统实战部署指南

Spring Boot+Vue+MySQL体育馆预约系统实战部署指南 简介本资源是一套完整的体育馆场地预约系统毕业设计项目面向计算机专业本科生及Java全栈初学者旨在解决传统场馆预约流程不规范、人工管理效率低、数据容错性差等实际问题。系统基于Spring Boot后端框架与Vue前端框架构建采用MySQL持久化存储涵盖场地管理、用户权限控制、论坛互动、公告发布及订单全流程处理等核心模块具备良好的工程实践性与可部署性。压缩包共750个文件包含101个Java后端逻辑文件、58个Vue组件页面、156个JS交互脚本、162个SVG图标资源及配套CSS/HTML/SQL等整体22.42MB结构清晰含build/run/install三类批处理脚本与多套样式文件便于快速本地部署与二次开发。目前已有79人学习下载提供源码、毕业论文、详细部署说明三位一体交付适合课程设计参考、毕设选题复用及Spring BootVue技术栈实战训练。1. 为什么体育馆预约系统总在开学季崩Spring Boot Vue MySQL 这套组合不是“能跑就行”而是要扛住真实并发、数据一致性、权限错位这三把刀每年九月高校体育馆预约平台准时迎来“开学暴击”学生抢羽毛球场、教师约健身房、社团争舞蹈室——同一时间点涌进上千请求MySQL 连接池打满、Vue 页面白屏卡死、管理员后台查不到谁约了哪块地。这不是 Demo 级别的“Hello World”而是一个必须支撑日均 5000 预约操作、支持多角色学生/教师/场馆管理员/超级管理员、具备时段锁、冲突检测、超时释放、短信通知可扩展能力的真实业务系统。它用 Spring Boot 做后端服务骨架Vue 做响应式前端交互MySQL 存储核心业务数据场地、用户、预约单、排班表三者不是简单拼凑而是围绕“高可用预约”这个强业务约束做深度协同比如 Vue 的预约日历组件必须和后端时段校验逻辑严格对齐MySQL 的booking表设计必须支持INSERT ... ON DUPLICATE KEY UPDATE防重插Spring Boot 的事务边界必须精确包裹“查余量→扣余量→生成订单”原子链。如果你正被课程设计、毕设、校内信息化项目卡在“部署不起来”“登录后空白”“预约成功但数据库没写入”这些具体问题上这篇笔记就是为你写的——不讲 Spring Boot 是什么只告诉你怎么让这套源码在你本地 Windows 或 Linux 机器上真正跑通、调通、稳住。2. 搭建环境从零配齐 Spring Boot Vue MySQL 三件套拒绝“npm install 后一片红”2.1 后端环境JDK 17 Maven 3.8.6 IntelliJ IDEA 社区版够用Spring Boot 2.7.x 及以上版本已正式放弃 JDK 8 支持而本项目源码中pom.xml显示java.version17/java.version强行用 JDK 8 会导致record类型编译失败、HttpClient新 API 报错。别信网上“改个版本号就能降级”的玄学操作——血泪经验JDK 17 是硬门槛不是可选项。安装步骤下载 Adoptium Temurin JDK 17 开源免费Windows 选x64 MSILinux 选tar.gz配置环境变量# Windows PowerShell管理员运行 [System.Environment]::SetEnvironmentVariable(JAVA_HOME, C:\Program Files\Eclipse Adoptium\jdk-17.0.112, Machine) [System.Environment]::SetEnvironmentVariable(PATH, $env:JAVA_HOME\bin;$env:PATH, Machine)# Linux Bash写入 ~/.bashrc export JAVA_HOME/opt/jdk-17.0.112 export PATH$JAVA_HOME/bin:$PATH验证java -version输出应为17.0.1或更高提示IntelliJ IDEA 社区版完全支持 Spring Boot 开发无需付费版。安装后进入Settings → Project → Project SDK指向你刚装的 JDK 17 路径即可。别用内置的 “Corretto” 或 “Zulu” JDK版本号对不上照样翻车。2.2 前端环境Node.js 18.17.0 npm 9.6.7Vue 3.2.x 黄金搭档Vue 3.2.x本项目package.json中vue: ^3.2.45与 Node.js 18 是官方推荐组合。Node.js 20 虽新但部分依赖如node-sass已弃用本项目用sass在构建时会报ERR_OSSL_EVP_UNSUPPORTEDNode.js 16 则因fetchAPI 不完整导致 Axios 请求拦截器失效。安装命令Windows 推荐用 nvm-windows 管理多版本# WindowsPowerShell Invoke-WebRequest -Uri https://nodejs.org/dist/v18.17.0/node-v18.17.0-x64.msi -OutFile node-v18.17.0.msi Start-Process msiexec -ArgumentList /i node-v18.17.0.msi /quiet -Wait# LinuxUbuntu/Debian curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt-get install -y nodejs验证node -v输出v18.17.0npm -v输出9.6.72.3 数据库MySQL 8.0.33非 5.7字符集与 JSON 字段是命门本项目application.yml中明确配置spring: datasource: url: jdbc:mysql://localhost:3306/gym_booking?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue注意serverTimezoneAsia/Shanghai和allowPublicKeyRetrievaltrue—— 这是 MySQL 8.0 强制要求。若你装的是 MySQL 5.7JSON类型字段如booking.extra_info存储设备借用清单会直接报错Invalid JSON text更致命的是5.7 默认latin1字符集当用户输入“羽毛球馆东区”这种带括号中文时入库变乱码后续按场馆名查询全失效。安装要点Windows下载 MySQL 8.0.33 MSI Installer 安装时务必勾选 “Add MySQL to PATH”并在 “Authentication Method” 页选择“Use Legacy Password Encryption”否则 Spring Boot 连接报Client does not support authentication protocolLinux用 APT 安装后立即执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_secure_password; FLUSH PRIVILEGES;创建数据库关键CREATE DATABASE gym_booking CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;注意必须是utf8mb4不是utf8。MySQL 的utf8实际只支持 3 字节 UTF-8 字符不支持 emoji 和部分生僻汉字而utf8mb4才是真正的四字节 UTF-8。这是线上环境数据错乱的第一大坑。3. 源码结构解剖看清gym-booking-backend和gym-booking-frontend如何咬合3.1 后端模块gym-booking-backend的三层骨架与关键切面解压.zip后你会看到gym-booking-backend文件夹。这不是一个单体 Maven 项目而是标准的 Spring Boot 多模块结构目录作用必看文件gym-booking-common公共实体、异常、工具类Result.java统一返回格式、CustomException.java自定义业务异常gym-booking-daoMyBatis Mapper 接口与 XMLBookingMapper.java预约核心 CRUD、VenueMapper.xml含foreach批量插入场地gym-booking-service业务逻辑层含事务控制BookingService.javaTransactional注解在createBooking()方法上gym-booking-webController 层REST 接口入口BookingController.javaPostMapping(/booking)对应前端预约提交重点盯BookingService.createBooking()方法Transactional(rollbackFor Exception.class) public Result createBooking(Booking booking) { // 1. 校验时段是否可约查 venue_schedule 表 if (!venueScheduleService.isTimeSlotAvailable(booking.getVenueId(), booking.getDate(), booking.getTimeSlot())) { return Result.fail(该时段已被预约请选择其他时间); } // 2. 扣减余量UPDATE venue_schedule SET available_count available_count - 1 venueScheduleService.decreaseAvailableCount(booking.getVenueId(), booking.getDate(), booking.getTimeSlot()); // 3. 插入预约单INSERT INTO booking booking.setCreateTime(LocalDateTime.now()); booking.setStatus(BookingStatus.PENDING); bookingMapper.insert(booking); return Result.success(预约成功); }这段代码揭示了数据一致性的核心保障所有操作在一个数据库事务中完成。如果第 2 步扣减失败如并发导致available_count为 0整个事务回滚第 3 步不会写入任何脏数据。这也是为什么你不能把“查余量”和“扣余量”拆成两个 HTTP 请求——网络延迟会让中间状态暴露给其他用户。3.2 前端模块gym-booking-frontend的路由与状态管理真相Vue 项目基于 Vue Router 4 和 Pinia非 Vuex。打开src/router/index.jsconst routes [ { path: /, redirect: /login }, { path: /login, name: Login, component: () import(/views/Login.vue) }, { path: /dashboard, name: Dashboard, component: () import(/views/Dashboard.vue), meta: { requiresAuth: true } // 路由守卫依据 }, { path: /booking, name: Booking, component: () import(/views/Booking.vue), meta: { requiresAuth: true, roles: [STUDENT, TEACHER] } // 角色白名单 } ]注意meta.roles字段——这是前端权限控制的第一道闸门。但请清醒这只是体验优化真正的权限校验在后端PreAuthorize(hasRole(STUDENT))注解里。如果你手动修改 URL 访问/booking前端会跳转到登录页但若绕过前端直调/api/booking接口后端 Spring Security 会返回403 Forbidden。Pinia store 关键点在src/stores/user.jsexport const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: JSON.parse(localStorage.getItem(userInfo)) || {} }), actions: { setToken(token) { this.token token; localStorage.setItem(token, token); // 持久化到 localStorage }, clearAuth() { this.token ; this.userInfo {}; localStorage.removeItem(token); localStorage.removeItem(userInfo); } } });这里暴露了一个常见误区不要把敏感信息如密码、JWT 密钥存在 localStorage。本项目仅存 token 和基础用户信息符合安全基线。但如果你要商用应将 token 存于 httpOnly Cookie并启用 CSRF 保护。3.3 前后端联调关键跨域与接口前缀的生死匹配Vue 开发服务器默认跑在http://localhost:8080Spring Boot 默认http://localhost:8081。浏览器同源策略会拦截请求所以必须配代理。打开vue.config.jsmodule.exports { devServer: { proxy: { /api: { target: http://localhost:8081, // 后端地址 changeOrigin: true, pathRewrite: { ^/api: // 把 /api 前缀去掉再转发 } } } } }这意味着前端代码里写axios.post(/api/booking)实际请求发往http://localhost:8081/booking。致命陷阱如果你修改了 Spring Boot 的server.port却忘了同步改vue.config.js中的target或者忘了删pathRewrite就会出现“前端控制台显示 200但 Network 面板里请求是 404”——因为请求被代理到了错误路径。验证方法启动后端后在浏览器直接访问http://localhost:8081/swagger-ui.html能打开 Swagger 文档页即证明后端已就绪再启动前端F12 查看 Network筛选 XHR点击预约按钮应看到POST /api/booking返回200且 Response 是{code:200,msg:预约成功,data:{}}。4. 部署说明落地从本地调试到 Linux 服务器上线避开 Nginx、PM2、MySQL 权限三连坑4.1 后端打包用 Maven 构建可执行 JAR别碰 war 包本项目pom.xml中packagingjar/packaging且spring-boot-maven-plugin已配置因此cd gym-booking-backend mvn clean package -Dmaven.test.skiptrue生成gym-booking-backend/target/gym-booking-backend-1.0.jar。不要用mvn install它只把 jar 装到本地 Maven 仓库不生成可执行包。启动命令Linuxnohup java -Xms512m -Xmx1024m -jar gym-booking-backend-1.0.jar --spring.profiles.activeprod backend.log 21 参数说明-Xms512m -Xmx1024m堆内存初始 512MB最大 1024MB。体育馆系统并发不高此配置足够若服务器内存 ≥4G可升至-Xmx2048m--spring.profiles.activeprod激活生产配置读取application-prod.yml其中数据库密码应为真实值nohup后台运行关闭终端不中断 backend.log 21标准输出和错误日志合并写入backend.log提示首次启动后检查backend.log是否有Started GymBookingBackendApplication in X.XXX seconds。若卡在Loading class com.mysql.cj.jdbc.Driver是 MySQL 驱动版本不匹配——本项目用mysql-connector-java:8.0.33别换成 5.1.x。4.2 前端构建生成静态文件扔给 Nginx 就完事cd gym-booking-frontend npm install npm run build生成dist/目录。将整个dist文件夹上传至 Linux 服务器如/var/www/gym-booking/。Nginx 配置/etc/nginx/conf.d/gym.confserver { listen 80; server_name gym.yourdomain.com; # 替换为你的域名或 IP root /var/www/gym-booking; index index.html; location / { try_files $uri $uri/ /index.html; # Vue Router history 模式必备 } location /api { proxy_pass http://127.0.0.1:8081; # 转发到后端 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }关键点try_files $uri $uri/ /index.html解决 Vue Router history 模式下刷新 404 问题location /api把所有/api/**请求代理到后端前端代码无需改任何 URL仍用/api/booking重启 Nginxsudo nginx -t sudo systemctl reload nginx4.3 MySQL 生产环境加固不只是创建数据库除了CREATE DATABASE gym_booking CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;还必须创建专用账号禁用 root 远程登录CREATE USER gym_applocalhost IDENTIFIED BY StrongPass!2024; GRANT SELECT, INSERT, UPDATE, DELETE ON gym_booking.* TO gym_applocalhost; FLUSH PRIVILEGES;修改application-prod.ymlspring: datasource: url: jdbc:mysql://localhost:3306/gym_booking?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: gym_app password: StrongPass!2024开启 MySQL 慢查询日志可选但强烈建议在/etc/mysql/mysql.conf.d/mysqld.cnf中添加slow_query_log 1 slow_query_log_file /var/log/mysql/mysql-slow.log long_query_time 2这能帮你揪出SELECT * FROM booking WHERE status PENDING这种没加索引的慢 SQL。5. 避坑指南那些让你熬夜到三点的“小问题”其实都有固定解法5.1 现象前端登录成功但跳转 Dashboard 后页面空白Network 里无任何 API 请求原因Vue Router 的router.push()被 Promise 拦截而beforeEach守卫中next()未正确调用。查看src/router/index.js的全局前置守卫router.beforeEach((to, from, next) { const token useUserStore().token; if (to.meta.requiresAuth !token) { next(/login); // ✅ 正确跳转 } else if (to.meta.roles !to.meta.roles.includes(useUserStore().userInfo.role)) { next(/403); // ✅ 正确跳无权限页 } else { next(); // ❌ 错误这里漏了 else 分支的 next() } });解决补全else分支确保所有路径都调用next()} else { next(); // ✅ 补上这一行 }5.2 现象预约提交后MySQLbooking表有记录但venue_schedule.available_count没减少原因BookingService.createBooking()中venueScheduleService.decreaseAvailableCount()方法内部用了Transactional但该 Service 类未被 Spring 扫描到缺少Service注解或包路径不在ComponentScan范围内。检查VenueScheduleServiceImpl.java// ❌ 错误没有 Service 注解 public class VenueScheduleServiceImpl implements VenueScheduleService { ... }解决加上ServiceService // ✅ 补上 public class VenueScheduleServiceImpl implements VenueScheduleService { ... }5.3 现象Linux 上启动 JAR 报java.lang.UnsatisfiedLinkError: /tmp/libnet.so原因Spring Boot 内嵌 Tomcat 在某些 Linux 发行版如 CentOS 7上需要libnet.so但该文件被临时目录清理机制删除。解决启动时指定临时目录java -Djava.io.tmpdir/opt/gym-tmp -jar gym-booking-backend-1.0.jar并提前创建目录mkdir -p /opt/gym-tmp。5.4 现象Nginx 代理后前端登录请求返回401 Unauthorized但直接 curl 后端接口正常原因Nginx 默认不传递 Authorization Header。解决在location /api块中添加proxy_set_header Authorization $http_authorization; proxy_pass_request_headers on;5.5 现象MySQL 8.0 连接报Public Key Retrieval is not allowed原因MySQL 8.0 默认禁用公钥检索而 JDBC 驱动需要它来加密传输密码。解决在 JDBC URL 中显式开启url: jdbc:mysql://localhost:3306/gym_booking?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueenabledTLSProtocolsTLSv1.2注意allowPublicKeyRetrievaltrue是开发环境快捷方案生产环境应配置 SSL 证书而非开此开关。6. 进阶技巧用 Actuator Prometheus 做轻量级监控把“系统还活着”变成“系统哪里卡了”6.1 后端接入 Spring Boot Actuator暴露健康、指标、线程堆栈在gym-booking-backend/pom.xml中添加dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency然后在application-prod.yml中配置management: endpoints: web: exposure: include: health,info,metrics,threaddump,prometheus endpoint: health: show-details: when_authorized启动后访问http://localhost:8081/actuator/health返回{status:UP}/actuator/metrics返回所有埋点指标。6.2 前端加个“健康看板”用 Vue 调用 Actuator 接口实时展示在src/views/Monitor.vue新建中template div classmonitor h2系统健康看板/h2 div v-ifhealth.status UP✅ 后端服务{{ health.status }}/div div v-else❌ 后端服务{{ health.status }}/div div当前活跃线程{{ metrics[jvm.threads.live]?.value || N/A }}/div div数据库连接池使用率{{ dbPoolUsage }}%/div /div /template script setup import { ref, onMounted } from vue import axios from /utils/request.js const health ref({ status: UNKNOWN }) const metrics ref({}) const dbPoolUsage ref(0) onMounted(async () { // 获取健康状态 const healthRes await axios.get(/actuator/health) health.value healthRes.data // 获取指标 const metricsRes await axios.get(/actuator/metrics) metrics.value metricsRes.data // 计算连接池使用率需后端暴露 HikariCP 指标 const poolRes await axios.get(/actuator/metrics/hikaricp.connections.active) dbPoolUsage.value Math.round((poolRes.data.value / 20) * 100) // 假设 max20 }) /script提示HikariCP 指标需在application-prod.yml中额外暴露management: endpoints: web: exposure: include: health,info,metrics,threaddump,prometheus,hikaricp6.3 用 Prometheus Grafana 做可视化5 分钟搭好下载 Prometheus 解压后编辑prometheus.ymlscrape_configs: - job_name: gym-backend static_configs: - targets: [localhost:8081] # Actuator 的 prometheus 端点启动./prometheus --config.fileprometheus.yml访问http://localhost:9090输入jvm_memory_used_bytes查看 JVM 内存趋势下载 Grafana 启动后添加 Prometheus 数据源导入 JVM 监控模板 ID 4701这样当某天学生反馈“预约按钮点了没反应”你不用猜——直接看 Grafana如果http_server_requests_seconds_count{status500}突增说明后端抛异常如果jvm_threads_live持续上涨大概率是线程泄漏。我带过的三个校内项目都是靠这套轻量监控在故障发生 2 分钟内定位到BookingService里一个没加Transactional的方法避免了半夜被电话叫醒。技术不一定要多炫但得让问题“看得见”。希望帮到你。本文还有配套的精品资源点击获取
返回列表