ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

微信小程序奶茶店点餐系统毕设:三端分离架构与数据库设计实战

微信小程序奶茶店点餐系统毕设:三端分离架构与数据库设计实战 简介这份资源是面向高校计算机相关专业学生的奶茶店点餐微信小程序毕业设计完整方案包含小程序前端、后台管理系统与数据库三部分适合作为毕设、期末大作业或课程设计选题也适合想入门微信小程序开发的新手参考。压缩包共481个文件约5.64MB其中101个Java文件构成后台服务端逻辑57个JS与50个Vue文件支撑前端页面与交互另有106个PNG、104个JPG等图片资源用于界面展示并附带SQL建库脚本、YML配置、MD说明文档及字体文件代码注释齐全配有使用文档部署后即可运行。目前已有407人学习关注。项目功能完善、界面美观、操作简单涵盖点餐、订单管理、后台维护等模块读者可据此快速理解小程序与后台的联调方式掌握从数据库设计到接口实现的完整流程也可直接用于答辩展示或二次开发。1. 奶茶店点餐小程序毕设从选题到能跑起来的那条最短路径每年毕业季后台被问得最多的一类问题就是基于微信小程序的奶茶店点餐系统到底能不能做、要花多久、答辩会不会被问穿。先说结论——这个选题是典型的「看起来简单、做起来有细节、答辩有话说」的高分毕设方向。它同时踩中了微信小程序、点餐业务、后台系统、数据库设计四个可展示的技术面老师能问的点多你能讲的深度也够。但绝大多数人翻车不是因为不会写代码而是因为一开始就没想清楚小程序端、后台管理端、数据库这三块到底怎么分工数据怎么流转哪些功能是必须做的哪些是加分项。这篇笔记就按我实际带过几届毕设的经验把这条路径从头到尾拆一遍让你看完能直接动手而不是继续在选题阶段反复横跳。2. 先想清楚三端分工小程序、后台、数据库各管什么2.1 为什么这个选题必须做成「三端分离」很多同学一上来就想用云开发一把梭觉得不用写后台多省事。但毕设答辩有个潜规则老师希望看到你具备完整的工程能力而不是只会调云函数。三端分离的结构——微信小程序负责用户交互、后台系统负责商家管理、数据库负责数据持久化——恰好能覆盖前端、后端、数据库三个维度的考核点。而且这个结构在真实奶茶店场景里也是成立的顾客扫码点单、店员在后台接单改库存、老板看营业数据三条线本来就是分开的。从技术选型上说小程序端用原生微信小程序框架就够了没必要上 uniapp 增加复杂度除非你导师明确要求跨平台。后台系统我一般推荐用 Vue Element UI 或者直接 SpringBoot 带一个简单的管理页面看你的技术栈。数据库用 MySQL 最稳资料多、老师熟、出问题好查。这里有个血泪经验不要为了显得高级去用 MongoDB 或者 Redis 做主存储毕设场景数据量小、关系明确MySQL 的关系模型反而更好讲清楚。2.2 三端各自的最小功能集小程序端必须有的商品列表按分类展示奶茶、商品详情规格选择杯型、糖度、温度、加料、购物车、下单结算、订单列表、订单详情、个人中心。这些是点餐系统的骨架缺一个老师都会问「为什么没有」。加分项可以加微信支付沙箱环境即可、优惠券、积分、评价。后台系统必须有的商品管理增删改查、上下架、分类管理、订单管理接单、完成、取消、用户管理、数据统计今日订单数、销售额。这里注意订单管理一定要有状态流转不能只做一个列表否则答辩时老师问「订单从下单到完成经历了什么」你就卡住了。数据库至少要设计这几张表用户表、商品表、商品分类表、规格表杯型/糖度/温度、购物车表、订单表、订单明细表、管理员表。表之间的关系要能画出来一对多、多对多各在哪里这是答辩必问。2.3 一个容易忽略的坑规格和库存的耦合奶茶点餐和普通电商最大的区别在于规格组合。一杯奶茶可能有杯型中杯/大杯、糖度无糖/三分/五分/七分/全糖、温度常温/少冰/多冰/热、加料珍珠/椰果/布丁四个维度。如果你把每个组合都当成一个独立 SKU 存进商品表数据量会爆炸如果只存基础商品下单时又没法准确扣库存。常见做法是商品表存基础信息规格表存可选项订单明细表里用 JSON 字段记录用户选择的具体规格组合。库存扣减只针对基础商品和加料杯型糖度温度不影响库存。这个设计思路答辩时讲出来老师会觉得你真的想过业务。3. 数据库设计八张表撑起整个点餐流程3.1 核心表结构设计先看整体 ER 关系用户下单产生订单订单包含多个订单明细每个明细对应一个商品和一组规格选择。商品属于某个分类商品有多个规格选项。管理员独立于普通用户。下面是我一般会用的建表语句以 MySQL 为例-- 用户表 CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, openid VARCHAR(64) NOT NULL COMMENT 微信openid, nickname VARCHAR(64) DEFAULT , avatar VARCHAR(255) DEFAULT , phone VARCHAR(20) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 商品分类表 CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(32) NOT NULL, sort INT DEFAULT 0 COMMENT 排序权重, status TINYINT DEFAULT 1 COMMENT 1启用 0禁用 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类; -- 商品表 CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, image VARCHAR(255) DEFAULT , description VARCHAR(255) DEFAULT , stock INT DEFAULT 0, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; -- 规格选项表杯型/糖度/温度/加料统一存这里 CREATE TABLE spec_option ( id INT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(16) NOT NULL COMMENT cup/sugar/temp/topping, name VARCHAR(32) NOT NULL, extra_price DECIMAL(10,2) DEFAULT 0.00, sort INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT规格选项; -- 订单表 CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, user_id INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0待接单 1制作中 2已完成 3已取消, remark VARCHAR(255) DEFAULT , create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表; -- 订单明细表 CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, product_id INT NOT NULL, product_name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL, spec_json JSON COMMENT 规格组合, KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细;这几张表的设计逻辑用户表用 openid 做唯一键因为微信小程序登录拿到的就是 openid商品表用 category_id 关联分类status 控制上下架规格选项表用 type 字段区分杯型、糖度、温度、加料extra_price 处理加料加价订单表用 order_no 做业务单号status 做状态流转订单明细表用 spec_json 存规格组合避免多表关联查询。参数说明price 用 DECIMAL(10,2) 而不是 FLOAT避免精度问题spec_json 用 MySQL 5.7 的 JSON 类型如果学校机房版本低就改成 TEXT 存 JSON 字符串所有表用 utf8mb4 字符集支持 emoji。3.2 购物车表要不要单独建购物车有两种做法存本地缓存wx.setStorageSync或者存数据库。我一般建议毕设场景存数据库因为老师会问「换手机购物车还在吗」存本地就答不上来。购物车表结构简单user_id、product_id、quantity、spec_json、create_time。查询时关联商品表拿最新价格和图片。CREATE TABLE cart ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, product_id INT NOT NULL, quantity INT DEFAULT 1, spec_json JSON, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT购物车;注意购物车不需要存价格因为价格可能变动结算时以商品表当前价格为准。这个细节答辩时能讲出来是加分项。4. 小程序端实现从商品列表到下单的完整链路4.1 请求封装与全局配置小程序端第一件事不是写页面而是封装请求。原生 wx.request 回调写法太乱我一般会封装成 Promise// utils/request.js const BASE_URL http://localhost:8080/api; function request(options) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { content-type: application/json, token: wx.getStorageSync(token) || }, success(res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data); } else if (res.data.code 401) { // token过期重新登录 wx.removeStorageSync(token); wx.showToast({ title: 请重新登录, icon: none }); reject(res.data); } else { wx.showToast({ title: res.data.msg || 请求失败, icon: none }); reject(res.data); } }, fail(err) { wx.showToast({ title: 网络异常, icon: none }); reject(err); } }); }); } module.exports { request };逻辑说明统一拼接 BASE_URL统一携带 token统一处理业务码。code 0 表示成功401 表示登录态失效。参数说明BASE_URL 在开发阶段指向本地后端上线要改成备案域名但毕设答辩用本地或局域网 IP 即可。4.2 商品列表与规格选择商品列表页按分类展示左侧分类导航右侧商品列表。核心是点击商品后弹出规格选择弹窗用户选完加入购物车。// pages/menu/menu.js Page({ data: { categories: [], products: [], currentCategory: 0, showSpec: false, currentProduct: null, selectedSpec: { cup: 0, sugar: 0, temp: 0, toppings: [] } }, onLoad() { this.loadCategories(); }, async loadCategories() { const categories await request({ url: /category/list }); this.setData({ categories }); if (categories.length 0) { this.loadProducts(categories[0].id); } }, async loadProducts(categoryId) { const products await request({ url: /product/list, data: { categoryId } }); this.setData({ products, currentCategory: categoryId }); }, openSpec(e) { const product e.currentTarget.dataset.product; this.setData({ showSpec: true, currentProduct: product, selectedSpec: { cup: 0, sugar: 0, temp: 0, toppings: [] } }); }, async addToCart() { const { currentProduct, selectedSpec } this.data; await request({ url: /cart/add, method: POST, data: { productId: currentProduct.id, quantity: 1, spec: selectedSpec } }); wx.showToast({ title: 已加入购物车 }); this.setData({ showSpec: false }); } });逻辑说明onLoad 先加载分类再加载第一个分类下的商品。openSpec 打开规格弹窗并重置选择。addToCart 把商品 ID 和规格组合发给后端。参数说明selectedSpec 里的 cup/sugar/temp 存选项 IDtoppings 存数组因为可以多选。后端收到后根据 ID 查规格表算加价。4.3 下单与订单状态流转下单接口要做三件事校验库存、生成订单、扣减库存。这三步必须在一个事务里否则会出现超卖。// 后端伪代码SpringBoot Transactional public OrderVO createOrder(Integer userId, OrderDTO dto) { // 1. 校验库存 for (OrderItemDTO item : dto.getItems()) { Product product productMapper.selectById(item.getProductId()); if (product.getStock() item.getQuantity()) { throw new BusinessException(库存不足 product.getName()); } } // 2. 生成订单 Orders order new Orders(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setStatus(0); order.setTotalPrice(calcTotal(dto)); orderMapper.insert(order); // 3. 扣减库存 写明细 for (OrderItemDTO item : dto.getItems()) { productMapper.decrStock(item.getProductId(), item.getQuantity()); OrderItem oi new OrderItem(); oi.setOrderId(order.getId()); oi.setProductId(item.getProductId()); oi.setQuantity(item.getQuantity()); oi.setSpecJson(JSON.toJSONString(item.getSpec())); orderItemMapper.insert(oi); } return buildOrderVO(order); }逻辑说明Transactional 保证三步要么全成功要么全回滚。先校验再扣减避免扣了库存但订单没生成。参数说明generateOrderNo 一般用时间戳加随机数decrStock 用 UPDATE product SET stock stock - ? WHERE id ? AND stock ? 这种写法防止并发超卖。订单状态流转0 待接单 → 1 制作中 → 2 已完成或者 0 → 3 已取消。后台接单改状态 1制作完成改状态 2。小程序端订单列表按状态筛选展示。5. 后台系统与联调商家端怎么接单改库存5.1 后台最小实现方案后台系统如果时间紧可以用现成的 admin 模板套。我一般推荐两种Vue Element UI 前后端分离或者 SpringBoot Thymeleaf 服务端渲染。前者适合你前端还行的情况后者适合只想写 Java 的情况。核心页面就四个登录页、商品管理、订单管理、数据统计。商品管理要支持列表分页、新增、编辑、上下架、删除。订单管理要支持列表按状态筛选、接单、完成、取消。数据统计展示今日订单数和销售额用一条 SQL 就能查SELECT COUNT(*) AS order_count, SUM(total_price) AS total_sales FROM orders WHERE DATE(create_time) CURDATE() AND status 2;5.2 前后端联调的三个关键点第一跨域问题。小程序开发工具里可以在「详情-本地设置」勾选「不校验合法域名」但后台系统如果前后端分离后端要加 CORS 配置。SpringBoot 加一个配置类即可。第二token 传递。小程序登录后后端返回 token后续请求放在 header 里。后台系统用 session 或 JWT 都行毕设场景 session 更简单。第三时间格式。数据库存 DATETIME后端返回给前端要统一格式化成 yyyy-MM-dd HH:mm:ss否则小程序端显示会出问题。5.3 微信登录的完整流程小程序端 wx.login 拿到 code传给后端后端调微信接口换 openid 和 session_key然后生成 token 返回。注意 code 只能用一次且五分钟过期。// 小程序端登录 wx.login({ success: async (res) { const data await request({ url: /auth/login, method: POST, data: { code: res.code } }); wx.setStorageSync(token, data.token); wx.setStorageSync(userInfo, data.userInfo); } });后端拿到 code 后请求微信的 code2Session 接口拿到 openid 后查用户表没有就注册有就更新登录时间最后生成 token。这个流程答辩必问要能画出来。6. 避坑与排查毕设答辩前必须过的五道坎6.1 库存超卖并发下单时库存扣成负数现象两个人同时下单最后一杯奶茶库存变成 -1。原因先查库存再扣减两步之间有时间窗口。解决用UPDATE product SET stock stock - ? WHERE id ? AND stock ?根据 affectedRows 判断是否扣减成功失败就抛异常回滚。6.2 订单号重复时间戳加随机数也会撞现象偶尔出现订单号唯一键冲突。原因高并发下时间戳相同、随机数碰撞。解决用时间戳 用户 ID 后四位 随机数或者直接用数据库自增 ID 拼接前缀。更稳的做法是用 Redis 的 INCR 生成序列号但毕设场景用 UUID 去掉横线也够。6.3 规格 JSON 解析失败前端传的格式和后端不一致现象下单时报 JSON 解析异常。原因前端传的是对象后端用 String 接收或者字段名对不上。解决前后端约定好 spec 的 JSON 结构后端用 RequestBody 接收 DTOspec 字段用 Map 或自定义类接收不要用 String 再手动解析。6.4 小程序请求域名未配置真机调试请求失败现象开发者工具正常真机预览请求全部失败。原因微信要求 request 合法域名必须备案且 HTTPS。解决毕设答辩用开发者工具演示即可或者用局域网 IP 加「不校验合法域名」选项。如果一定要真机用内网穿透工具临时映射一个 HTTPS 域名但注意答辩现场网络环境。6.5 数据库中文乱码emoji 和生僻字显示问号现象商品名里的 emoji 或者生僻字存进去变成问号。原因字符集不是 utf8mb4。解决建库建表都用 utf8mb4连接字符串加 characterEncodingutf8mb4MySQL 配置文件里也确认 default-character-setutf8mb4。7. 让答辩加分的一个技巧把订单状态做成可追溯的时间线大部分毕设的订单状态就是一个数字字段改了就改了老师问「这单什么时候接的、什么时候完成的」你答不上来。我一般会加一张订单状态日志表每次状态变更都插一条记录CREATE TABLE order_status_log ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, from_status TINYINT, to_status TINYINT NOT NULL, operator VARCHAR(32) DEFAULT system, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_order (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单状态日志;然后在订单详情页把这条时间线渲染出来待接单 → 制作中 → 已完成每个节点带时间。这个功能代码量不大但答辩时展示出来老师会觉得你的系统有真实业务感而不是简单的增删改查。更进一步你可以用这条日志做数据统计比如算平均制作时长这就是从「能跑」到「有亮点」的差距。我自己的习惯是每做一个功能先问自己「这个数据以后能不能用来讲故事」。订单状态日志就是典型例子它本身不复杂但给了你答辩时展开讲的空间。数据库设计也一样多一张日志表、多一个统计字段可能就是你从及格到优秀的距离。希望帮到你。本文还有配套的精品资源点击获取
返回列表