基于Spring Boot + UniApp的酒馆预约系统实战开发指南

基于Spring Boot + UniApp的酒馆预约系统实战开发指南

在本地生活服务数字化浪潮中,酒馆预约系统作为同城预约服务的重要细分场景,正受到越来越多酒吧、Livehouse等商户的重视。一套完整的酒馆预约系统通常包含用户端、商户管理端与后台服务端三大部分。本文将从技术实战角度出发,详细拆解基于 Spring Boot + MyBatis Plus + MySQL 构建后端服务,利用 UniApp 开发跨平台用户端,以及基于 Vue + Element UI 搭建管理后台的完整开发流程。无论您是正在规划该项目的产品经理,还是准备动手实现的开发者,本文都将提供极具参考价值的技术架构方案与核心代码逻辑。

一、项目背景与技术选型分析

酒馆预约业务的特殊性在于其营业时间通常集中在夜间,且存在台位类型划分(如卡座、散台、包厢)、预约时段管理、多人拼桌、酒水套餐预选等复杂需求。相较于普通的餐饮排队叫号,酒馆预约更强调时段库存控制预约状态流转的准确性。

从知识库中多个同城预约服务系统(如露营、蛋糕、鲜花、台球助教预约)的共性来看,技术栈选型高度一致。对于酒馆预约系统,我们推荐以下成熟组合:

  • 后端服务:Spring Boot + MyBatis Plus + MySQL。Spring Boot 简化了项目配置与部署流程;MyBatis Plus 提供了强大的逆向工程与通用CRUD能力,可显著提升开发效率;MySQL 则能够稳定支撑预约订单这类事务性较强的数据存储。
  • 用户端:UniApp(Vue 3语法)。一套代码可同时编译为 H5、小程序及 Android/iOS App,完美适配酒馆顾客通过小程序或公众号预约的核心场景。
  • 管理后台:Vue 3 + Element Plus。用于酒馆老板或店长进行桌台管理、预约审核、订单查看、营业数据统计等操作。

该架构的优势在于后端逻辑与前端展示完全解耦,便于后续业务扩展(如增加婚礼包场、主题派对预约等模块)。同时,UniApp 的跨平台特性可以极大降低多端适配的开发成本。

二、数据库核心表结构设计与状态机

在设计酒馆预约系统数据库时,我们需要跳出简单的“增删改查”思维,重点解决资源冲突超卖问题。以下是三个核心表的精简设计方案:

