点餐预约核销系统开发实战:基于SpringBoot与uniapp的架构设计
点餐预约核销系统,是餐饮行业数字化转型中常见的一类业务系统,它将“线上预约、到店点餐、消费核销”三个环节串联起来,形成完整的交易闭环。从技术视角来看,它并不只是简单的CRUD,而是涵盖了预约时段管理、桌台状态维护、订单状态机、核销码安全校验等复杂业务逻辑。本文将以一个典型的餐饮门店场景为例,分享点餐预约核销系统的整体架构、技术选型、数据库设计及核心难点实现,希望能为同样在开发此类系统的工程师提供一个可落地的参考方案。
一、点餐预约核销系统的业务闭环拆解
开发该系统的步,是对业务链路进行抽象梳理。虽然不同门店的管理细节有差异,但核心链路基本可以归结为“预约建档—到店履约—核销完成”三个大阶段。
1. 预约阶段
用户在小程序或App端选择就餐时间、人数与桌型,提交预约并完成预付后,系统生成一条待履约的预约单。预约的本质是对未来时间段资源的预占,后续所有业务操作都会围绕“预约单号”进行。在这个环节,需要考虑“预约时段”与“桌台占用”的冲突判定、超时自动取消等问题。
2. 到店点餐阶段
用户到店后,通过扫描桌台完成签到并激活桌台。此时预约单状态从“已预约”变更为“已到店”,点餐功能被开启。点餐系统根据桌台号和预约人数打开菜单,菜单可以是单独的菜品,也可以是预购的套餐。点餐流程如果采用购物车模式,下单数据终会写入订单表和订单明细表。
3. 核销阶段
核销是该类型系统的关键动作。用户持有的可能是一张预约回执码、一份团购券,或者一个储值套餐权益。核销动作触发后,系统需要校验核销凭证的有效性、是否在有效期内、是否被重复使用,然后将凭证标记为“已核销”。核销应与订单状态联动,可以将核销记录和订单支付记录做终统一。
下表展示了系统中核心的状态流转路径:
| 对象 | 核心状态 | 触发动作 |
|---|---|---|
| 预约单 | 待支付 / 已预约 / 已到店 / 已取消 / 已完成 | 支付回调、到店扫码、超时取消、核销完成 |
| 桌台 | 空闲 / 占桌 / 保洁中 | 预约成功、到店签到、更换桌台等 |
| 核销码 | 未使用 / 已锁定 / 已核销 / 已过期 | 用户出示、前端提交、异常超时释放 |
理清状态机后,具体开发时就能避免出现“订单未支付但已占用桌台”或“核销成功但订单未结算”等数据不一致问题。
二、系统架构与技术选型方案
点餐预约核销系统的交互方包括消费者端、商家端和管理后台。从主流开发效率与技术成熟度来看,常见技术组合可以参照具体业务体量来选型。对于大部分中腰部餐饮连锁或独立门店,**SpringBoot + MyBatisPlus + MySQL(后端服务)、uniapp(用户端跨平台)、Vue + ElementUI(管理后台)**是一种开发成本低、交付速度较快、后续易于二开的架构方案。
- 后端服务层:SpringBoot负责提供RESTful API,建议按业务域拆包,如order模块、reservation模块、verification模块。MyBatisPlus负责ORM层操作,它的条件构造器、分页插件、逻辑删除功能可以显著减少重复SQL编写。
- 用户端应用:基于uniapp(Vue语法)开发,可在保留原有业务逻辑的同时交付小程序、H5页面以及App安装包。业务中要注意不同平台的登录鉴权差异(比如小程序使用code换取会话),以及地图/定位等原生功能的插件兼容性。
- 管理后台:Vue + ElementUI的组合相对适合常见表单页、列表页和权限配置场景,核销记录查询、门店桌台管理、菜单分类维护、预约时段模板设置等页面可以快速搭建。
在部署与运维层面,系统可以采用标准的单体应用方式部署,数据库按业务增长选择单库或一主多从架构。当预约并发量达到一定规模时,考虑引入Redis作为分布式缓存和分布式锁服务端即可。
三、核心数据模型设计:从预约单到核销记录
无论是点餐预约还是核销系统,核心的表结构都应具有清晰的职责边界。在设计阶段,建议围绕两个“主轴线”展开:一条是预约库存链路(桌台、时段、预约单),另一条是交易履约链路(订单、核销凭证、核销记录)。下面用简化的方式给出主要数据表的逻辑字段:
- 门店桌台表(shop_table):门店ID、桌台编号、桌台类型(大厅/包间)、可容纳人数、当前状态、当前绑定预约单号。由于桌台属于空间资源,状态更新时通常需要锁行或乐观锁。
- 预约时段表(reservation_time_slot):门店ID、时段开始时间、时段结束时间、可预约桌台总量、已预约数量。时段可以按午市、晚市批量生成,也可以支持自定义日期区间。
- 预约单表(reservation_order):预约单号、用户ID、门店ID、桌台ID、联系人姓名、、预约时间、就餐人数、状态、超时锁定截止时间。建议做加密存储,防止数据库泄露导致客户信息外流。
- 核销凭证表(verification_certificate):凭证码、绑定用户ID、凭证类型(团购券/套餐券/抵扣券)、有效期、状态、核销门店ID、核销操作人、核销时间。核销凭证码建议用无业务含义的随机字符串或分布式ID生成,防止可猜测导致的薅羊毛风险。
- 核销记录表(verification_record):核销记录流水号、核销凭证ID、预约单号、订单ID、核销门店、操作人、核销来源、核销状态。核销记录本身是流水型数据,不允许UPDATE删除,只能新增。
表之间关键的关联关系是:预约单通过桌台ID关联门店桌台;用户端的点餐订单通过预约单号与预约行为关联;核销凭证与预约单号一旦绑定,就形成了“先预约,后消费”的溯源关系。
四、关键技术实现:并发预约与幂等核销
在实际编码过程中,有两个容易出bug的高复杂度环节:预约时段冲突处理与核销防止重复提交。这里给出一些可以复用的实现思路。
1. 预约时段的防并发占位
当两个用户同时提交同一桌台、同一就餐时间的预约请求时,如果只在应用层先select再update,很可能出现数据覆盖。降低冲突概率可以采用两条策略:
首先是数据库层的约束。可以构建一个“门店ID + 桌台ID + 预约日期 + 时间段”的索引,预约插入时如果该桌台指定回话已被占用,则数据库会因为键冲突直接拒绝第二条插入。
如果业务允许同一桌台在不同时段灵活拆分,那么可以通过带条件更新实现原子化占用:
UPDATEshop_tableSETcurrent_reservation_no=#{reservationNo},status=1WHEREid=#{tableId}ANDtable_date=#{diningDate}ANDtime_slot_id=#{timeSlotId}ANDcurrent_reservation_noISNULL在执行该UPDATE后,通过返回的影响行数(int affectedRows)判断是否抢占成功。若affectedRows > 0,代表预约成功;否则说明桌台已被其他请求占用,可给用户提示选择其他时段。
2. 核销的幂等设计
核销的本质是把一个凭证从“未使用”变化为“已核销”。容易出错的是网络超时