点餐预约核销系统的架构脉络

一、点餐预约核销系统的架构脉络

"点餐预约核销系统"不是一个单一功能模块,而是三条流程的交汇:用户点餐、商家预约、门店核销。三者合一时,系统需要同时支持 **在线点单下单**、**预约未来时段或座位**、**到店出示凭证并核销** 这一整套状态流转。

在架构设计上,核心可以拆分为五个部分:

  • **用户端**:基于Uniapp,可一键打包H5、App、小程序,覆盖用户选择门店、浏览菜单、提交预约点餐、查看核销码等操作

  • **管理后台**:基于Vue + ElementUI,面向门店/商家/管理员,处理菜单、桌台、预约时段、订单明细的维护

  • **服务端**:Spring Boot提供接口服务,MyBatis Plus负责数据ORM与分页查询,MySQL做持久化存储,用于承载会员、商品、订单、预约单、核销记录等核心业务表

  • **Redis缓存层**:用于解决库存/预约人数并发瓶颈,实现验证码一次性核销的原子性操作(后面会细说)

  • **消息队列**:可选配置,适合连锁规模下订单状态变更、异步通知、预约未到店自动取消等场景

整体数据流可以概括为:用户在手机端发起点餐或预约 -> 接口写入MySQL订单表/预约表 -> 后台/门店端接收新预约提醒 -> 用户到店 -> 用户出示电子凭证 -> 门店使用核销工具完成确认 -> 核销状态变更,整单流转闭合。

二、点餐预约领域模型与数据库设计

深度剖析这类系统,表结构设计直接决定系统能撑多大业务量。需要建立清晰的领域模型,不能将"预约"和"点餐"混在一张表里完成,否则核销判断逻辑会越写越乱。合理的表结构层级可以按如下思路划分:

1. 基础资源表

  • **门店表**(shop):记录门店名称、地址、营业时间、桌台数量;业务场景为独立门店时该表只有一条记录,连锁场景则开启多租户/区域隔离

  • **桌台/座位表**(table):桌号、桌台码、可容纳人数、当前状态(空闲/占用/锁定)

  • **预约时段表**(time_slot):这是预约系统的灵魂,用于定义预约的粒度

时段表设计很重要,很多预约系统后期变更的坑就在这里。建议预生成时段段数据,如午餐时段 11:00-13:00、下午茶 14:00-17:00、晚餐时段 17:30-21:30。store_type判断是按桌台预约还是按时间段预约。

2. 交易链路表

| 表名 | 作用 | 核心字段 |

|------|--------|----------|

| order | 点餐订单主表 | 订单号、用户ID、门店ID、订单状态、支付状态、金额 |

| order_item | 订单条目子表 | 订单ID、商品ID、商品名称、数量、实付小计 |

| reservation | 预约单主表 | 预约编号、用户ID、门店ID、时段ID、桌台ID、预约状态 |

| verification_record | 核销记录流水表 | 核销码、订单ID/预约单ID、核销员ID、核销时间、核销状态 |

在这里要注意:如果用户点餐和预约是绑定的,一个门店高峰期可能产生两个系统码,一个是"预约码"证明预留桌台,一个是"取餐码"或者"核销码"用于消费。很多失败的设计是混用,导致一个码被核了两次。

3. 核心字段设计补充

  • 核销码字段,建议保存一份冗余的 `verify_code`,随机字符串,但数据库表索引。不要使用自增ID做核销凭证,很容易被猜测或遍历

  • 状态字段(order_status/verification_status)使用`tinyint`,0待支付,1已支付/待消费,2已完成,3已取消,4已退款。核销记录里增加一个 `consume_channel` 标识是门店后台核销、扫码枪核销,还是自助核销

三、点餐预约与核销的状态机设计

这部分是技术干货,也是业务逻辑容易出bug的地方。

预约流程状态机

用户在点餐预约系统中提交预约请求后,状态流转需要明确:

```

发起预约 → 待确认 → 已确认(锁定桌台/时段)

↓ ↓

超时自动取消 用户到店签到

待点餐(可预先点好餐)

核销完成 / 就餐完成

```

