☰
SpringBoot+微信小程序外卖点餐系统毕设全栈项目实战解析
2026/9/26 4:27:29 网站建设 项目流程

简介:面向Java后端及微信小程序开发者的外卖点餐系统毕业设计资料包,针对传统餐饮业线下运营效率低、信息不透明等痛点,给出完整可运行的项目方案。系统采用SpringBoot+MySQL+Vue架构,前端基于微信小程序,覆盖用户、商家、管理员三类角色,功能模块划分清晰:用户能浏览美食、下单、管理购物车与订单、查看优惠券;商家可维护菜品、处理订单和配置优惠券;管理员负责分类管理、信息审核与订单统计。资源整理为zip压缩包,大小33.44MB,内含项目源码、SQL数据库脚本及运行说明,导入开发工具完成环境配置后即可快速启动调试。已有84人学习下载,适合毕业设计、课程设计或外卖系统二次开发,可帮助掌握小程序与后端接口联调、订单状态流转、权限控制及数据库设计等关键能力。

1. 外卖点餐小程序毕设包:一份能直接跑的 SpringBoot + 微信小程序全栈源码

微信小程序外卖点餐系统这套源码,最容易被低估的地方是它的“完整度”。市面上常见的毕设资源要么只有后端没有前端,要么前端是纯网页,真正把小程序端、SpringBoot 后端、MySQL 数据库和运行说明凑齐的并不多。这个资源是一个标准的前后端分离项目,用户、商家、管理员三个角色的功能都做了,覆盖了从美食浏览、加购下单到订单管理、优惠券发放的完整闭环,拿来交毕业设计或者 Java 课程设计基本够用。

适合两类人:一是毕设/课设要交但时间紧,需要一个能跑通、能讲清楚的项目;二是想搞清楚小程序和 SpringBoot 之间怎么调接口的初学者。整套系统的技术栈很常规——Vue 风格的小程序前端、SpringBoot 后端、MySQL 存储,没有花哨组件,反而适合学习和答辩。下面从结构、运行、核心链路到踩坑点,按实战顺序拆给你看。

2. 先把结构看明白:三端角色、核心功能与数据库表设计

2.1 前后端分离与三角色权限边界

拿到源码包第一件事不是急着跑,而是先确认它的工程结构。这个项目是典型的前后端分离:小程序端负责页面展示和用户交互,SpringBoot 负责提供 RESTful 接口,MySQL 负责数据持久化。三者通过 HTTP 请求通信,小程序端用 wx.request 调后端接口,后端处理完业务逻辑后返回 JSON。

角色权限边界是这类系统的核心设计点,也是答辩时老师最爱问的地方。用户端能看到美食列表、购物车、自己的订单和优惠券;商家端只能管自己的美食、优惠券和订单状态;管理员端做全局分类管理、信息审核和订单统计。权限控制的常见做法是在后端拦截器里校验登录态,再按角色放行接口——如果源码里用了拦截器或 AOP 实现,那这块就能当亮点讲。

另外注意一个细节:正文提到前端基于 Vue,但微信小程序原生语法本身不是 Vue。这里有两种可能,一种是用 uni-app 这类跨端框架写的,语法接近 Vue;另一种是源码说明里泛指 MVVM 写法。打开包内的小程序目录看一下,如果有 pages 目录且是 .wxml/.wxss/.js 文件,就是原生小程序;如果有 src 目录且是 .vue 文件,就是 uni-app 工程。两种的启动方式不一样,先认清再动手。

2.2 数据库表设计:八张核心表怎么划分

外卖点餐系统的数据库设计是另一个答辩高频区。按功能反推,这套系统的核心表至少包含以下这些,结构如下:

表名核心字段职责
userid, openid, nickname, phone, balance用户基本信息
merchantid, name, phone, address, status商家信息
foodid, category_id, merchant_id, name, price, image, stock美食信息
categoryid, name, sort美食分类
cartid, user_id, food_id, quantity, checked购物车
ordersid, order_no, user_id, merchant_id, total_amount, status, create_time订单主表
order_detailid, order_id, food_id, food_name, food_price, quantity订单明细快照
couponid, name, amount, min_amount, merchant_id, stock优惠券
user_couponid, user_id, coupon_id, status, get_time用户持有优惠券

