☰
微信小程序点餐系统实战:从扫码点餐到后厨出单的完整链路
2026/10/27 23:22:10 网站建设 项目流程

简介:这是一套面向微信小程序初学者与前端开发者的点餐系统实战源码包,围绕菜单展示、分类浏览、购物车、订单管理、微信支付、用户登录与后台管理等核心模块展开,帮助读者在真实项目中理解小程序从原型设计、数据库规划到接口对接与上线审核的完整链路。压缩包共438个文件,约1.44MB,以117个js业务逻辑脚本、86个md说明文档、82个json配置、67个png界面素材为主,另含wxss样式、wxml结构、ts类型文件及eslint、yml等工程配置,目录层次清晰,便于按模块检索与二次修改。目前已有13762人学习下载,适合希望借助现成demo快速跑通点餐流程、对照源码补齐数据绑定、网络请求、本地存储与支付集成等关键知识点的开发者参考实践。

1. 微信小程序点餐系统:从扫码到后厨出单,一条能跑通的链路长什么样

周末去一家新开的快餐店,桌上贴着一张二维码,扫码后直接进小程序点餐、支付,后厨打印机同步出单。这套流程背后,就是微信小程序点餐系统在跑。它解决的核心问题很具体:把「顾客看菜单、下单、付款」和「后厨接单、出餐」这两段原本靠服务员口头传递的链路,压缩成一次扫码加一次点击。适合谁做?中小餐饮门店想省人力、想接外卖平台之外的私域订单,或者开发者想拿一个完整项目练手微信小程序开发实战。这篇文章不讲空泛概念,只讲一条能跑通的链路:前端页面怎么搭、登录和手机号怎么拿、订单怎么落到后端、后厨怎么收到通知,以及我踩过的那些坑。读完你至少能判断这个方向值不值得投入,以及第一步该动哪里。

2. 点餐系统的小程序端:页面结构、登录与手机号获取

2.1 先定页面骨架,再谈交互

点餐小程序的页面数量不多,但每个页面的职责必须切干净。常见做法是拆成五个页面:首页(门店信息加扫码入口)、菜单页(分类加菜品列表)、购物车页(已选菜品加数量加减)、订单确认页(桌号、备注、优惠)、订单详情页(状态流转)。页面之间用wx.navigateTo跳转,购物车数据用全局状态管理,不要在每个页面里各存一份。

我一般会在app.json里先把tabBar配好,只留「点餐」和「我的订单」两个入口。菜单页的分类用左侧竖排、右侧菜品列表的经典布局,滚动联动用scroll-view的scroll-into-view属性实现。这里有个容易忽略的点:菜品图片一定要走 CDN 并压缩到 200KB 以内,否则菜单页首屏加载会明显卡顿,用户等三秒就可能退出。

{ "pages": [ "pages/index/index", "pages/menu/menu", "pages/cart/cart", "pages/order/order", "pages/orderDetail/orderDetail" ], "tabBar": { "list": [ { "pagePath": "pages/menu/menu", "text": "点餐" }, { "pagePath": "pages/order/order", "text": "我的订单" } ] }, "window": { "navigationBarTitleText": "点餐", "navigationBarBackgroundColor": "#ffffff" } }

这段配置决定了小程序的页面注册顺序和底部导航。pages数组第一项是启动页,通常放首页;tabBar的list至少两项,每项要有pagePath和text。注意tabBar页面必须出现在pages数组里,否则真机上底部导航会直接不显示,这个坑我见过不止一次。

2.2 登录换 code,手机号要单独授权

微信小程序的登录流程分两步:先wx.login拿临时 code,后端用 code 换openid和session_key;再通过button的open-type="getPhoneNumber"获取手机号。很多新手会把这两步混在一起,结果发现拿不到手机号。原因是手机号获取需要用户主动点击按钮授权,不能静默调用。

// 第一步:静默登录,拿 code 换 openid wx.login({ success: (res) => { if (res.code) { wx.request({ url: 'https://your-api.com/auth/login', method: 'POST', data: { code: res.code }, success: (resp) => { // 后端返回自定义登录态 token,存本地 wx.setStorageSync('token', resp.data.token); } }); } } }); // 第二步:用户点击按钮授权手机号 // WXML 中:<button open-type="getPhoneNumber" bindgetphonenumber="onGetPhone"> onGetPhone(e) { if (e.detail.code) { // 把 e.detail.code 发给后端,后端换真实手机号 wx.request({ url: 'https://your-api.com/auth/phone', method: 'POST', header: { Authorization: wx.getStorageSync('token') }, data: { code: e.detail.code } }); } }

逻辑说明:wx.login的 code 只能用一次,五分钟内有效,后端换完openid后要生成自己的 token 返回给前端。手机号授权返回的e.detail.code也是临时凭证,同样要后端去换。参数上,token建议放header的Authorization字段,不要拼在 URL 里。注意:如果用户拒绝授权手机号,要有降级方案,比如允许先下单、后补手机号,否则转化率会掉一截。

2.3 购物车与订单确认的数据流

