☰
完整点餐小程序源码拆解:前端、后台与数据库三件套实战
2026/10/11 23:03:36 网站建设 项目流程

简介:完整的微信小程序点餐项目源码,覆盖用户点餐端、后台管理端和数据库脚本,适合具备基础小程序知识、希望进阶全栈或餐饮信息化场景的开发者参考。资源包共84个文件,大小仅1.25MB;逻辑集中在js、服务端脚本与sql中,页面由wxml和wxss构建,json负责项目配置,png/jpeg提供界面预览,pem/p12对应支付相关证书,目录按前端、服务端和数据库清晰分层,便于定位。目前已有4700人学习下载。功能模块涵盖菜单展示、加购、订单提交、微信支付和订单状态管理,后台支持菜品、订单、用户维护,也支持调整数量、删除菜品等操作;数据库设计了菜品、订单、订单详情、用户、支付五张核心表,配套README、截图与SQL初始化脚本,整体目录可导入开发工具,快速跑通前后端联调,适合作为课程设计或二次开发的基础模板。

1. 完整点餐小程序源码:三块齐全才叫完整,缺一块都得自己补

这份点餐小程序源码,第一眼要看的不是页面多漂亮,而是后端和数据库是否真的给你了。实际拆包后,目录里一般会是小程序前端工程、后台管理页面、SQL 数据库脚本三块,互相咬合。市场上不少自称完整的点餐资源其实只有前端页面,下单接口、商家后台、表结构都得自己补,等于拿到一个半成品。这份源码的价值在于它能直接跑通“用户在小程序里选餐 → 提交订单 → 后台接单改状态 → 数据落库”整条链路。适合三类人:交课程设计的学生、接餐饮方向外包的一线工程师、想快速搭扫码点餐原型的个人开发者。下面按我拆项目的顺序来写:先看清结构,再跑起来,最后讲最容易踩的坑。

2. 系统拆解:小程序端、后台管理端与数据库表如何配合

拿到这种带后台和数据库的源码,我习惯先画三个子模块的职责边界。小程序端只负责展示和收集用户操作,后台管理端处理商家操作,MySQL 数据库是两者共同的落点。只要这个边界不乱,后面改代码就有方向。小程序端就算换成 uniapp、后台从 Node 换成 Java,表结构和接口语义基本不会变,这也是这份源码最值得参考的部分:它把点餐业务里最通用的骨架先立住了。

2.1 小程序端页面职责:五个页面各自只干一件事

小程序端的页面结构通常按 tabBar 分三块入口:首页、订单、我的,再加购物车和订单详情两个二级页面。页面不多,但每个页面的职责要分清楚,不然改到后面代码会乱成一团。

页面路径页面职责主要请求接口
pages/index/index分类展示、菜品列表、购物车入口/api/category/list、/api/dish/list
pages/cart/cart购物车数量增减、金额计算、提交订单/api/order/create
pages/order/list按状态展示订单列表/api/order/list
pages/order/detail订单详情、备注信息、操作按钮/api/order/detail
pages/user/info微信登录、头像昵称展示/api/user/login

首页一般是左分类、右菜品的两栏布局,分类用横向胶囊滚动,菜品卡片展示图片、价格和加号按钮。购物车页面在点餐场景里不需要注册账号,直接通过 wx.login 拿 code 交给后台换 openid,所以购物车数据和用户身份用 openid 绑定,这是微信点餐和普通电商 App 最大的区别。订单列表那个页面,真实项目里一定会做下拉加载更多,课程 demo 往往一次拉全量数据;如果这份源码没有做分页,建议你自己补上,后面接真实流量时会省很多事。

2.2 数据库表结构:五张主干表把菜单域和订单域分开

点餐系统的表结构,跑不掉的五张表是:分类表、菜品表、用户表、订单主表、订单明细表。分类和菜品属于菜单域,订单主表和明细表属于订单域,用户表独立。订单明细单独建表,是因为一个订单包含多个菜品,不能把多行数据塞进订单主表,这是关系型数据库的基本设计方式。

起库脚本常见是这个样子:

