温馨提示本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片1. 项目背景与需求分析乒乓球私教课通常采用“先购课、后上课、按次核销”的运营模式。学员购买一定课时的私教课程后每次上课由教练或前台进行核销扣除对应课时这一过程在行业内称为“消课”。传统的人工登记方式存在记录易丢失、课时统计不准确、学员与教练对账困难等问题因此需要一个基于 Spring Boot 的消课系统来管理课程、学员、教练以及课时核销流程。本系统的核心目标是实现私教课课时的数字化管理覆盖学员管理、教练管理、课程套餐管理、消课记录、课时统计与报表等核心业务帮助球馆提升运营效率减少人工差错。2. 系统功能模块设计根据乒乓球私教课的业务特点系统划分为以下核心功能模块学员管理维护学员基本信息、联系方式、剩余课时、购课历史等。教练管理维护教练档案、授课时间、授课记录等。课程套餐管理定义私教课套餐如“10 次课包”“20 次课包”包含课时数和有效期。购课管理学员购买课程套餐后系统自动增加对应剩余课时。消课管理核心模块支持教练或前台为学员进行上课核销扣除一次课时并生成消课记录。课时统计与报表按日、月、教练、学员等维度统计消课数据辅助运营决策。3. 数据库表设计系统采用 MySQL 作为持久化存储核心数据表包括学员表、教练表、课程套餐表、购课订单表、消课记录表等。以下为主要表结构设计。3.1 学员表student字段名类型说明idbigint主键自增namevarchar(50)学员姓名phonevarchar(20)联系电话remaining_lessonsint剩余课时数created_timedatetime创建时间3.2 教练表coach字段名类型说明idbigint主键自增namevarchar(50)教练姓名phonevarchar(20)联系电话specialtyvarchar(100)擅长方向3.3 课程套餐表course_package字段名类型说明idbigint主键自增namevarchar(100)套餐名称lesson_countint包含课时数pricedecimal(10,2)套餐价格valid_daysint有效期天数3.4 购课订单表purchase_order字段名类型说明idbigint主键自增student_idbigint学员 IDpackage_idbigint套餐 IDlesson_countint购买课时数purchase_timedatetime购买时间3.5 消课记录表lesson_consume字段名类型说明idbigint主键自增student_idbigint学员 IDcoach_idbigint教练 IDconsume_timedatetime消课时间remarkvarchar(200)备注4. 核心技术架构系统采用 Spring Boot 作为后端框架结合 Spring MVC 提供 RESTful API使用 MyBatis-Plus 作为 ORM 框架简化数据库操作前端可选用 Vue 或微信小程序进行对接。整体架构分为控制层、业务层、数据访问层三层结构。前端Vue / 小程序 ↓ HTTP / JSON Spring Boot Controller 层 ↓ Service 业务层事务管理 ↓ Mapper 数据访问层MyBatis-Plus ↓ MySQL 数据库5. 核心业务实现5.1 购课业务学员购买课程套餐时系统需要同时完成订单创建和学员剩余课时增加两个操作因此必须使用事务保证数据一致性。以下为购课服务的核心代码示例。Service public class PurchaseService { Autowired private PurchaseOrderMapper orderMapper; Autowired private StudentMapper studentMapper; Transactional(rollbackFor Exception.class) public void purchase(Long studentId, Long packageId) { CoursePackage coursePackage packageMapper.selectById(packageId); if (coursePackage null) { throw new BusinessException(课程套餐不存在); } // 创建购课订单 PurchaseOrder order new PurchaseOrder(); order.setStudentId(studentId); order.setPackageId(packageId); order.setLessonCount(coursePackage.getLessonCount()); order.setPurchaseTime(new Date()); orderMapper.insert(order); // 增加学员剩余课时 Student student studentMapper.selectById(studentId); student.setRemainingLessons(student.getRemainingLessons() coursePackage.getLessonCount()); studentMapper.updateById(student); } }5.2 消课业务消课是系统的核心操作。学员上课前教练或前台需要校验学员剩余课时是否充足充足则扣除一次课时并生成消课记录。为防止并发情况下超扣课时消课操作必须使用数据库行锁或乐观锁机制。Service public class ConsumeService { Autowired private StudentMapper studentMapper; Autowired private LessonConsumeMapper consumeMapper; Transactional(rollbackFor Exception.class) public void consumeLesson(Long studentId, Long coachId, String remark) { // 使用悲观锁锁定学员记录防止并发超扣 Student student studentMapper.selectByIdForUpdate(studentId); if (student null) { throw new BusinessException(学员不存在); } if (student.getRemainingLessons() 0) { throw new BusinessException(剩余课时不足无法消课); } // 扣除一次课时 student.setRemainingLessons(student.getRemainingLessons() - 1); studentMapper.updateById(student); // 生成消课记录 LessonConsume consume new LessonConsume(); consume.setStudentId(studentId); consume.setCoachId(coachId); consume.setConsumeTime(new Date()); consume.setRemark(remark); consumeMapper.insert(consume); } }在上述代码中selectByIdForUpdate使用数据库的SELECT ... FOR UPDATE语句对学员记录加行锁确保同一学员的并发消课请求串行执行避免出现剩余课时被扣成负数的问题。6. 并发控制与数据一致性消课系统对数据一致性要求较高主要面临以下并发场景同一学员在同一时间被多个教练尝试消课、学员购课与消课同时发生等。针对这些问题系统采用以下策略数据库行锁消课时使用SELECT ... FOR UPDATE锁定学员记录保证同一学员的消课操作串行执行。事务管理购课、消课等涉及多表更新的操作统一使用Transactional注解保证操作的原子性。唯一约束消课记录表可增加业务唯一索引避免重复提交导致的重复消课。乐观锁兜底在学员表中增加版本号字段更新时校验版本号防止极端情况下的数据覆盖。7. 课时统计报表系统需要为运营人员提供多维度的课时统计报表包括按教练统计的授课课时、按学员统计的剩余课时、按时间段统计的消课趋势等。以下为按教练统计消课数量的 SQL 示例。SELECT c.name AS coach_name, COUNT(lc.id) AS consume_count FROM lesson_consume lc LEFT JOIN coach c ON lc.coach_id c.id WHERE lc.consume_time #{startTime} AND lc.consume_time #{endTime} GROUP BY c.id, c.name ORDER BY consume_count DESC;8. 系统测试与验证为保证消课系统的正确性需要针对核心业务编写单元测试和接口测试。重点覆盖以下场景正常消课、剩余课时不足时消课、并发消课、购课后课时增加等。以下为消课服务的单元测试示例。SpringBootTest public class ConsumeServiceTest { Autowired private ConsumeService consumeService; Test public void testConsumeLessonSuccess() { // 准备学员剩余课时为 5 Long studentId 1L; Long coachId 1L; consumeService.consumeLesson(studentId, coachId, 正常上课); Student student studentMapper.selectById(studentId); Assertions.assertEquals(4, student.getRemainingLessons()); } Test public void testConsumeLessonInsufficient() { // 准备学员剩余课时为 0 Long studentId 2L; Long coachId 1L; Assertions.assertThrows(BusinessException.class, () - consumeService.consumeLesson(studentId, coachId, 无课时上课)); } }9. 总结与展望本文设计并实现了一个基于 Spring Boot 的乒乓球私教课消课系统覆盖了学员管理、教练管理、课程套餐、购课、消课、统计报表等核心业务。系统通过数据库行锁和事务机制有效解决了并发消课场景下的数据一致性问题保证了剩余课时的准确性。后续可以从以下方向继续优化引入 Redis 缓存热点学员数据、增加消息队列削峰处理高并发消课请求、开发微信小程序端方便学员自助查看剩余课时和约课、增加课时到期提醒功能等进一步提升系统的易用性和扩展性。
阅读完成 · 觉得有帮助?