温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片!
1. 项目背景与意义
随着全民健身意识的提升和瑜伽运动的普及,传统瑜伽馆依赖手工登记、电话预约的管理模式已难以满足日益增长的会员需求和精细化管理要求。课程信息更新不及时、预约冲突、会员数据分散、财务统计繁琐等问题,严重影响了场馆的运营效率和用户体验。
因此,设计并实现一套基于SpringBoot的瑜伽馆课程预约管理系统具有重要的现实意义:
- 提升运营效率:将课程发布、会员预约、签到核销、财务对账等流程线上化、自动化,减少人工操作和错误。
- 优化用户体验:为会员提供7x24小时在线课程浏览、一键预约、个人课表查看、历史记录查询等便捷服务。
- 实现数据驱动决策:系统化收集课程热度、会员活跃度、收入等数据,为课程优化、营销策略制定提供数据支持。
- 降低管理成本:通过系统规范业务流程,减少沟通成本,实现无纸化办公。
本系统旨在为中小型瑜伽馆提供一个功能完整、易于部署和维护的数字化管理解决方案。
2. 技术栈选型
系统采用前后端分离的架构模式,后端基于SpringBoot框架快速构建,具体技术栈如下:
2.1 后端技术栈
- 核心框架:Spring Boot 2.7.x
- Web框架:Spring MVC
- 安全框架:Spring Security + JWT (JSON Web Token)
- 数据持久层:
- ORM框架:MyBatis-Plus
- 数据库:MySQL 8.0
- 连接池:HikariCP
- 缓存:Redis (用于存储会话、验证码、热门课程信息)
- 消息队列:RabbitMQ (可选,用于异步处理预约成功通知、课程提醒等)
- API文档:SpringDoc OpenAPI 3 (Swagger UI)
- 构建工具:Maven
- 单元测试:JUnit 5, Mockito
2.2 前端技术栈 (建议)
- 框架:Vue 3 + Element Plus 或 React + Ant Design
- 构建工具:Vite / Webpack
- 状态管理:Vuex / Pinia (Vue) 或 Redux / MobX (React)
- HTTP客户端:Axios
- 路由:Vue Router / React Router
2.3 开发与部署
- 版本控制:Git
- 容器化:Docker, Docker Compose
- 持续集成/部署:Jenkins 或 GitLab CI/CD (可选)
3. 系统核心功能模块设计
系统主要分为后台管理端和会员小程序/网页端,核心功能模块如下:
3.1 后台管理端
- 仪表盘:展示关键运营数据(今日预约数、课程总数、会员数、收入概览)。
- 课程管理:课程的增删改查、排期、设置最大预约人数、教练关联。
- 会员管理:会员信息管理、会员卡办理与充值、消费记录查询。
- 预约管理:查看所有预约记录、处理预约取消、手动签到/核销。
- 教练管理:教练信息管理、课程关联、排班查看。
- 场地管理:瑜伽教室信息管理。
- 财务管理:课程收入统计、会员充值记录、财务报表生成。
- 系统管理:角色权限管理、操作日志、系统参数配置。
3.2 会员端
- 首页/课程浏览:按日期、类型、教练筛选查看可预约课程。
- 课程预约与取消:选择课程并预约,在规定时间内取消预约。
- 个人中心:
- 我的课表:查看已预约的课程及历史记录。
- 我的会员卡:查看余额、充值记录。
- 个人信息管理。
- 消息通知:接收预约成功、课程开始前提醒等消息。
4. 数据库核心表设计
以下是部分核心数据表的结构设计:
-- 会员表 CREATE TABLE `member` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(50) NOT NULL COMMENT '用户名/手机号', `password` varchar(255) NOT NULL COMMENT '密码(加密)', `real_name` varchar(20) DEFAULT NULL COMMENT '真实姓名', `phone` varchar(20) NOT NULL COMMENT '手机号', `avatar` varchar(500) DEFAULT NULL COMMENT '头像', `balance` decimal(10,2) DEFAULT '0.00' COMMENT '账户余额', `status` tinyint DEFAULT '1' COMMENT '状态(1正常 0禁用)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_phone` (`phone`) ) ENGINE=InnoDB COMMENT='会员表'; -- 课程表 CREATE TABLE course ( id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', title varchar(100) NOT NULL COMMENT '课程标题', description text COMMENT '课程描述', course_type varchar(20) DEFAULT NULL COMMENT '课程类型(如:哈他瑜伽,流瑜伽)', coach_id bigint NOT NULL COMMENT '教练ID', room_id bigint DEFAULT NULL COMMENT '教室ID', max_capacity int NOT NULL COMMENT '最大预约人数', duration int NOT NULL COMMENT '课程时长(分钟)', price decimal(10,2) DEFAULT '0.00' COMMENT '单次课程价格', status tinyint DEFAULT '1' COMMENT '状态(1可预约 0已取消)', PRIMARY KEY (id) ) ENGINE=InnoDB COMMENT='课程表'; -- 课程排期表 (核心) CREATE TABLE course_schedule ( id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', course_id bigint NOT NULL COMMENT '课程ID', start_time datetime NOT NULL COMMENT '课程开始时间', end_time datetime NOT NULL COMMENT '课程结束时间', current_booked int DEFAULT '0' COMMENT '当前已预约人数', schedule_status tinyint DEFAULT '1' COMMENT '排期状态(1可预约 0已满 2已取消)', PRIMARY KEY (id), KEY idx_course_time (course_id,start_time) ) ENGINE=InnoDB COMMENT='课程排期表'; -- 预约记录表 CREATE TABLE booking ( id bigint NOT NULL AUTO_INCREMENT COMMENT '主键', member_id bigint NOT NULL COMMENT '会员ID', schedule_id bigint NOT NULL COMMENT '排期ID', booking_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '预约时间', status tinyint DEFAULT '1' COMMENT '状态(1已预约 2已签到 3已取消 4已过期)', checkin_time datetime DEFAULT NULL COMMENT '签到时间', cancel_time datetime DEFAULT NULL COMMENT '取消时间', PRIMARY KEY (id), UNIQUE KEY uk_member_schedule (member_id,schedule_id), -- 防止重复预约 KEY idx_schedule_status (schedule_id,status) ) ENGINE=InnoDB COMMENT='预约记录表';5. 核心业务逻辑与代码实现
5.1 课程预约服务 (核心并发控制)
预约是系统的核心高并发场景,需处理并发超卖问题。这里使用数据库乐观锁(版本号)或Redis分布式锁来保证数据一致性。
import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; @Service public class BookingService { @Resource private CourseScheduleMapper scheduleMapper; @Resource private BookingMapper bookingMapper; @Resource private RedisTemplate<String, Object> redisTemplate; /** 课程预约(使用数据库乐观锁) @param memberId 会员ID @param scheduleId 课程排期ID @return 预约结果 */ @Transactional(rollbackFor = Exception.class) public BookingResult bookCourse(Long memberId, Long scheduleId) { // 1. 查询排期信息 CourseSchedule schedule = scheduleMapper.selectById(scheduleId); if (schedule == null) { return BookingResult.fail("课程排期不存在"); } if (!ScheduleStatus.AVAILABLE.equals(schedule.getScheduleStatus())) { return BookingResult.fail("课程不可预约"); } // 2. 检查是否已预约 LambdaQueryWrapper<Booking> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Booking::getMemberId, memberId) .eq(Booking::getScheduleId, scheduleId) .in(Booking::getStatus, BookingStatus.BOOKED, BookingStatus.CHECKED_IN); if (bookingMapper.selectCount(queryWrapper) > 0) { return BookingResult.fail("您已预约该课程"); } // 3. 乐观锁更新已预约人数 int updated = scheduleMapper.updateBookedCount(scheduleId, schedule.getCurrentBooked()); if (updated == 0) { // 更新失败,说明期间人数已满或被修改 return BookingResult.fail("预约失败,课程可能已满"); } // 4. 创建预约记录 Booking booking = new Booking(); booking.setMemberId(memberId); booking.setScheduleId(scheduleId); booking.setStatus(BookingStatus.BOOKED); bookingMapper.insert(booking); // 5. 发送预约成功通知(可异步) // notificationService.sendBookingSuccess(memberId, scheduleId); return BookingResult.success(booking.getId(), "预约成功"); } } // CourseScheduleMapper.xml 中的乐观锁更新SQL // <update id="updateBookedCount"> // UPDATE course_schedule // SET current_booked = current_booked + 1, // schedule_status = CASE WHEN current_booked + 1 >= max_capacity THEN 0 ELSE 1 END // WHERE id = #{scheduleId} // AND current_booked = #{oldBookedCount} -- 版本控制 // </update>5.2 用户认证与授权 (JWT)
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; @Component public class JwtTokenProvider { @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expiration}") private long jwtExpirationMs; public String generateToken(String username, String role) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtExpirationMs); return Jwts.builder() .setSubject(username) .claim("role", role) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); } public String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(jwtSecret) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token) { try { Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token); return true; } catch (Exception e) { // log error return false; } } }5.3 课程排期查询接口
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.time.LocalDate; import java.util.List; @RestController @RequestMapping("/api/course-schedule") public class CourseScheduleController { @Resource private CourseScheduleService scheduleService; /** 按日期查询可预约课程排期 */ @GetMapping("/available") public Result<List<CourseScheduleVO>> getAvailableSchedules( @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date, @RequestParam(required = false) Long coachId, @RequestParam(required = false) String courseType) { LambdaQueryWrapper<CourseSchedule> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(CourseSchedule::getScheduleStatus, ScheduleStatus.AVAILABLE); if (date != null) { queryWrapper.ge(CourseSchedule::getStartTime, date.atStartOfDay()); queryWrapper.lt(CourseSchedule::getStartTime, date.plusDays(1).atStartOfDay()); } if (coachId != null) { queryWrapper.eq(CourseSchedule::getCoachId, coachId); } // ... 其他条件构造 List<CourseSchedule> schedules = scheduleService.list(queryWrapper); List<CourseScheduleVO> voList = schedules.stream() .map(this::convertToVO) .toList(); return Result.success(voList); } }6. 总结与展望
本文详细介绍了基于SpringBoot的瑜伽馆课程预约管理系统的设计背景、技术栈选型、核心功能模块、数据库设计以及关键业务代码实现。系统采用成熟的Java技术栈,确保了系统的稳定性、安全性和可扩展性。
未来可扩展的方向包括:
- 智能化推荐:基于会员的预约历史和偏好,推荐合适的课程和教练。
- 小程序集成:开发微信小程序,提供更便捷的移动端体验。
- 数据可视化大屏:为管理者提供实时运营数据可视化看板。
- 积分与营销系统:引入积分体系、优惠券、团课等营销功能,提升用户粘性。
该系统为瑜伽馆的数字化转型提供了一个坚实的技术基础,开发者可根据具体业务需求进行功能裁剪和二次开发。