超时自动取消的实现,有两种参考做法:

  • **延迟消息机制**:下单预约成功后,向消息队列发送一条延迟任务(如30分钟),在此时间后查库,如果状态仍然是待支付/待确认,就自动将预约单和预占资源置为释放状态

  • **定时扫描补偿**:启动一个定时任务,定时扫描超过N分钟未支付的预约记录,将该记录关闭,同时需要归还扣减的库存(例如餐位是10人,预约满了会被占掉一个名额,取消预约就释放名额)

核销状态流转

一个核销码必须是"单次一次性"。状态变化:

```

核销码生成(初始UNUSED)

用户到店,工作人员执行核销 → 查验证码是否有效

→ UNUSED则更新为USED,记录核销人/核销时间

→ 如果状态已经USED则返回异常码

```

**核心实现技巧**:不要先SELECT再UPDATE去判断核销状态,而应使用 MySQL的原子更新完成一次性核销:

```sql

UPDATE verification_record

SET verification_status = 1, verifier_id = #{userId}, verification_time = NOW()

WHERE verify_code = #{code} AND verification_status = 0

```

当该UPDATE影响的行数等于1时,说明核销成功;影响行数为0时,说明该核销码已被使用或不存在,直接返回"核销失败/重复使用"的提示。这条方案可以避免并发场景下同一码被多人同时请求造成的超卖/重复核销。

若引入Redis,可以将核销码作为Key存入Redis,使用Redis `SETNX`(key不存在时设置)命令实现更高效的并发控制。比如以核销码为 key,`expire = 300秒`(用户出示码到核销需在几分钟内完成),操作核销时执行 `SETNX` 成功则代表抢占核销名额,然后异步落库写核销记录。

四、预约库存与高峰点餐的并发处理

预约核销系统一个典型痛点就是"预约满员 / 高峰期用户点餐并发高"。比如奶茶店、餐厅在午餐/晚餐高峰,如果只是简单读数据库判断当前预约数是否达到上限,极易出现超卖。

预约座位数预占方式可供参考:

**方式一:数据库乐观锁**。在time_slot表增加一列 `reserved_count`,预约下单时执行:

```sql

UPDATE time_slot

SET reserved_count = reserved_count + 1

WHERE id = #{slotId}

AND reserved_count < max_count

```

更新成功则预约成功,更新失败说明该时段已满。

**方式二:Redis计数器预减**。引入Redis的`DECR`进行原子自减:

```

可用库存 = MAX_COUNT

用户请求预约 -> 执行 DECR reserve_available

如果返回值 >=0,预约流程继续

如果返回值 <0,直接返回"该时段预约已满",同时执行 INCR回补库存

```

用Redis分布式锁防止"取消预约并发归还库存"时多还造成的数据不一致,这一块可以展开讲很多细节。

五、对接小程序端与多端适配

后端体系搭建完毕后,前端部分要用Uniapp来适配小程序、H5和App。核销系统前端通常有两种工作台场景:

  1. **用户端小程序**:用户在点餐预约系统首页选择门店 -> 浏览菜单并加入购物车 -> 选择预约到店时间 -> 提交点餐与预约 -> 支付完成后生成核销凭证(可包含预约信息和点餐清单编号信息)

  2. **商家核销端**:可以做成小程序内的"扫码核销"页面,相当于店员端;也可以放在管理后台的PC工作台里。用uniapp配合App端可以调用原生相机扫码,在H5端可引入`html5-qrcode`这一类的插件实现扫码解析与交互。

在数据联调时,有一个细节要处理好------接口鉴权。用户端与核销端对接口的权限有严格区分。用户端核销码生成需要登录态;店员端核销操作应走独立的员工账号体系,且需要操作日志留痕(防止员工滥用核销权限)。服务端建议在过滤器/拦截器中校验JWT中的角色标识,核销接口必须校验角色,非门店管理人员不可访问。

六、二次开发与系统扩展方向建议