CREATE DATABASE IF NOT EXISTS ordering_db DEFAULT CHARSET utf8mb4; USE ordering_db; CREATE TABLE `category` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '分类名', `sort_order` int NOT NULL DEFAULT 0 COMMENT '排序值,越大越靠前', `status` tinyint NOT NULL DEFAULT 1 COMMENT '1 显示 0 隐藏', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜品分类表'; CREATE TABLE `dish` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `category_id` int unsigned NOT NULL COMMENT '所属分类', `name` varchar(100) NOT NULL, `price` decimal(10,2) NOT NULL COMMENT '价格,保留两位小数', `image` varchar(255) DEFAULT '' COMMENT '菜品图片', `stock` int NOT NULL DEFAULT 999 COMMENT '库存,999 表示不限', `status` tinyint NOT NULL DEFAULT 1 COMMENT '1 上架 0 下架', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='菜品表'; CREATE TABLE `user` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL COMMENT '微信用户唯一标识', `nickname` varchar(50) DEFAULT '', `avatar` varchar(255) DEFAULT '', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='微信用户表'; CREATE TABLE `orders` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '业务订单号', `openid` varchar(64) NOT NULL COMMENT '下单用户 openid', `total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额', `status` tinyint NOT NULL DEFAULT 0 COMMENT '0 待接单 1 备餐中 2 已完成 3 已取消', `remark` varchar(255) DEFAULT '' COMMENT '用户备注', `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_order_no` (`order_no`), KEY `idx_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; CREATE TABLE `order_items` ( `id` int unsigned NOT NULL AUTO_INCREMENT, `order_id` int unsigned NOT NULL COMMENT '订单主表 id', `dish_id` int unsigned NOT NULL, `dish_name` varchar(100) NOT NULL COMMENT '下单时的菜品名快照', `price` decimal(10,2) NOT NULL COMMENT '下单时的单价快照', `count` int NOT NULL DEFAULT 1, PRIMARY KEY (`id`), KEY `idx_order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单明细表';

几个字段设计经验值得说透。价格字段必须用decimal(10,2),不能用float,float 是近似值,0.1 加 0.2 会变成 0.30000000000000004,算总价时差一分钱都会被用户投诉。order_items 表里的dish_name和price是快照字段,下单那一刻把菜品名和单价拷贝一份存进明细表,以后后台改价、改菜名,历史订单仍然保持当时的数据。这是订单系统的基本功,很多课程设计项目忽略这一点,后台改一次价格,历史订单全部跟着变,老板直接傻眼。order_no要加唯一索引,后台做对账、用户查单都靠它定位。

2.3 后台管理端与小程序端的联动:上架、改价、接单怎么生效

后台管理端通常以网页形式出现,目录可能是 admin/,实现方式可能是原生 HTML+JS,也可能是 Vue3 工程。原理只有一套:它和小程序共用后端接口,只是权限更高,小程序端只能读菜单、写订单,后台还要能写菜单、改订单状态。常见做法是后台加一个简单的登录页,登录状态用 token 校验;更完整的源码会做角色区分。

商家在小程序后端管理端改价、上架、下架,数据写进数据库后,小程序重新拉取菜品列表就能看到新菜单。这里有个关键点:已经加进购物车的菜,价格不会自动变。要避免提交订单时价格对不上,正确姿势是下单接口在服务端重新读菜品表的最新价格,而不是信任购物车传来的价格。后台页面的按钮逻辑一般长这样:

// admin/js/dish.js —— 后台修改价格 / 上架下架共用更新接口 async function updateDish(dishId, payload) { const res = await fetch('/api/dish/update', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: dishId, ...payload }) }) const data = await res.json() // 约定 code===0 表示成功,失败时把后端返回的 msg 直接弹给管理员 if (data.code === 0) { location.reload() } else { alert(data.msg || '更新失败') } } // 常见用法示例:updateDish(12, { price: 28.5 }) 或 updateDish(12, { status: 0 })

这段代码的核心是把“菜品 id + 要改的字段”作为 payload 传给同一个接口,比每个按钮单独写一个接口维护成本低很多。从这里能看出三个模块怎么联动:后台改库 → 小程序重新拉取看到新菜单 → 用户下单 → 订单写回库 → 后台订单列表多一条新单。一个点餐数据闭环就出来了。

3. 本地跑起来:导入源码后按这三步把服务整通

把源码跑成能点餐的项目,一共三步:导入小程序端、初始化数据库、启动后台服务。顺序也很重要,小程序端只是界面,数据库和后台才是数据源,两者没起来,页面编译得再漂亮也是空壳。下面按我实际操作过的顺序来。

