每年这个时候,我都会在后台收到一堆关于毕业设计怎么选题、怎么做、怎么避开那些坑的私信。今年被问得最多的类型里,健身房管理系统绝对排得上号——需求明确、技术栈主流、功能边界清晰,做起来不容易翻车,答辩也有东西可讲。如果你正打算做一个基于SpringBoot+Vue的管理系统,又恰好选了健身这个场景,那这篇内容大概率能帮你省下几个晚上的无头摸索时间。
我拿到这个项目的标题时,第一反应是:它对应的不是那种“图书管理”式的老掉牙CRUD,而是一个带业务逻辑、带角色划分、带状态流转的完整前后端分离应用。场景虽然不大,但麻雀虽小五脏俱全——会员管理、课程排期、私教预约、器材维护、统计分析,每一块都能展开讲不少东西,论文里也有充足的内容可以写。这个选题放在毕业设计里,属于那种“看起来不难,但做完能学到真东西”的类型。
下面我会从设计思路、技术选型、数据库设计、前后端核心实现到实操踩坑,把整个项目的来龙去脉拆开揉碎讲一遍。内容里涉及的具体表结构、代码片段和接口设计,都是基于常见实践补全整理的,你完全可以直接拿去做参考或二次开发。
1. 整体设计与技术选型思路
1.1 为什么是SpringBoot + Vue的组合
先说技术选型。SpringBoot + Vue这套组合近几年来在毕业设计和中小型项目中几乎成了事实标准,不是没有原因的。从后端角度看,SpringBoot极大降低了SSM时代繁琐的XML配置成本,让开发者能把精力集中在业务逻辑本身;从前端角度看,Vue渐进式的学习曲线和组件化开发模式,恰好匹配一个从零开始、没有太多前端工程化经验的学生团队。
更重要的是,前后端分离的架构天然自带“亮点属性”。它能让你在毕业论文里名正言顺地写上一整章关于前后端交互的设计,包括RESTful API规范、跨域处理、Token鉴权、Axios请求封装、路由守卫等知识点。这些内容既是面试官爱问的东西,也是你写论文时最容易凑出篇幅的地方。
从实际的开发效率来评估,SpringBoot的自动配置和起步依赖、Vue的组件复用和热更新,都能让一个3到4人的小组或单打独斗的个体在4到6周内完成一个可用性不错的管理系统。这个周期放在毕业设计里,属于比较从容的节奏。
1.2 角色权限模型:用户体系是一切的起点
健身房管理系统的业务核心,是不同角色在一个场馆里的协作流程。按照常见的业务逻辑,我会把用户划分为三种角色:管理员、教练、会员。不同角色的所见所闻和可操作能力,必须通过权限模型做隔离。
第一种管理员,负责后台的全局管理——会员资料的增删改查、课程编排、私教订单的确认和取消、器材报修的处理、经营数据的查看,权限最高,能访问几乎所有接口。
第二种教练,负责处理与自己相关的业务——查看自己被分配的课程表、记录会员的体测数据、提交器材维护工单,但修改不了会员的账户信息和收费标准。
第三种会员,面向小程序端或前台用户端——查看课程安排、预约课程、购买私教套餐、查看自己的健身记录和体测报告。
这个模型的落地方式,我推荐用一张用户表加一个角色字段,而不是一上来就搞Spring Security那套复杂的权限体系。毕业设计阶段,用拦截器或AOP配合自定义注解控制接口访问,已经足够展示你对权限控制的思考,同时不拖慢开发进度。后面如果时间充裕,再考虑引入Spring Security + JWT做统一鉴权,这样论文的技术深度也能提升一个档次。
1.3 功能模块边界划分
把需求拆成模块,是编码之前必须完成的一步。结合健身房的实际使用场景,我会把系统划分为六块:
- 会员管理模块:会员资料的录入、修改、续费、状态变更,以及会员卡类型的定义。
- 课程管理模块:团操课程和私教课程的分类、排期、教练绑定,课程名额库存管理。
- 预约与订单模块:会员端查看课程并预约,教练端确认预约,管理员端处理退款或改期。
- 器材管理模块:器材档案、状态巡检、报修工单提交、维修进度记录。
- 体测管理模块:会员的体测数据沉淀,包括BMI、体脂率、肌肉量等指标,按周期生成变化趋势。
- 统计报表模块:按日、周、月维度统计会员增长数、课程预约量、收入数据,用图表展示。
需要提醒的是,功能模块不是越多越好。如果时间紧张,体测管理和统计报表可以做成最简单的形态——体测只做数据录入和列表展示,统计只做几个基础指标的折线图和柱状图——这样既能保证业务链路完整,又不会把自己拖进细节的泥潭。
2. 数据库设计:用六张表撑起一套业务
2.1 核心表结构规划
数据库设计是毕业设计里最不能省的一步。表设计得合理,后面写代码就是流水线作业;表设计得别扭,改起来能让人崩溃。基于上面的模块划分,我会在项目里规划以下核心表:
用户表:字段包括用户ID作为主键、用户名、加密密码、姓名、性别、手机号、角色、头像、状态、注册时间。
会员表:会员ID作为主键、关联用户ID只用于某些特殊场景、会员卡类型、开卡时间、到期时间、剩余上课次数、身高、体重、体脂率、备注。
课程表:课程ID作为主键、课程名称、课程类型标识区分团课还是私教、教练ID、健身房场地、开课时间、结束时间、课程容量、已选人数、状态字段表示未开始或已结束或已取消。
预约表:预约ID作为主键、会员ID、课程ID、预约状态字段表示已预约或已取消或已完成、预约时间、取消时间。
器材表:器材ID作为主键、器材名称、存放位置、购买日期、上次检修时间、维护状态字段表示正常或报修或维修中、备注。
体测记录表:记录ID作为主键、会员ID、体测日期、体重、体脂率、BMI、肌肉量、基础代谢、体测备注。
这六张表基本上覆盖了一个健身房管理系统最核心的数据模型。需要注意的是,教练实际上也属于用户,所以我选择在用户表上加角色字段,但对于会员会额外建立一张会员卡扩展表,存放套餐、到期日这类会员特有属性。这样设计不仅查询清晰,也方便后续功能扩展时不动根基。
2.2 状态字段的取舍与应用策略
在以上的字段规划中,最容易被忽略的是各种状态字段,但恰恰是这些状态字段,决定了你的业务流程是否严谨。
拿预约表来举例。会员提交预约后,状态默认为已预约;课程开始后,系统通过定时任务或手动触发把状态变更为已完成;如果会员在上课前取消,则状态变更为已取消,同时返还课程库存。教练端的确认操作,则把状态从已预约流转到已确认。这一套状态机的设计,虽然逻辑不复杂,但会在论文里成为一个很好的业务亮点,答辩时也经得起追问。
器材表的维护状态同理。管理员或教练在巡检中发现器材异常,可以直接在系统中把状态改为报修,提交工单后维修人员处理完再改为正常。这样系统不仅记录了最终结果,还记录了整个事件的流转过程,随时可以追溯历史,实用性要比单纯一张“器材列表”高得多。
提示:凡是涉及状态变更的操作,在代码里一定要在事务内完成。比如取消预约时,不仅要把预约表状态改掉,还要把课程表的已选人数减一,这两条SQL任何一条失败都会导致数据不一致,必须用@Transactional做原子性保护。
2.3 外键、索引与查询设计原则
不少同学建表时有一个习惯:能建外键就建外键。但在实际项目中,我强烈不建议在数据库层面使用物理外键,原因是它会成为数据写入的瓶颈,而且毕业设计阶段往往没有专职DBA来维护外键带来的约束开销。
更合理的策略是代码层维护关联。比如预约表和课程表、会员表之间的关系,通过关联ID在业务代码中显式判断和操作。在保证数据一致性的同时,也保留了SQL的灵活性——插入、更新时不需要外键校验开销,统计报表时也可以自由JOIN。
查询性能上,我会提前为高频查询字段添加索引。预约表的会员ID和课程ID、课程表的开课时间、用户表的手机号,这几个字段在业务过程中会大量出现在WHERE、ORDER BY、GROUP BY语句里,加索引能明显提升响应速度。数据量在十万级别以内时,这套方案完全够用。
3. 后端核心实现:从登录鉴权到业务接口闭环
3.1 登录鉴权与JWT实践
登录鉴权是每个管理系统都回避不了的环节。我的建议是不要用传统的Session方案,而是直接上JWT——它天然适合前后端分离架构,扩展性好,而且也是面试和论文中的高频考点。
具体实现思路是一个自定义的登录接口,成功验证后调用工具类生成Token,把用户ID、角色、过期时间这几项关键信息塞进Token,返回给前端。前端每发一次请求,都在请求头里带上Authorization字段,后端写一个拦截器统一解析,校验Token的合法性和时效性,然后把用户信息放进ThreadLocal或RequestContext,方便后续业务代码直接取用。
下面是一个简化版的后端校验逻辑框架:
public class JwtInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录和静态资源等白名单路径 if (handler instanceof HandlerMethod) { String token = request.getHeader("Authorization"); if (token == null || !JwtUtil.verify(token)) { response.setStatus(401); return false; } // 从Token中取出用户标识,存入请求上下文 Long userId = JwtUtil.getUserId(token); request.setAttribute("userId", userId); } return true; } }需要注意,JWT的核心价值是无状态,所以服务端不需要保存Token。但这也带来了一个缺点:Token在过期之前无法主动失效。如果遇到用户修改密码或强制下线等场景,就需要引入Redis黑名单或Token版本号机制。毕业设计阶段,我建议先不做那么复杂,把过期时间设为2小时,同时允许前端在过期后用刷新Token重新换取,就已经是超出要求的亮点了。
3.2 接口设计规范与统一返回体
写接口之前,先把接口规范和统一返回体定下来。这是让后续代码风格统一的基石,也是项目中代码可维护性的试金石。
我习惯定义一个Result对象,里面包含状态码、消息、数据三个字段。成功时状态码是200,业务异常时根据场景定义状态码,比如参数错误400、未登录401、无权限403、资源不存在404,系统异常统一500。前端Axios拦截器拿到返回体后,根据状态码做统一处理,成功就弹数据,失败就弹提示。
接口路径一般按资源命名,比如:
- POST /api/auth/login 用户登录
- GET /api/members 会员列表
- POST /api/members 新增会员
- PUT /api/members/{id} 修改会员信息
- DELETE /api/members/{id} 删除会员
- GET /api/courses 课程列表
- POST /api/appointments 会员预约课程
- DELETE /api/appointments/{id} 取消预约
按这套命名规则写下去,前后端联调时基本不用对文档,看路径就知道是在操作什么资源,这会让开发体验流畅很多。
3.3 核心业务接口的代码逻辑
接下来展示一个预约课程的后端核心逻辑。这个流程里涉及库存扣减、重复预约校验、会员状态校验三个核心点,一个都不能漏。
@Service public class AppointmentServiceImpl implements AppointmentService { @Resource private AppointmentMapper appointmentMapper; @Resource private CourseMapper courseMapper; @Resource private MemberMapper memberMapper; @Override @Transactional(rollbackFor = Exception.class) public Result createAppointment(Long userId, Long courseId) { // 1. 校验会员状态 Member member = memberMapper.selectByUserId(userId); if (member == null || member.getStatus() == 0) { return Result.error("会员状态异常,无法预约"); } // 2. 校验课程是否存在、是否已满 Course course = courseMapper.selectById(courseId); if (course == null || course.getStatus() == 2) { return Result.error("课程不存在或已取消"); } if (course.getSelectedCount() >= course.getCapacity()) { return Result.error("课程名额已满"); } // 3. 防重复预约 int exist = appointmentMapper.countByMemberAndCourse(member.getId(), courseId); if (exist > 0) { return Result.error("您已预约过该课程"); } // 4. 扣减库存 + 生成预约记录 courseMapper.increaseSelectedCount(courseId); Appointment appointment = new Appointment(); appointment.setMemberId(member.getId()); appointment.setCourseId(courseId); appointment.setStatus(0); // 0-已预约 appointmentMapper.insert(appointment); return Result.success("预约成功"); } }这个代码示例虽然简短,但它基本代表了一个完整管理系统中业务接口的写法——先校验后操作,重要操作加事务。你可能觉得这只是普通的CRUD叠加状态,但毕业设计的核心评分点恰恰在于这些细节的严谨程度。
注意:扣库存操作建议使用SQL原子操作
UPDATE course SET selected_count = selected_count + 1 WHERE id = ?,而不是先SELECT出来在代码里加一再UPDATE回去。前者在高并发下能避免超卖风险,后者在并发场景会产生脏数据,这个细节如果能在答辩时讲清楚,会给答辩老师留下非常好的印象。
3.4 文件上传与静态资源访问
健身房管理系统里,头像上传、体测报告的图片上传都是常见需求。SpringBoot里处理文件上传非常简单:
@PostMapping("/api/upload") public Result upload(@RequestParam("file") MultipartFile file) { String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); String fileName = UUID.randomUUID().toString() + suffix; String savePath = "E:/upload/" + fileName; file.transferTo(new File(savePath)); return Result.success("http://localhost:8080/files/" + fileName); }上传后的文件存放在本地磁盘,然后配置一个静态资源映射,让 /files/** 路径能够访问到磁盘目录里的文件。这样就避免了直接把图片存到数据库里的尴尬——数据库只存URL,查询时压力会小很多。这个小细节同样可以在论文的数据库设计章节里写一笔,作为“大字段不入库”的设计原则案例。
部署时唯一要注意的是,文件目录不能写在项目工程目录里面,否则每次重新打包部署都会把上传的东西覆盖掉。我身边就有同学因为图省事把图片存到了项目static目录下,导致后期重启服务图片全部丢失,这种坑踩一次就够了。
4. 前端核心实现:Vue3 + Element Plus实战要点
4.1 前端工程结构与初始化
前端部分我推荐Vue3 + Vite + Element Plus + Axios + Pinia这套组合。相比Vue2 + Webpack,Vue3的生态已经足够成熟,而且毕业设计用最新版本本身就是加分项。
创建项目时直接用Vite脚手架初始化,然后安装依赖。下面是工程的目录规划:
src/ ├── api/ # 接口请求封装 │ ├── login.js │ ├── member.js │ └── course.js ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 │ └── index.js ├── store/ # Pinia状态管理 │ └── user.js ├── views/ # 页面视图 │ ├── login.vue │ ├── layout.vue │ ├── dashboard/index.vue │ ├── member/index.vue │ ├── course/index.vue │ └── appointment/index.vue └── utils/ └── request.js # Axios封装这种结构你在网上的多数开源项目里都能看到,属于比较常规的前后端分离结构。它的好处是目录职责单一,哪个文件管什么都不需要过脑子。
4.2 Axios请求封装与拦截器落地
Axios封装是前端工程化里绕不开的一步。统一封装之后,页面里一行代码就能发起请求,而且日志、错误提示、鉴权处理都在一个地方统一管理,改起来只需要动一个文件。
import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:自动附加Token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config }) // 响应拦截器:统一处理状态码和业务异常 request.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { ElMessage.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res.data }, error => { if (error.response && error.response.status === 401) { ElMessage.error('登录状态已过期,请重新登录') router.push('/login') } else { ElMessage.error('网络异常,请稍后重试') } return Promise.reject(error) } ) export default request封装完成后,接口模块的代码就非常清爽了。比如会员管理模块:
// api/member.js import request from '@/utils/request' export function getMemberList(params) { return request({ url: '/members', method: 'get', params }) } export function addMember(data) { return request({ url: '/members', method: 'post', data }) } export function updateMember(id, data) { return request({ url: `/members/${id}`, method: 'put', data }) }页面里调用时只需要写一行const list = await getMemberList(params),然后就能拿到数据。代码量少、出错率低,联调时也容易定位问题。
4.3 路由守卫与按角色动态渲染菜单
权限控制在前端主要是两类工作:一类是路由层面的访问拦截,另一类是菜单和按钮的按需渲染。
先看路由守卫。Vue Router提供了beforeEach钩子,可以在跳转前判断登录状态和角色权限:
router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path === '/login') { next() return } if (!token) { next('/login') return } const role = localStorage.getItem('role') if (to.meta.roles && !to.meta.roles.includes(role)) { next('/403') // 无权限页面 return } next() })菜单渲染方面,可以维护一个菜单配置数组,每个菜单项有标题、图标、路径和允许的角色列表。渲染时根据当前用户的角色过滤菜单数组,这样管理员能看到整个后台菜单,教练和会员看不到与他们无关的操作入口。
这个方案实现起来简单直接,又能体现出你对权限控制的思考层次。比在一开始就去研究RBAC的按钮级权限设计要务实得多——那种方案不是不对,而是毕业设计的时间成本不划算。
4.4 图表展示与数据可视化
统计报表模块我推荐用ECharts,这是国内使用率最高的前端图表库,文档全、坑少、效果也足够好看。
需要在容器里放一个div,然后在onMounted生命周期里初始化图表,请求数据后setOption即可。一个会员增长趋势折线图的核心逻辑如下:
import * as echarts from 'echarts' const chartRef = ref(null) let chartInstance = null onMounted(async () => { chartInstance = echarts.init(chartRef.value) const res = await getMemberGrowth() chartInstance.setOption({ xAxis: { type: 'category', data: res.map(item => item.date) }, yAxis: { type: 'value' }, series: [{ type: 'line', data: res.map(item => item.count), smooth: true, areaStyle: {} }] }) }) onBeforeUnmount(() => { chartInstance && chartInstance.dispose() })ECharts在这里只需要展示趋势和对比,不需要做得太炫。但有一个细节我必须在表格里说明:页面切换时ECharts实例一定要销毁,不然会出现内存泄漏和图表错乱的诡异Bug。这个细节在网上教程中经常被忽略,而在实际操作中几乎一定遇到。
5. 开发流程实录与踩坑记录
5.1 一份可复用的项目里程碑排期
很多同学做毕业设计不是做不出来,而是没有节奏感。前面磨磨蹭蹭,最后两周通宵赶工,质量和心态双崩。合理的排期我建议按照下面的时间线安排:
第一周:需求分析和数据库设计。画用例图、ER图,把表结构确定下来,这一周不做编码。
第二周:搭建前后端工程骨架。后端写好统一返回体、异常处理、JWT工具类和拦截器,前端搭好路由、Axios封装和登录页面。
第三周:完成用户管理和会员管理模块。这是其他所有业务的基础,先跑通登录和基础的CRUD。
第四周:完成课程管理和预约模块。这是系统的核心业务,包含状态流转和库存操作,预留充足的调试时间。
第五周:完成器材管理、体测管理和统计报表模块。
第六周:写论文、画图表、整理项目文档、准备答辩演示流程。
这个节奏的前提是你每天能投入三到四个小时,周末加倍。如果你平时有其他课或实习,建议把周期拉长到八周,不要压缩每轮测试的时间。
5.2 我在实际开发中遇到的五个典型问题
开发过程中必然踩坑,有些问题当时让人抓狂,但回头看都是经验值。这里挑几个典型问题分享给你。
第一个是跨域问题。前端跑在8080,后端跑在8081,前端请求直接被浏览器拦截。刚开始我不知道有CORS这个东西,一度怀疑是后端接口路径写错了。解决办法有两种:后端加跨域配置,或者前端用Vite的proxy代理。我更推荐后者,因为它能在联调阶段保持代码不动,只用代理转发解决环境差异。
第二个是Axios拦截器里的状态码双标准。后端返回的业务状态码是200,但HTTP层的状态码也可能是200。如果没有区分清楚,后端返回业务错误时前端不会进入error回调,而会走success回调,导致明明登录密码错误,页面却跳转到了首页。理解了这一点之后,我在响应拦截器里加了一层业务状态码判断,问题才彻底消除。
第三个是后端日期时间差8小时的问题。前端提交的时间到后端显示成了前一天,排查半天发现是JSON序列化时区配置不对。解决方案是在application.yml里统一配置Jackson的时区:
spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8第四个是Element Plus表单校验的触发时机。在弹窗里新增会员时,明明填了手机号,点击确定却提示“手机号不能为空”,原因是我没有在关闭弹窗时重置表单校验状态。对表单组件调用clearValidate方法,或者直接调用resetFields,问题才能彻底解决。
第五个是列表分页参数丢失。从第二页切换回第一页时,查询条件被清空,数据看起来像是“跳走了”。这通常是因为分页组件触发了查询事件,而查询事件使用了独立的搜索参数对象。解决办法是把分页参数和搜索条件合并成一个对象,每次翻页时携带原始条件重新请求。
6. 论文写作要点与答辩加分项
6.1 论文结构怎么排
论文结构是否清晰,直接影响评分印象分。一份健身房管理系统论文的常规章节可以这样组织:
绪论部分讲背景和意义,说明为什么要做这个系统、国内外研究现状如何。技术选型部分解释为什么用SpringBoot、Vue、MySQL这些技术,以及它们之间如何配合。需求分析部分描述系统参与者、功能用例和非功能需求。总体设计部分画系统架构图、功能模块图、ER图和数据库表结构。系统实现部分按模块贴核心代码、截图和界面说明。测试部分写测试用例表、测试结论和遗留问题。
这套章节结构在绝大多数毕业设计论文中都能用,逻辑顺序和系统开发流程完全一致,写起来也比较顺畅。
6.2 答辩时一定要讲清楚的三个亮点
答辩环节最怕老师提问,但如果你真正理解了项目的核心设计,提问反而成了你展示的机会。我认为有三个点值得提前准备:
第一个是权限设计方案。老师几乎必问“不同角色如何控制访问权限”。这时候你可以从JWT Token里的角色信息、拦截器校验、前端动态菜单三个层面来解释,既能展示技术理解,又能表现出系统设计的完整性。
第二个是预约和库存的业务逻辑。重点讲清楚“扣库存为什么用原子SQL更新而不是先查后改”,以及“当多个会员同时预约最后一节课时的并发场景怎么保证不超卖”。哪怕你实际没有遇到并发问题,把这个思路讲明白,也能证明你对业务深度的思考是到位的。
第三个是数据库表之间的关系和状态字段设计。把预约状态流转、器材维护状态流转画成状态图,再加一张各表字段设计说明的表格,基本就能覆盖老师对数据库层的全部提问。
6.3 项目演示的流程编排
演示环节同样需要排练。我建议你提前准备一份演示脚本,按以下顺序走一遍:登录页面展示不同角色的不同菜单,打开会员管理新增一名会员,在课程管理新增一节课程,用会员账号预约这节课,到预约管理查看状态更新,到统计报表查看数据变化。整个过程控制在五到八分钟,每一步的操作都对应到系统的核心功能,会给你加分不少。
7. 功能扩展方向:让项目再往前一步
如果你的进度比预期快,或者论文需要更多的创新点,下面几个方向可以作为扩展参考。
第一个方向是引入Redis缓存。把课程列表、预约热门榜这类高频只读数据放进Redis,降低数据库压力,同时把用户Token放进Redis实现主动失效和刷新。这个方案能明显提升系统的并发表现,也让论文的技术深度上了一个台阶。
第二个方向是增加消息通知机制。当课程被取消时,通过WebSocket或邮件模板给已预约的会员发送通知。这个功能能体现系统的人性化设计,在用户体验维度加分。
第三个方向是微信小程序客户端。如果你有能力用uni-app或原生小程序写一个轻量级的会员端,让会员在手机上查看课程、预约、接收通知,那么整个系统的商业完整度会大幅提升,答辩时的展示效果也很好。
第四个方向是数据大屏可视化。针对场馆的经营数据做一个独立的大屏页面,用ECharts绘制大尺寸的图表、指标卡和趋势图,放在实验室或办公室的壁挂屏上,视觉冲击力非常强。
这些扩展方向不必都去做,选一个深度完成就绰绰有余了。提醒一句:任何扩展都要在核心功能全部稳定之后再做,不要本末倒置。
做毕业设计这件事,与其把它当成一个任务,不如当成一次完整的产品开发练习。你可能花了三个月写代码,最后答辩只有十分钟展示,但这三个月的过程中积累的调试经验、架构思维和踩坑教训,是任何考试都无法替代的。我个人做完这个项目后最大的体会是:代码能力不是看会了多少API,而是遇到问题时能不能定位原因、找到方案、然后再避免同类问题第二次发生。这套健身房管理系统谈不上惊艳,但如果你从头到尾亲手做过一遍,对SpringBoot和Vue的理解一定会发生质变。
最后分享一个小技巧:开发过程中养成写开发日志的习惯,哪怕每天只记两三行。遇到什么问题、怎么解决的、用了多久,都随手记下来,写论文的详实度会高不少。这套健身房管理系统只是一个起点,你可以根据自己的想法去调整字段、增加模块、优化交互——把它变成真正属于你自己的作品。