☰
基于微信小程序和SpringBoot的餐厅点餐系统开发实践
2026/10/15 11:38:02 网站建设 项目流程

做毕设那会儿,A同学拿了个标题来找我,大意是“基于微信小程序的数字化餐厅服务管理系统”,还带着一串“智慧餐饮”“移动端服务平台”的修饰词。他问我这玩意儿到底要写什么、从哪下手。我看了眼标题,把他拉到屏幕前说:放到代码层面,它就是一套SpringBoot写后端、微信小程序写前端、MySQL存数据的餐厅点餐系统,外加桌台管理、订单流转和支付回调。别被“智慧餐饮”四个字吓到,真正要啃的是三条线:登录态怎么串起来、下单这步怎么保证不超卖不重复、订单状态从待支付到已完成怎么稳着走。

这篇文章我就顺着这三条线,把SpringBoot加微信小程序这套点餐系统的完整开发思路、核心表结构、下单链路的代码逻辑、小程序端交互和支付联调,以及我实测下来最容易翻车的地方,一次性讲清楚。适合正在做毕设、准备做课程设计,或者单纯想低成本弄一套餐饮点餐demo的开发者参考。

1. 先想清楚:SpringBoot加小程序,这套组合到底解决了什么问题

很多同学拿到这个题目,第一反应是到处找源码、拼界面,但真正答辩的时候被老师问一句“为什么选这个架构”就卡住了。所以我先把选址的底层逻辑聊明白,后面写代码才有底气。

1.1 从传统点餐流程里的三个痛点说起

传统餐厅点餐,哪怕规模不大的中小门店,也有几个绕不开的痛点。第一,服务员手写点菜单,后厨再对着纸单喊菜备餐,高峰期一忙就容易漏单、错单,对账的时候更是扯皮。第二,顾客结账要去前台排队,支付方式多、退款要人工核算,门店对账全是Excel手工活。第三,菜品库存、售罄状态、优惠活动这些信息,顾客坐在桌上根本看不到,只能一遍遍问服务员,服务成本全堆在人力上。

把点餐这件事挪到微信小程序里,核心不是“用了个新东西”,而是把这三条线全部数字化:顾客扫码进小程序,自己看菜、下单、支付;后厨在管理端看到实时订单,按序出菜;数据库里自动生成订单和流水,对账只需要查表。所谓“智慧餐饮”“数字化餐厅服务管理”,本质上就是让一份点餐数据从产生、流转到结算,全程可追踪、可统计,不需要任何一张纸质单据。

1.2 技术选型:为什么不是SSM、不是微服务、不是App

我接这个题目时的第一个建议就是:后端用SpringBoot,别碰微服务。理由很直接。这套系统的真实规模,撑破天就是一个小程序并发几十个请求,单体架构完全够用。微服务拆来拆去,光注册中心、配置中心、服务间调用,就得占掉毕设报告一大半篇幅,还要处理分布式事务,纯属给自己挖坑。

那为什么是SpringBoot而不是SSM?SpringBoot把SpringMVC、MyBatis、Tomcat这些组件自动装配好了,你只需要加几个starter、写配置项,就能跑起来。对做毕设的人来说,省下来的时间应该花在业务逻辑上,而不是折腾XML配置文件。另一方面,SpringBoot生态成熟,微信登录、支付SDK、Redis缓存都有现成封装,遇到问题随便一搜就是答案,不会卡在一个冷门坑里出不来。

前端为什么不选原生App或者H5?原生App要装、要更新、上架审核周期长,评委打开你手机第一件事是找安装包,多尴尬。H5虽然免安装,但调用微信支付、获取微信授权登录的体验明显不如小程序顺手。小程序天然带微信登录能力,扫码即用、用完即走,用户不用下载任何东西,这个特性恰好匹配餐厅点餐这种高频短时场景。再配合桌台上的二维码,顾客进门扫一下就能点餐,完全不用店员引导。

1.3 这套系统的真实定位:适合谁、拿来干什么