订单明细表单独拆出来是必须的——如果用户下单后商家改了价格,订单里已经保存的 food_price 不会被影响,这是电商系统的通用做法,叫“快照”。答题时可以提一句,能加分。

2.3 源码包目录阅读顺序

拿到包后按这个顺序去读代码,不要在无关文件上耗时间:先读数据库脚本(一般是 .sql 文件)了解表结构,再读后端 application.yml 看端口和数据库配置,然后读小程序端的 request 封装文件(常见名字是 request.js 或 api.js),最后再进页面代码。这个顺序能让你在最短时间内建立完整认知,后面排查问题也有方向。

3. 把项目拉起来跑:环境配置、SQL 初始化和微信开发者工具联调

3.1 环境版本怎么选:JDK、Maven、MySQL 和小程序工具

跑这种毕设项目,环境版本是最容易翻车的点。按这套系统的主流配置,推荐版本组合如下:

  • JDK 1.8:SpringBoot 2.x 最稳妥的搭配,用太高版本反而可能遇到依赖兼容问题
  • Maven 3.6+:管理后端依赖,需要能访问 Maven 中央仓库
  • MySQL 5.7 或 8.0:5.7 兼容性最好,8.0 需要改驱动配置,后面避坑章节细说
  • 微信开发者工具:最新稳定版即可,导入小程序目录时要选对目录层级
  • Node.js:如果包内前端是 uni-app 工程则需要,用来跑 npm 命令

端口规划也很重要。SpringBoot 默认跑在 8080,微信开发者工具默认不占端口,二者一般不冲突。小程序端最终要访问的接口地址形如 http://localhost:8080/api/xxx,这个地址在联调阶段需要写进小程序代码里。

3.2 SQL 脚本导入:编码和字符集一次设对

数据库初始化是第一个大坑。找到包内的 .sql 文件后,不要直接双击打开然后复制到 Navicat 里执行,正确做法是在命令行或 Navicat 里先创建数据库,再执行脚本。推荐用命令行操作:

mysql -u root -p CREATE DATABASE takeout DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE takeout; source /path/to/takeout.sql;

先建库再导表是标准做法,CHARACTER SET utf8mb4 这一步非常关键。如果建库时用的是默认的 latin1 或 utf8,导入后查中文数据大概率会出现乱码,后面所有页面上的菜品名都是“锟斤拷”。utf8mb4 比 utf8 多支持 emoji 和生僻字,现在的新系统统一用 utf8mb4 就对了。

导入完成后建议立刻做一个验证:打开 user 表或 food 表,看中文数据是否正常显示、有没有多余的空表。大多数毕设包的 SQL 脚本里会自带几条测试数据,这些数据正好用来做后续联调验证。

3.3 SpringBoot 后端启动:application.yml 里必须改的三个地方

后端工程导入 IDEA 后,先等 Maven 把依赖拉完——这个过程可能比较久,因为要下载 SpringBoot、MyBatis、数据库驱动等一堆 jar 包。然后打开 src/main/resources/application.yml 或 application.properties,重点确认以下配置:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/takeout?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true

url 里的 useSSL=false 关闭 SSL 校验能省掉一堆连接警告;serverTimezone=Asia/Shanghai 解决 MySQL 8.x 的时区报错;characterEncoding=utf8mb4 保证读写中文不乱码。这三个参数是毕设项目里最容易因为少写一个导致启动失败或数据乱码的点。

driver-class-name 的值要跟你的 MySQL 版本匹配:MySQL 5.7 用 com.mysql.jdbc.Driver,MySQL 8.x 用 com.mysql.cj.jdbc.Driver。如果源码包里用的是旧驱动而你的库是 8.x,启动时会直接报 ClassNotFoundException 或 Communications link failure,最省事的解决办法是去 pom.xml 里把 mysql-connector-java 的版本改到 8.x。

启动主类后,看到类似 Tomcat started on port(s): 8080 的日志就说明后端起来了。先用浏览器或 Postman 访问一个接口验证,比如 GET http://localhost:8080/api/food/list,能返回 JSON 就说明数据库连接和 MyBatis 映射都正常。

