GogoAI健身约课管理平台,私教排课模块搭建思路
痛点分析
私教排课不同于普通团课,一对一为主,对教练时间、场地、会员课时校验要求更高。很多平台只做简单的时段录入,缺少三方资源冲突校验:同一个教练同一时段被重复排课、同一块训练场地同时安排两节私教课、会员同时预约两节课程。
批量周期性排课逻辑硬编码,每周重复课表调整麻烦;调课、取消后,课时回滚、消息通知、签到核销链路断裂。并发约课场景下,前端校验很容易出现超约,课时扣减和排课不同步;缺少缓冲间隔,上完一节课立刻排下一节,没有预留清洁、交接时间。连锁模式下,教练跨门店排班、数据隔离、权限管控也是难点,排课变更缺少日志追溯。
GogoAI健身约课平台独立搭建私教排课模块,把教练、场地、会员作为三类核心资源,统一冲突校验,支持单次排课、周期性循环排课,搭配预约状态机,打通课时扣减、签到核销、消息提醒链路。
模块整体设计
后端基于Spring Boot + MyBatis Plus开发,Redis缓存教练空闲时段、课表快照,MQ异步处理通知、课时流水。模块拆分为教练排班模板、课次生成、冲突校验引擎、会员约课、签到核销、调课取消流程。
核心资源约束:教练时间、场地时段、会员个人时间,同时支持课间缓冲时长配置。课次分为单次课、循环课(按周重复,设置结束日期),循环课会批量生成未来多节实体课次,修改母模板可以选择同步更新已生成课次或者仅作用于未来课次。
核心数据表:coach_schedule_template教练排班模板、private_course课次表、course_booking预约记录表、coach_block_time教练屏蔽时间、course_log课次变更日志。连锁场景下绑定门店ID,配合总部权限模块做数据隔离。
冲突校验引擎核心代码实现
冲突校验采用区间重叠判断,同时校验教练、场地、会员三类资源,事务内加锁,避免并发超约。
@Service @Slf4j public class PrivateCourseCheckService { /** * 校验时段是否冲突 * @param coachId 教练ID * @param storeId 门店ID * @param roomId 场地ID * @param memberId 会员ID * @param startTime 课程开始时间 * @param endTime 课程结束时间 * @return 校验结果 */ @Transactional(rollbackFor = Exception.class) public ResultDTO checkConflict(Long coachId, Long storeId, Long roomId, Long memberId, LocalDateTime startTime, LocalDateTime endTime) { // 增加缓冲时间,自动向后延伸缓冲时长 int bufferMin = 10; LocalDateTime realEnd = endTime.plusMinutes(bufferMin); // 校验教练时段冲突 long coachCount = courseMapper.countCoachConflict(coachId, startTime, realEnd, storeId); if (coachCount > 0) { return ResultDTO.fail("教练该时段已有课程安排"); } // 校验场地冲突 long roomCount = courseMapper.countRoomConflict(roomId, startTime, realEnd, storeId); if (roomCount > 0) { return ResultDTO.fail("训练场地时段已被占用"); } // 校验会员自身时间冲突 if(memberId != null){ long memberCount = bookingMapper.countMemberConflict(memberId, startTime, endTime, storeId); if (memberCount > 0) { return ResultDTO.fail("您已有课程与该时段重叠"); } } return ResultDTO.success(); } }SQL采用区间重叠条件:start_time < #{realEnd} AND end_time > #{startTime},首尾相接的课程不会判定冲突。
循环批量排课实现逻辑
运营后台配置循环规则:每周周几、起止时间、教练、场地、循环结束日期,系统按照规则批量生成课次。生成前循环调用冲突校验引擎,遇到冲突自动跳过或者标红提示运营人员手动调整。
循环课保留模板ID,每一条生成的课次记录template_id。运营修改模板时,提供两个选项:仅新增后续课次、同步更新全部未开始课次;已经上课、已预约的历史课次默认不自动变更,防止误操作影响已预约会员。
教练可以设置屏蔽时段,比如休息日、临时请假,屏蔽时段内循环排课自动跳过,不会生成课次。
预约状态机与课时核销
私教课预约状态:待预约、已预约、待上课、已完成、已取消、爽约。会员提交预约时,先校验私教课时卡剩余次数,锁定课时;预约成功后扣减课时或者锁定次数,避免多次占用。 上课到场后,前台或者教练端签到核销,课次流转到已完成;提前取消预约,根据取消规则判断是否返还课时,比如课前2小时取消返还,临近上课取消不返还课时。
@Service public class BookingStateService { @Autowired private CourseBookingMapper bookingMapper; @Autowired private LessonCardService lessonCardService; public ResultDTO cancelBooking(Long bookingId) { CourseBooking booking = bookingMapper.selectById(bookingId); if (!BookingStatus.BOOKED.name().equals(booking.getStatus())) { return ResultDTO.fail("当前预约状态不支持取消"); } // 判断是否满足退课时条件 if(canRefundLesson(booking)){ lessonCardService.unfreezeLesson(booking.getMemberId(), booking.getCardId(),1); } booking.setStatus(BookingStatus.CANCELLED.name()); bookingMapper.updateById(booking); // 投递消息队列通知教练、会员 rabbitTemplate.convertAndSend("course_exchange","course.cancel",bookingId); return ResultDTO.success(); } }调课、临时加课与变更通知
教练临时调课分为两种:更换上课时间、更换教练/场地。调课操作时,新参数重新走冲突校验,校验通过之后更新课次记录,同时给已预约会员和教练推送小程序消息。
课次变更日志完整记录变更前后的时间、教练、场地信息,用于后续对账和纠纷追溯。临近上课的课次限制随意修改,部分场景需要管理员二次确认。
定时任务提前推送上课提醒,一般课前24小时、课前1小时两次提醒,降低会员爽约率。
缓存与并发控制优化
教练课表、可预约时段存入Redis缓存,减少前端日历查询对数据库的压力;课次新增、取消、调课之后主动清理对应教练、门店的缓存。 预约提交阶段,数据库层面加行锁,防止高并发下同一课次被多个会员同时预约。私教一对一模式,一个课次仅允许一个会员预约,不需要处理多人上限,但团课复用这套校验逻辑可以扩展人数上限控制。
和其他模块联动
排课模块和会员卡种模块联动,预约时校验私教卡剩余课时;和门禁入场权限联动,私教课预约成功后,该时段自动开放场地入场权限;和消息推送模块联动,预约成功、取消、调课、上课提醒自动推送;和财务分账模块联动,私教课核销完成后,自动生成教练课时提成流水,参与营收分账计算。 同时接入总部权限体系,店长只能查看本店教练课表,区域经理查看管辖门店,总部可以查看全部门店排课数据。
落地效果总结
私教排课模块落地GogoAI健身约课平台之后,解决了人工排课容易撞课、循环课维护成本高、调课之后课时处理混乱的问题。三层资源冲突校验从底层避免资源占用冲突,状态机管控预约全生命周期,课时锁定、返还规则可配置。
后台可视化日历排课界面,支持批量循环排课、一键调课,运营人员不用开发配合就可以快速调整课表。整套模块和门禁、会员、财务、消息推送模块打通,形成从排课、会员约课、签到核销、提成结算的完整闭环,适配连锁自助健身房私教业务运营。