You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Node.js+MongoDB的美发店预约系统排班转可预约时段咨询

美发店预约系统排班与预约功能实现方案

现有排班结构优化建议

你当前拆分午休时段的设计逻辑是合理的,适配了美发店的常规运营规则,仅补充几个小优化点降低后续开发风险:

  • 统一字段规则:所有status = true的排班记录必须携带shift数组,避免示例中周三排班缺失shift字段导致的解析报错
  • 时间格式改为24小时制(如08:00、13:00),省去上下午标识的解析逻辑,降低时间计算出错概率
  • 顶层新增staffId字段关联对应员工,方便多员工场景下的批量查询与数据隔离

1. 排班数据转换为用户侧可预约时段

核心思路是按门店服务的最小时间粒度(比如剪发30分钟、烫染可按多倍粒度计算)拆分排班时间段,生成标准化的时间槽位:

实现步骤:

  • 提前配置服务最小粒度,比如const MIN_SERVICE_DURATION = 30; // 单位:分钟
  • 遍历员工排班列表,筛选出status = true的有效排班日期
  • 对每个排班日的多段工作时间,按最小粒度拆分生成连续的时间槽,每个槽位携带日期、起始/结束时间、关联员工ID等属性

示例代码:

// 时间转分钟数,方便计算
const timeToMinutes = (timeStr) => {
  const [hour, minute] = timeStr.split(':');
  return parseInt(hour) * 60 + parseInt(minute);
};
// 分钟数转时间字符串
const minutesToTime = (minutes) => {
  const hour = String(Math.floor(minutes / 60)).padStart(2, '0');
  const minute = String(minutes % 60).padStart(2, '0');
  return `${hour}:${minute}`;
};
// 生成可预约槽位
const generateAvailableSlots = (staffShifts, serviceDuration = 30) => {
  const slots = [];
  staffShifts.forEach(dayShift => {
    if (!dayShift.status || !dayShift.shift?.length) return;
    dayShift.shift.forEach(timeRange => {
      const startMin = timeToMinutes(timeRange.startTime);
      const endMin = timeToMinutes(timeRange.endTime);
      let current = startMin;
      while (current + serviceDuration <= endMin) {
        slots.push({
          day: dayShift.day,
          startTime: minutesToTime(current),
          endTime: minutesToTime(current + serviceDuration),
          isAvailable: true
        });
        current += serviceDuration;
      }
    });
  });
  return slots;
};

2. 预约成功后标记时段为已预约

首先需要新增MongoDB的appointments预约集合,核心字段设计如下:

{
  staffId: ObjectId, // 关联提供服务的员工
  date: String, // 预约日期,格式为YYYY-MM-DD
  startTime: String, // 预约开始时间
  endTime: String, // 预约结束时间
  userId: ObjectId, // 关联预约用户
  status: Number, // 预约状态:0=已取消 1=已生效 2=已完成
  serviceType: String // 预约的服务项目
}

实现流程:

  1. 用户提交预约请求时,先校验请求的时段是否在员工排班范围内,且未被其他用户预约
  2. 校验通过后往appointments集合插入一条状态为1的预约记录
  3. 为了避免并发预约导致的超卖问题,可以给staffId + date + startTime组合字段加唯一索引,确保同个员工同时段只能有一个有效预约

3. 过滤已预约时段返回可用空位

核心逻辑是将生成的全量可预约槽位,和对应员工对应日期的已生效预约记录做差集,过滤掉已占用的槽位:

实现步骤:

  1. 接收前端查询参数:员工ID、查询的日期范围
  2. 拉取该员工在查询范围内的排班数据,调用第一步的方法生成全量可预约槽位
  3. 查询appointments集合,筛选出该员工、该日期范围内status = 1的所有有效预约记录
  4. 遍历全量槽位,将已存在于预约记录中的槽位标记为不可用,或直接过滤掉,最终返回可用槽位列表

示例代码:

const getFilteredAvailableSlots = async (staffId, queryDate, serviceDuration) => {
  // 1. 拉取员工排班
  const staff = await StaffModel.findOne({ _id: staffId });
  const allSlots = generateAvailableSlots(staff.shift, serviceDuration);
  // 2. 拉取对应日期的已生效预约
  const appointments = await AppointmentModel.find({
    staffId,
    date: queryDate,
    status: 1
  });
  const occupiedSlots = new Set(appointments.map(item => item.startTime));
  // 3. 过滤已占用槽位
  return allSlots.filter(slot => !occupiedSlots.has(slot.startTime));
};

内容的提问来源于stack exchange,提问作者Ahsan Abrar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 19:45:04