注意:如果项目里配置了端口不是 8080,比如 8081 或 8888,后面小程序端的 baseUrl 也要同步改,不然接口全超时。

3.4 小程序端联调:baseUrl 和开发者工具的坑

小程序端导入微信开发者工具后,第一件事是找到接口封装文件,把里面的 baseUrl 指向本地后端地址。常见位置在小程序根目录下的 utils/request.js 或 api/api.js,核心逻辑类似这样:

const BASE_URL = 'http://localhost:8080/api'; function request(url, method, data) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method: method || 'GET', data: data || {}, header: { 'Content-Type': 'application/json' }, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else { reject(new Error('请求失败:' + res.statusCode)); } }, fail: (err) => { reject(err); } }); }); } module.exports = { request, BASE_URL };

BASE_URL 是整个前后端联调的枢纽,换成你本机的局域网 IP(比如 http://192.168.1.100:8080/api)就能让真机调试。header 里的 Content-Type 要跟后端接收参数的方式匹配——如果后端接口用 @RequestBody 接收 JSON,就保持 application/json;如果后端用 @RequestParam 接收表单参数,这个值要改成 application/x-www-form-urlencoded,否则后端拿不到参数会报 400。

开发者工具里有一个必须注意的设置:本地联调时,要在“详情 - 本地设置”里勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这是因为小程序的正式环境要求所有请求域名都配置为 HTTPS 且加入白名单,而本地调试用的是 http://localhost,不勾选这个选项请求会直接被拦截。真实开发环境的域名配置方式,避坑章节再展开。

4. 核心链路拆解:购物车、下单、优惠券与订单状态流转

4.1 加购到下单:购物车接口与订单生成逻辑

外卖系统最核心的业务链路是“选菜 → 加购物车 → 提交订单 → 商家接单”。这套流程里购物车和订单的关系,是答辩必问的环节。

购物车的常见实现是:用户点“加入购物车”时,小程序端调 POST /api/cart/add 接口,携带 foodId 和 quantity。后端先查这份菜是否已经在购物车里,如果已存在就执行数量累加,不存在则插入一条新记录。注意这里的数量累加是在事务里做的,避免高并发场景下重复加购导致数量错乱。

提交订单时,后端做的是更重的操作。一个常规下单接口的逻辑如下:

@Transactional public OrderVO createOrder(OrderRequest request) { // 1. 校验购物车非空且商品均为在售状态 List<CartItem> items = cartMapper.selectCheckedItems(request.getUserId()); if (items.isEmpty()) { throw new ServiceException("购物车为空"); } // 2. 遍历购物车计算总价 BigDecimal total = BigDecimal.ZERO; for (CartItem item : items) { Food food = foodMapper.selectById(item.getFoodId()); if (food == null || food.getStatus() != 1) { throw new ServiceException("商品已下架:" + item.getFoodName()); } total = total.add(food.getPrice().multiply(new BigDecimal(item.getQuantity()))); } // 3. 生成订单主记录,状态为待支付 String orderNo = generateOrderNo(); Order order = new Order(); order.setOrderNo(orderNo); order.setUserId(request.getUserId()); order.setTotalAmount(total); order.setStatus(0); orderMapper.insert(order); // 4. 批量插入订单明细并清空购物车 for (CartItem item : items) { orderDetailMapper.insert(buildDetail(order.getId(), item)); } cartMapper.clearCheckedItems(request.getUserId()); return buildOrderVO(order); }

这套逻辑的关键点是:金额必须在后端计算,不能信任前端传来的 totalAmount——小程序页面上的价格可以被篡改。第 2 步遍历购物车时从数据库重新查价格乘数量,才是安全做法。加 @Transactional 是为了保证主单、明细、清购物车三步要么全成功要么全回滚,否则会出现“订单建了但明细没写进去”的脏数据。generateOrderNo() 的常见实现是用时间戳加随机数生成唯一订单号,答辩时可以讲一句“订单号有唯一索引约束,避免并发重复”。

4.2 优惠券的选品逻辑:限额、优先使用和状态流转

优惠券模块看着简单,实际坑不少。这套系统的优惠券模型是“平台发券、用户领券、下单抵用”,用户端能看到可领的优惠券,领了之后在下单时勾选使用。

优惠券的使用有几个边界条件要在后端校验清楚。一是门槛,满 30 减 5 的券,订单金额不足 30 就不能用;二是归属,优惠券必须属于当前登录用户,不能拿别人的券;三是状态,券的状态有未使用、已使用、已过期三种,前端展示的是可选的券,后端下单接口也要再查一次状态防止并发下两张订单把同一张券用了两次。

比较常见的实现是用户点击“领取”时调 POST /api/coupon/receive,后端先查库存和领取状态再插入 user_coupon 记录。下单时如果带了 couponId,后端在算完总价后执行抵扣,并把该券状态置为已使用。这里要注意:一个订单只能用一张券,如果源码里做了多张券叠加的逻辑,那就要看它有没有处理优先级和互斥规则,这是能和老师展开聊的细节。

4.3 订单状态机:从待支付到完成的五态流转

订单状态是外卖系统的另一条主线。正常流程是 0-待支付 → 1-已支付 → 2-制作中 → 3-配送中 → 4-已完成,外加一个 -1-已取消 的异常态。商家端操作的是 1 到 2 的接单动作和 2 到 3 的出餐动作,用户端操作的是支付动作和可能的取消动作。

状态流转的代码实现通常是控制层接收请求,按当前状态判断是否允许跳转到目标状态。例如商家接单接口,只有状态为 1(已支付)的订单才能被置为 2(制作中),如果已经是 3 还调接单接口,后端要返回“订单状态异常”而不是直接改状态。这是个很值得在答辩时强调的点:状态机不是简单的 update 语句,而是带前置校验的状态迁移。

这里也存在最容易翻车的点:取消订单的条件。多数毕设项目的约定是待支付状态可以取消,已支付状态需要商家同意才能取消。如果源码里没有做这个区分,你最好自己补上,否则老师会问“用户付完款直接取消订单,商家已经备菜了怎么办”。

4.4 登录与身份识别:openid 的正确用法

小程序登录是这套系统里面向微信生态特有的一环。正确流程是:小程序端调用 wx.login 拿到临时 code,传给后端;后端拿 code 调微信接口换 openid;用 openid 去 user 表查用户,查到就返回登录成功,查不到就先注册再登录。

// 小程序端 wx.login({ success: (res) => { if (res.code) { wx.request({ url: BASE_URL + '/user/login', method: 'POST', data: { code: res.code }, success: (loginRes) => { const token = loginRes.data.token; wx.setStorageSync('token', token); } }); } } });

后端拿到 code 后调微信的 jscode2session 接口换 openid,再把当前登录状态记录下来。常见做法是后端用 openid 生成一个自定义 token 返回给小程序端,小程序端后续请求带上这个 token,后端拦截器解析 token 得到用户身份。注意 token 里不要存 openid 明文,而是存一个随机字符串并映射到用户 ID,这样即使 token 泄露也不会直接暴露微信身份信息。

5. 避坑指南:数据库编码、请求域名和端口冲突的五个实战排查

5.1 MySQL 8.x 驱动和时区报错:启动秒挂

现象:SpringBoot 工程启动时直接报Access denied for user或The server time zone value 'Öйú±ê׼ʱ¼ä',数据库连接失败。

原因:两种情况。一是密码不对或用户权限不够;二是 MySQL 8.x 默认时区是 UTC,而驱动连接串里没指定 serverTimezone,导致驱动不认。那个乱码样的错误信息其实是“中国标准时间”被错误解码的显示。解决:连接串改成jdbc:mysql://localhost:3306/takeout?serverTimezone=Asia/Shanghai,同时确认 pom.xml 里的驱动版本是mysql-connector-java8.x。如果第 5 章第 3 章配过一遍还报错,检查是不是 MySQL 服务本身没启动——Windows 下到服务管理器确认 MySQL 状态。

5.2 中文乱码:建库时漏了字符集

现象:数据库里存的中文全部变成???或乱码,页面商品名不可读,但是英文和数字正常。

原因:建库时用了默认字符集,通常是 latin1,存不了中文。解决:重建数据库,创建语句显式指定DEFAULT CHARACTER SET utf8mb4——直接ALTER DATABASE takeout CHARACTER SET utf8mb4也可以,但已存在表里的乱码数据无法恢复,只能清掉重导。从那以后我建库必带字符集参数,宁可多敲几个字母也不吃这个哑巴亏。

5.3 微信开发者工具请求失败:合法域名校验没关

现象:小程序页面加载不出数据,Console 里报request:fail,但同样的接口用浏览器访问完全正常。

原因:微信开发者工具默认校验合法域名,而本地后端地址是http://localhost:8080,既不是 HTTPS 也不在白名单里,被拦了。解决:开发者工具右上角“详情 - 本地设置”勾选“不校验合法域名”。真机预览时则是另一回事——小程序真机环境无法关闭校验,必须在小程序管理后台把后端域名配置成 HTTPS 并加入 request 合法域名,且该域名必须备案。本地开发用 IP 地址或 localhost 没问题,一旦上真机就必须要正式域名,这是毕设演示前最容易被忽视的一环。

5.4 8080 端口被占用:后端起来了但页面白屏

现象:IDEA 里点启动没有报错,但浏览器访问http://localhost:8080时显示的是别的应用页面,小程序端所有接口全部超时。

原因:本机已有其他进程占用了 8080 端口,SpringBoot 启动日志里其实有输出端口被占用的异常,但被忽略。解决:Windows 下执行netstat -ano | findstr 8080找到占用进程的 PID,去任务管理器结束它,或者干脆在 application.yml 里把server.port改成 8081,同时同步修改小程序端的 BASE_URL。这里有个小技巧:启动日志里看到Tomcat started on port(s): 8081就说明端口换成功了,很多新手不看日志,端口换了都不知道。

5.5 图片加载不出来:数据库只存了相对路径

现象:商品列表能显示文字,但所有美食图片都是裂图。打开数据库看 food 表,image 字段存的是/img/food/xxx.jpg这样的相对路径。

原因:图片路径写错了基准地址。小程序端 img 标签或 image 组件的 src 如果直接用相对路径,它会解析成小程序包内的路径,当然找不到。解决:小程序端请求后端时,把图片字段拼接成完整地址:http://localhost:8080+/img/food/xxx.jpg。同时确认后端有没有把本地的图片目录映射成静态资源——SpringBoot 里可以通过配置类把本地磁盘目录映射到/img/**路径,这个映射没有配的话,完整地址也照样 404。具体操作是后端新建一个 WebMvcConfigurer 配置类,添加 resourceHandler 指向存放图片的实际目录。

6. 进阶验证:造数据、过链路,答辩前先把自己当测试

这套系统跑通不难,但“能跑”和“能演示得漂亮”是两码事。我自己的习惯是在答辩前按一条完整业务链路走一遍,同时把演示数据准备好。

先造数据。只看自带测试数据是不够的,因为毕业设计演示需要展示“多分类、多商家、多订单”的效果。手动往 food 表里再插十几条菜,覆盖荤菜、素菜、主食、饮品几个分类;再给两个商家各分配几道菜,这样演示时切换商家就能看到不同的菜单。商家端要有至少一个订单待接单、一个订单制作中、一个已完成,管理员端要能展示订单统计结果——这些状态分布能在短时间内让老师看清系统全貌。

再过一条核心链路。从小程序端注册登录开始,进入美食列表选择商品加入购物车,购物车页调整数量后去结算,选择一张满足条件的优惠券,提交订单,然后切换到商家端接单、出餐,最后回到用户端确认收货。整个过程录制下来,就是 2 分钟流畅的演示视频。这条链路里任何一步卡住,都说明系统里存在你没预判到的问题,提前暴露比现场翻车强得多。

最后准备两个能深入讲的技术点。第一个是事务:下单接口为什么加 @Transactional,不加会有什么后果(主单写入成功但明细写入失败,产生脏数据)。第二个是订单状态机的权限控制:为什么用户不能直接调接口把订单改成已完成,状态流转的判断条件是什么。这两个点说清楚,老师基本能判断你是真的理解这套系统,而不是只会跑起来看页面。

这个包里能挖的细节其实不少,但还是要按自己的理解走一遍代码,尤其是订单和优惠券这两块。从那以后我每次拿到新的项目包都会先复现完整链路再谈优化,这条习惯替我挡住了至少三次演示翻车。希望帮到你。

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

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

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

立即咨询