说白了,这套SpringBoot加微信小程序的点餐系统,最合适的场景就是两类人。一类是做毕业设计或课程设计的在校生,题目要覆盖前端、后端、数据库、接口设计、支付联调这些点,这套系统的技术栈刚好能全部覆盖,而且每块都能讲出深度。另一类是准备给中小餐饮门店做低成本数字化方案的个人开发者或小团队,不需要采购昂贵的收银系统,用一套开源点餐系统加一个小程序就能解决问题。

你要清楚它的边界:它不是企业级的智慧餐厅大脑,不做复杂的会员营销、不做多门店进销存,它聚焦的是“点餐、支付、订单流转”这个最小可用闭环。把这个闭环做到稳定、清晰、可解释,才是这个题目真正的得分点。

2. 搭建系统骨架:模块划分、数据表设计与几个要命的细节

想清楚定位之后,就可以动手搭工程了。这一章不写具体工具安装步骤,那些网上一搜一大把,我重点讲工程结构怎么拆、数据表怎么设计,以及我见过的所有新手项目里最容易翻车的几个设计细节。

2.1 后端工程怎么拆,才不会越写越乱

SpringBoot工程我建议不要一上来就是一个大杂烩包,所有类堆在controller、service、mapper三层里,项目还没到两千行就开始乱。比较稳妥的做法是用Maven做一个多模块工程,按职责分成三个模块:

  • common模块:放通用返回结果、异常处理、工具类、JWT工具、常量定义。
  • api模块:专门暴露给微信小程序调用的接口,统一前缀/api/v1,权限拦截器也放在这里。
  • admin模块:给门店管理员用的后台接口,比如菜品管理、订单管理、桌台管理,接口前缀统一/admin/v1。

如果嫌三个模块在IDE里切换麻烦,用单工程多包名也行,包结构必须有意识地去分:config、controller、service、mapper、entity、dto、vo。这里要特别注意,entity对应数据库表,dto接收前端入参,vo返回给前端展示,三者不要混用。很多同学喜欢直接把实体类返给前端,结果密码字段、openid字段全暴露了,这在答辩时是会被点名的安全漏洞。

接口路径命名也最好带上版本号,哪怕你根本没做多版本兼容,/api/v1/dishes这种写法也能让老师看到你有演进意识。

2.2 核心数据表:从用户到订单明细

数据库我默认选MySQL,表结构围绕“用户—桌台—菜品—订单—订单明细”五条主线设计。下面这张表清单是这套系统的核心,照着建基本不会有大的返工。

表名关键字段用途说明
t_userid、openid、nickname、avatar、phone、create_time微信用户信息,openid唯一
t_tableid、table_no、seat_num、status、qr_code桌台信息,status区分空闲/占用
t_categoryid、name、sort菜品分类,比如热菜、凉菜、饮品
t_dishid、category_id、name、image、price、stock、status、sales菜品主表,status控制上架下架
t_orderid、order_no、user_id、table_id、total_amount、status、pay_time、remark订单主表,一个订单对应一个桌台
t_order_itemid、order_id、dish_id、dish_name、price、quantity、taste订单明细表,存菜品快照
t_cartid、user_id、dish_id、quantity、checked购物车表,也可以放前端本地缓存

这里最核心的设计点在于t_order_item这张明细表,它存的不仅仅是dish_id,还冗余了dish_name和price。为什么?因为菜品后期的价格和名称可能调整,如果订单明细里只有外键,历史订单查询时菜品名会跟着变,价格也会对不上账。做餐饮系统,订单是账单凭证,必须保留下单那一刻的商品快照,这个习惯在面试和答辩中都很加分。

2.3 数据库设计里最容易翻车的四个细节

第一,金额一律用DECIMAL(10,2),禁止用float或double。二进制浮点数在金额运算时会出现类似0.1加0.2不等于0.3的精度问题,做支付项目这是硬伤。Java对应类型用BigDecimal,别用Double。

