健身管理系统开发实战:消息通知、预约排队、售后工单模块
一、业务痛点分析
健身房系统在落地时,经常遇到三类典型问题。消息推送杂乱无章,重复推送、消息丢失、课前提醒失效,会员错过课程;预约模块在课程满员后缺少排队替补机制,有人取消预约时空位无法自动流转,资源利用率低;设备报修、会员投诉没有标准化工单流程,问题口头传递、处理进度无法追溯,售后闭环难以形成。
这三个模块存在强联动关系:预约变更、排队替补、工单状态变更都需要触发消息通知,单独开发很容易出现状态不一致、并发超卖、消息幂等性失效等问题,需要统一事件驱动模型来串联。
二、消息通知模块设计与实现
消息通知采用事件驱动架构,不与业务主流程强耦合,使用消息队列异步消费,支持站内消息、小程序模板消息、短信多渠道分发,同时做幂等、重试、消息模板管理。
核心设计要点:事件源统一发布通知事件,通知服务做渠道分发、消息持久化、已读状态管理;增加消息防重表,基于业务唯一key防止重复推送;失败消息进入死信队列,支持后台重发。
@Service
public class FitnessNotifyService {
@Autowired
private NotifyRecordMapper notifyRecordMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendNotify(NotifyEvent event) {
// 幂等校验,防止重复发送
LambdaQueryWrapper<NotifyRecord> wrapper = Wrappers.lambdaQuery();
wrapper.eq(NotifyRecord::getBizKey, event.getBizKey());
if (notifyRecordMapper.selectCount(wrapper) > 0) {
return;
}
// 持久化通知记录
NotifyRecord record = new NotifyRecord();
record.setUserId(event.getUserId());
record.setBizType(event.getBizType());
record.setBizKey(event.getBizKey());
record.setContent(event.getContent());
record.setStatus(0);
notifyRecordMapper.insert(record);
// 投递消息队列异步推送
rabbitTemplate.convertAndSend("fitness.notify.exchange", "notify.send", event);
}
}
通知场景覆盖:预约成功通知、课前定时提醒、排队替补成功通知、售后工单接单/完结通知。定时提醒可以结合延时队列实现,避免定时轮询数据库带来的性能损耗。

三、预约排队模块设计与实现
预约排队分为预约主流程和候补排队流程。预约时先校验课程容量,余量充足直接生成预约单;课程已满,则加入FIFO候补队列,存入Redis有序集合,保证排队顺序。
当已有会员取消预约,系统释放名额,自动取出队首候补用户,自动转正预约并触发通知事件。这里必须使用分布式锁,防止并发取消、并发转正造成超卖。
@Service
public class CourseBookingService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private RedissonClient redissonClient;
@Autowired
private FitnessNotifyService notifyService;
public void booking(Long courseId, Long userId) {
String lockKey = "course:booking:lock:" + courseId;
RLock lock = redissonClient.getLock(lockKey);
try {
lock.lock(5, TimeUnit.SECONDS);
CourseInfo course = getCourseById(courseId);
Long bookedCount = getBookedCount(courseId);
if (bookedCount < course.getMaxNum()) {
// 直接预约成功
createBookingOrder(courseId, userId);
notifyService.sendNotify(buildSuccessEvent(courseId, userId));
} else {
// 进入候补排队
String queueKey = "course:wait:" + courseId;
redisTemplate.opsForZSet().add(queueKey, userId.toString(), System.currentTimeMillis());
notifyService.sendNotify(buildWaitEvent(courseId, userId));
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
候补队列需要设置有效期,超过开课时间自动清理队列,避免无效数据堆积。同时支持会员主动退出排队,删除Redis队列内对应记录。

四、售后工单模块设计与实现
售后工单用于处理健身器械故障报修、会员服务投诉、场地问题反馈,完整生命周期:工单创建、分派、接单、处理、复核、完结、关闭。
状态流转控制:新建→待分派→已接单→处理中→待复核→已完结,支持驳回重新处理,所有状态变更记录日志,每次状态变更都推送消息通知对应会员和处理人员。
工单支持自动分派和手动派单两种模式,自动分派可以根据处理人员负载、擅长类型进行路由。
@Service
public class WorkOrderService {
@Autowired
private WorkOrderMapper orderMapper;
@Autowired
private FitnessNotifyService notifyService;
@Transactional(rollbackFor = Exception.class)
public void changeStatus(Long orderId, Integer status, Long operatorId) {
WorkOrder order = orderMapper.selectById(orderId);
// 状态机校验,非法状态流转直接拦截
if (!checkStatusFlow(order.getStatus(), status)) {
throw new RuntimeException("工单状态流转不合法");
}
order.setStatus(status);
order.setOperatorId(operatorId);
orderMapper.updateById(order);
// 状态变更触发通知
notifyService.sendNotify(buildOrderEvent(order));
}
}
工单表除基础描述、故障类型、图片附件字段外,保留状态变更日志表,用于后续统计工单响应时长、故障类型分布,辅助场馆运维优化。

五、模块联动与落地注意事项
三个模块共用一套事件发布能力,预约转正、工单状态变更作为事件源,统一交给通知模块消费。开发时需要重点关注并发安全:预约扣减、名额释放、候补转正全部加分布式锁;消息通知做好幂等,避免重复推送;延时任务做好补偿,防止MQ消息丢失导致课前提醒漏发。
数据库层面,预约单、工单、通知记录做好分库索引,课程ID、用户ID作为高频查询字段建立索引;Redis只做排队队列、热点容量缓存,核心业务数据持久落库。
测试重点覆盖高并发场景:多人同时取消预约、候补队列连续转正、短时间大量报修工单提交,验证是否存在超约、消息风暴、工单状态错乱问题。
六、总结
健身管理系统里消息通知、预约排队、售后工单是支撑场馆运营的核心业务模块,三者不是独立功能,而是依靠事件驱动机制串联起来。消息通知模块作为基础设施,统一承载各类业务变更的触达能力;预约排队解决课程资源高效复用,降低空课率;售后工单完成会员问题闭环,沉淀运维数据。
在技术实现上,通过MQ异步解耦、Redis实现排队队列、分布式锁保障并发安全、状态机管控工单流转,能够在保证性能的前提下,规避超卖、消息重复、状态异常等常见问题。这套方案可以直接复用在私教预约、团课管理、场地预约等场景,在后续迭代中,可以在此基础上叠加智能排班、工单SLA超时预警等能力,持续完善整套健身管理系统。