毕业设计实战:基于Spring Boot的健身房管理系统毕设全流程指南✨
去年做健身房管理系统毕设时,我在会员管理和课程预约的关联逻辑上卡了整整一周——一开始没处理"会员到期"逻辑,过期会员还能预约课程,导师演示时直接发现了这个业务漏洞😫 经过反复修改优化,终于总结出了这套健身房管理系统毕设实战经验!
一、需求分析:抓住健身房管理核心业务
刚开始我想做一个"万能健身系统",包含了营养管理、体测分析、社交功能等模块,结果导师说"重点不突出,核心会员管理和课程预约不清晰"。后来明白,健身房管理系统要围绕会员-课程-器材这个核心业务链展开。
1. 核心用户角色与功能
系统主要面向两类用户,功能定位要精准:
-
管理员端(经营管理核心):
- 会员管理:办理会员卡、设置到期时间、会员信息维护
- 教练管理:录入教练信息、设置教练专长、管理教练排班
- 课程管理:发布团课信息、设置课程容量、管理课程预约
- 器材管理:登记健身器材、维护器材状态、管理器材使用
- 健身房管理:维护分店信息、设置营业时间
-
会员端(服务体验核心):
- 课程预约:查看课程表、预约团课课程、取消预约
- 会员信息:查看会员状态、剩余有效期、消费记录
- 教练查询:查看教练信息、专长领域、用户评价
- 器材查看:了解器材信息、使用指南
2. 需求分析实战技巧
- 实地调研很重要:我实地走访了3家健身房,发现他们最需要"会员到期自动提醒"功能,于是重点设计了会员状态管理
- 绘制业务流程图:用DrawIO画出"会员办卡→预约课程→上课记录"的完整流程,答辩时清晰展示业务逻辑
- 明确业务规则:如"会员到期前7天提醒""课程开始前2小时可取消""同一时段只能预约一门课程"等约束条件
3. 可行性分析要点
- 技术可行性:Spring Boot+MySQL技术成熟,开发资料丰富
- 经济可行性:全部使用开源工具,零开发成本
- 操作可行性:界面设计参考主流健身APP,用户上手快
二、技术选型:稳定高效的开发方案
曾经尝试用微服务架构,结果服务注册发现配置卡了3天,最终选择单体架构,开发效率大大提升!
1. 技术栈选择理由
| 技术组件 | 选择理由 | 避坑提醒 |
|---|---|---|
| Spring Boot 2.7 | 配置简单,内嵌Tomcat | 不要用3.x,兼容性问题多 |
| MySQL 8.0 | 数据关系明确,适合会员管理系统 | 记得配置时区为东八区 |
| JSP + Bootstrap | 开发速度快,学习成本低 | Bootstrap用3.x版本 |
| MyBatis | SQL可控性强,方便优化 | 注意XML配置文件的路径 |
2. 开发环境搭建
# 创建Spring Boot项目
使用Spring Initializr
选择Web、MySQL、MyBatis依赖
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/gym_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
三、数据库设计:健身业务数据建模
最初设计时将会员信息和课程预约放在同一张表,导致查询性能低下。重新设计后按业务模块分表,系统响应速度大幅提升。
1. 核心数据表设计
会员表(yonghu)
CREATE TABLE `yonghu` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`yonghu_name` varchar(100) NOT NULL COMMENT '会员姓名',
`yonghu_phone` varchar(20) NOT NULL COMMENT '手机号',
`yonghu_id_number` varchar(20) DEFAULT NULL COMMENT '身份证号',
`yonghu_photo` varchar(255) DEFAULT NULL COMMENT '头像',
`yonghu_email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`huiyuan_type` int(11) DEFAULT '1' COMMENT '会员类型:1普通/2VIP',
`daoqi_time` date NOT NULL COMMENT '到期日期',
`insert_time` datetime DEFAULT NULL COMMENT '注册时间',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_phone` (`yonghu_phone`),
KEY `idx_daoqi` (`daoqi_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
课程预约表(kecheng_yuyue)
CREATE TABLE `kecheng_yuyue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`yonghu_id` int(11) NOT NULL COMMENT '会员ID',
`kecheng_id` int(11) NOT NULL COMMENT '课程ID',
`yuyue_time` datetime NOT NULL COMMENT '预约时间',
`yuyue_status` int(11) DEFAULT '0' COMMENT '预约状态:0已预约/1已取消/2已完成',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `fk_yonghu` (`yonghu_id`),
KEY `fk_kecheng` (`kecheng_id`),
UNIQUE KEY `uk_yonghu_kecheng` (`yonghu_id`, `kecheng_id`, `yuyue_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程预约表';
课程表(kecheng)
CREATE TABLE `kecheng` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`kecheng_name` varchar(100) NOT NULL COMMENT '课程名称',
`jiaolian_id` int(11) NOT NULL COMMENT '教练ID',
`jianshenfang_id` int(11) NOT NULL COMMENT '健身房ID',
`kecheng_types` int(11) DEFAULT NULL COMMENT '课程类型',
`kecheng_time` datetime NOT NULL COMMENT '上课时间',
`kecheng_duration` int(11) DEFAULT '60' COMMENT '课程时长(分钟)',
`max_people` int(11) DEFAULT '20' COMMENT '最大人数',
`signed_people` int(11) DEFAULT '0' COMMENT '已报名人数',
`kecheng_content` text COMMENT '课程介绍',
`create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='课程表';
2. 表关系设计要点
- 状态字段设计:预约状态(已预约/已取消/已完成)、会员状态(正常/即将到期/已到期)
- 唯一约束:同一会员同一时间段只能预约一门课程
- 容量控制:课程报名人数不能超过最大容量
四、核心功能实现与代码详解
1. 课程预约模块(会员端核心功能)
Service层实现:
@Service
public class KechengYuyueServiceImpl implements KechengYuyueService {
@Autowired
private KechengYuyueMapper kechengYuyueMapper;
@Autowired
private KechengMapper kechengMapper;
@Autowired
private YonghuMapper yonghuMapper;
@Override
@Transactional
public void yuyueKecheng(Integer kechengId, Integer yonghuId) {
// 1. 检查会员状态
YonghuEntity yonghu = yonghuMapper.selectById(yonghuId);
if (yonghu.getDaoqiTime().before(new Date())) {
throw new RuntimeException("会员已到期,请先续费");
}
// 2. 检查课程容量
KechengEntity kecheng = kechengMapper.selectById(kechengId);
if (kecheng.getSignedPeople() >= kecheng.getMaxPeople()) {
throw new RuntimeException("课程已满员,无法预约");
}
// 3. 检查时间冲突
Integer conflictCount = kechengYuyueMapper.selectCount(
new EntityWrapper<KechengYuyueEntity>()
.eq("yonghu_id", yonghuId)
.eq("yuyue_status", 0) // 已预约状态
.eq("DATE(yuyue_time)",
new SimpleDateFormat("yyyy-MM-dd").format(kecheng.getKechengTime()))
);
if (conflictCount > 0) {
throw new RuntimeException("该时段已预约其他课程");
}
// 4. 检查是否重复预约
Integer existCount = kechengYuyueMapper.selectCount(
new EntityWrapper<KechengYuyueEntity>()
.eq("yonghu_id", yonghuId)
.eq("kecheng_id", kechengId)
.in("yuyue_status", Arrays.asList(0, 2)) // 已预约和已完成
);
if (existCount > 0) {
throw new RuntimeException("已预约过该课程");
}
// 5. 创建预约记录
KechengYuyueEntity yuyue = new KechengYuyueEntity();
yuyue.setYonghuId(yonghuId);
yuyue.setKechengId(kechengId);
yuyue.setYuyueTime(kecheng.getKechengTime());
yuyue.setYuyueStatus(0); // 已预约
kechengYuyueMapper.insert(yuyue);
// 6. 更新课程报名人数
kecheng.setSignedPeople(kecheng.getSignedPeople() + 1);
kechengMapper.updateById(kecheng);
}
@Override
@Transactional
public void cancelYuyue(Integer yuyueId) {
KechengYuyueEntity yuyue = kechengYuyueMapper.selectById(yuyueId);
if (yuyue == null) {
throw new RuntimeException("预约记录不存在");
}
// 检查取消时间(课程开始前2小时可取消)
Date now = new Date();
long diff = yuyue.getYuyueTime().getTime() - now.getTime();
long hours = diff / (1000 * 60 * 60);
if (hours < 2) {
throw new RuntimeException("课程开始前2小时内不可取消");
}
// 更新预约状态
yuyue.setYuyueStatus(1); // 已取消
kechengYuyueMapper.updateById(yuyue);
// 更新课程报名人数
KechengEntity kecheng = kechengMapper.selectById(yuyue.getKechengId());
kecheng.setSignedPeople(kecheng.getSignedPeople() - 1);
kechengMapper.updateById(kecheng);
}
}
Controller层:
@Controller
@RequestMapping("/member/yuyue")
public class MemberYuyueController {
@Autowired
private KechengYuyueService kechengYuyueService;
@PostMapping("/yuyue")
public String yuyueKecheng(Integer kechengId, HttpServletRequest request) {
try {
// 获取当前登录会员
YonghuEntity member = (YonghuEntity) request.getSession().getAttribute("member");
kechengYuyueService.yuyueKecheng(kechengId, member.getId());
request.setAttribute("msg", "预约成功");
} catch (Exception e) {
request.setAttribute("msg", "预约失败:" + e.getMessage());
}
return "redirect:/member/kecheng/list";
}
@PostMapping("/cancel")
public String cancelYuyue(Integer yuyueId, HttpServletRequest request) {
try {
kechengYuyueService.cancelYuyue(yuyueId);
request.setAttribute("msg", "取消成功");
} catch (Exception e) {
request.setAttribute("msg", "取消失败:" + e.getMessage());
}
return "redirect:/member/yuyue/list";
}
}
2. 会员到期提醒模块(系统核心功能)
Service层实现:
@Service
public class MemberRemindServiceImpl implements MemberRemindService {
@Autowired
private YonghuMapper yonghuMapper;
@Override
@Scheduled(cron = "0 0 9 * * ?") // 每天上午9点执行
public void checkMemberExpire() {
// 查询7天内到期的会员
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 7);
Date sevenDaysLater = calendar.getTime();
List<YonghuEntity> expireMembers = yonghuMapper.selectList(
new EntityWrapper<YonghuEntity>()
.le("daoqi_time", sevenDaysLater)
.gt("daoqi_time", new Date()) // 还未到期
.eq("remind_sent", 0) // 未发送提醒
);
for (YonghuEntity member : expireMembers) {
// 发送提醒(这里可以集成短信、邮件等)
sendRemindMessage(member);
// 标记已发送提醒
member.setRemindSent(1);
yonghuMapper.updateById(member);
}
}
private void sendRemindMessage(YonghuEntity member) {
// 实现提醒发送逻辑
// 可以是系统消息、短信、邮件等
System.out.println("提醒会员:" + member.getYonghuName() +
",您的会员将于" + member.getDaoqiTime() + "到期");
}
}
3. 器材管理模块(管理员功能)
Service层实现:
@Service
public class QicaiServiceImpl implements QicaiService {
@Autowired
private QicaiMapper qicaiMapper;
@Override
public PageUtils queryPage(Map<String, Object> params) {
Page<QicaiEntity> page = new Page<>(
Integer.parseInt(params.get("page").toString()),
Integer.parseInt(params.get("limit").toString())
);
EntityWrapper<QicaiEntity> ew = new EntityWrapper<>();
// 器材名称模糊查询
if (params.get("qicaiName") != null) {
ew.like("qicai_name", params.get("qicaiName").toString());
}
// 器材类型筛选
if (params.get("qicaiTypes") != null) {
ew.eq("qicai_types", params.get("qicaiTypes"));
}
// 状态筛选
if (params.get("status") != null) {
ew.eq("status", params.get("status"));
}
IPage<QicaiEntity> result = qicaiMapper.selectPage(page, ew);
return new PageUtils(result);
}
@Override
@Transactional
public void updateQicaiStatus(Integer qicaiId, Integer status) {
QicaiEntity qicai = new QicaiEntity();
qicai.setId(qicaiId);
qicai.setStatus(status);
qicaiMapper.updateById(qicai);
}
}
五、系统测试:确保业务流程可靠
曾经以为功能实现就完成了,结果测试时发现会员到期后还能预约课程,紧急加了会员状态校验。
1. 功能测试用例
课程预约测试:
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 会员已到期 | 到期会员预约课程 | 提示"会员已到期,请先续费" |
| 课程已满员 | 会员预约满员课程 | 提示"课程已满员,无法预约" |
| 时间冲突 | 会员预约同一时段课程 | 提示"该时段已预约其他课程" |
| 正常预约 | 会员预约有空位的课程 | 提示"预约成功",课程人数+1 |
会员管理测试:
| 测试场景 | 操作步骤 | 预期结果 |
|---|---|---|
| 会员续费 | 管理员为会员续费 | 会员到期时间延长,状态恢复正常 |
| 到期提醒 | 系统检查到期会员 | 自动发送到期提醒消息 |
2. 业务规则测试
- 预约限制:同一时段只能预约一门课程
- 取消规则:课程开始前2小时内不可取消
- 容量控制:课程报名人数不超过最大容量
- 会员状态:到期会员无法预约课程
六、部署与演示准备
1. 数据库初始化脚本
-- 插入测试数据
INSERT INTO `yonghu` VALUES
(1, '张三', '13800138000', '110101199001011234', '/images/user1.jpg', 'zhangsan@qq.com', 1, '2024-12-31', NOW(), NOW()),
(2, '李四', '13800138001', '110101199001011235', '/images/user2.jpg', 'lisi@qq.com', 2, '2024-06-30', NOW(), NOW());
INSERT INTO `jiaolian` VALUES
(1, '王教练', '13800138002', '/images/coach1.jpg', 1, '国家级健身教练', '资深私教,擅长增肌减脂', NOW());
INSERT INTO `kecheng` VALUES
(1, '瑜伽入门', 1, 1, 1, '2024-01-15 19:00:00', 60, 20, 0, '适合初学者的瑜伽课程', NOW());
2. 演示流程设计
按照真实业务流程演示:
- 会员注册登录→查看课程表→预约瑜伽课程
- 管理员登录→添加新课程→管理会员信息
- 会员取消预约→系统释放名额→其他会员预约
3. 答辩重点准备
- 业务逻辑亮点:会员状态管理、课程容量控制、时间冲突检测
- 技术实现亮点:Spring定时任务、事务管理、数据一致性
- 系统特色:完整的健身业务流程、智能提醒功能
结语
基于Spring Boot的健身房管理系统毕设成功的关键在于:抓住会员管理核心,处理好课程预约业务,保证数据一致性。这套系统虽然业务逻辑相对复杂,但一旦掌握,无论是完成度还是技术含量都能得到导师认可。
记住:会员状态管理、课程预约逻辑、器材状态跟踪是三大得分点!需要完整源码、数据库脚本、部署文档的同学可以在评论区留言。
点赞收藏这篇,下次找流程不迷路~祝宝子们毕设顺利,轻松毕业!😘