第二,订单号不要用数据库自增id裸奔。自增id会暴露订单量,而且很多场景需要提前生成订单号。我建议订单号规则为yyyyMMddHHmmss + 4位随机数 + 后4位用户id,比如2025062013201578200366。并发高的时候,同一毫秒可能生成重复号,随机数部分通常够用,真不放心可以做成全局发号器。

第三,状态字段用tinyint加注释表示,不要直接存中文。status字段的值含义写在字段注释里,比如0待支付 1已支付 2备餐中 3待取餐 4已完成 5已取消 6已退款。虽然看起来不如枚举字符串直观,但数据库层面用整型存状态,索引效率和存储空间都比字符串好,Java代码里再用枚举类做映射,两头都舒服。

第四,索引不是越多越好,但三个索引必须有:t_order(user_id)、t_order(status, create_time)、t_dish(category_id)。第一个索引支撑“我的订单”,第二个索引支撑超时关单和后台订单列表筛选,第三个索引支撑点餐页分类查询。

3. 下单链路深度拆解:登录态、库存扣减与订单状态机

点餐系统的核心难点不在界面,而在下单这一条链路上。一个用户从打开小程序到完成支付,后端要在正确的位置做校验、扣库存、写订单、防重复,任何一步漏了,都会在真实使用中暴露问题。

3.1 微信登录:code换openid的完整闭环

微信小程序登录的原理不复杂,但这里有个常见的错误做法,就是让前端直接把code对应的数据处理掉,或者把openid传给后端作为用户标识。正确闭环是这样的:

  1. 前端调用wx.login(),拿到一个临时登录凭证code,有效期很短,大概五分钟。
  2. 前端把code发到后端/api/v1/auth/login。
  3. 后端用code + appid + secret调用微信开放平台的jscode2session接口,换回openid和session_key。
  4. 后端拿着openid去t_user表查询,查不到就自动注册一个新用户。
  5. 后端自己生成一个登录凭证,我用的是JWT,把userId等必要信息放进去,返回给前端。
  6. 前端把JWT存到小程序的storage里,后续所有接口在header带Authorization: Bearer <token>。

session_key是用来解密手机号等敏感信息的,绝对不能返回给前端。很多教程会把openid直接返给前端,虽然危害没有session_key那么大,但我也不建议这么做。登录接口只返回token和用户基本信息就够了,openid留在后端内部使用,这种“最小暴露”原则在答辩时能体现你的安全意识。

JWT要设置过期时间,一般24小时。过期后前端请求接口会收到401,这时应该自动重新走一遍wx.login换取新token,而不是把用户踢回登录页。小程序就是这种静默登录模式,用户全程无感知。

3.2 创建订单:一个下单请求在后端要过的“五道关卡”

很多新手写的下单接口就是“前端传菜品列表和总金额,后端插入订单表”就完了,这是标准的错误示范。一次正经的下单请求,后端至少要过五道关卡。

关卡一是登录校验,这通常在拦截器里做,不进入业务方法就拦截掉,没有有效token直接返回401。

关卡二是参数校验,桌台id是否存在、桌台是否处于空闲状态、订单明细是否为空、每个明细的数量是不是正整数。这里我用Spring的@Validated注解配合@NotNull、@Min这类标注,省去手写一堆if。

关卡三是菜品校验,菜品是否存在、是否已下架、库存是否足够。菜品要查库,不能信前端传的菜品名称和价格。价格必须以数据库为准重新计算,前端只能传菜品id和数量,总金额由后端计算,防止用户改包价格。

关卡四是库存扣减,这一步是整个下单链路并发风险最高的地方。如果我先select库存再看是否大于0,然后 update,两个请求同时进来就会超卖。正确姿势是用一条带条件的update完成原子扣减:

Integer result = dishMapper.deductStock(dishId, quantity); // UPDATE t_dish SET stock = stock - #{quantity}, sales = sales + #{quantity} // WHERE id = #{dishId} AND stock >= #{quantity} if (result == null || result == 0) { throw new BizException("菜品[" + dish.getName() + "]库存不足"); }

这条SQL只有一行,但它在数据库层面保证了“扣减成功的前提是库存充足”,写进UPDATE的条件里,并发情况下数据库行锁会保证只有一个请求能成功扣减到0,另外一个影响行数为0,直接抛异常回滚。

