ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

健身房预约管理系统部署实战:SpringBoot+Vue+小程序+MySQL四件套

健身房预约管理系统部署实战:SpringBoot+Vue+小程序+MySQL四件套 简介这是面向高校计算机专业毕业设计、课程设计场景的健身房预约管理系统完整源码包。项目基于Java、SpringBoot、Vue与MySQL构建前后端分离架构包含微信小程序用户端与管理后台覆盖用户注册登录、课程预约、教练信息浏览、课程与教练管理、系统设置等核心模块下载后按环境配置即可直接运行项目完整无缺适合需要完整可演示项目的毕业生或初学者参考。资源包共1641个文件以Java、Vue、JS等前后端源码和微信小程序WXML/WXSS页面文件为主同时包含数据库SQL脚本、Maven构建配置、PNG/JPG图片素材及项目开发说明整体体积16.69MB目录结构清晰便于直接导入开发环境。已有121人学习下载项目经严格调试可完整呈现健身房预约业务流程对理解小程序与SpringBoot前后端交互、MySQL数据表设计具有实际参考价值尤其适合毕业设计快速落地与二次开发。1. 健身房预约管理系统小程序、SpringBoot、Vue、MySQL 四件套怎么串起来不少人和我说从网上下了一套健身房预约管理系统的源码但卡在“环境跑不起来”这一步连数据库都连不上更别提看到预约流程。这套基于微信小程序 Java SpringBoot Vue MySQL 的健身房预约管理系统是典型的三端分离结构用户端在小程序里完成注册、选时段、预约、签到管理员通过 Vue 后台管理课程与教练SpringBoot 统一暴露接口MySQL 存业务数据。它适合正在做毕设、课设或者想快速抄一套完整前后端分离项目来练手的人。下面我按自己实际跑通的顺序从工程结构、后端启动、小程序联调、后台打包到避坑细节一次讲清楚。2. 先拆技术栈与工程结构三端边界、目录职责与四张核心表2.1 三端职责与请求链路在动手部署之前先把项目切成三块看微信小程序端、SpringBoot 后端、Vue 管理后台。小程序端负责 C 端用户的登录、浏览课程、预约、取消、签到界面偏移动交互Vue 管理后台负责管理员维护教练、课程、时段以及查看预约记录SpringBoot 后端是唯一的数据出口所有写操作都要经过它的事务控制避免预约库存超卖或重复预约。常见工程结构我会这样组织backend放 SpringBoot 的 Maven 工程miniapp放微信小程序原生工程admin-web放 Vue 管理后台。源码包如果按这个思路拆你拿到手后先不要急着启动先看三个目录的README或配置文件确认端口约定。通常后端跑在localhost:8080Vue 开发服务器跑在8081小程序通过wx.request把请求指向后端的局域网 IP 或域名。请求链路可以理解为小程序或 Vue 页面发起 HTTP 请求 → SpringBoot Controller 接收 → Service 层做业务校验与事务处理 → Mapper 操作 MySQL → 返回统一 JSON。前端不直接连数据库这也是安全和后续扩展的基础。小程序端因为微信平台限制正式上线必须配 HTTPS 域名但本地开发可以勾选“不校验合法域名”。启动顺序上我一般会先启动 MySQL再启动 SpringBoot最后才开 Vue 和小程序开发者工具。如果后端还没起来就先调小程序页面只会白屏加一串request:fail容易让人误以为代码有问题。反过来先把后端接口用 curl 或浏览器测通再让前端对接排错范围会小很多。2.2 核心数据表member、course、timeslot、booking健身房预约的核心业务围绕“谁能约、约什么、什么时间、约没约上”展开。常见设计是四张核心表用户表member存手机号、昵称、会员状态课程表course存课程名称、教练ID、人数上限时段表timeslot存某天某时段的开始结束时间预约表booking存用户ID、时段ID、预约状态、创建时间。如果源码里还有教练表coach和管理员表admin也不要觉得意外他们只是把课程归属和后台登录拆得更细。这样的表结构设计有几个好处第一用户和课程不会直接耦合预约关系的中间表可以独立扩展比如以后要加私教课只需要在course里增加类型字段不用改动预约主链路。第二即便业务规则调整比如允许同一个用户约多个时段也不需要重构表只改约束就行。SQL 建表时我建议把约束提前想清楚不要光靠 Java 代码判断。比如booking表对(user_id, timeslot_id)加唯一索引这是防止重复预约的最后一层防线timeslot的剩余名额可以通过course的人数和已预约数计算但更稳妥的方式是给timeslot加一个remain字段在事务里先SELECT ... FOR UPDATE再扣减。下面给出一段简化版建表 SQL注意字段类型和索引CREATE TABLE member ( id bigint NOT NULL AUTO_INCREMENT, phone varchar(20) NOT NULL, nickname varchar(50) DEFAULT , status tinyint DEFAULT 1 COMMENT 1正常 0冻结, PRIMARY KEY (id), UNIQUE KEY uk_phone (phone) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE course ( id bigint NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, coach_id bigint DEFAULT NULL, max_people int DEFAULT 10, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE timeslot ( id bigint NOT NULL AUTO_INCREMENT, course_id bigint NOT NULL, start_time datetime NOT NULL, end_time datetime NOT NULL, remain int DEFAULT 10, PRIMARY KEY (id), KEY idx_course_id (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE booking ( id bigint NOT NULL AUTO_INCREMENT, user_id bigint NOT NULL, timeslot_id bigint NOT NULL, status tinyint DEFAULT 1 COMMENT 1已预约 2已签到 3已取消, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_user_timeslot (user_id, timeslot_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段 SQL 里uk_phone和uk_user_timeslot就是数据库层的“后悔药”哪怕业务代码漏写了重复判断数据库也会把重复预约挡下来。status字段用tinyint存状态值比字符串省空间查询也快。create_time用DEFAULT CURRENT_TIMESTAMP插入时不用手动填减少一处出错可能。注意这个语法依赖 MySQL 5.6 以上版本如果用的是老版本建表会直接报语法错误。2.3 接口风格与统一返回体前后端分离的项目最怕接口返回格式各写各的。这个资源里 SpringBoot 后端一般会抽一个ResultT统一返回体里面包含code、message、data三个字段。前端判断code 200就算成功否则弹message。我建议你拿到源码后先搜Result类确认成功和失败的状态码再开始联调避免前端把 200 当成功、后端却返回 0 的错位。统一返回体的代码通常长这样public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT r new Result(); r.code 200; r.message success; r.data data; return r; } public static T ResultT error(Integer code, String message) { ResultT r new Result(); r.code code; r.message message; return r; } }这里的code不要只放 200 和 500建议业务错误从 4001 开始编号比如 4001 表示参数错误、4002 表示预约冲突、4003 表示课程不存在。前端拿到非 200 的 code 时直接读取message展示给用户这样后端改提示文案时前端不用动。接口路径上习惯用/api/user/**、/api/admin/**区分端后续加 Shiro 或 Spring Security 也好按路径配权限。另外要注意日期格式。后端返回给前端的LocalDateTime默认可能是一长串数字或ISO格式小程序端不能直接用。配置里加上统一的日期格式化或者用JsonFormat(pattern yyyy-MM-dd HH:mm:ss)能省很多解析代码。管理后台的分页接口建议统一用page和pageSize入参返回体里带total、list小程序和 Vue 都能复用同一套分页组件。3. 把后端跑起来SpringBoot 项目配置、建库与接口自测3.1 环境清单与版本选型后端要跑起来最稳的组合是 JDK 8 Maven 3.6.x MySQL 5.7。这个资源如果用的是 SpringBoot 2.xJDK 8 完全够用如果源码里是 SpringBoot 3.x那必须配 JDK 17否则启动直接报UnsupportedClassVersionError。我的习惯是先看pom.xml里的spring-boot-starter-parent版本号再决定装哪个 JDK别上来就装最新的 JDK 21。MySQL 版本也有讲究。本地开发建议用 MySQL 5.7 或 8.0注意 8.0 的驱动和连接串写法略有不同。如果你下载的源码里application.yml用的是com.mysql.jdbc.Driver那大概率是给 5.7 用的8.0 要改成com.mysql.cj.jdbc.Driver并且连接串里要加serverTimezoneAsia/Shanghai。装 MySQL 时务必记好 root 密码后面建库建表都要用。如果你在 Windows 上装 MySQL 5.7 时老是在服务启动那一步转圈常见原因是以前残留的my.ini里指定了不存在的目录删掉重新初始化即可。环境清单我用表格给你列出来照着装就好组件推荐版本用途JDK8 或 17编译运行 SpringBootMaven3.6.3依赖管理MySQL5.7 / 8.0数据存储Node.js14/16/18运行 Vue 管理端微信开发者工具最新稳定版运行小程序提示如果机器上已经装了多个 JDK启动前用java -version确认当前默认版本不然 Maven 可能用到错误的环境。3.2 application.yml 配置与数据库初始化拿到源码后第一步不是启动而是改数据库配置。打开backend/src/main/resources/application.yml把数据源地址、用户名、密码改成你本机的值。常见配置如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/gym_booking?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0这里的serverTimezoneAsia/Shanghai是 MySQL 8 的必需品不加会报时区错误。jackson.time-zone保证返回给前端的日期是北京时间否则小程序端显示的时间会差 8 小时。mybatis-plus的逻辑删除配置是可选的如果源码用了这个建议保留删除预约记录时自动变成update而不是物理删除。数据库初始化脚本一般在sql目录或doc/sql目录下文件名类似gym_booking.sql。用命令行导入最直接mysql -u root -p gym_booking gym_booking.sql如果gym_booking数据库不存在需要先创建CREATE DATABASE gym_booking DEFAULT CHARACTER SET utf8mb4;。导入成功后执行SHOW TABLES;看到member、course、timeslot、booking等表说明数据没问题。注意不要直接打开 SQL 文件复制到图形化工具里执行文件里如果有DELIMITER开头的存储过程命令行导入更不容易出错。3.3 启动后端并用 Swagger 做冒烟测试启动后端我一般用 Maven 命令不用 IDE 按钮因为这样能看到完整日志。在backend目录执行mvn clean install -DskipTests mvn spring-boot:run如果源码里有 Swagger 或 Knife4j 依赖启动成功后访问http://localhost:8080/doc.html就能看到接口文档。没有 Swagger 也别急直接看 Controller 里的RequestMapping路径用 curl 测试。比如测试获取课程列表curl -X GET http://localhost:8080/api/course/list \ -H Content-Type: application/json返回体应该是{code:200,message:success,data:[...]}结构。如果返回 500先看 SpringBoot 控制台堆栈90% 是数据库连接问题或者表名对不上。这里有个小技巧启动日志里出现Tomcat started on port(s): 8080才说明端口正常如果被占用会在日志里报Port already in use改用server.port8081即可。冒烟测试的目的是先把后端这条线跑通把接口能通、数据库能查这件事坐实再去碰小程序和 Vue否则后面全是双端联调的脏活。如果源码里没有 Swagger你也可以临时加入springdoc-openapi依赖但最好先跑通再说别在没启动成功时引入新变量。4. 小程序端与 Vue 管理端从登录授权到预约下单4.1 微信小程序登录与手机号获取小程序端第一个绕不开的是登录。微信小程序现在推荐用wx.login拿 code再把 code 发给后端由后端调微信接口换 openid。但健身房预约这类业务通常还要绑定手机号所以一般流程是先wx.login静默登录再通过“获取手机号”按钮让用户授权手机号后端把手机号和 openid 绑定到member表。用户在页面上点击“微信一键登录”时会触发一个buttonopen-type设为getPhoneNumber然后在回调里拿加密数据。简化版的小程序端请求封装是这样function request(path, method, data) { return new Promise((resolve, reject) { wx.request({ url: http://localhost:8080/api path, method: method || GET, data: data || {}, header: { Content-Type: application/json }, success(res) { if (res.data.code 200) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); }这里url写localhost只能用于开发者工具模拟。真机调试时必须把localhost换成电脑的局域网 IP比如http://192.168.1.100:8080同时微信开发者工具必须勾选“不校验合法域名”。如果你是第一次接手这个项目先把小程序里的接口 baseURL 抽成一个常量统一改一处别在十几个页面里硬编码。后端处理code换 openid 时需要调用微信接口https://api.weixin.qq.com/sns/jscode2session把appid和secret换成一个openid和session_key。这个secret不要写在前端或application.yml里明文暴露最好放在环境变量或配置中心。拿到 openid 后先查member表没有则插入新用户有则直接返回用户信息。手机号获取那块最新版微信已经改成每次都要用户主动点击授权code是一次性的后端拿着临时 code 去换手机号缓存时间很短要尽快调接口。4.2 预约与取消的接口对接细节预约接口的核心参数是courseId、timeslotId和userId。下单时最好把userId从全局变量里取别让用户自己传防止越权。调用预约接口的伪代码如下async function bookCourse() { try { const data await request(/booking/add, POST, { timeslotId: this.data.selectedTimeslotId, courseId: this.data.courseId }); wx.showToast({ title: 预约成功, icon: success }); } catch (e) { // 错误提示已经在 request 里处理 } }后端在高峰期可能会出现同一时段多人同时抢预约的情况单纯的在 Java 里先查再更新会有超卖风险。常见做法是用数据库行锁或乐观锁UPDATE timeslot SET remain remain - 1 WHERE id ? AND remain 0更新影响行数为 0 就说明名额已经没了。你拿到源码后可以看 Service 层是否用了事务注解Transactional并且确认remain 0条件写在 SQL 里而不是 Java 里。取消预约则要判断时间边界通常开场前 30 分钟内不允许取消防止破坏课程安排。这个规则如果你在源码里没找到可以写在application.yml的自定义配置里比如booking.cancel-minute-limit30方便后续调整。取消操作不要删记录把status改成 3并把timeslot.remain加回去否则数据统计会失真。联调时如果发现前端参数名和后端RequestBody里的字段对不上优先看后端代码里的实体字段比如timeslotId在 Java 里可能是timeslotId在 MySQL 里是timeslot_idMyBatis 会自动做驼峰映射但 JSON 键名必须和 Java 字段一致。对不上时就报 400 参数错误前端控制台能看到具体原因。4.3 Vue 管理后台的 API 封装与打包回 SpringBootVue 管理后台面向管理员功能包括课程管理、教练管理、预约记录查看。它的接口调用我用 axios 封装别忘了携带 token。如果后端没有做登录鉴权管理端可以先用简单的账号密码登录后端返回 token 后前端存到localStorageaxios 拦截器里统一加上Authorization头。以下是 Vue 端的 axios 拦截器片段import axios from axios; const service axios.create({ baseURL: process.env.VUE_APP_BASE_URL || http://localhost:8080/api, timeout: 10000 }); service.interceptors.request.use(config { const token localStorage.getItem(admin_token); if (token) { config.headers.Authorization Bearer ${token}; } return config; }); service.interceptors.response.use(response { if (response.data.code ! 200) { // 统一弹错误提示 } return response.data; });开发时用npm run serve启动 VueVite 或 Webpack 默认端口在8081附近浏览器里访问的是管理界面。部署上线时最省事的方式是把 Vue 打好包扔进 SpringBoot 的static目录让后端一起出。执行npm run build后会生成dist目录把dist里的内容拷贝到backend/src/main/resources/static重启后端后直接访问http://localhost:8080/就是管理后台。这里有个坑Vue 打包后访问接口的路径是相对路径还是绝对路径取决于vue.config.js里的publicPath。如果你直接把 dist 放进 SpringBootpublicPath最好设为./否则刷新时资源路径会 404。另外如果 Vue 用了history路由模式SpringBoot 需要配置 history fallback否则刷新非首页路径会出现 404常见做法是加一个 Controller 把未匹配到接口的前端路由转发到index.html。环境变量也要注意。.env.development里的VUE_APP_BASE_URL指向开发后端.env.production里如果留空则打包后请求会发到当前域名下的/api正好匹配我们塞进 SpringBoot 的路径。切换环境时别只改代码要检查.env文件。5. 避坑与常见问题五个能把人卡半天的细节5.1 环境类翻车JDK 版本、Maven 仓库与 MySQL 时区现象后端启动时控制台报UnsupportedClassVersionError错误信息里能看到class file version。原因本机 JDK 版本低于源码编译版本比如用 JDK 8 去跑 SpringBoot 3.x 的 class。解决打开pom.xml看java.version再把 IDEA 或 Maven 的 JDK 切到对应版本。如果嫌切来切去麻烦直接装 JDK 8 和 JDK 17 两套用环境变量按项目切。启动 SpringBoot 3.x 时JDK 8 一定不行不要浪费时间排查依赖。现象Maven 依赖下载极慢或mvn clean install卡在Downloading半天没反应。原因默认 Maven 中央仓库在国内访问不稳定。解决修改 Maven 的settings.xml把中央仓库镜像换成阿里云镜像。镜像配置写在mirrors标签里注意mirrorOf要写central不然不生效。改完后再执行一次mvn clean install你会看到下载速度像换了个网络。现象MySQL 启动时报The server time zone value ?D1ú±? is unrecognized。原因MySQL 8 的 JDBC 连接串没加serverTimezone参数。解决在application.yml的url末尾追加serverTimezoneAsia/Shanghai同时把driver-class-name改成com.mysql.cj.jdbc.Driver。还有个连带问题MySQL 8 默认字符集是utf8mb4如果建表脚本里写了ENGINEInnoDB DEFAULT CHARSETutf8中文昵称和课程名称存储可能没问题但特殊表情符号会丢建议统一用utf8mb4。5.2 业务逻辑与联调重复预约、库存扣减、顶部导航栏现象同一个用户对同一时段快速点了两次预约第一次成功第二次也返回成功数据库中产生两条记录。原因业务层只做了“先查再插”并发下两个线程都查到没有记录于是都执行插入。解决第一层在booking表加唯一索引uk_user_timeslot第二层在插入时捕获DuplicateKeyException并转为“请勿重复预约”提示。如果修改表结构不方便至少要在 Service 方法上加Transactional并把查询和插入放到同一个事务里。并发量不大的场景这招也够用。现象真机调试小程序时请求一直fail但开发者工具里一切正常。原因真机上localhost指向的是手机自己不是电脑而且没在小程序后台配置 request 合法域名。解决开发阶段把接口地址改为电脑局域网 IP并在开发者工具右上角打开“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。如果后端也需要手机能访问注意关掉电脑防火墙或至少放行 8080 端口。遇到请求返回的数据和预期不一致用 Charles 抓包看请求体和返回体比在代码里打日志快得多。现象自定义导航栏的小程序页面顶部内容被刘海屏遮住点击预约按钮位置偏上。原因没有适配状态栏高度直接用了position: fixed定到顶部 0。解决用wx.getWindowInfo()或wx.getMenuButtonBoundingClientRect()获取状态栏高度把自定义导航栏的padding-top设成这个高度。这个坑在 iPhone 上尤其明显安卓通常没问题但也要一起适配。如果你不想管这个细节用小程序的默认导航栏最省事但界面观感会差一些。6. 进阶验证与可用性检查用一次性数据跑通完整预约闭环6.1 从注册到签到的全链路自测我习惯在新环境上跑通一个完整预约闭环再交差。你不需要把全部用例都执行一遍但至少要走通“注册 → 登录 → 选课程 → 选时段 → 预约 → 取消 → 再预约 → 签到”这个主路径。准备一组一次性数据一个测试手机号、一个课程、两个可用时段。用 curl 或 Postman 依次调用接口把每一步的返回码记下来。如果预约成功后再次预约同一时段返回 4002/“已预约”说明唯一索引生效如果名额只剩 1 时两个人同时下单只有一个成功说明库存扣减逻辑可靠。签到接口要额外确认是否校验了当前时间在课程开始前后各 15 分钟内如果不校验管理员后台手动签到就形同虚设。校验通过后用UPDATE booking SET status 2 WHERE id ?更新状态不要用删除操作。6.2 安全与越权自查这类毕业设计项目最容易被老师问的问题就是越权。你要检查后端接口里是否每个操作都从 token 或 session 里拿当前用户而不是接受前端传入的userId。比如取消预约接口路径是/api/booking/cancel/{id}如果 Service 只删bookingId不看用户那么 A 用户就能取消 B 用户的预约。正确的做法是DELETE FROM booking WHERE id ? AND user_id ?把用户 ID 作为条件之一。另一个常见隐患是管理端接口没有鉴权。如果/api/admin/course/delete接口裸奔任何人拿到地址都能删课程。简单方案是加一个拦截器校验Authorization头里的 token 是否存在且未过期。如果源码里没做至少要给自己留个 TODO别等项目答辩时让老师当场演示越权漏洞。从那以后我每次拿到新项目源码都会强制走一遍“看 pom 版本 → 改数据库配置 → 启动后端 → 小程序真机联调 → 管理端打包”的流程顺手把容易翻车的地方记在项目的README里。这份资源里的代码只要按上面的顺序操作大概率半天内就能跑起来过程中遇到的问题也都不是玄学绝大多数是环境或边界条件。希望帮到你。本文还有配套的精品资源点击获取
返回列表