3.1 导入小程序端:选“导入”而不是“新建”,AppID 先用测试号

下载解压后,第一件事是确认 app.json 在哪一层,app.json 所在目录才是小程序工程根目录。我一般这样操作:打开微信开发者工具,点导入项目,目录定位到 app.json 那一层。很多人习惯点新建项目,把整个压缩包目录塞进去,结果工具提示找不到 app.json,其实是根目录选错了。如果代码包里既有 miniprogram 目录又有 server 目录,小程序根目录通常是 miniprogram。

导入时 AppID 先选“测试号”,避免和源码里写死的 appid 冲突。如果这份源码用到微信登录,之后再换成自己的小程序 AppID,同时要把后台配置里的 secret 一起换掉,否则登录接口会报 invalid code。导入成功后,如果首页编译一片空白,先看控制台是不是网络请求报错,别急着改代码,大概率是后台服务还没启动。

提示:开发者工具有时会因为之前缓存的原因显示旧页面,此时清缓存重编译往往比反复改代码更有效。

3.2 初始化 MySQL 数据库:命令行导入 SQL 文件,注意字符集

数据库不初始化,后台和小程序都只能空转。先把 MySQL 服务打开,然后命令行进入 mysql,执行 SQL 文件。SQL 文件如果放在源码包的 sql/ 目录下,常见导入写法是这样:

mysql -uroot -p # 进入 mysql 提示符之后执行 source /your/path/sql/init.sql; SHOW TABLES;

执行 source 没有报错说明导入完成。SHOW TABLES应该能看到五张表;如果表数量不对,说明 SQL 中间某条语句执行中断,最常见原因是文件路径带中文或者编码不对。更稳妥的办法是用 Navicat 或 DBeaver 这类可视化工具直接运行 SQL 文件,碰到语法错误会提示具体到行号,比自己猜好很多。

导入完成后建议跑一条验证语句:

SELECT COUNT(*) FROM dish;

能返回 10 行以上的数据,且中文菜品名不出现乱码,说明数据导入成功且字符集正确。乱码的原因和解决办法在第五章专门说,先往下走。

3.3 启动后台服务:改配置、装依赖、验证接口,一个都不能省

大多数这种源码的后台是 Node.js 写的,入口在 server/app.js 或 server/index.js。启动前先改数据库配置,这是最容易自找麻烦的一步。默认配置往往写着 root 空密码或者陌生密码,直接启动必然报连接失败。

// server/config.js —— 数据库连接与小程序密钥配置 module.exports = { port: 3000, db: { host: '127.0.0.1', port: 3306, user: 'root', password: '改成你自己的 MySQL 密码', database: 'ordering_db' // 和 SQL 文件里的库名保持一致 }, wx: { appid: '你的小程序 AppID', secret: '你的小程序 AppSecret' } }

要核对的是数据库 host、port、user、password、database 这五个值,任何一个对不上,后台都连不上库。如果源码里带的是.env.example文件,就复制一份改名.env再改,比硬改源码更干净。

依赖安装和服务启动我习惯分开做,避免出错时不知道是哪一步的问题:

cd server # 进入后台服务目录 npm install # 首次运行安装依赖 npm start # 启动服务,看到 listening on 3000 表示成功

如果你的 server 目录下没有 package.json,说明源码把后端代码打成松散结构了,需要自己补一个 Node 工程骨架;不过绝大多数完整源码都会带 package.json,这是后台能跑起来的前提。启动后先不回小程序,用 curl 验证接口连通性:

curl http://127.0.0.1:3000/api/category/list

返回带code: 0的 JSON 就说明后台和数据库都通了。curl 报错时看后台终端日志,最常见的是数据库密码错误和 MySQL 没启动。接口通了之后回开发者工具点编译,首页应该就有分类和菜品数据了。

4. 点餐主链路代码走读:购物车、下单事务与订单状态机

这一章是整份源码含金量最高的部分。点餐系统不是一个展示页面,而是从用户加购到后台接单的完整状态流转。读懂了下单接口的设计,你就知道这类项目里哪些地方会影响线上稳定性。

4.1 小程序端请求封装:统一 baseURL 和错误处理,别到处写 wx.request