1. 桌台资源表(table_info

CREATE TABLE `table_info` (
  `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
  `table_name` VARCHAR(50) NOT NULL COMMENT '桌台名称(如A01卡座)',
  `category` TINYINT NOT NULL COMMENT '台型分类:1-散台 2-卡座 3-包厢',
  `capacity` INT NOT NULL COMMENT '建议容纳人数',
  `min_consume` DECIMAL(10,2) DEFAULT NULL COMMENT '消费(仅作为业务规则字段,不涉及实际在线支付)',
  `status` TINYINT DEFAULT 1 COMMENT '状态:0-停用 1-启用',
  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

2. 预约订单表(appointment_order
在订单表中,除了记录用户ID、、备注等基础信息外,关键的字段是 appointment_datetime_slot_id。为了性能考虑,通常不直接存储时间段字符串,而是存储一个时段字典ID。同时设置 order_status 字段用于追踪状态。

3. 时段库存表(table_stock
防超卖的核心在这里。酒馆通常按小时或按场次(如场19:00-21:00,第二场21:00-24:00)划分时段。我们在 table_stock 表中同时存储 table_idtime_slot_id,并以记录是否存在作为该时段是否可订的依据。

// 伪代码:下单时确保时段性
public boolean createAppointment(AppointmentDTO dto) {
    // 利用数据库约束(table_id + time_slot_id + appointment_date)
    // 或使用 SELECT ... FOR UPDATE 对库存记录加锁,判断是否已被占用
    LambdaQueryWrapper<TableStock> wrapper = Wrappers.lambdaQuery();
    wrapper.eq(TableStock::getTableId, dto.getTableId())
           .eq(TableStock::getTimeSlotId, dto.getTimeSlotId())
           .eq(TableStock::getStockDate, dto.getAppointmentDate());
    TableStock stock = tableStockMapper.selectOne(wrapper, false);
    if (stock != null && stock.getStatus() == 0) {
        // 已被锁定或预约,抛异常提示
        throw new ServiceException("该时段已被预约");
    }
    // 后续逻辑:新增订单,并将对应的库存记录status置为1
}

通过合理的表结构设计,我们将酒馆预约中“某张桌子在某天某个时段是否空闲”这一个多维度的复杂查询,简化为了单表记录的索引查询。对于依赖此场景的上门服务、同城预约类系统,该设计思路同样具备通用性。

三、后端核心接口开发与异常拦截

后端模块建议按功能拆分为 bizsystemtask,这里重点讲解预约业务中的几个关键接口逻辑。

1. 查询可预约桌台
用户端进入预约页面时,需要让用户选择日期与人数。后端接口需要返回所有满足“容纳人数”且“目标时间段未被占用”的桌台列表。

@PostMapping("/availableTables")
public Result<List<TableVO>> getAvailableTables(@RequestBody QueryTableDTO dto) {
    // 1. 根据人数筛选出合适的桌型
    // 2. 查询该日期下指定的 time_slot_id 的库存
    // 3. 将库存表中不存在的table_id进行过滤(说明该时段可用)
    // 4. 若当前登录用户是会员,还可返回该桌台对应的套餐推荐列表
}

这类接口在实现时需要额外注意 N+1 问题,避免在循环中查询数据库,建议通过 IN 查询后采用内存 Map 匹配。

2. 预约状态机管理
预约状态不应只是简单的“待确认”与“已完成”。在酒馆场景下,建议包含以下状态:

  • 待支付订金(如有此业务规则)
  • 待商户确认(酒馆需要核对预约信息,剔除恶意占座)
  • 预约成功(商户确认后,用户收到模板消息推送)
  • 履约完成(用户到店核销)
  • 爽约超时(超过保留时间未到店自动释放台位)
  • 已取消(支持提前N小时无损取消)

这些状态建议使用整数存储,并在后端通过策略模式进行流转控制。避免在业务代码中散落大量的 if 判断。

// 商户端审核预约接口
@PostMapping("/audit")
public Result<?> auditOrder(@RequestBody AuditDTO auditDTO) {
    // 验证操作者角色是否为店长
    // 校验当前订单状态是否为“待商户确认”
    // 若审核通过,则状态改为预约成功,并触发短信或订阅消息下发任务
}

3. 全局异常处理与日志
对于预约类系统,并发冲突是常见的异常场景。建议在启动类上开启 @EnableTransactionManagement,并在 ControllerAdvice 中捕获 DuplicateKeyException 和自定义业务异常。

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ServiceException.class)
    public Result<?> handleServiceException(ServiceException e) {
        // 预留日志打印逻辑
        return Result.fail(e.getMessage());
    }
}

在开发阶段建议开启 SQL 日志打印,利用 MyBatis Plus 的 SQL监控 插件定位慢查询语句。要特别注意针对 appointment_order 表中的 appointment_datetel_no 建立联合索引,确保在高并发查询下依旧能保持敏捷响应。

四、用户端与管理端的协作开发

用户端与管理端的核心开发流程基本一致,这里重点讲解基于 UniApp 的小程序端在 API 封装层面的注意事项,以及管理端如何实现高效的预约日历视图。

1. 用户端(UniApp)请求封装
在小程序端中,请求头需要携带 token 用于鉴权。鉴于酒馆预约是一个低频操作,但秒杀特定节假日(如圣诞节、跨年夜)时会存在并发,建议用户在页面端做防重复点击处理。

// utils/request.js 核心封装逻辑
const BASE_URL = 'https://your-api-server.com';

export const request = (options) => {
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + options.url,
      method: options.method || 'GET',
      data: options.data || {},
      header: {
        'Authorization': uni.getStorageSync('token') || ''
      },
      success: (res) => {
        if (res.data.code === 200) {
          resolve(res.data.data);
        } else if (res.data.code === 401) {
          uni.redirectTo({ url: '/pages/login/login' });
        } else {
          uni.showToast({ title: res.data.msg, icon: 'none' });
          reject(res.data);
        }
      }
    });
  });
}

2. 管理端(Vue + Element Plus)日历与时间线
在管理端后台,店长关心的是一张“桌位预定情况总览”。我们可以利用 Element Plus Calendar 组件结合 v-loading 指令来加载数据。当你点击某一天时,右侧表格出现所有时段的预约流水记录。

虽然管理端代码不属于线上用户可直接感知的范围,但它承担了极其重要的“降本增效”职责。一个清晰的管理后台能帮助酒馆工作人员精准掌握翻台节奏,极大地降低人工沟通成本——这也是从零开始开发该系统核心的指导原则。

开发建议:项目开发完成后,务必使用 uniapp 的云打包功能生成 wgt 包进行热更新,避免每次修改用户端 UI 都要发版审核的情况。同时,部署文档与接口文档需严格执行统一规范,以便降低后续二次开发的理解成本。

FAQ:酒馆预约系统开发常见问题

1. 如何防止同一个用户在同一个时段预约多张台位?
:需要在后端通过 Redis 或数据库层面做幂等控制。简单的方式是在 appointment_order 表中增加 user_id + appointment_date 作为约束条件,同时利用数据库锁防止并发请求导致的脏数据写入。更严谨的做法是引入分布式锁,在生成预约单号时锁住该用户的预约权限。

2. 客户爽约率较高,能否在系统层面做限制?
:可以引入信用机制。本系统可通过定时任务(如 Spring 的 @Scheduled 注解)在每日固定时间点扫描超过保留时间仍未到店核销的订单,自动将其标记为 “爽约”。当用户的爽约次数达到设定阈值(比如3次)后,系统限制其连续 7 天无法发起新预约,从而保护商户的翻台率不受恶意占座影响。

3. 小程序的预约成功通知如何实现?
:在用户支付订金且通过商户审核后,由后端向下单接口中预存的 formId 或用户 openid 发送订阅消息模板。需要注意小程序的订阅消息支持“一次性订阅”和“长期订阅”。在开发阶段,建议将 subscribeMessage.send 接口的调用情况记录到日志表中,便于排查模板 ID 配置错误或用户拒收导致的投递失败问题。

4. 由于营业时间横跨深夜,若在 23:50 分预约后天的桌台,如何避免日期计算错误?
:不要采用当前系统时间进行拼接。前端在传参时必须将选择的时间点转换成时间戳传入后端,后端统一采用 LocalDate.plusDays(offset) 进行日期计算,并将预约截止时间定义为次日凌晨 6:00 而非 24:00,以避免因营业日跨天导致的数据错乱。通过合理的字段设计与严谨的边界测试,可彻底规避此问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值