关卡五是事务一致性。下单整个流程,包括扣库存、写订单主表、写订单明细表,必须放在同一个事务里。我习惯给方法加@Transactional(rollbackFor = Exception.class),注意一定要写rollbackFor,默认情况下它只在RuntimeException时回滚,如果你抛的是受检异常,事务不会回滚,库存就被扣了但订单没生成,就麻烦了。

下单成功后,返回订单id和订单号给小程序端,前端拿到之后才能发起支付。这里还要做一层防重复提交,最简单的手段是前端下单按钮在请求期间置灰,后端再加一层幂等校验,比如同一个用户同一桌台在1秒内不能重复提交订单,可以用Redis的setIfAbsent做分布式锁,单机部署的话用本地锁或者数据库唯一约束也够。

3.3 订单状态机:从待支付到已完成,每一步都不能乱走

订单状态是整个系统的脊梁骨,所有业务都是围着它转的。我强烈建议在代码里新建一个订单状态枚举类,把合法流转写死在枚举里,而不是在业务代码里随便if判断。下面这张表是完整的状态流转规则:

当前状态可流转到触发条件
0 待支付1 已支付 / 5 已取消用户支付成功;用户取消或超时关单
1 已支付2 备餐中 / 6 已退款商户接单;用户申请退款且商户同意
2 备餐中3 待取餐 / 6 已退款后厨出餐;商户因缺货取消
3 待取餐4 已完成用户确认取餐或系统自动确认
4 已完成无终态
5 已取消无终态,需恢复库存
6 已退款无终态,需恢复库存并原路退款

超时未支付的状态流转值得单独拿出来说。我见过很多项目只做“用户手动取消”,没有做超时关单。结果演示的时候,下了单不支付,订单永远挂在那里,库存也一直被占着,评委一句“如果用户不支付怎么办”就把人问住了。处理方案有三种:一是定时任务扫描status=0且create_time超过15分钟的订单,批量关单并恢复库存;二是用Redis延迟队列;三是下单时把订单信息放入延迟队列,到期自动执行。毕设场景用方案一就够了,一个@Scheduled注解加一条update SQL就能搞定。

订单关单时一定要恢复库存,这一步和下单扣库存是反向操作,同样要注意只能恢复一次。我通常会把状态更新写成带条件的:UPDATE t_order SET status=5 WHERE id=#{id} AND status=0,影响行数为1才恢复库存,否则说明订单已经被处理过了,直接跳过,这就天然避免了重复关单。

4. 小程序端:从首页到支付,把交互和联调一次说透

后端链路理清楚之后,小程序端反而没有太多悬念,主要工作是页面搭建和支付联调。但“没悬念”不等于“没坑”,尤其是微信支付的签名验签这块,几乎每个第一次做的同学都会栽一遍。

4.1 页面结构与点餐流程的交互设计

小程序端我建议按四个主页面设计,加上一个扫码落地逻辑:

  • 首页:展示门店公告、推荐菜品轮播图、分类入口和精选菜品,主要起到导流作用。
  • 点餐页:核心页面。左侧是菜品分类栏,右侧是菜品列表,每个菜品显示图片、价格、月售、库存状态,底部悬浮购物车栏,点击加减实时更新数量和合计金额。
  • 订单页:分“进行中”和“历史”两个标签页。进行中的订单展示支付倒计时、备餐状态、待取餐提示。
  • 个人中心:展示用户信息、历史订单、优惠券入口(如果有)、联系客服。

点餐页左侧分类右侧列表的布局,本质是一次典型的“主从联动”交互。左侧点击分类,右侧scroll-view滚动到对应位置;右侧手动滚动,左侧高亮自动切换。这个联动用scroll-into-view属性实现最方便,每个菜品块加一个id,分类点击时把scroll-into-view设为对应的分类id即可。

