SpringBoot+Vue+MySQL体育馆管理系统源码解析与部署实战 这套体育馆管理系统源码名字里写得很清楚SpringBoot 后端 Vue 前端 MySQL 数据库并且特意标注了“可直接运行”。我拿到这套项目后第一反应是终于遇到一个不含糊的实战项目了。很多同学毕设或者小团队内部系统最怕的就是源码下载下来跑不起来环境缺这缺那光启动就要折腾两天。这套系统我之前完整部署过一次整体结构常规但不平庸功能覆盖了体育馆常见的场地预约、会员管理、教练排班、订单统计等场景非常适合拿来学习前后端分离架构或者直接改改用了做课程设计。这篇文章我不会只把启动命令复述一遍而是把整个项目从设计思路、后端核心逻辑、前端页面实现、到本地部署的每个关键环节都拆开来讲。你跟着操作一遍不仅能跑起来还能搞清楚每个表、每个接口、每个页面之间是怎么配合的遇到报错也知道去哪查。1. 项目整体设计与技术选型思路1.1 海滨体育馆到底需要一个什么样的系统先别急着敲代码我们先想想业务场景。一家体育馆日常要管理的不只是“订个场”这么简单。客户需要线上查看空闲时段、预约羽毛球馆、篮球场或游泳馆管理员要能设置场馆开放时间、管理会员、处理退款、统计每日营业额。如果只用Excel表登记数据一多就会乱预约冲突、账目不清几乎无法避免。所以这套系统的核心价值就是把场地资源、会员信息、预约订单、收费记录统一放到一个可视化系统里管理。我拆开源码看过它的主要模块包括场馆管理维护场馆名称、类型、开放时段、收费标准预约管理用户选择场馆和时段下单后台生成订单会员管理记录会员卡类型、余额、有效期支持会员价订单财务管理记录实收金额、支付方式、退款状态系统用户管理区分管理员和普通员工不同角色不同权限这样的结构不算复杂但对一个中小型体育馆来说已经覆盖了日常运营80%以上的场景。你如果拿它做毕设答辩时能把每个模块说清楚老师很难挑出大毛病。1.2 为什么选 SpringBoot Vue MySQL 这套组合这个项目的技术选型很典型也很适合多数人学习。SpringBoot负责后端接口开发它简化了 Spring 框架的配置内置 Tomcat一个java -jar就能启动服务Vue负责前端页面组件化开发配合 Element UI 这类组件库可以快速搭建管理后台MySQL存数据开源稳定学校和企业都用它而且招聘市场上这套组合的需求量一直很大。有人可能会问为什么不选 Spring Cloud、微服务、Redis 这些东西说实话对一个体育馆管理系统来说微服务完全是过度设计。单体应用启动快、部署简单、排错直观数据和业务都在一个事务里逻辑清晰。学习阶段把单体做扎实比硬上一堆中间件有用得多。另外我注意到这套源码的前后端是分离的开发环境里后端跑 8080 端口前端跑 8081 端口前面通过代理转发请求。这种模式下前端和后端可以并行开发你单独改页面不需要重启 Java 服务单独调接口也不需要重新编译前端开发体验比老式 JSP 模板舒服太多。1.3 整体功能模块与数据流转关系从源码目录结构可以很容易看出项目的分层思路后端按 Controller - Service - Mapper 三层分包控制器只接收参数和返回结果业务逻辑写在 Service数据库操作用 MyBatis-Plus 或 JPA 的 Mapper 完成前端按页面视图划分通常是src/views/admin、src/views/user这种结构路由配置在router/index.js数据库包含用户表、场馆表、预约单表、订单表、会员卡表等数据流转也很直接用户在 Vue 页面上点击“确认预约”前端把选的场馆、时间段、价格组装成一个 JSON通过 axios 发送 POST 请求到后端/api/order后端先验证该时段是否已被占用再判断用户余额或在线支付的金额是否足够最后写库生成订单把状态返回给前端。这一条线走通系统的核心闭环就成立了。提示建议拿到源码后先从数据库脚本看起。.sql文件里表结构的注释往往比代码更能帮你理解整个业务模型。2. 后端核心实现与关键细节2.1 环境准备与项目初始化我这台机器是 Windows 11部署这套系统用到的工具版本如下你可以直接参照JDK 1.8 或 JDK 11我用的是 JDK 1.8稳定Maven 3.6.3MySQL 5.7 / 8.0Node.js 14.17.0IDEA 或 Eclipse后端工程导入 IDEA 后先等 Maven 把依赖下完。然后找到application.yml或application.properties核心配置就是数据库连接server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/sports_center?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 jpa: database-platform: org.hibernate.dialect.MySQL5InnoDBDialect hibernate: ddl-auto: none show-sql: true注意 MySQL 8.0 必须加serverTimezone否则报时区错误。ddl-auto设为none表示不用框架自动建表你手动执行项目里的 SQL 脚本即可这是很多项目能“开箱可运行”的前提。2.2 数据库表设计与实体映射的讲究打开数据库脚本核心表设计我总结一下表名用途关键字段user后台用户/会员账户username,password,role,statusvenue体育馆场地name,type,price_per_hour,open_time,close_timereservation预约记录user_id,venue_id,date,start_time,end_time,statusorder订单流水order_no,amount,pay_type,create_time密码字段记得不是明文。这个项目里用了 MD5 加盐或 BCrypt可以直接在UserServiceImpl中看到加密逻辑。如果想用http.antMatchers(/api/**)做接口权限确实要保证前端传过来的密码加密方式一致否则登录接口会一直报“用户名或密码错误”。MyBatis-Plus 的实体类一般用TableName(reservation)注解映射表名字段用驼峰命名开启map-underscore-to-camel-case后start_time能自动映射成startTime非常省事。2.3 权限设计与登录鉴权实现我不知道你有没有遇到过这种情况明明功能都写完了但用户能直接调接口改别人订单。这个项目里用了 JWTJSON Web Token做身份识别我用下来体验是简单够用比 Session 合适前后端分离。登录流程是前端调用/api/user/login传username和password后端比对成功生成一个 JWT 令牌把用户 ID 和角色塞进去返回给前端前端把 Token 存在localStorage每次发请求在拦截器中加Authorization: Bearer token后端的拦截器解析 Token拿着用户信息放行或拒绝请求我在测试时故意用一个普通用户 Token 去调管理员接口结果返回 403。这说明权限确实生效了。如果你改这套系统建议把角色判断从if (admin.equals(role))换成自定义注解PreAuthorize代码会干净很多也更容易维护。2.4 场馆预定与订单状态的业务逻辑预定功能是这个系统的灵魂。后端的实现逻辑大致是前端传venueId、预约日期、开始小时和结束小时后端先查询该场馆在指定日期当前时段的预约记录QueryWrapperReservation wrapper new QueryWrapper(); wrapper.eq(venue_id, venueId) .eq(date, date) .eq(status, 1) .lt(start_time, endTime) .gt(end_time, startTime); Integer count reservationMapper.selectCount(wrapper); if (count 0) { return Result.error(该时段已被预约); }没有冲突就生成订单并扣除用户余额或记录在线支付这里判断时间段是否重叠的 SQL 条件值得你花时间理解新预约开始小于已有预约结束 且 新预约结束大于已有预约开始。这两个条件同时满足说明时间有重叠。这比单纯比较startTime startTime科学得多也是面试里经常问的一道编程题。如果订单状态有“已取消”字段删除时最好用逻辑删除也就是把status改成 0而不是物理 DELETE。这样统计报表才能追回历史取消记录也不容易把用户钱包金额算错。3. 前端 Vue 页面的落地要点3.1 Vue 项目结构与路由设计前端工程目录结构通常很清晰src ├─ api │ ├─ login.js │ └─ reservation.js ├─ assets ├─ components ├─ router │ └─ index.js ├─ store │ └─ module ├─ views │ ├─ Login.vue │ ├─ Dashboard.vue │ ├─ venue │ │ └─ VenueList.vue │ ├─ reservation │ │ └─ ReservationCreate.vue │ └─ admin └─ main.js路由设计里我比较关注的是路由守卫。在router/index.js中router.beforeEach((to, from, next) { const token localStorage.getItem(token); if (to.path ! /login !token) { next(/login); } else { next(); } })这能保证用户在未登录状态下无法直接访问后台页面。你搜索源码时如果发现没有这行逻辑建议补上否则系统登录权限形同虚设。3.2 axios 封装与接口调用的统一处理前端所有请求应该经过一个统一的request.js封装而不是每个页面里写一堆axios.get。这样做的理由很简单你可以统一处理令牌、统一报错提示、统一打印日志。我比较喜欢这个项目里的封装方式import axios from axios; const service axios.create({ baseURL: process.env.VUE_APP_BASE_URL, timeout: 10000 }); service.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] Bearer token; } return config; }); service.interceptors.response.use( response response.data, error { if (error.response.status 401) { localStorage.clear(); location.href /login; } return Promise.reject(error); } ); export default service;这样一旦 Token 过期前端自动跳回登录页用户不会看到一堆奇怪的报错。你在部署时如果发现有跨域问题多半是后端的跨域配置没打开或者代理没配对。3.3 核心页面组件的实现细节预约场馆页面通常用到 Element UI 的日期选择器和时间选择器。用户选中某个场地页面上会实时显示当前时段价格el-date-picker v-modelreservation.date typedate placeholder选择日期 :disabled-datedisabledDate /el-date-pickerdisabledDate可以用来禁掉过去日期避免用户填昨天的订单。场地卡片列表里通常有一个“查看空闲时间”的功能。前端拿到场馆的预约列表后用数组生成当天从开门到关门的连续时段再把已被预约的时段标记为灰色之后用户只能选择绿色时段。这类交互写起来不难但比后端接口更直观适合新手练习。注意前端页面提交到后端的金额不要只靠前端计算。所有价格校验必须在后端重新做一遍因为用户可以改请求参数。这种“前端显示、后端控制”的原则前后端分离项目里一定要记住。4. 本地部署与直接运行指南4.1 你需要在电脑上装哪些工具这套系统实现“直接运行”的前提是环境齐全。我把清单列出少了哪个赶紧补JDK 8 以上配置好JAVA_HOMEMaven配置好本地仓库路径方便下依赖MySQL 5.7并创建数据库Node.js 14 以上自带 npm 包管理工具一个趁手的 IDE后端用 IDEA前端用 VSCode上面少了任意一个项目都不可能真正跑起来。所谓“可直接运行”指的是一套完整代码加配置而不是一个被剪裁的演示片段。4.2 数据库导入与后端启动步骤先在 MySQL 里建库CREATE DATABASE sports_center DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后使用命令导入源码提供的 SQL 文件mysql -u root -p sports_center sports_center.sql导入完成后检查一下reservation、order表几条数据是否正常。接着用 IDEA 打开后端目录等待 Maven 同步完成在运行配置中选主类SportsCenterApplication点 Run。看到类似下面的日志说明后端已经成功启动Tomcat started on port(s): 8080 (http) with context path 4.3 Vue 前端安装依赖与启动调试打开前端目录终端里执行npm install这一步会拉取所有依赖。如果 npm 源慢可以切到国内镜像npm config set registry https://registry.npmmirror.com安装完成后运行npm run dev看到Local: http://localhost:8081就说明前端起来了。这时浏览器访问http://localhost:8081登录页出现说明前后端已经联调成功。4.4 常见启动报错排查表我把实际运行中别人经常遇到的几个问题整理成表按这个查能省很多时间报错信息可能原因解决方式Access denied for user rootlocalhost数据库密码填错修改application.yml里的passwordUnknown database sports_center没建库或库名不对先执行CREATE DATABASEPort 8080 was already in use.端口被占用netstat -ano查进程并结束或改server.portError: Cannot find module node-sass前端依赖没装全重新npm install或升级为sassNetwork Error前端代理没配置检查vue.config.js里proxy是否指向 8080提示前端请求跨域还可能是后端没开跨域配置。SpringBoot 中可以加一个配置类实现WebMvcConfigurer重写addCorsMappings或者直接用CrossOrigin注解。5. 实操踩坑记录与优化建议5.1 我遇到的五个实际坑第一个坑数据库时区。第一次我用的 MySQL 8.0连接 URL 没写serverTimezone启动直接报The server time zone value Öйú±ê׼ʱ¼ä。这属于基础配置问题但足够卡新手半天。第二个坑前端 Node 版本太高。原本用的 Node 17 跑 Vue2 项目编译报错内容很奇怪最后把 Node 降到 14问题消失。第三个坑JWT 密钥每次重启后旧的 Token 全部失效。这个项目里 JWT 密钥是写死的重启后应该还有效如果写在随机数里用户会被迫重新登录注意这细微差别。第四个坑预约时间冲突判断不严谨。源码最初只判断了完全相同的开始时间到现在我改成了交叉区间判断。不加这个优化用户可以把同一场地不同时段都占满也会出现真实业务中“从 10 点到 12 点和从 11 点到 13 点撞车”的情况。第五个坑前端页面刷新后白屏。原因大多是 history 模式的路由在 nginx 没配置try_files。本地 dev 不会遇到打包部署时就需要加一句location / { try_files $uri $uri/ /index.html; }5.2 数据库连接池与字段类型的优化标题里说“可直接运行”但真要投入生产有几个点要改连接池默认配置可能用 HikariCP这是 SpringBoot 2 系列的默认选项并发不高没什么问题。要把maximum-pool-size调到与业务峰值匹配订单金额字段用decimal(10,2)而不是double避免浮点误差。如果你翻 SQL 发现有人用了float建议马上改掉时间字段建议用datetime存完整时间而不是把日期和时间拆成两个varchar。否则你统计“某月销售额”时SQL 写起来要人命5.3 这套系统还能往哪些方向扩展如果你不满足于原样运行可以考虑加这么几个功能短信/邮件预约提醒在订单生成后调一个异步任务提前一天发送提醒体验会好很多微信小程序端后端接口基本都是 JSON只要再做一套小程序页面就能打通移动端预约数据统计可视化对接 ECharts做场馆利用率、营收趋势、峰值时段分析多租户模式如果要把系统卖给不同体育馆可以给场馆表加tenant_id实现一套代码多馆共用这些扩展方向里最简单的入门改造是加 ECharts 图表因为数据表已经齐全只要写几个聚合查询接口就行。我曾经花一个下午给这套系统加了三张报表答辩或演示时效果提升非常明显。我个人在实际操作中的体会是这套“海滨体育馆管理系统”的价值不在于代码多么华丽而在于它是一条完整的前后端分离学习链路。你把它跑起来是第一步把链路里每一步的“为什么”弄懂才是真正的收获。网上能下载的源码很多但真正变成自己的一定是你亲手排查过报错、动手改过业务逻辑的那些代码。先用起来再改起来这套系统就能成为你能力水平的一块不错的垫脚石。