BitMap 优化美业模版的「预约模块」

预约功能难点

在实现云开发微搭低代码的「美业行业模版」时,经常会对在工作日预约周末的某项服务,并且制定特定的技师。在预约的时候,可能会发生重复预约。

什么是重复预约呢? 例如:预约A中,预约了员工A在 2021/10/01 的 9:00 -10:00;预约B中,预约了员工B在 2021/10/01 的 8:00 - 12:00 ;两个预约在时间段上有重叠。

预约难点体现在

  • 为了防止重复预约,在下单预约的时候,应该进行检测
  • 控制检查预约时间、新建预约、删除预约、修改预约的时间复杂度

解决方案

新增员工工作时间表(employer_work_time),字段如下:

1
2
3
4
_id: string;
employer_id: string; // 关联员工表记录的_id
date: string; // 日志标准格式,例如:2021/10/10
time: number; // 被占用时间。

关于员工工作时间表(employer_work_time)的time字段的设计:

  • 每天时间按照30分钟进行切分,一天可以切分为48份。
  • 使用BitMap,用无符号二进制表示,需要48位,初始状态每位都是0。共需要6byte,mysql中一个bigint type即可,flexdb中使用number即可。
  • 二进制运算时间复杂度O(1)

原预约记录表新增字段:

1
2
3
4
// 关联员工工作时间表记录的_id,能查到提供服务的员工,以及提供服务的日期
employer_work_time_id: string;
// 和 employer_work_time 表的time字段类型保持一致
service_time: number;

整体流程

存储格式

如果用户A预约的时候,预约了员工A的 0:00 - 1:00 ,那么二进制为:11000…000(共46个0),存储的字段值就是:211106232532992 。

另一个用户B在进行预约的时候,假设也选中了员工A,接口查询到员工A在10-01这天的数据有记录,并且time为211106232532992。将time字段转成二进制数组,即:[1, 1, 0, 0, … , 0]。

前端拿到此数组后,可以将为1的时间段禁用。用户B预约时,无法选中员工A的0:00~1:00时间段,从而达到防止重复预约的目的。

新增预约

  • 用户创建预约:假设用户B选中了1:00 - 1:30,前端传给后端的time字段就是:[0, 0, 1, 0, 0, …],处理成二进制之后是:0010…0(45个0)。然后进行校验、数据更新、以及失败返回:
    • 数据校验:前端传入的二进制与数据表中记录的字段进行 & 操作( 0010...0 & 11000...00 = 00....0000 ),如果结果是0,那么就说明合法,没有重复字段,进行数据更新;如果结果不为0,那么就说明有重复字段,接口失败返回。
    • 数据更新:
      • 更新员工工作时间表的time字段。time字段的新值为前端传入的二进制和数据表中记录的字段的 | 操作的结果。这里就是 010...0 | 11000...00 = 1110....000,代表 0:00 - 1:30 这个时间段都有预约了。
      • 更新预约记录表的service_time字段。值为前端传入的二进制,即:0010…0

取消预约

  • 用户取消预约:假设用户B取消预约。取出预约记录表的service_time字段,根据employer_work_time_id反查员工工作时间表记录,更新其上的time字段。

运算使用异或操作即可:0010...0 ^ 1110....000 = 110...0

更新预约

  • 用户更新时间:操作上也是二进制,相当于先取消,再创建预约。借助「事务」保证原子性即可。