小程序端每个页面都要调接口。如果每个页面都写一遍 wx.request,将来改接口域名时你要翻遍整个文件。所以源码里通常会有一个 utils/request.js,把 wx.request 包一层。我读代码时第一步就看这个文件,因为所有接口的基地址、超时时间、错误码约定都在这里。

// utils/request.js —— 统一请求封装 const BASE_URL = 'http://127.0.0.1:3000' // 真机调试时改成电脑的局域网 IP function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method: method, data: data, timeout: 8000, header: { 'Content-Type': 'application/json' }, success: (res) => { // 约定响应结构:{ code: 0, data: ..., msg: '错误原因' } if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data) } else { wx.showToast({ title: res.data.msg || '请求失败', icon: 'none' }) reject(new Error(res.data.msg || '请求失败')) } }, fail: (err) => { wx.showToast({ title: '网络异常,请检查后台服务', icon: 'none' }) reject(err) } }) }) } module.exports = { request }

把 wx.request 的 success/fail 回调包装成 Promise,页面里就能用 async/await 直接读数据,不用再嵌套回调。BASE_URL 是整个小程序端唯一要改的常量:模拟器阶段用 127.0.0.1,真机调试改成电脑局域网 IP,正式上线换成 HTTPS 域名。timeout 设 8 秒在点餐场景足够,这个项目没有大文件传输,超过 8 秒基本就是服务端异常了。错误码约定是这里最容易翻车的点,后端返回的是 code 还是 status,msg 字段叫什么,前后端必须一致,否则页面端判断永远走不到成功分支。

4.2 后台下单接口:事务包住库存扣减,价格以服务端查询为准

下单是点餐系统里最考验功底的接口。核心要求是:库存扣减和订单落库必须放在同一个数据库事务里,要么都成功,要么都回滚。如果先查库存、再插订单、最后单独扣库存,中间进程崩掉,就会出现库存减了但订单没生成的脏数据,用户端体验非常差。下面这段是典型实现:

// server/routes/order.js —— 下单接口 const express = require('express') const router = express.Router() const db = require('../db') // 该模块导出的是 mysql2/promise 连接池 router.post('/create', async (req, res) => { const { openid, items = [], remark = '' } = req.body if (!Array.isArray(items) || items.length === 0) { return res.status(400).json({ code: 1, msg: '购物车不能为空' }) } const conn = await db.getConnection() try { await conn.beginTransaction() const orderNo = 'DK' + Date.now() + Math.floor(Math.random() * 1000) let totalAmount = 0 const orderItems = [] for (const item of items) { const [rows] = await conn.query( 'SELECT id, name, price, stock FROM dish WHERE id = ? AND status = 1 FOR UPDATE', [item.dishId] ) if (rows.length === 0) { throw new Error(`菜品 ${item.dishId} 不存在或已下架`) } const dish = rows[0] if (dish.stock < item.count) { throw new Error(`「${dish.name}」库存不足,当前剩余 ${dish.stock}`) } await conn.query('UPDATE dish SET stock = stock - ? WHERE id = ?', [item.count, dish.id]) totalAmount += dish.price * item.count orderItems.push([dish.id, dish.name, dish.price, item.count]) } const [orderResult] = await conn.query( 'INSERT INTO orders (order_no, openid, total_amount, remark, status) VALUES (?, ?, ?, ?, 0)', [orderNo, openid, totalAmount.toFixed(2), remark] ) for (const item of orderItems) { await conn.query( 'INSERT INTO order_items (order_id, dish_id, dish_name, price, count) VALUES (?, ?, ?, ?, ?)', [orderResult.insertId, ...item] ) } await conn.commit() res.json({ code: 0, data: { orderNo, totalAmount: totalAmount.toFixed(2) } }) } catch (err) { await conn.rollback() // 事务中任何一步失败,库存和订单一起回滚 res.status(400).json({ code: 1, msg: err.message }) } finally { conn.release() // 连接要还回连接池,否则并发一高连接就被耗尽 } })

