专注小程序 / APP 开发,用代码改变生活

获取方案
全国分站 主站首页 北京分站 石家庄分站 唐山分站 保定分站 廊坊分站 沧州分站 郑州分站

美容院小程序预约系统实战开发

小程序开发 2026-10-12 3 阅读 0 点赞 原创

美容院最头疼的问题不是没客人,而是客人来了排不上、技师闲着没人约、电话预约记错时间。做一套小程序预约系统,本质是把「排班、选项目、锁时段、通知」这四件事搬到线上。这篇文章从数据模型到核心代码,讲一遍我实际落地的方案。

一、先想清楚业务模型

美容院预约和餐厅订座不一样,它有三个强约束:

  • 服务时长不固定:补水护理 60 分钟,热玛吉可能 120 分钟,时段是「浮动」的。
  • 资源是双重的:既要占技师,也要占房间/仪器。
  • 不能重叠:同一个技师在同一时间段只能接一单。

所以数据表至少要有四张核心表:staff(技师)、service(项目)、schedule(排班)、appointment(预约单)。

关键字段设计

appointment (
  id, user_id, staff_id, service_id,
  start_time DATETIME,   -- 预约开始
  end_time   DATETIME,   -- 由 service.duration 推算
  status TINYINT,        -- 0待确认 1已确认 2已完成 3已取消
  room_id, remark, created_at
)

注意 end_time 一定要落库,别每次查询再算。否则做冲突检测时你会在 SQL 里写一堆 DATE_ADD,索引也用不上。

二、可预约时段怎么算

用户选完项目后,前端要展示「今天还有哪些时间能约」。这一步是整个系统最容易被做烂的地方。

常见错误做法:前端把 9:00 到 20:00 每 30 分钟列一遍,让用户自己点。结果就是用户点了一个已经被占用的时段,提交时报错,体验极差。

正确做法是服务端计算可用时段,逻辑分三步:

  1. 取该技师当天的排班区间(比如 09:00–18:00,中间 12:00–13:00 午休)。
  2. 取出当天所有未取消的预约,按时间排序,形成「已占用区间」。
  3. 用项目时长去排班区间里滑动,跳过所有与已占用区间重叠的起点。

冲突检测的 SQL 写法

判断某个新时段是否冲突,标准写法是「新区间的开始小于老区间的结束,且新区间的结束大于老区间的开始」:

SELECT COUNT(*) FROM appointment
WHERE staff_id = ?
  AND status IN (0, 1)
  AND start_time < #{newEnd}
  AND end_time   > #{newStart};

返回 0 就说明这个时段能约。这个条件在 (staff_id, start_time) 上建联合索引,命中率很高。

三、并发下的「重复预约」问题

上面那段查询有个隐患:两个用户同时查,都返回 0,然后都插入成功,技师被约了两次。

解决办法有三种,按推荐程度排序:

  • 数据库唯一索引 + 重试:给 (staff_id, start_time) 加唯一索引,插入冲突就返回「该时段已被约」。简单粗暴但有效。
  • 悲观锁:事务里 SELECT ... FOR UPDATE 锁住技师排班行,适合并发量不大的场景。
  • Redis 分布式锁:以 lock:staff:{id}:{date} 为 key,适合多实例部署。

我自己的项目用的是第一种,因为美容院单店并发极低,唯一索引足够,还省了引入 Redis 的成本。

下单接口示例

async function createAppointment(userId, staffId, serviceId, startTime) {
  const service = await getService(serviceId);
  const endTime = addMinutes(startTime, service.duration);

  const conflict = await db.query(
    'SELECT 1 FROM appointment WHERE staff_id=? AND status IN (0,1) AND start_time < ? AND end_time > ? LIMIT 1',
    [staffId, endTime, startTime]
  );
  if (conflict.length) throw new Error('该时段已被预约');

  return db.insert('appointment', {
    user_id: userId, staff_id: staffId, service_id: serviceId,
    start_time: startTime, end_time: endTime, status: 0
  });
}

四、消息通知别偷懒

预约类小程序,通知做不好等于白做。至少要覆盖三个节点:

  • 预约成功:立即推送订阅消息,带上门店地址和技师姓名。
  • 到店前 2 小时:提醒用户,降低爽约率。
  • 技师确认/改期:状态变更时同步通知。

微信小程序的订阅消息是「一次授权一次发送」,所以要在用户点击「确认预约」时同步调用 requestSubscribeMessage,把模板 ID 一次性申请到位。用定时任务扫描 start_time 在 2 小时后的订单发提醒,别用 setTimeout,服务重启就丢了。

五、几个踩过的坑

  • 时区:数据库存 UTC,前端展示转本地,别混着来,否则跨天预约必然出错。
  • 取消规则:建议设置「开始前 4 小时可免费取消」,超时取消标记为爽约,累计三次限制预约。规则要写进用户协议。
  • 技师排班:一定要支持临时请假和调休,否则某天技师请假,当天所有预约全废。
  • 项目时长:允许门店在后台改,但已生成的预约单要固化 end_time,不能跟着变。

六、小结

美容院预约小程序的技术难点不在 UI,而在时段计算和并发防重。把这两个点做扎实,剩下的就是排班管理、消息通知和后台运营的常规活。如果只是单店自用,微信云开发 + 云数据库就能快速上线;要做成多门店 SaaS,就老老实实上独立后端和索引设计。先跑通一家店的流程,再谈复制。

团长分佣小程序开发实战美甲店小程序开发实战:从0到1

评论 (0)

还没有评论,快来抢沙发吧~

好想法,值得被认真交付

从小程序、APP 到全栈网站,一站式把想法变成可落地的产品

微信咨询

微信扫码,直接沟通需求