简介:一份面向高校毕业设计、课程设计及期末大作业的奶茶店点餐微信小程序全栈项目,涵盖用户点餐小程序、基于Java与Vue的后台管理系统以及数据库初始化脚本,适合有Java基础、需要快速搭建可运行演示项目并用于答辩的学生。资源共481个文件,主要包含Java后台服务代码、Vue管理页面、小程序前端逻辑、SQL数据库脚本与使用说明文档,另有106张界面截图、104张素材图等,压缩包整体约5.64MB,结构紧凑、按功能分目录,便于按需查阅。目前已有407人学习下载,属于高实用性毕设参考。项目经过严格调试,附有代码注释与使用说明,新手也能按文档完成部署;后台涉及Redis服务与OrderController等订单处理模块,管理端覆盖商品、订单等常见运营功能,可完整支撑毕业设计展示和二次开发扩展。
1. 一份能直接答辩的奶茶点餐毕设:它到底解决了什么
拿到一份基于微信小程序的毕业设计点餐项目源码,最怕的不是代码多,而是解压之后不知道该点哪个文件。这套基于微信小程序的毕业设计点餐源码,把小程序端、后台系统、数据库三块打包在一起,前端用原生小程序语法,后端是 Java 风格接口,配合 Redis 做缓存,整体属于「小程序点餐 + 管理后台」的经典毕设结构。它对标的需求很具体:课程设计、期末大作业、毕业设计需要一套能现场演示、能截图、能讲清楚流程的完整系统,而不是一个只讲原理的空壳。新手照着说明部署,能在一台电脑上跑通点餐、下单、管理商品;想拿高分的,也能在答辩时把登录态、订单状态机、数据缓存这些点讲出深度。
这份资源适合两类人:一类是时间紧、需要快速交付完整项目的开发者;另一类是已经有点基础、想搞懂小程序与后端怎么联调的人。下面按我实际拆包的顺序,把每一层的关键点拆开讲,顺便把新手最容易翻车的地方标出来。
2. 从登录到下单:拆解小程序与后端的关键请求链路
小程序和普通网页最大的区别在于登录态不是自己生成的,必须借助微信的临时凭证换取用户身份。这套项目里能看到典型的「code 换 openid」流程:小程序端调登录接口,后端把 openid 作为用户唯一标识,再用 Redis 生成自定义 token 返回前端。搞清楚这条链路,整个前后端联调就通了一半。
2.1 资源包结构:从文件名反推项目分层
从资源里的文件能直接看出项目构成:.babelrc是前端 ES6 转译配置,app.188b12b2b5eba28bd74908fa929bc5ca.css是构建后的全局样式,index.html、loading.gif、favicon.ico是打包出的静态资源,后端部分有RedisService.java和OrderController.java。这说明小程序前端经过了构建步骤,后端则是以 Java 源码形式提供的,数据库脚本在 sql 目录里。
拿到这类混合型资源,我一般会先做一次目录归类,把编译产物、源码、文档分开:
workspace/ ├── mini-program/ # 小程序端源码(含构建产物) │ ├── pages/ │ ├── app.js │ └── app.json ├── backend/ # Java 后端源码 │ ├── RedisService.java │ └── OrderController.java ├── sql/ └── docs/ # 说明文档这里的关键是不要把index.html当成小程序入口,小程序真正入口是app.json里注册的页面路径。.babelrc的作用是让低版本微信基础库也能运行新语法,如果后续要改前端代码,需要先跑一遍构建命令重新生成 CSS,否则你改的是源码、跑的还是旧包。
2.2 登录与鉴权:临时 code 是怎么变成用户身份的
微信小程序登录的标准流程是:前端调用wx.login拿到一个 5 分钟有效的临时 code,把它发给后端,后端拿 code 换 openid 和 session_key,最后生成自己的 token。这套项目把 token 存在 Redis 里,并设置过期时间,前端后续请求通过请求头携带。
小程序端登录代码通常长这样:
// 小程序端登录逻辑 wx.login({ success: (res) => { const code = res.code; // 临时凭证,5 分钟有效且只能用一次 wx.request({ url: 'https://your-domain.com/api/user/login', method: 'POST', data: { code }, success: (resp) => { if (resp.data.code === 0) { // 把后端发的 token 存到本地,后续请求都带上 wx.setStorageSync('token', resp.data.data.token); } } }); } });后端对应的接口会先调微信接口换 openid,再把 token 写入 Redis:
@PostMapping("/api/user/login") public Result login(@RequestBody LoginReq req) { // 调用微信接口,用 code 换 openid String openid = wxService.code2Session(req.getCode()).getOpenid(); // 生成自定义 token,并设置 7 天过期 String token = UUID.randomUUID().toString().replace("-", ""); redisService.set("token:" + token, openid, 7 * 24 * 3600); return Result.success(token); }注意两个细节:code 是一次性的,重复使用会报错,所以前端不要并发调登录接口;token 的 key 加前缀token:,方便在 Redis 里统一管理,也便于排查问题。openid 是用户在当前小程序下的唯一标识,不是全局用户 ID——不同小程序对同一个用户产生的 openid 不同,这一点答辩时容易被问到。
提示:不要把微信小程序的 appsecret 写进前端代码或提交到仓库。后端调用微信接口时使用,前端只传 code 即可。
2.3 请求封装与登录态失效处理
后端接口不是所有请求都放开的,下单、管理商品这些接口必须在请求头带上登录凭证。常见做法是封装一个request.js,统一处理 token 注入和 401 跳转:
// 封装微信请求,自动携带 token const request = (url, data, method = 'GET') => { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); // 从本地取 token wx.request({ url: baseUrl + url, data, method, header: { Authorization: 'Bearer ' + token }, // 统一放请求头 success: (res) => { if (res.statusCode === 401) { // 登录态失效,清掉旧 token 并跳登录页 wx.removeStorageSync('token'); wx.navigateTo({ url: '/pages/login/login' }); return; } resolve(res.data); }, fail: reject }); }); };这里最常见的坑是并发请求同时返回 401,多个页面同时跳转登录页,界面会闪一下甚至报错。我一般会在 401 处理里加一个全局开关,只在第一次失败时跳转,后面的 401 直接静默失败。另一个容易忽略的问题是请求超时时间,默认 60 秒太长,后端接口如果卡住,用户会以为死机了,改成 10~15 秒更合适。
围绕登录链路,后端还需要一个拦截器统一校验 token,否则每个 Controller 都要重复写解析逻辑。拦截器里先从请求头取 Authorization,再用 token 去 Redis 查 openid,查不到就直接返回 401,查到就放行并把 openid 放到请求上下文里,供后面的下单接口使用。
3. 订单核心链路:Redis 缓存与接口的状态机设计
点餐系统的业务核心在订单模块。用户浏览商品、加入购物车、提交订单、支付、商家接单、完成,这一串动作背后是订单状态在流转。如果只是把状态存在数据库里,每次改一个字段,逻辑会越写越乱,所以正规做法是给订单定义一个清晰的状态机,接口只允许合法状态跳转。
3.1 点餐数据流与订单状态枚举
完整的点餐数据流是:小程序首页拉取商品列表 → 商品详情页加入购物车 → 购物车页提交订单 → 后端计算金额生成订单 → 用户支付 → 订单状态变为已支付 → 商家端接单并完成出餐。整个流程里,订单状态是最容易被扣分的地方,很多课程设计只用两个字段「未支付/已支付」凑合,导致售后、取消订单这些需求没法落地。
推荐至少保留 5 个状态:
| 状态值 | 含义 | 说明 |
|---|---|---|
| 0 | 待支付 | 订单已生成,用户还需付款 |
| 1 | 已支付 | 支付成功,等待商家制作 |
| 2 | 制作中 | 商家已接单,正在制作 |
| 3 | 已完成 | 订单履约完成 |
| 4 | 已取消 | 用户或超时取消 |
状态机的好处是每一步的合法跳转是明确的:待支付可以取消,已支付不能直接取消,制作中只能到已完成。后端接口里对状态做前置校验,避免用户拖拽请求绕过支付环节把订单改成已完成。
3.2 RedisService 在订单模块里的角色
资源里的RedisService.java不是简单封装一个连接,它承担了三类职责:token 存储、商品缓存、订单状态临时记录。商品列表和商品详情通常是高频读取的,一个奶茶店商品撑死了几十条,不值得每次都查数据库,放 Redis 里能显著减少数据库压力。
一个可落地的封装长这样:
public class RedisService { private StringRedisTemplate redisTemplate; // 写入缓存,带过期时间,单位是秒 public void set(String key, Object value, long timeoutSeconds) { redisTemplate.opsForValue().set( key, JSON.toJSONString(value), timeoutSeconds, TimeUnit.SECONDS); } // 读取缓存并反序列化为指定类型 public <T> T get(String key, Class<T> clazz) { String json = redisTemplate.opsForValue().get(key); return json == null ? null : JSON.parseObject(json, clazz); } // 给已有 key 续期 public void expire(String key, long timeoutSeconds) { redisTemplate.expire(key, timeoutSeconds, TimeUnit.SECONDS); } }参数说明:key 要带业务前缀,比如product:detail:1、product:list:all;超时时间商品缓存设 30 分钟即可,token 设 7 天。反序列化的目标类型要传对,否则JSON.parseObject会抛类型转换异常。还有一个容易被忽略的点:查询数据库结果为空时,也往 Redis 写一个空值并设置较短过期时间,防止大量无效请求直接打到数据库,这就是常说的缓存穿透。
购物车这一层不建议放 Redis,购物车本质是用户会话级数据,用户可能边加边改,放小程序本地 storage 操作更流畅,等提交订单时一次性把商品列表和数量传给后端即可。
3.3 OrderController 的接口设计与状态校验
后端下单接口要处理的不只是插入一条订单记录,还涉及商品价格校验、库存扣减、订单号生成、订单明细快照。资源里带OrderController.java,这类 Controller 一般会聚合这些逻辑:
@RestController @RequestMapping("/api/order") public class OrderController { @PostMapping("/create") public Result create(@RequestBody OrderCreateReq req, @RequestHeader("Authorization") String token) { // 根据 token 从 Redis 拿 openid,找不到就是未登录 String openid = redisService.get("token:" + token, String.class); if (openid == null) { return Result.fail(401, "登录状态已失效"); } // 校验商品与价格,生成订单号,写入订单表 Order order = orderService.create(openid, req.getItems()); return Result.success(order); } @GetMapping("/{orderId}") public Result detail(@PathVariable Long orderId) { return Result.success(orderService.detail(orderId)); } @PostMapping("/cancel") public Result cancel(@RequestBody OrderCancelReq req) { // 只有待支付状态才能取消 Order order = orderService.getById(req.getOrderId()); if (order.getStatus() != OrderStatusEnum.UNPAID.getCode()) { return Result.fail("当前状态不可取消"); } orderService.updateStatus(req.getOrderId(), OrderStatusEnum.CANCELED.getCode()); return Result.success(); } }订单号不要用数据库自增 ID,对外暴露订单量会显得不专业,常见做法是「时间戳 + 随机数」或雪花算法生成 18~20 位订单号,放在order_no字段里。订单明细要保存下单时的商品名称、单价、数量,而不是只存商品 ID——因为商品价格后面可能调整,历史订单必须保留当时的快照。
3.4 支付回调与模拟支付的选择
很多毕设在这里翻车:配了真实微信支付,但因为没有商户号或者回调地址不对,订单支付后状态一直不变。真实的微信支付需要商户号、API 密钥、证书和回调域名,个人开发者很难一次性配通,所以课程设计和本科毕设我一般建议做「模拟支付」。
模拟支付接口就是在后端把待支付状态直接改成已支付:
@PostMapping("/mockPay") public Result mockPay(@RequestBody MockPayReq req) { // 仅供演示:直接跳过微信支付,把订单改成已支付 orderService.updateStatus(req.getOrderId(), OrderStatusEnum.PAID.getCode()); return Result.success(); }答辩讲解时可以说清楚真实支付的流程:小程序调微信支付接口 → 微信拉起收银台 → 用户付款 → 微信服务器回调后端通知结果 → 后端验签后更新订单状态。模拟支付演示的是后半段的回调处理逻辑,只是省略了前半段的真实扣款。如果你非要配真实支付,回调地址必须是线上 HTTPS 域名,本地 localhost 收不到回调,这也是状态一直不变的最常见原因。
4. 后台系统与数据库设计:表结构落地的几个细节
奶茶点餐小程序的管理端一般不需要单独做 App,大多是管理后台网页,处理商品上架、分类维护、订单查看这些操作。表结构设计决定了后台功能能不能顺利展开,也决定了答辩时能不能把数据关系讲明白。
4.1 核心表结构与字段设计
按这套系统的业务范围,最少需要 6 张表:用户表、分类表、商品表、订单表、订单明细表、轮播图表。字段设计可以参考下面的结构:
| 表名 | 关键字段 | 说明 |
|---|---|---|
| t_user | id、openid、nickname、avatar、phone、create_time | openid 唯一索引 |
| t_category | id、name、sort、status | sort 控制排序,status 控制是否显示 |
| t_product | id、category_id、name、cover、price、original_price、sales、stock、status | 价格用小数,库存用整数 |
| t_order | id、order_no、openid、total_price、status、create_time、pay_time、remark | 订单号唯一,状态见上文枚举 |
| t_order_item | id、order_id、product_id、product_name、price、quantity、subtotal | 冗余商品快照 |
| t_banner | id、image、link、sort、status | 首页轮播图 |
订单明细冗余product_name和price是刻意为之:商品表改了名称或价格,历史订单的明细不能跟着变,否则对账时对不上。这也是订单系统的通用思路。用户表只留 openid 和个人信息,账号密码这类字段在这个项目里并不需要,因为登录完全走微信授权。
4.2 管理端功能:商品上下架与订单处理
管理后台最常见的管理操作是:新增商品时填写名称、选择分类、上传图片、设置原价和现价、填库存;修改商品时可以直接改字段,也可以把 status 改成下架状态;订单页面按状态筛选,待支付订单可以标记为已支付,已支付订单可以更新为制作中、已完成。
这类操作在后端对应的是另一个管理端 Controller,接口路径一般带/admin前缀,并且要加管理员权限校验。很多毕设只校验了用户登录,结果普通用户也能调管理接口把商品删了,这是安全性的硬伤。管理员的校验可以简单一点:管理登录时签发一个带role字段的 token,后端拦截器里判断角色不是管理员就直接拒绝。
4.3 数据库初始化:字符集、时区、导入顺序
数据库脚本导入之前,先建库并指定字符集,否则后面商品名里带 emoji 表情会变成乱码:
mysql -u root -p -e "CREATE DATABASE milk_tea DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" mysql -u root -p milk_tea < milk_tea.sql这里必须用utf8mb4,utf8存不了 emoji 和部分生僻字。第二个命令把 SQL 文件导入新建的库里,导入成功后可以用SHOW TABLES;验证是否生成了 6 张表。
后端连接数据库的配置文件同样要注意字符集和时区:
spring: datasource: url: jdbc:mysql://127.0.0.1:3306/milk_tea?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你自己的密码 redis: host: 127.0.0.1 port: 6379serverTimezone=Asia/Shanghai不配的话,本地时间通常比数据库时间慢 8 小时,订单创建时间全是「昨天」,答辩时非常尴尬。Redis 端口是 6379 就不用改,如果本机 Redis 设了密码,还要在spring.redis.password里补上。
5. 避坑与排查:5 个典型问题的现象、原因与解决
这套项目我在部署和复现过程中遇到过不少问题,下面 5 个是最常见、最影响演示的,按现象、原因、解决三个维度记录。
5.1 真机预览白屏,开发者工具却正常
现象:在微信开发者工具里点餐页面能正常加载,一扫码真机预览就白屏,接口也报错。
原因:开发者工具默认勾选了「不校验合法域名」,但真机上小程序要求所有wx.request的域名必须在微信公众平台配置合法域名,而且必须是 HTTPS。资源里的后端接口如果是本地 IP 或未备案域名,真机自然请求不通。
解决:开发调试阶段,在开发者工具「详情 → 本地设置」里勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」;上线或给老师演示前,把小程序的 request 合法域名配置成已经备案且支持 HTTPS 的服务器地址,后端也要同步部署到服务器。
5.2 接口时不时返回 401,页面被反复踢回登录页
现象:点餐过程中突然弹出登录失效,或者切到订单页就白屏,控制台里全是 401。
原因:token 过期时间设置太短,常见错误是把登录凭证的过期时间设成 30 分钟,用户边逛边下单就过期了;另一个原因是并发请求同时返回 401,多次触发navigateTo跳登录页,页面栈出问题。
解决:token 有效期统一设 7 天,set时把过期时间一并写入,续期时expire操作。前端 401 处理加全局标记,只允许首次 401 触发跳转:
let isRedirecting = false; // 全局标记 if (res.statusCode === 401) { if (isRedirecting) return; isRedirecting = true; wx.removeStorageSync('token'); wx.navigateTo({ url: '/pages/login/login' }); }5.3 后端一启动就连不上 Redis,商品列表全是空的
现象:后端项目启动时报Could not get a resource from the pool,小程序端商品列表接口超时。
原因:本机没装 Redis,或者 Redis 装了但没启动。很多人以为后端报错是代码问题,其实是服务没起来。
解决:本地用 Redis 官方 Windows 版,双击redis-server.exe启动,然后验证连通性:
redis-cli -h 127.0.0.1 -p 6379 ping返回PONG才说明连接正常。如果 Redis 设置了密码而配置文件里没写,也会报认证失败,把spring.redis.password补上即可。
5.4 订单支付后状态一直停在「待支付」
现象:用户点了支付按钮也跳转了支付流程,但订单状态没有变成已支付。
原因:真实支付的回调地址没配置成外网可访问的 HTTPS 地址,微信支付回调根本打不到本地;或者是模拟支付按钮压根没有调后端接口,前端只是出现了一个支付成功的弹窗。
解决:课程设计阶段直接调mockPay接口,绕过真实支付链路。演示时点「模拟支付」按钮,前端调/api/order/mockPay,后端把待支付改成已支付,再刷新订单列表就能看到状态变化。这样做还能保证演示不依赖商户号。
5.5 订单时间比当前时间晚了 8 小时,商品名 emoji 变问号
现象:订单列表里创建时间是 14:00,本地实际已经 22:00;商品名里的表情符号全部变成??。
原因:数据库连接串缺少serverTimezone参数,默认用了系统时区导致偏移;数据库和表字符集用了utf8而不是utf8mb4,emoji 无法存储。
解决:连接串末尾追加serverTimezone=Asia/Shanghai;建库语句统一用utf8mb4,已经建错的库执行ALTER TABLE t_product CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;修复。
6. 部署验证与答辩技巧:用一张接口自检表兜底
整套系统的部署顺序是有讲究的,乱序启动容易造成「后端起来了但数据源没初始化」的假象。我按下面的顺序走,基本不会出问题:
- 启动 MySQL,确认
milk_tea库存在且表已导入; - 启动 Redis,
redis-cli ping返回 PONG; - 启动 Java 后端,观察控制台没有连接报错;
- 微信开发者工具导入小程序端源码,appid 改成你自己的测试号;
- 小程序端检查
baseUrl是否指向后端地址,如果是本地调试就配http://127.0.0.1:8080。
启动完之后,别急着点页面,先用一份接口自检表把核心链路过一遍。下面这张表我每做一个小程序毕设都会列一次:
| 接口路径 | 请求方式 | 预期结果 |
|---|---|---|
| /api/user/login | POST | 返回 token,Redis 里出现 token: 前缀的 key |
| /api/product/list | GET | 返回商品列表,非空 |
| /api/order/create | POST | 生成订单号,状态为待支付 |
| /api/order/mockPay | POST | 订单状态变为已支付 |
| /api/order/cancel | POST | 待支付订单可取消,已支付订单返回错误 |
答辩现场演示时,把登录、点餐、模拟支付、管理端改商品价格这四件事按顺序走一遍,比临时翻代码讲细节稳妥得多。讲技术亮点不用全讲,挑三个就够:登录态用 Redis 存 token 和 openid 的映射,订单状态机控制合法流转,订单明细用快照字段保证历史数据可靠。这三条都是评委爱追问的点,你提前把对应代码位置记住,问到哪条都能翻到。
有个小细节值得专门说:管理后台改完商品价格后,记得把 Redis 里的商品缓存删掉或用expire让它尽快过期,不然前台显示的还是旧价格。当时第一次给导师演示时,后台改了价格页面纹丝不动,查到最后是缓存没清,当场有点狼狈。从那以后,我每次改后台数据都强制走一遍缓存清理的动作,顺手在前端加了一个下拉刷新重新拉列表。做小程序毕设,真正拉开差距的往往不是功能多少,而是这些边界细节能不能自圆其说。这份源码覆盖的点餐流程、后台管理和数据库设计,足够支撑起一堂完整的答辩演示,剩下就看你在细节上愿意花多少心思了,希望帮到你。
本文还有配套的精品资源,点击获取