这段代码有五个关键点。第一,价格一定是从服务端 dish 表里查出来的,而不是前端传过来的。用户可以抓包改请求体,把购物车里的价格改成 0.01 元,如果后端信任前端传的 price,订单金额就失真了。所以在 request.js 里我更推荐只传 dishId 和 count,不传 price 字段。第二,SELECT ... FOR UPDATE是行级锁,同一时间多个用户点同一个菜品时,后到的请求会排在锁后面,等前一个事务提交后再读数据,读到的是扣完库存后的值,从机制上防止超卖。第三,循环里抛出异常后走 rollback,之前扣过的库存全部撤销,不会出现订单没建但库存少了的情况。第四,明细表里保存 dish_name 和 price 快照,下单后即使菜品改名调价,历史订单也不会乱。第五,conn.release()写在 finally 里,保证连接一定能还回连接池,漏掉这一段,并发稍微高一点连接就会被耗尽,整个后台接口全部超时。

4.3 订单状态机:小数字枚举值控制状态流转,后台接单用条件更新

订单主表里 status 字段用 tinyint 存枚举值,而不是直接存中文。数字枚举便于索引和统计,显示文案交给前端映射。各状态的流转关系如下:

状态值状态触发操作小程序端展示
0待接单用户下单成功显示“待商家接单”
1备餐中后台点击接单显示“备餐中”,隐藏取消按钮
2已完成后台确认出餐显示“已完成”和评价入口
3已取消商家拒单或用户取消显示“已取消”和重新下单入口

后台接单和完成这两个操作,实现时要注意一个细节:更新语句必须带前置状态条件。直接UPDATE orders SET status=1 WHERE id=?在多人同时操作后台时会把状态洗乱,正确写法是这样:

// server/routes/order.js —— 后台接单/完成接口,全部使用条件更新 router.post('/accept', async (req, res) => { const { orderId } = req.body const [result] = await db.query( 'UPDATE orders SET status = 1 WHERE id = ? AND status = 0', [orderId] ) // affectedRows === 0 说明订单不是待接单状态,可能已取消或被别人接走 res.json({ code: result.affectedRows > 0 ? 0 : 1, msg: result.affectedRows > 0 ? '接单成功' : '订单状态已变化,请刷新列表' }) }) router.post('/complete', async (req, res) => { const { orderId } = req.body const [result] = await db.query( 'UPDATE orders SET status = 2 WHERE id = ? AND status = 1', [orderId] ) res.json({ code: result.affectedRows > 0 ? 0 : 1, msg: result.affectedRows > 0 ? '订单完成' : '只有备餐中的订单才能完成' }) })

这里WHERE id = ? AND status = 前置状态的写法叫条件更新,靠affectedRows判断是否真的更新了行,能有效避免重复接单、重复完成这类并发问题。这个技巧不止点餐系统适用,凡是带状态流转的业务模块,比如外卖配送、工单系统,都应该这么写。

5. 避坑排查:五条常见翻车记录,对照症状改就行

这一章列的是我复现这类带后台和数据库的源码时最容易碰到的五个问题,都是实际踩过的血泪经验。每一条按现象、原因、解决的顺序写清楚,你遇到同款可以照着排查。

5.1 后台起不来,报错 Cannot find module 'express'

现象:cd server 后按文档执行 npm start,Node 直接抛Cannot find module 'express'或Cannot find module 'mysql2'。

原因:依赖没有安装完整,常见情况是拿到代码后跳过 npm install 直接启动。还有一种是 npm 版本和 package-lock.json 不匹配,导致部分依赖没装上。

解决:先删掉 node_modules 和 package-lock.json,重新执行npm install。另外 Node 版本建议用 14、16、18 这几个偶数版本,部分旧依赖在 Node 20 上编译原生模块会失败,这不是源码问题,是运行环境版本不匹配。

5.2 数据库导入后中文乱码,菜品名称显示成一串特殊符号

现象:后台页面菜品名称显示成香è¹这类乱码,SELECT 查出来也是乱码。

原因:建库时没有指定 utf8mb4 字符集,MySQL 用默认 latin1 存中文字符,写入时就已经损坏。还有一种可能是 SQL 文件本身是 UTF-8,但 mysql 客户端连接时用默认字符集导入,传输阶段就被转错了。

解决:建库时显式写CREATE DATABASE ordering_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;。如果已经建错,执行ALTER DATABASE ordering_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,并且把每张表也改成 utf8mb4。连接 mysql 客户端时加上--default-character-set=utf8mb4再 source。注意已经写进库的乱码数据是恢复不回来的,要清空表重新导入。

5.3 小程序请求失败,控制台提示“不在合法域名列表中”

现象:开发者工具编译后首页空白,Network 面板显示request:fail url not in domain list。