购物车不要只存在页面 data 里,因为用户可能从菜单页加菜后直接跳订单页。我一般用getApp().globalData.cart存一份,同时用wx.setStorageSync做持久化,防止小程序被系统回收后数据丢失。订单确认页提交前,必须做三件事:校验桌号是否为空、校验购物车是否为空、把菜品列表和总价重新算一遍再发给后端。前端算价只是展示,后端必须再算一次,这是防止篡改的基本要求。

// 提交订单 const app = getApp(); const cart = app.globalData.cart; if (!cart.length) { wx.showToast({ title: '请先选菜', icon: 'none' }); return; } wx.request({ url: 'https://your-api.com/order/create', method: 'POST', header: { Authorization: wx.getStorageSync('token') }, data: { tableNo: this.data.tableNo, items: cart.map(i => ({ dishId: i.id, count: i.count })), remark: this.data.remark }, success: (res) => { // 清空购物车,跳订单详情 app.globalData.cart = []; wx.redirectTo({ url: `/pages/orderDetail/orderDetail?id=${res.data.orderId}` }); } });

参数说明:tableNo来自扫码时带的参数或用户手输,items只传菜品 ID 和数量,单价和总价由后端查库计算。这样即使前端被改,后端也不会算错钱。常见错误是把单价也传给后端并信任它,这是典型的翻车点。

3. 后端订单服务:接口设计、状态机与后厨通知

3.1 订单接口的最小集合

一个能跑的点餐系统,后端至少要有五个接口:登录换 openid、手机号解密、菜单查询、创建订单、查询订单状态。菜单查询要支持按分类返回,创建订单要返回订单号和支付参数。订单状态机建议用数字枚举:0 待支付、1 已支付、2 制作中、3 已完成、4 已取消。状态流转只能单向,不能从已完成跳回制作中,否则后厨会重复出单。

# Django 示例:订单模型与状态枚举 from django.db import models class Order(models.Model): STATUS_CHOICES = [ (0, '待支付'), (1, '已支付'), (2, '制作中'), (3, '已完成'), (4, '已取消'), ] order_no = models.CharField(max_length=32, unique=True) openid = models.CharField(max_length=64) table_no = models.CharField(max_length=16) total_price = models.DecimalField(max_digits=10, decimal_places=2) status = models.IntegerField(choices=STATUS_CHOICES, default=0) created_at = models.DateTimeField(auto_now_add=True) class OrderItem(models.Model): order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE) dish_id = models.IntegerField() dish_name = models.CharField(max_length=64) price = models.DecimalField(max_digits=10, decimal_places=2) count = models.IntegerField()

这段模型定义了两个表:订单主表和订单明细表。order_no要唯一,建议用时间戳加随机数生成。status用整数枚举,方便前端判断和数据库索引。注意total_price用DecimalField而不是FloatField,金额计算用浮点会出现 0.1+0.2 不等于 0.3 的经典问题,对账时就是血泪经验。

3.2 支付回调与状态推进

微信支付的回调是订单状态从「待支付」变「已支付」的唯一可靠来源。不要依赖前端支付成功的回调来改状态,因为用户可能支付后直接杀进程。后端收到支付回调后,先验签,再查订单是否已处理,最后把状态改成已支付并触发后厨通知。这里必须做幂等:同一笔回调可能来多次,用order_no加transaction_id做唯一约束。

# 支付回调处理(伪代码,验签逻辑按微信官方文档实现) def pay_callback(request): data = parse_xml(request.body) if not verify_sign(data): return xml_response('FAIL', '签名错误') order_no = data['out_trade_no'] order = Order.objects.filter(order_no=order_no).first() if not order: return xml_response('FAIL', '订单不存在') if order.status != 0: # 已处理过,直接返回成功,保证幂等 return xml_response('SUCCESS', 'OK') order.status = 1 order.save() notify_kitchen(order) # 触发后厨通知 return xml_response('SUCCESS', 'OK')

逻辑说明:先验签防止伪造回调,再查订单状态做幂等,最后改状态并通知后厨。参数上,out_trade_no就是我们的order_no,transaction_id是微信侧流水号,建议存到订单表里方便对账。注意回调必须返回 XML 格式的SUCCESS,否则微信会持续重试,把服务器打满。

3.3 后厨通知的三种落地方式

后厨怎么收到新订单?常见做法有三种:一是后厨放一台平板,页面轮询订单接口;二是用 WebSocket 长连接推送;三是接小票打印机,通过云打印服务出单。轮询最简单,但延迟高、请求多;WebSocket 实时性好,但需要维护连接;云打印最省事,但依赖第三方服务。我一般会先上轮询,间隔五秒,等单量上来再换 WebSocket。

// 后厨平板轮询示例 setInterval(() => { wx.request({ url: 'https://your-api.com/kitchen/pending', header: { Authorization: kitchenToken }, success: (res) => { if (res.data.length) { // 有新订单,播提示音并刷新列表 playNoticeSound(); this.setData({ orders: res.data }); } } }); }, 5000);

