36 lines
2.3 KiB
Markdown
36 lines
2.3 KiB
Markdown
# 架构决策:真实预约容量模型
|
||
|
||
日期:2026-08-01
|
||
状态:Accepted
|
||
范围:单店 SaaS 真实试点
|
||
|
||
## 决策
|
||
|
||
1. 服务项目配置 `duration_minutes`,范围 30~480 分钟且按 30 分钟递增。
|
||
2. 预约保存 `duration_minutes` 快照,后续修改服务项目不会改变已承诺号源。
|
||
3. 门店配置 `booking_capacity`(1~10),表示同一半小时容量桶可并行服务的顾客数。
|
||
4. 可约性必须检查服务从开始到结束覆盖的每个半小时桶;有效预约和 `walk_in` 各消耗一个名额,`blocked` 关闭覆盖范围内全部名额。
|
||
5. 创建预约或手动占用时,对门店行使用悲观写锁,在同一事务内重新读取占用并判定,避免两个并发请求同时通过可用性检查。
|
||
6. `booking_last_slot_start` 保留兼容语义:它是最后一个 30 分钟容量桶的开始时间,营业容量结束时刻为其后 30 分钟;服务必须在结束时刻前完成。
|
||
|
||
## 为什么先做门店容量
|
||
|
||
现有预约在开始服务时才绑定 `assigned_user_id`,宠主下单也不选择技师。在这种数据结构下宣称已经具备“技师排班”会制造假精度。试点阶段先由老板配置门店可真实兑现的并发接待数,足以阻止长服务跨档冲突和同店并发超卖。
|
||
|
||
个人技师日历延后到预约前可分配技师、员工可维护可工作时段、服务项目可声明技师技能之后再引入。届时门店容量仍作为总上限,不会推翻本决策。
|
||
|
||
## 兼容与迁移
|
||
|
||
- 旧接口未传 `serviceType` 查询号源时采用 60 分钟安全默认值;创建预约必须解析到本店有效服务项目。
|
||
- 历史系统默认与预约按稳定名称映射回填:洗澡 60、美容 120、洗澡+美容 150、剪指甲/驱虫 30,未知服务 60 分钟。
|
||
- 旧手动占用回填为 30 分钟;旧门店并发接待数回填为 1。
|
||
- 生产不自动执行迁移,发布前按 `backend/db/migrations/README.md` 备份、测试和人工执行。
|
||
|
||
## 验收
|
||
|
||
- 120 分钟服务会占用连续四个半小时桶。
|
||
- 容量为 2 时,同一桶允许两个有效占用,第三个被拒绝。
|
||
- `walk_in` 消耗一个名额;`blocked` 覆盖处全部不可约。
|
||
- 候选服务结束晚于营业容量结束时刻时不可约。
|
||
- 并发创建不会突破门店容量。
|