恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

前后端分离实战:SpringBoot2+Vue3健身房管理系统搭建与部署

  • 首页
  • 资讯中心
  • /
  • 前后端分离实战:SpringBoot2+Vue3健身房管理系统搭建与部署

相关资讯

水下垃圾检测YOLO实战:浑浊水域目标定位与鲁棒训练指南 2026/10/14 15:29:03
一卡通消费系统实战:Spring Boot+Vue前后端分离架构与扣款一致性设计 2026/10/14 15:29:03
Dockerfile核心指令详解:分层机制、多阶段构建与镜像优化实践 2026/10/14 15:29:03

最新资讯

离线语音合成完全指南:从选型到避坑的TTS落地路径
MySQL慢查询诊断实战:从连接检查到索引优化的DBA排查指南
MOS管在无刷电机的作用有哪些?
MySQL存储过程、函数与触发器:数据一致性的最后一道防线
STM32F103C8T6与SCL3300高精度角度传感SPI例程详解
实验 2.10 自动随机变化的颜色

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

前后端分离实战:SpringBoot2+Vue3健身房管理系统搭建与部署

发布时间:2026/10/14 15:29:03
前后端分离实战:SpringBoot2+Vue3健身房管理系统搭建与部署 前后端分离的Java项目这几年在技术社区里一直热度不减尤其是一套能跑通完整业务流的SpringBoot2Vue3系统对做毕业设计、转行练手或者小团队自用来说都非常合适。最近我经手了一套健身房管理系统的完整源码技术栈是SpringBoot2MyBatis-PlusMySQL8.0做后端Vue3Element Plus做前端还附带了数据库脚本和说明文档。我花了一整天把整个项目从环境搭建到最终部署完整跑了一遍过程中踩了不少坑也顺手梳理了这套系统的设计思路和核心代码逻辑今天一次性把这些东西都写出来。这篇文章不是简单贴源码而是想清楚讲明白这套健身房管理系统“为什么这样设计”、“关键代码怎么落地”、“哪些地方容易翻车”。如果你正准备用类似技术栈做一个带会员管理、课程预约、器材报修这类业务场景的Web系统或者想找一个结构清晰的前后端分离项目来学习这篇文章应该能帮你少走很多弯路。1. 项目全貌健身房管理系统到底在管理什么1.1 业务角色与核心流程拆解健身房管理系统乍一听范围挺大实际上核心业务是非常具体的。我拿到源码后先做了两件事看数据库表结构再看前端路由。前后端两侧一对齐整个业务边界就清晰了。这套系统的业务角色主要分成四类前台接待、会员、私教教练、系统管理员。前端路由和后台菜单都是围绕这四个角色展开的。前台接待的日常工作最繁琐核心场景是会员到店后的开卡、续费、充值、扣次还有处理课程预约和器材报修登记。系统里对应的是“会员管理”和“收银中心”两个模块。会员端相对简单核心是查看自己的卡信息、剩余次数、预约记录以及在线预约团操课或私教课。教练端最核心的功能是排课和查看自己的课表教练可以在一段时间内添加可授课时段会员在教练开放的时段内进行预约。管理员端则是全局视角管理员工账号、课程分类、器械档案、财务报表以及查看各类统计图表。整个系统的核心闭环是这样的前台上新会员开卡 - 会员到店刷手环/报手机号消费 - 会员约课 - 教练上课 - 器材出现问题走报修流程 - 财务每天查看营业额。所有的功能模块都是围绕这条业务链展开的这也是为什么这套系统比简单做几个CRUD页面有价值得多。1.2 功能模块地图一套系统怎么覆盖健身房日常我按照源码里的控制器和前端页面把功能模块画了一张脑图。这里帮你用文字梳理出来你在看源码的时候可以对照着看登录认证模块验证码登录、JWT签发、路由权限控制会员管理模块会员档案CRUD、会员卡开卡、续费、充值、挂失/补卡、到期提醒课程管理模块团操课分类、私教课分类、课程排期管理、教练排课预约管理模块会员端预约课程、预约记录查询、教练端查看预约名单器材管理模块器材档案登记、器材报修、维修状态流转、保养计划记录收银与财务模块充值计费、消费扣费、收支流水、营业统计报表报表统计模块会员增长趋势、到店率统计、课程预约热度、卡项销售占比系统管理模块员工账号管理、角色权限分配、菜单管理、操作日志我实际看代码的时候发现源码里还额外做了一个“操作日志”功能用AOP切面记录管理员的所有写操作包括操作人、操作时间、请求路径、参数、执行结果。这个功能虽然不起眼但在答辩或面试时是一个不错的加分项因为它在业务代码之外展示了切面编程的实战能力。2. 技术栈选型思路为什么偏偏是这套组合2.1 SpringBoot2 MyBatis-Plus把开发效率放在第一位当前做Java Web项目技术选型上有几种主流路线Spring Boot 2搭配Spring Data JPASpring Boot 2搭配MyBatis-Plus以及更新一些的Spring Boot 3搭配MyBatis-Plus。这套源码选的是SpringBoot2.x加MyBatis-Plus的组合。为什么这么选这里有一个很实在的权衡逻辑。Spring Boot 3要求JDK17起步虽然性能更好但对很多还在用JDK8的开发环境不太友好。SpringBoot2.x版本对JDK8的支持非常成熟生产环境稳定时间足够长学习资料也最多。更重要的是SpringBoot2.x搭配MyBatis-Plus的生态非常成熟网上无论遇到什么问题几乎都能搜到解决方案。再说MyBatis-Plus。它对比原生MyBatis最大的优势是“单表CRUD免写SQL”。用户表、卡种表、预约记录表这类基础表的增删改查全部用BaseMapper提供的方法搞定连SQL都不用写一行。分页查询也内置了物理分页插件只需要配置一个PaginationInnerInterceptor就能自动拼接LIMIT语句。我用这套源码里的“会员列表”查询举个例子。后端接收page、limit、keyword三个参数用LambdaQueryWrapper构造查询条件一行代码都不用写XML就能完成分页搜索public PageResultMember pageMember(long page, long limit, String keyword) { LambdaQueryWrapperMember wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(keyword), Member::getName, keyword) .orderByDesc(Member::getCreateTime); PageMember p new Page(page, limit); memberMapper.selectPage(p, wrapper); return new PageResult(p.getTotal(), p.getRecords()); }这个代码块体积不大但解决了查询列表80%的场景。真正复杂的报表统计SQL才会手写到XML里这也是MyBatis-Plus在实际项目中非常受欢迎的核心理由简单场景零SQL复杂场景能兜底。2.2 Vue3 Element PlusSPA交互比传统模板引擎舒服在哪健身房管理系统如果放在十年前大概会用JSP加Bootstrap前后端混在一起写。现在这一套选择了前后端彻底分离后端只提供JSON接口前端用Vue3做单页应用。前端这边用了Vite构建工具、Vue3的组合式API、Element Plus组件库、Pinia状态管理路由用的Vue Router。这个技术组合在目前的前端开源项目里非常常见。我看了前端代码后发现一个值得学习的点封装了一个统一的request模块。所有HTTP请求都走同一条通道统一挂载token、统一处理401跳转、统一弹错误提示。import axios from axios import { ElMessage } from element-plus import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) request.interceptors.response.use( res { const data res.data if (data.code ! 200) { ElMessage.error(data.msg || 请求失败) return Promise.reject(new Error(data.msg)) } return data }, err { if (err.response err.response.status 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(err.message || 网络异常) return Promise.reject(err) } )这是一个非常标准的前端封装思路。所有接口都统一返回{code, msg, data}格式code为200表示成功登录过期统一跳转页面。看起来简单但在前后端分离项目中这套封装能省下大量重复代码。2.3 MySQL8.0特定版本的坑与配置数据库用了MySQL8.0这在当前已经是主流版本。不过MySQL8.0和MySQL5.7之间存在几个实际坑很多第一次使用的人会栽在这里。第一个坑是驱动类名变了。MySQL8.0对应的JDBC驱动类不再是com.mysql.jdbc.Driver而是com.mysql.cj.jdbc.Driver同时URL需要额外加上时区和公钥配置spring: datasource: url: jdbc:mysql://localhost:3306/gym_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver这里有几个细节值得说明。serverTimezoneAsia/Shanghai如果不配默认和本地时间相差8小时查出来的时间全部是错的。allowPublicKeyRetrievaltrue如果不配使用一些客户端连接时会直接报Public Key Retrieval is not allowed的错误。第二个坑是字符集。MySQL8.0默认字符集是utf8mb4实际上这比5.7时代的utf8要合理得多但前提是建库时显式指定。如果建表时没有指定字符集遇到表情符号存储会直接报错。这套源码的初始化SQL里在建表语句尾部显式加了ENGINEInnoDB DEFAULT CHARSETutf8mb4这个细节值得学习。第三个坑是密码加密规则。MySQL安装时如果选择了默认加密方式正好配合MySQL8.0自带的caching_sha2_password但某些老版本的图形化客户端可能连不上。这种情况要么换新客户端要么在MySQL里把用户加密方式改成mysql_native_password我在部署文档里看到源码作者把这个情况的解决方案也写进去了。3. 数据库设计这套系统最值钱的地基3.1 会员业务相关的表设计会员模块是整套系统的核心。我在数据库里翻了半天发现围绕“会员”这个概念拆了很多张表互相咬合得非常清晰。主表member存的是会员基础档案包括姓名、手机号、性别、生日、身份证号、紧急联系人、开卡门店等。注意手机号字段在数据库里设了唯一索引因为手机号是会员登录和身份识别的主要凭证。卡种表card_type定义健身房在售的各类会员卡字段大致是卡名称月卡、季卡、年卡、次卡、卡类型时长卡/次数卡、有效时长天数、总次数、价格、状态。这张表本身不复杂但它是一个典型的数据字典表跟会员卡是分离开的。真正跟会员绑定的卡是member_card表记录的是某个会员在某段时间内持有的具体卡实例。字段包括会员id、卡种id、开卡时间、到期时间、剩余次数、状态。这里有一个关键设计冗余了卡种的快照信息。如果卡种价格改了已经售出的卡不受到影响这样处理后续账目核对时非常方便。还有一张recharge_record表记录会员的每次充值字段包括会员id、充值金额、赠送金额、支付方式、操作员id、充值时间。这种流水表的共同设计原则是只增不改如果要退款也是新插一条负向记录而不是去修改原来的记录。这种模式叫流水账财务做账的时候离不开它。3.2 课程预约与教练排期防止冲突的关键设计健身房系统的复杂度很大程度上集中在课程预约上。这套源码用三张表来处理这块业务。教练表coach跟员工表employee是分开的。教练的个人基本信息在employee里教练特有的职业资质、主教课程类型、课时费等信息放在coach表里两个表用employee_id关联。这种“主数据 扩展信息”的模式在很多业务系统里都会用到。课程排期表course_schedule存的是“未来某天某个时段教练会上某节课”这一事实。字段包括课程id、教练id、上课日期、开始时间、结束时间、预约人数上限。预约记录表booking_record存的是每个会员的预约行为排期id、会员id、预约时间、状态已预约/已取消/已上课/已爽约。这套预约系统在数据库层面最值得关注的是“防冲突”策略。一个教练同时间段不能排两节课一个会员同时间段不能预约两节课。源码里做的是一个双重保障应用层代码在插入前先查一遍重叠区间数据库层给coach_id和start_time、end_time建了唯一索引。代码层面的查询逻辑是这样的SELECT COUNT(*) FROM course_schedule WHERE coach_id #{coachId} AND #{endTime} start_time AND #{startTime} end_time这个判断两个时间段是否重叠的写法很经典用“新结束时间大于旧开始时间且新开始时间小于旧结束时间”来判定有交集。比直接比较一个相等条件要严谨得多。3.3 器材报修与员工管理状态机的妙处器械管理模块看着简单实际处理好需要状态流转意识。器材表equipment的字段包含器械名称、编号、所在区域、购买日期、当前状态而状态值是这套表设计里最有意思的地方。器材状态在系统里被定义成一个枚举正常、维修中、已报废。一次完整的报修流程是这样的前台发现器材故障在equipment_repair表里新增一条报修单状态设为待维修维修师傅接单后状态改为维修中维修完成状态改为已修复管理员复核后把器材主表状态切回正常如果发现彻底坏了则改为已报废。这个状态流转模型放到任何工业类管理系统里都通用。它的核心收益是维修历史和器材主数据分离每一次维修都有完整的留痕记录。即使后面要统计某台器械一年修了几次查维修表就能快速得出答案不需要去解析主表里的备注字段。3.4 RBAC权限表四张经典表打天下系统管理部分用了最经典的RBAC模型四张核心表user表员工登录账号、role表角色、menu表菜单、user_role和role_menu两张关联表。这套方案的逻辑很清晰用户不直接和菜单权限挂钩而是先挂一个角色角色再挂一批菜单。前台的登录用户被分到“前台”角色教练被分到“教练”角色管理员被分到“管理员”角色。我看到源码里后端登录成功后会查询用户对应的菜单列表返回给前端。前端拿到菜单数据后动态生成侧边导航栏没有权限的菜单直接不渲染。这里的权限控制粒度精确到按钮级别比如管理员可以删除会员普通前台只能编辑。说到RBAC模型实际项目里有两种做权限的方式一种是后端每次请求都做接口级别的权限校验另一种是前端只做菜单显隐控制。这套系统两种都做了后端用拦截器加自定义注解来校验接口权限前端用动态路由来控制页面展示。完整的做法应该就是这样双管齐下才能真正达到权限安全。4. 核心业务实现手把手拆解关键逻辑4.1 登录鉴权链路JWT配合拦截器的完整闭环登录部分这套源码没有用极易引入复杂配置的Spring Security而是选择JWT配自定义拦截器。这个选择对毕业设计级别和中小型项目来说很合理代码更直观也更容易在答辩时讲清楚。登录流程是这样的用户输入账号密码和验证码 - 后端校验验证码 - 校验账号密码 - 登录成功后生成一个JWT令牌返回给前端。public String login(LoginDTO loginDTO) { Captcha captcha captchaService.get(loginDTO.getUuid()); if (captcha null || !captcha.getCode().equalsIgnoreCase(loginDTO.getCaptcha())) { throw new BusinessException(验证码错误); } User user userMapper.selectOne(new LambdaQueryWrapperUser() .eq(User::getUsername, loginDTO.getUsername())); if (user null || !PasswordUtil.matches(loginDTO.getPassword(), user.getPassword())) { throw new BusinessException(用户名或密码错误); } if (user.getStatus() 0) { throw new BusinessException(账号已被禁用); } String token JwtUtil.createToken(user.getId(), user.getUsername()); user.setLastLoginTime(LocalDateTime.now()); userMapper.updateById(user); return token; }密码存储这块要特别说一句这套系统用的不是明文密码也不是简单的MD5而是加盐的哈希算法。MD5虽然网上还有人在教程里用但在真实项目里是明确不推荐的因为彩虹表攻击可以轻松逆向出弱密码。加盐值之后安全性会高很多。登录成功后每次请求携带token后端注册了一个拦截器做统一校验public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { if (!(handler instanceof HandlerMethod)) { return true; } String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); try { Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); request.setAttribute(username, claims.get(username)); return true; } catch (Exception e) { throw new BusinessException(登录已过期请重新登录); } } throw new BusinessException(未登录); }这种写法的优势是只要注册拦截器的时候排除掉登录接口和静态资源路径其他所有需要登录的接口自动受到保护不需要每个控制器里写重复的鉴权代码。不过这里有一个容易被忽略的工具类设计JwtUtil里必须同时包含两个方法createToken用来生成tokenparseToken用来解析token。密钥放在配置文件里而不是硬编码在代码中。我看到源码里把JWT的过期时间也做成可配置项了默认是2小时前端通过401响应统一跳登录页。整个链路非常顺畅。4.2 会员开卡事务处理多表写入怎么保证数据一致开卡这个操作表面上看只是往会员卡表里插一条记录但放到真实业务里如果操作中断就会出现“钱已收入但会员卡没开出来”的尴尬局面。这套源码在开卡逻辑上用了Spring的声明式事务。来看一下开卡的核心代码逻辑Transactional(rollbackFor Exception.class) public void openCard(OpenCardDTO openCardDTO) { // 1. 创建会员档案如果会员不存在 Member member createOrGetMember(openCardDTO.getMemberInfo()); // 2. 计算开卡时间与到期时间 CardType cardType cardTypeMapper.selectById(openCardDTO.getCardTypeId()); LocalDateTime startTime LocalDateTime.now(); LocalDateTime endTime startTime.plusDays(cardType.getValidityDays()); // 3. 创建会员卡实例 MemberCard memberCard new MemberCard(); memberCard.setMemberId(member.getId()); memberCard.setCardTypeId(cardType.getId()); memberCard.setStartTime(startTime); memberCard.setEndTime(endTime); memberCard.setRemainingTimes(cardType.getTotalTimes()); memberCard.setStatus(1); memberCardMapper.insert(memberCard); // 4. 记录收款流水 RechargeRecord record new RechargeRecord(); record.setMemberId(member.getId()); record.setAmount(cardType.getPrice()); record.setType(1); record.setOperatorId(SecurityUtils.getCurrentUserId()); rechargeRecordMapper.insert(record); }类上标注Transactional(rollbackFor Exception.class)表示只要方法内部抛出任何异常之前执行的插入操作全部回滚数据库自动撤销。注意rollbackFor Exception.class这个参数不能少虽然Spring默认对RuntimeException回滚但对检查型异常默认是提交事务的。我在跑代码的时候特意测试了这个异常场景在步骤3和步骤4之间故意抛出一个运行时异常模拟系统崩溃。实测结果是member表、member_card表、recharge_record表全部没有新增数据事务生效了不会出现脏数据。这里需要提一个Spring事务的经典大坑事务自调用失效。如果在一个类内部的另外方法里调用这个openCard方法注解会失效因为事务是通过代理对象生效的直接调用本类方法是绕过代理的。我看到源码里的调用方式是从Controller层调Service层跨Bean调用规避了这个问题。很多人开发时在这个问题上翻车可以参考这个写法。4.3 课程预约冲突检测这段逻辑是精髓课程预约的并发冲突是这套系统里最值得深入研究的一段逻辑。表面上需求很简单会员选一个课点预约按钮系统判断是否冲突不冲突则写入预约记录。先看判断冲突的核心代码public void bookCourse(BookingDTO bookingDTO) { CourseSchedule schedule scheduleMapper.selectById(bookingDTO.getScheduleId()); if (schedule null) { throw new BusinessException(课程排期不存在); } if (schedule.getBookedCount() schedule.getMaxCount()) { throw new BusinessException(该课程已约满); } // 检查会员是否在同一时间段已有预约 LambdaQueryWrapperBookingRecord wrapper new LambdaQueryWrapper(); wrapper.eq(BookingRecord::getMemberId, bookingDTO.getMemberId()) .eq(BookingRecord::getStatus, 1) .apply(({0} end_time AND {1} start_time), schedule.getStartTime(), schedule.getEndTime()); Long count bookingRecordMapper.selectCount(wrapper); if (count 0) { throw new BusinessException(您在该时间段已有预约请先取消原预约); } // 扣减名额并插入预约记录 scheduleMapper.updateBookedCount(schedule.getId()); BookingRecord booking new BookingRecord(); booking.setMemberId(bookingDTO.getMemberId()); booking.setScheduleId(schedule.getId()); booking.setStatus(1); bookingRecordMapper.insert(booking); }注意这里查询冲突用了一个动态拼接的条件.apply(({0} end_time AND {1} start_time), ...) 把两个时间参数拼进SQL。这个写法的门道在于它在数据库层面判断“待预约课程的开始时间小于已预约记录的结束时间并且待预约课程的结束时间大于已预约记录的开始时间”这就是区间重叠判断。如果两个用户同时点击同一个课最后一个名额会出现超卖问题吗源码里给schedule表的booked_count字段做了updateBookedCount操作SQL是这样的UPDATE course_schedule SET booked_count booked_count 1 WHERE id #{id} AND booked_count max_count这行SQL利用数据库的行级锁和条件更新保证了多人同时约课时不会出现超卖只有一条更新语句能成功另一条的更新影响行数为0然后抛出约满异常。这种方案也是市面上处理库存扣减比较轻量可靠的做法。4.4 数据报表统计复杂SQL的正确打开方式健身房的经营者最关心的问题是今天卖了多少钱、来了多少人、哪个课最受欢迎。这套系统的报表统计部分用了几个比较复杂的SQL查询。以“近7天营业额趋势”为例核心思路是把营收数据按天分组聚合public ListRevenueStatistic recentRevenue(int days) { LocalDate endDate LocalDate.now(); LocalDate startDate endDate.minusDays(days - 1); DateTimeFormatter formatter DateTimeFormatter.ofPattern(yyyy-MM-dd); return rechargeRecordMapper.selectRevenueByDateRange( startDate.format(formatter), endDate.format(formatter)); }对应的XML SQLselect idselectRevenueByDateRange resultTypeRevenueStatistic SELECT DATE(create_time) AS date, IFNULL(SUM(amount), 0) AS revenue FROM recharge_record WHERE DATE(create_time) BETWEEN #{startDate} AND #{endDate} GROUP BY DATE(create_time) ORDER BY DATE(create_time) /select这里有一个隐藏的技巧如果某一天没有任何收入在结果集中这个日期是缺失的。前端如果直接拿结果画折线图图表会缺少中间某天导致线条断开。处理办法是前端补全日期或者后端循环填充数据。系统源码里后端专门做了一个补全逻辑遍历所有日期缺失的日期补0金额这样前端拿到的永远是连续7天的数据。这么一看报表开发确实不光是写SQL更重要的是考虑数据缺失、前端图表展示体验这些细节。这套源码里的处理方式是比较规范的做法。5. 环境搭建与部署避坑实录5.1 本地开发环境准备版本矩阵不能乱搭把项目跑起来的第一步是准备一套能匹配源码的本地环境。这里我把每个组件的版本要求列出来这是我实际验证过的组合缺一个版本都可能出问题JDK 8或11推荐用JDK8SpringBoot2对8的兼容性最稳Maven 3.6用来构建后端模块MySQL 8.0.x注意5.7在部分SQL语法上不兼容Node.js 16低于16跑不动Vite3构建工具npm或pnpm安装前端依赖我最初在这个环节犯过一次错本机是JDK17直接eveluate后端项目发现SpringBoot2里的某些自动配置类在JDK17下反射会报错。后来切回JDK8立刻正常。所以如果你用JDK17遇到奇怪的错误第一反应应该是换个JDK版本试试。前端依赖安装也可能踩坑。npm install在大陆网络环境下经常失败解决方案是配置镜像源npm config set registry https://registry.npmmirror.com系统源码里有package.json直接进入前端目录执行npm install再npm run dev本地Vite默认端口是5173。后端端口在application.yml里配置我看到的版本用的是8080端口。5.2 配置文件与数据库初始化数据库初始化是整个搭建过程中最容易出问题的一环。源码中提供了init.sql文件用MySQL客户端执行一下即可mysql -uroot -p init.sql初始化脚本里创建了数据库gym_system并包含所有表的建表语句和初始测试数据。里面自带管理员账号用户名是admin密码是加盐哈希后的值登录时没办法直接看到明文只能正常走登录接口。我建议你在执行初始化脚本前先检查一下脚本里是否存在DROP TABLE IF EXISTS语句。如果不存在且本地已经建过同名表执行脚本会报错。稳妥的步骤是先手动drop掉旧库再导入新脚本DROP DATABASE IF EXISTS gym_system; CREATE DATABASE gym_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;后端配置文件里的数据库连接信息务必按本机实际环境修改。尤其是密码源码里的都是作者本地的dev密码你自己部署时必须改。5.3 前后端联调与生产构建本地开发时前端开发服务器调用后端接口会遇到跨域问题。源码里后端写了一个全局CORS配置类允许跨域请求开发阶段直接访问Vite的5173端口就能连上8080的后端。到了生产部署阶段前后端分离项目通常是后端打成一个jar包前端构建成静态文件丢进Nginx。前端构建命令npm run build构建产物在dist目录下把整个dist目录上传到服务器Nginx配置如下server { listen 80; server_name your-domain.com; root /var/www/gym/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }这个配置里最关键的是第二段location /用try_files把路由回退到index.html。如果不加这段Vue Router使用history模式时页面一旦刷新Nginx会直接返回404因为服务端没有对应的真实文件路径。同理图片等静态资源也通过这种方式进行访问。后端jar包启动命令mvn clean package -DskipTests java -jar gym-admin.jar --server.port8080如果Nginx和后端不在同一台机器proxy_pass里的127.0.0.1要改成后端服务器的真实IP。6. 常见问题与排查经验速查表我完整跑通这套项目过程中整理了遇到过的几个高频问题每一个都对应真实项目中很典型的坑。这里整理成速查表方便你直接对照排查。问题现象可能原因排查与解决办法前端npm install报错网络超时或证书错误默认npm源访问慢或网络受限切换npm镜像源删除node_modules和package-lock.json后重装启动后端时数据库连接失败MySQL服务未启动或用户名密码错误先确认本机MySQL能正常连接再核对application.yml中的账号密码查询数据库时间比本地少8小时JDBC连接串缺少时区参数在数据库连接URL上追加serverTimezoneAsia/Shanghai分页查询返回全量数据LIMIT不生效MyBatis-Plus分页插件未注册检查是否配置PaginationInnerInterceptor到MyBatis拦截器链里点击部署到Nginx的页面刷新变成404Vue Router history模式与Nginx不兼容给Nginx加try_files $uri $uri/ /index.html规则上传的图片在Linux服务器上访问不到文件存储在本地磁盘路径Nginx映射没配把文件路径地址用配置文件管理并让Nginx或SpringBoot代理此路径数据库连接每天固定时间断开报错与MySQL wait_timeout有关连接空闲超时被断开数据库连接池配置timeout参数执行空闲连接回收大批量课程导入时报错事务回滚SQL语法或唯一键冲突查看完整异常堆栈确认是否有唯一索引重复数据前端请求总是401token过期或请求头未携带token检查请求拦截器是否统一挂载Authorization检查登录态过期时间会员卡明明显示剩余次数用完却仍能预约booked_count和剩余次数扣减没有放在同一个事务里核对预约方法是否有Transactional事务控制这里的排查思路不一定只适用于这套系统。很多Java Web前后端分离项目在部署阶段都会遇到一样的坑养成看日志、看堆栈的习惯特别重要。7. 一些私房话这套系统应该怎么去学习和扩展这套健身房管理系统源码最让我欣赏的地方是它的业务完整度会员、卡种、课程、预约、器材、收银、报表、权限全部打通。很多练手项目的通病是只有简单的单表CRUD你看完只能学到怎么调用框架完全理解不了业务闭环。这套系统至少像是一个真实可运营的健身房后台每个模块之间都是有关联的。如果你要拿它做毕业设计或项目面试讲解我建议按这个顺序去研究源码先看数据库设计把每张表的职责搞清楚再看后端Controller层怎么暴露接口然后看Service层怎么组织业务逻辑最后才是看前端页面怎么对接。千万不要直接打开前端页面开始点点点那样你什么都学不到。我带着读者操作的习惯是把后端代码拉进开发工具从前到后走一遍请求路径打开调试模式顺着一次请求把每一层代码都看一遍。基于这套系统还能做不少有价值的扩展。比如增加一个私教课程购买功能会员可以单独购买私教课时包增加一个健身房门禁的对接方案把会员卡数据推送给硬件设备或者增加一个基于腾讯云或阿里云的短信提醒给会员发送到期续费通知。这些功能扩展如果写在论文里或面试介绍里都会是加分项。我自己在复盘这次完整跑项目的过程中最大的一个体会是技术栈本身不是难点SpringBoot和Vue3的用法都是固定的真正的难点永远是理清业务关系和数据关系。这套系统把健身房的真实业务压缩成了一个可运行的软件模型光这一点就够你咀嚼一阵子了。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号