原因:微信开发者工具默认开启合法域名校验,而本地调试地址是http://127.0.0.1:3000,既不是 HTTPS 也不在小程序后台配置的 request 白名单里。

解决:开发阶段在开发者工具右上角点“详情 → 本地设置 → 勾选『不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书』”。这个选项只影响本地开发,不影响发布。正式上线前必须把接口换成已备案的 HTTPS 域名,并且在小程序管理后台把 request 合法域名加白,否则线上用户全部请求失败。

5.4 模拟器数据正常,真机预览一片空白

现象:电脑模拟器里分类、菜品都显示正常,手机扫码预览后列表空白,请求全是超时。

原因:真机环境里127.0.0.1指向的是手机自己,不是你的电脑。后台服务如果监听的是回环地址,手机根本访问不到电脑上的服务。

解决:把 utils/request.js 里的 BASE_URL 改成电脑的局域网 IP,比如http://192.168.1.101:3000。后台服务监听地址要改成0.0.0.0,实现方式是在 express 启动时写成app.listen(3000, '0.0.0.0')。手机和电脑必须处于同一个 Wi-Fi,再把电脑防火墙对 3000 端口放行。调试期图省事可以直接关防火墙,但不建议作为长期方案。

5.5 后台服务能启动,接口却一直 404

现象:npm start 正常,控制台也打印了 listening,但curl http://127.0.0.1:3000/api/category/list返回 404。

原因:路由文件没有被挂载到入口文件,或者挂载路径和请求路径对不上。比如 express 入口里只写了app.use('/', indexRouter),没有把订单路由、菜品路由挂到/api前缀下,接口自然找不到。

解决:打开 server 入口文件(通常是 app.js 或 index.js),检查有没有app.use('/api/order', orderRouter)、app.use('/api/dish', dishRouter)这类挂载代码。再打开对应路由文件,看 router 定义的具体路径是否和请求 URL 一致。对齐后重启后台,404 就消失了。这一步是拼源码时最容易被忽略的地方,拿到手先看入口文件再启动,能少走很多弯路。

6. 验收技巧:用两个账号跑通闭环,再用一条 SQL 回查数据

跑通页面只是第一步,真正能确认这份源码“可用”,要按下面这套流程验收一遍。

6.1 双账号模拟真实点餐场景

验证点餐系统能不能交付,不能只看一个账号能点单。我通常的做法是:在微信开发者工具里建两个编译模式,分别用两个不同的测试号编译,一个模拟顾客 A,一个模拟顾客 B。先让 A 下单,再到后台管理页点接单,切回 A 的订单列表确认状态从“待接单”变成“备餐中”。接着让 B 下单,确认两个订单不串号。串号问题在简易 demo 里并不少见,根源是 openid 写死成同一个测试值,这种实现是及不了格的。确认状态正常后,再让后台把订单置为“已完成”,用户端订单列表随之更新,这才算一个闭环。注意观察下单备注、菜品数量、总金额是否都正确回显,这些细节最暴露代码粗糙程度。

6.2 用一条 SQL 把订单主表和明细表串起来

页面验证只能说明接口通,数据层是否正确还得回数据库看。我最后会用一条 SQL 把订单主表和明细表串起来:

SELECT o.order_no, o.status, o.total_amount, o.created_at, GROUP_CONCAT(CONCAT(oi.dish_name, '×', oi.count) ORDER BY oi.dish_name) AS items FROM orders o LEFT JOIN order_items oi ON oi.order_id = o.id GROUP BY o.id ORDER BY o.id DESC;

返回的每一行就是一个订单,items 列把多个菜品合并成一个字段,用菜名×数量的形式展示。比如看到麻婆豆腐×2, 回锅肉×1,说明下单接口确实把主表和明细都写进了库;再对比 orders.total_amount 和手工计算的价格是否一致,能验证服务端价格计算没有漏项。这个习惯我从一次交付翻车后养成的:那次页面看着一切正常,结果数据库里订单明细的 dish_name 全是空字符串,因为后端插入时拿错了字段。从那以后,我每次接收带后台和数据库的源码,不管页面多光鲜,都会先把三块模块独立验证一遍,再组合联调,最后用这条 SQL 收尾检查。希望这份点餐小程序源码的拆解和避坑记录能帮到你,尤其是第一次碰订单事务的时候。

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

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

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

立即咨询