桌台扫码的逻辑要提前设计好。我给每张桌台生成的二维码,本质上是一个带参数的路径,比如pages/order/order?tableId=8。用户第一次进小程序时,参数会被存在onLoad的options里。建议在获取到tableId后存入全局数据,这样用户在小程序里跳转过程中不会丢失桌台信息。下单的时候,桌台id必须从页面上带过来,和后端校验的桌台状态一一对应,防止用户“人在A桌,单下到B桌”。

购物车数据存哪里,我建议直接用前端Storage存储,不需要单独建购物车表。用户选菜、改数量都只操作本地缓存,结算时把购物车数据作为下单参数一次性传给后端。这样做的好处是交互零延迟,以后想做跨端同步购物车再加接口也不迟。

4.2 请求封装、Token维护与登录态刷新

小程序里最忌讳每个页面都直接调wx.request,到处复制粘贴success回调。我做了一个统一请求封装的request.js,核心逻辑有三层。

第一层,统一拼接baseURL和路径,设置请求头。从wx.getStorageSync('token')读取token,拼到Authorization字段。第二层,统一处理HTTP状态码,200时走成功分支,401时触发重新登录流程,其余错误码弹出wx.showToast并reject。第三层,所有网络请求用Promise封装,页面里async/await调用,代码可读性会好很多。

这里有个很关键的登录态刷新细节。401不一定代表用户真的没登录,也可能是token过期了。所以我写了一个ensureLogin方法,在收到401时,先判断当前是否已经在处理登录中,避免并发请求同时触发多次wx.login。只让一个请求去执行wx.login换token,其他请求在Promise队列里等待,token刷新完成后再重放失败的业务请求。虽然代码量不多,但这个处理能让整个小程序在真实使用时体验非常顺滑。

4.3 微信支付联调:签名、回调、幂等一个都不能少

支付是整个毕设里最硬核的部分,也是评委最爱问的地方。整体流程是这样:小程序下单成功后,拿到订单id,调用后端/api/v1/pay/prepay;后端用订单信息组装支付参数,调用微信支付统一下单接口(现在一般是v3版),拿到prepay_id;后端再把prepay_id和时间戳、随机串、签名返回给小程序;小程序调wx.requestPayment拉起支付面板;用户支付成功后,微信服务器异步回调后端接口,回调里更新订单状态。

支付签名这块,很多同学容易搞混。你在后端调微信支付接口时,要用商户私钥对请求数据签名;微信返回的响应,也要用微信支付平台证书验签;小程序端wx.requestPayment需要的参数,是后端用prepay_id二次签名生成。两套签名逻辑、三处验签,绝对不能合并。我在做的时候直接用官方SDK,wechatpay-java或者旧版wechatpay-apache-httpclient,把签名和验签都封装好,比手写加密代码安全得多。

支付回调是最容易写错的地方。回调接口不能简单“成功就返回success”,要做三件事。第一,验签,确认请求确实来自微信支付,防止伪造回调。第二,解密resource里的订单数据,取出out_trade_no和transaction_id。第三,处理幂等,先查订单当前状态,如果已经是已支付,直接返回成功,不再重复更新,防止微信重试回调导致状态被覆盖或库存被重复扣减。

回调处理成功后要返回{"code":"SUCCESS"},微信收到这个返回值才会停止重试。如果处理失败返回非200,微信会按照一定策略多次重试回调,这是设计好的机制,千万不要在回调里做任何限流。

我调试支付时踩过的最典型的坑,是在本地联调时回调地址填了localhost,结果微信服务器根本访问不到。正确做法是把后端部署到一台有公网HTTPS地址的服务器上,或者在本地用内网穿透工具把回调地址暴露出去。当时我图省事,直接让后台定时任务把超时未支付订单关掉,结果测试支付时回调一直不进来,订单状态卡在待支付,查了半天才发现是回调地址不通。

5. 从开发到演示:我踩过的坑和一份必查清单

最后这部分不是代码,是写这套系统的过程中,我在本地联调、临场演示和答辩准备三个阶段踩过的坑。这些坑不解决,代码写得再漂亮都会在最后一步掉链子。