在实际技术咨询与方案调研中,"二次开发与源码交付"常和这套系统绑定出现。从开发人员视角来看,选择合适的开源底座能省去搭建基础工程的时间,将重心放到业务差异化中。技术选型知识库中也有如下共性经验值得参考:

1. 业务扩展方向

  • 奶茶店、咖啡店4.0版本系统普遍要支持桌码点餐,即用户到店扫桌台码直接点单,系统根据桌台定位到具体桌号,订单自动推送至后厨制作端

  • 预约停车的场景可以把"娱乐点餐预约席位"扩展到"车位预约 + 入场核销",底层其实一样,换成time_slot绑定车位信息即可

  • 家政/旅游报名预约类系统,可拆出"服务人员/师傅指派"子模块,预约完成后自动分配服务人员

2. 技术侧微扩展

底层框架Spring Boot + MyBatis Plus的数据操作层已经是通用方案。建议将通用代码抽取为组件式开发,后续切换业务时将预约、点餐、核销做成三个独立业务模块。如果未来系统要升级为微服务,将核销模块拆为独立服务也非常顺手。

在数据埋点层,核销的同时应异步插入消费行为表(用于平台后续分析"哪个时段核销率、哪个菜品取消率异常"等),具备大数据分析能力。这块可以用AOP切面,在核销接口执行成功后发送MQ消息,解耦主链路。

七、FAQ:点餐预约核销系统中开发者常问的三个技术点

**Q1:预约与点餐必须分开吗?系统能不能同时接预约订单和即时订单?**

如果只是功能上区分,可以在一张订单主表里加`order_type`字段(1即时点餐,2预约单)。但建议仍然保留独立的预约主表,原因是即时单不需要锁库存和锁时段,若混在一张表中,预约状态机的流转会干扰餐品订单的基础状态。

**Q2:核销通知是如何实时触达后厨或门店的?**

常见方案是WebSocket或SSE推送。用户核销成功后,服务端发送一个消息给后厨的接单大屏/后厨打印机(打印出餐小票);技术实现上可以在核销接口中同步订阅事件,基于Spring Boot的`ApplicationEventPublisher`发布事件,后厨监听端订阅待处理事件。

**Q3:核销时系统掉线了怎么办?如何保证不重复核销、也不漏核销?**

系统在离线时可用"预先下发批次码"方案处理。门店从管理后台下载一批备用码到本地设备,离线核销后在本地标记,等网络恢复后批量上传至服务器,服务端使用上文提到的UPDATE原子校验去重。若业务量不大,也可以直接依赖MySQL的索引兜底,确保一个码不可能在库中同时出现两次成功核销记录。


总体而言,"点餐预约核销系统"的实现难点并不在单一模块,而是把点餐业务流、预约时段库存、核销一次性校验串联在一个闭环中。合理设计状态机、控制并发扣减与核销原子性,配合Spring Boot + Uniapp的成熟技术栈,可以把系统的稳定性和可维护性提升上来,为后续向停车预约、家政上门等场景做能力复用打好基础。

相关推荐
jianqiang.xue1 小时前
审查技能库(上):内存安全四件套
stm32·单片机·物联网·架构·esp32
代码方舟1 小时前
O2O二手车交易平台B端车商管理后台:基于天远车信盟出险构建自动化车况评估网关
java·运维·人工智能·自动化
爱学堂IT分享1 小时前
图灵Java互联网架构师六期
java·开发语言
坐吃山猪1 小时前
【多线程】Semaphore使用
java·数据库·oracle·多线程
纪卓志George1 小时前
打破语言范式:在 Go 里用动态代理实现 AOP
架构·go
ZYJCSZKJ2 小时前
基于微服务架构的本地生活POI团购系统设计与高并发实践
微服务·架构·生活
秋名RG2 小时前
Java 核心特性一览
java·开发语言
kkai人工智能2 小时前
GPT-6 细节泄露:提示词工程已死,蜂群 Agent 正式接管 AI 架构
人工智能·gpt·架构
霸道流氓气质2 小时前
Spring AI 技术细节:RAG QuestionAnswerAdvisor 设计与实现
java·人工智能·spring