参数说明:kitchenToken是后厨账号的独立 token,和顾客 token 分开,权限只允许查待制作订单和改状态。轮询间隔不要低于三秒,否则后端压力大。注意后厨页面要加自动重连和错误提示,网络断了要能看出来,不然漏单了都不知道。

4. 避坑与排查:点餐系统上线前必须过的五道坎

4.1 手机号获取失败,提示「该小程序不支持」

现象:用户点击获取手机号按钮,弹窗提示不支持。原因:手机号快速验证需要小程序主体为企业认证,个人主体没有这个权限。解决:个人开发者可以先用「手机号验证组件」的替代方案,比如让用户手动输入手机号加短信验证码,或者只保留 openid 登录,手机号作为可选字段。

4.2 支付回调没收到,订单一直待支付

现象:用户明明付了钱,订单状态还是 0。原因:回调地址配置错误,或者服务器防火墙拦了微信的请求。解决:检查微信支付后台的回调地址是否公网可达,用curl从外网访问一下;再看服务器日志有没有收到请求。如果验签失败,检查密钥和证书是否匹配。

4.3 菜单页图片加载慢,首屏白屏

现象:进入菜单页后图片一张张慢慢出来,用户等不及就退了。原因:图片没压缩、没走 CDN、没设懒加载。解决:图片统一压到 200KB 以内,上传到对象存储并开 CDN,image标签加lazy-load属性。首屏只加载前两个分类的图片,后面的滚动到再加载。

4.4 购物车数据丢失,用户重新选菜

现象:用户加完菜切到微信聊天,再回来购物车空了。原因:小程序被系统回收,页面 data 重置。解决:购物车数据同时存globalData和wx.setStorageSync,页面onShow时从本地恢复。注意存储有 10MB 上限,购物车数据很小,不用担心。

4.5 后厨重复出单,同一订单打印两次

现象:后厨小票机同一单出了两张。原因:支付回调重复触发,或者轮询接口没有做去重。解决:回调处理加幂等判断,轮询接口返回的订单列表要带printed标记,后厨端打印后调接口标记已打印。更稳妥的做法是后端在状态变成「已支付」时只发一次通知,用消息队列保证。

5. 进阶技巧:用订阅消息把「出餐提醒」做成闭环

前面讲的链路到后厨出单就结束了,但顾客还不知道餐好了。微信小程序的订阅消息可以补上这一环:用户下单时授权「出餐提醒」,后厨点「已完成」时后端调订阅消息接口,给用户推一条「您的餐已准备好」。这个功能不难,但有几个细节决定成败。

首先,订阅消息必须由用户主动点击授权,且每次授权只能发一条。所以要在订单确认页放一个「接收出餐提醒」的勾选框,用户勾了才调wx.requestSubscribeMessage。其次,模板要提前在微信公众平台申请,字段和内容要匹配,否则发送会报错。最后,后端发送时要带page参数,让用户点消息直接跳到订单详情页。

// 下单前请求订阅授权 wx.requestSubscribeMessage({ tmplIds: ['YOUR_TEMPLATE_ID'], success: (res) => { if (res['YOUR_TEMPLATE_ID'] === 'accept') { // 用户同意,把授权状态传给后端记录 wx.request({ url: 'https://your-api.com/order/subscribe', method: 'POST', header: { Authorization: wx.getStorageSync('token') }, data: { orderId: this.data.orderId, subscribed: true } }); } } });

参数说明:tmplIds是模板 ID 数组,一次最多三个。res里对应模板 ID 的值为accept表示同意,reject表示拒绝。注意这个接口必须在用户点击事件里调用,不能放在onLoad里静默调,否则会失败。

后端发送订阅消息的代码大致如下:

# Django 调用微信订阅消息发送接口 import requests def send_ready_notice(openid, order_no): url = 'https://api.weixin.qq.com/cgi-bin/message/subscribe/send' params = {'access_token': get_access_token()} body = { 'touser': openid, 'template_id': 'YOUR_TEMPLATE_ID', 'page': f'pages/orderDetail/orderDetail?orderNo={order_no}', 'data': { 'thing1': {'value': '您的餐已准备好'}, 'character_string2': {'value': order_no} } } resp = requests.post(url, params=params, json=body) return resp.json()

逻辑说明:access_token要缓存,不能每次现取,否则会触发频率限制。data里的字段名要和模板定义一致,thing类型限 20 个字符以内,character_string限 32 位。注意发送失败要记日志,常见错误是用户没授权或授权次数用完,这时候不要重试,重试也没用。

验证方法很简单:下单时勾选提醒,后厨点完成后看手机有没有收到消息。如果没收到,先查access_token是否有效,再查模板 ID 和字段名是否匹配,最后看用户授权次数是否已耗尽。我一般会在后端加一个「补发」按钮,万一漏发可以手动补,但补发也要消耗授权次数,所以别滥用。

这套点餐系统从扫码到出餐提醒,核心链路就这些。我自己的习惯是先把登录和下单跑通,再补支付和后厨通知,最后加订阅消息。每加一个环节就用真机测一遍,不要等全做完再联调,否则问题堆在一起很难定位。希望帮到你。

本文还有配套的精品资源,点击获取

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

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

立即咨询