5.1 本地联调阶段最容易卡住的三个地方

第一个,小程序开发工具默认不允许访问任意域名,直接请求http://localhost:8080会报“不在以下request合法域名列表中”。开发调试时在“详情—本地设置”里勾选“不校验合法域名”,就能连本地后端。但记得只是开发环境这么干,上线前的真机预览还是要配置HTTPS合法域名。

第二个,如果是在手机上用真机预览,localhost是手机自己,不是你的电脑。这时要把后端启动地址换成电脑的局域网IP,比如http://192.168.1.100:8080,并且保证手机和电脑在同一个WiFi下。很多同学第一次真机调试,小程序白屏或者请求超时,十有八九是这个原因。

第三个,微信登录接口必须在小程序后台配置合法域名,否则wx.login在正式版本里都正常,但自己电脑调试时因为域名没备案或没配置,一直报错。登录接口的域名校验和普通请求是同一个配置,建议第一天就把小程序AppID申请好,后台管理权限拿到手,不要拖到最后一周。

5.2 演示临场翻车的隐藏点

演示翻车最可怕的不是功能没有,而是功能本来好的,到了台上突然不对劲。我总结了三类常见的隐藏问题,建议演示前一晚逐项检查。

数据库时区问题。MySQL连接串里如果不指定时区,而服务器默认时区不是北京时间,订单的create_time会比真实时间少8小时。想象一下,演示的时候你下单两小时前,页面却显示下单时间是“昨天”,这种画面太尴尬了。连接串里加上serverTimezone=Asia/Shanghai,同时把JVM时区也设置为GMT+8,两边统一。

金额精度问题。下单之后,订单列表显示的金额和实际支付金额差一分钱,别笑,这是用Double累计金额的经典效果。解决办法就是前面说的,数据库用DECIMAL,Java用BigDecimal,前端展示时再统一保留两位小数。

图片域名问题。小程序请求图片也有域名白名单,如果菜品图片放在某个图床,而没有在小程序后台配置downloadFile合法域名,正式版打开点餐页图片全是裂的。演示机预览时很多人会忽略这一点,因为本地开发勾了“不校验合法域名”。演示前一定用预览版或者体验版跑一遍,不要一直用开发工具。

网络波动问题。现场观众连的是同一WiFi,演示到一半小程序白屏或者接口超时是常见事故。我在演示脚本里设置了一个兜底:所有请求超时时间设为15秒,并且给关键页面加了错误重试按钮,真出问题至少能快速恢复。

5.3 答辩准备:技术角度和演示角度的递进思路

答辩的时候,老师其实不指望你把整套系统的每一行代码讲清楚,他们更想听到的是“你有没有真正理解这个系统在解决什么问题”。我建议从三个递进角度准备。

第一个角度是用户侧。讲清楚顾客从扫码到支付的完整路径,着重说“为什么小程序比App更适合餐厅点餐”,回答要落到免安装、微信登录、支付闭环这些具体能力上。

第二个角度是门店侧。讲清楚服务员如何通过管理端接单、后厨如何看到出餐列表、桌台状态如何回收。这个角度能体现“数字化管理”,而不是只有一个孤零零的点餐页面。

第三个角度是技术侧。这是拉开差距的地方。至少准备两个“为什么”的答案:为什么库存扣减要用带条件的update而不是先查后改,为什么支付回调要做幂等处理。这两个问题能答上来,基本就能证明你系统设计里考虑过并发安全和数据一致性,而不是只做了个CRUD。我在答辩前把数据库表关系图、状态机流转图、支付回调时序图各画了一张,打印出来带进教室,讲解时直接对着图说,老师全程跟得上思路,问的问题也就自然绕开了刁钻角度。

最后分享一个小技巧。这套系统做完了,不要急着写报告,先把演示录个视频存手机里。真到答辩那天设备出了状况,直接播视频也能救场。点餐这个题目看着烂大街,但只要你把“登录—下单—支付—订单流转”这条主链路吃透,把每一层的细节都讲出自己的理解,它就是一个完整且能打的项目。祝顺利。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询