简介:一套完整的微信点餐小程序毕业设计资料包,基于微信小程序与SSM框架及MySQL数据库开发,覆盖用户注册登录、菜品推荐、喜欢列表、订单生成等核心模块,适合计算机相关专业毕业生用于课程设计和毕业设计,也适合想学习小程序前端与Java后端整合的开发者快速上手。资源共1262个文件,压缩包约60.63MB,主要包括Java后端源码、Vue管理端页面、小程序wxml/wxss/js文件、SQL数据库脚本,以及开题报告、毕业论文、答辩PPT和视频演示文档,目录结构清晰便于按模块查阅。目前已有133人学习下载。资料从项目背景、需求分析到系统设计均有完整论述,并附操作演示视频与答辩材料,可帮助理解线上点餐系统的完整实现流程,节省从零搭建时间。
1. 微信点餐小程序毕业设计:别急着改 UI,先看透这张订单表
你拿到的"微信点餐小程序毕业设计"通常不是一份简单的前端页面,而是一套能跑起来的完整交付物:微信小程序负责点餐界面,SSM 后端处理订单和菜品逻辑,MySQL 存着用户、商品、购物车和订单数据,外面再裹上开题报告、毕业论文、答辩 PPT 和视频演示。很多同学拿到源码后的第一反应是改 UI、换 Logo,结果答辩时被老师问一句"订单状态为什么用字符串而不是 int"就卡住了。实际上这套项目里最值钱的部分不是界面,而是数据库表设计和 SSM 的分层边界,这两点才是答辩最常被追问的地方。我把这套东西当成一个可以照着复现的工程来拆,先看表,再看接口,最后再谈怎么把它变成你自己的毕设。
2. SSM 与微信小程序的分工:请求从页面到数据库的完整链路
2.1 小程序端 wx.request 的封装与点餐页面加载
打开一个典型的微信点餐小程序源码,pages 目录下基本是首页、分类页、菜品列表页、购物车页和订单确认页。小程序端只做一件事:把用户看得到的界面渲染出来,同时在合适的时机调用后端接口拿数据。这里最基础的网络能力来自微信官方提供的wx.request,它和网页里的 axios 类似,但有几个差异:域名必须登记、请求头不能任意自定义、返回值需要手动解包。我一般会在 utils 目录下做一层封装,避免每个页面重复拼 URL 和处理错误。
// utils/request.js const BASE_URL = 'http://192.168.1.100:8080/order-mini-api'; function request(url, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method: method, data: data, header: { 'Content-Type': 'application/json' }, timeout: 10000, success: (res) => { // 约定后端统一返回 { code: 0, data: ... } if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data); } else { reject(res.data); } }, fail: reject }); }); } module.exports = { request, BASE_URL };BASE_URL 需要根据你本机后端实际的 IP、端口和上下文路径修改。比如 SSM 项目部署在 Tomcat 的 webapps/order-mini 下,那这里就是http://localhost:8080/order-mini。注意小程序真机预览时,localhost指向手机自己,而不是你的电脑,所以开发时常用局域网 IP。timeout设置为 10 秒,避免接口没响应时按钮一直转圈。后端返回体约定成{ code, data, message }三层结构,前端才能统一处理业务错误和 HTTP 错误。
菜品列表页通常还要处理分页。微信小程序里最自然的做法是配合onReachBottom做"加载更多",而不是一次把所有菜品拉回来。下面是一个常见写法:
// pages/dish/dish.js const { request } = require('../../utils/request'); Page({ data: { dishes: [], page: 1, pageSize: 10, loading: false, finished: false }, onLoad() { this.loadDishes(true); }, onReachBottom() { if (!this.data.finished) { this.loadDishes(false); } }, loadDishes(reset) { if (this.data.loading) return; this.setData({ loading: true }); const page = reset ? 1 : this.data.page + 1; request(`/api/dish/list?page=${page}&pageSize=${this.data.pageSize}`) .then((res) => { const list = reset ? res.rows : this.data.dishes.concat(res.rows); this.setData({ dishes: list, page: page, loading: false, finished: list.length >= res.total }); }) .catch(() => this.setData({ loading: false })); } });这里res.rows和res.total来自后端分页结果,具体字段名要和你源码里的PageBean保持一致。pageSize在毕设项目里设为 10 或 20 都行,太大了会拖慢低端手机渲染。要注意的是concat不能直接改this.data.dishes,必须通过setData赋值,这是小程序性能优化的基本常识。
2.2 SpringMVC 路由与 Service 事务:订单提交为什么不能少 @Transactional
SSM 是 Spring + SpringMVC + MyBatis 的组合,毕业设计选它而不是 Spring Boot,最大的原因是课程里讲了这套分层,答辩老师也熟悉。Controller 层只做参数接收和结果转发,Service 层写业务规则,Mapper 层访问数据库。以提交订单这个点餐系统最核心的动作为例,后端 Controller 通常长这样:
// controller/OrderController.java @RestController @RequestMapping("/api/order") public class OrderController { private final OrderService orderService; public OrderController(OrderService orderService) { this.orderService = orderService; } @PostMapping("/submit") public Result submit(@RequestBody OrderSubmitDTO dto) { // dto 里包含 userId、菜品列表、备注等 Long orderId = orderService.createOrder(dto); return Result.ok(orderId); } }@RestController会把返回对象直接序列化成 JSON,而不是走 JSP 视图,这是小程序端最需要的。@RequestBody用来接收小程序传来的 JSON 数据,注意小程序请求头里必须带Content-Type: application/json,否则 SpringMVC 解析不了。Result.ok(orderId)是统一返回体,保证前端res.data.code === 0的判断能成立。如果你拿到的源码里没有 Result 类,建议自己补一个,答辩时讲"统一返回结构"也是加分项。
订单提交的业务逻辑在 Service 层。一次下单要往 order 主表插一条记录,再往 order_item 明细表插入多条菜品记录。如果主表插入成功而明细表失败,订单总金额和实际菜品就对不上了。所以createOrder方法上必须加事务注解:
// service/impl/OrderServiceImpl.java @Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private OrderItemMapper orderItemMapper; @Transactional(rollbackFor = Exception.class) public Long createOrder(OrderSubmitDTO dto) { Order order = new Order(); order.setUserId(dto.getUserId()); order.setTotalAmount(dto.getTotalAmount()); order.setOrderStatus(0); // 0 待支付 orderMapper.insert(order); for (OrderItemDTO item : dto.getItems()) { OrderItem oi = new OrderItem(); oi.setOrderId(order.getOrderId()); oi.setDishId(item.getDishId()); oi.setDishName(item.getDishName()); oi.setPrice(item.getPrice()); oi.setQuantity(item.getQuantity()); orderItemMapper.insert(oi); } return order.getOrderId(); } }@Transactional(rollbackFor = Exception.class)里的rollbackFor参数不是可有可无的。Spring 默认只对 RuntimeException 回滚,如果业务方法抛出受检异常(比如库存不足自定义了一个 Exception),不加这个参数事务就不会回滚,最终留下脏数据。我在检查源码时经常看到有人只写@Transactional,没有指定异常类型,这属于理解上的坑。答辩时老师问"事务怎么控制",你可以直接回答:利用 Spring 声明式事务,在 Service 方法上声明边界,任何异常都回滚。
2.3 MyBatis 的 resultMap 与 SQL 是如何被调用起来的
SSM 里的 Mapper 层是数据库操作的最底层。MyBatis 允许你写 XML 来维护 SQL,不把 SQL 散落在 Java 代码里。以订单查询为例,Mapper 接口只定义方法名,真正的 SQL 写在 XML 里,namespace必须与接口全限定名一致。
<!-- mapper/OrderMapper.xml --> <mapper namespace="com.example.mapper.OrderMapper"> <resultMap id="OrderMap" type="com.example.entity.Order"> <id column="order_id" property="orderId"/> <result column="user_id" property="userId"/> <result column="total_amount" property="totalAmount"/> <result column="order_status" property="orderStatus"/> </resultMap> <select id="selectByUserId" resultMap="OrderMap"> SELECT order_id, user_id, total_amount, order_status FROM `order` WHERE user_id = #{userId} ORDER BY create_time DESC </select> </mapper>这里的order表名必须加反引号,因为 order 是 MySQL 的保留字,不加反引号在大部分版本里会直接报语法错误。#{userId}是预编译参数占位符,MyBatis 会把它转成 JDBC 的?,能防止 SQL 注入。resultMap把数据库下划线字段order_id映射成 Java 属性orderId,如果不写 resultMap,也可以在 mybatis-config.xml 里开启驼峰映射,但毕设里显式写出来更容易对着论文讲。
MyBatis 的调用链路是:Controller 调用 Service 方法,Service 注入 Mapper 接口,Mapper 接口的全限定名与 XML 的 namespace 绑定,Mapper 接口方法名与 XML 里的 id 绑定。很多项目翻车就翻在修改了接口方法名但没改 XML 的 id,运行时才报Invalid bound statement (not found)。检查的时候先看这些名字是否完全一致,这是一个成熟的一线习惯。
3. 把 MySQL 点餐库建起来:初始化 SQL、字符集与 JDBC 连接参数
3.1 核心表设计:用户、菜品、分类、订单、订单明细、购物车
微信点餐小程序的数据量不会很大,但表关系要能支撑到答辩。常见的设计是六张表:用户表、分类表、菜品表、购物车表、订单主表和订单明细表。它们之间的关系是:用户可以用 openid 唯一标识,一个用户有多个分类可见,每个分类下有多个菜品,用户可以把菜品加入购物车,提交购物车后生成订单。订单主表和明细表是典型的一对多关系。
| 表名 | 职责 | 关键字段 |
|---|---|---|
| user | 存储微信用户信息 | user_id, openid, nickname, avatar_url |
| category | 菜品分类 | category_id, name, sort |
| dish | 菜品基础信息 | dish_id, category_id, name, price, image_url, status |
| cart | 购物车记录 | cart_id, user_id, dish_id, quantity |
order | 订单主表 | order_id, user_id, total_amount, order_status, create_time |
| order_item | 订单明细表 | item_id, order_id, dish_id, dish_name, price, quantity |
这里有两个设计点值得在论文里写。第一个是user表用openid做唯一键,而不是只看自增 id。微信小程序的wx.login拿到的是临时 code,后端用 code 换取 openid,同一个微信号在同一个小程序里的 openid 是固定的。这样即使用户没有手动填手机号,系统也能识别身份。第二个是order_item里冗余了dish_name和price,这是故意的。菜品名称和价格将来可能调整,但历史订单必须保持当时的下单快照。这个"快照"思想在电商系统里很常见,答辩老师一听就知道你不是照抄的。
3.2 初始化 SQL:从建库到写进第一批测试数据
数据库脚本是源码包里最不能丢的文件。我一般会先看db目录下有没有.sql文件,再决定是否重新建库。一个合适的初始化 SQL 至少包含CREATE DATABASE、建表语句和几条测试数据。注意字符集统一用utf8mb4,它能存 emoji 表情,避免用户昵称里有特殊符号时写入失败。
CREATE DATABASE IF NOT EXISTS wx_order_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE wx_order_db; CREATE TABLE `user` ( user_id BIGINT AUTO_INCREMENT PRIMARY KEY, openid VARCHAR(64) NOT NULL UNIQUE, nickname VARCHAR(64) DEFAULT '', avatar_url VARCHAR(255) DEFAULT '', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `category` ( category_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) NOT NULL, sort INT DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `dish` ( dish_id INT AUTO_INCREMENT PRIMARY KEY, category_id INT NOT NULL, name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, image_url VARCHAR(255) DEFAULT '', status TINYINT DEFAULT 1 COMMENT '1上架 0下架' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `cart` ( cart_id INT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, dish_id INT NOT NULL, quantity INT DEFAULT 1, UNIQUE KEY uk_user_dish (user_id, dish_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `order` ( order_id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, order_status TINYINT DEFAULT 0 COMMENT '0待支付 1已支付 2已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE `order_item` ( item_id BIGINT AUTO_INCREMENT PRIMARY KEY, order_id BIGINT NOT NULL, dish_id INT NOT NULL, dish_name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT DEFAULT 1 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;注意cart表加了一个联合唯一键uk_user_dish,表示同一个用户对同一个菜品只有一条购物车记录,重复点击加入购物车时只需要UPDATE quantity,而不用再插一条。这个设计能直接规避后面要讲的重复订单问题的一部分。order_status用TINYINT而不是字符串,是为了查询和状态比较更方便,论文里写"状态值 0/1/2 对应业务状态"比写"待支付/payed/取消"更规范。DECIMAL(10,2)是金额字段的规范做法,不要用 FLOAT,否则计算总金额时会出现精度问题。
3.3 数据源配置:MySQL 8.0 驱动、时区与多环境切换
导入 SQL 之后,后端要能连上数据库。SSM 项目的数据源配置一般放在jdbc.properties或applicationContext.xml里。最常见的坑是本地装了 MySQL 8.0,但 pom 里还在用 MySQL 5.x 的驱动,启动时直接报驱动类找不到。下面是一个兼容性较好的配置:
# jdbc.properties jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/wx_order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=123456com.mysql.cj.jdbc.Driver是 8.x 之后的驱动类,旧版写的是com.mysql.jdbc.Driver。如果驱动类不匹配,启动时会出现ClassNotFoundException。serverTimezone=Asia/Shanghai必须加,否则 MySQL 8 会报时区错误。allowPublicKeyRetrieval=true是为了解决连接时出现Public Key Retrieval is not allowed的问题,这在 MySQL 8 默认加密插件下很容易触发。useSSL=false代表本地开发不启用 SSL 连接,减轻握手开销。
如果你的源码里没有 properties 文件,而是直接在 Spring XML 里写的<property name="jdbcUrl" value="..."/>也正常。毕业设计里不必强行拆成多环境,但至少不要把数据库密码硬编码在 Java 类里。我一般把jdbc.properties放在src/main/resources下,在 Spring 配置文件里用<context:property-placeholder location="classpath:jdbc.properties"/>引入,这样将来换数据库时只改一个文件。
4. 本地跑通这套源码:后端启动、数据库导入与小程序预览的完整步骤
4.1 用 Maven 打包并启动 SSM 后端
拿到源码之后不要急于在微信开发者工具里点编译,先让后端跑起来。SSM 项目通常用 Maven 管理依赖,你可以在 IDEA 里打开pom.xml,等待 Maven 下载依赖。如果没有 IDEA,也可以用命令行先验证依赖是否完整:
mvn clean package -DskipTestsclean会清除之前的 target 目录,-DskipTests跳过单元测试,避免测试环境配置问题打断打包。打包产物一般是.war文件,放到 Tomcat 的webapps目录下,然后启动 Tomcat。如果你用的 Tomcat 8.5 或 9,JDK 版本要匹配,否则启动时可能报UnsupportedClassVersionError。看到日志里出现INFO: Deployment of web application archive ... has finished就说明后端起来了。
在 IDEA 里更常见的做法是配置本地 Tomcat,运行一次后会在webapps/ROOT或你指定的 context path 下部署。无论哪种方式,重点记住你部署后的访问前缀。比如项目名是order-mini,那么接口地址就是http://localhost:8080/order-mini/api/...,小程序端的 BASE_URL 必须和这里一致。这个前缀拼错是最低级的错误,但几乎每年都有同学在这里卡住。
4.2 导入 SQL 数据库文件和初始化数据
后端连的数据库需要先导入。打开终端,进入源码包里的db或sql目录,执行:
mysql -u root -p < wx_order_db.sql在 Windows 的 cmd 里如果文件路径带空格,就先把工作目录切到 sql 文件所在目录再执行。如果 SQL 文件里没有CREATE DATABASE,需要手动先建库再导入:
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS wx_order_db DEFAULT CHARACTER SET utf8mb4;" mysql -u root -p --default-character-set=utf8mb4 wx_order_db < wx_order_db.sql--default-character-set=utf8mb4这个参数在 Windows 上尤其重要,否则中文菜品名称可能变成乱码。导入后进入 MySQL 检查一下:
mysql -u root -p -e "USE wx_order_db; SHOW TABLES;"看到六张表都出现,再查一下dish表是否有数据。经常有同学导入后表是空的,导致小程序页面白屏。这通常是因为 SQL 文件里只有建表语句,测试数据单独放在另一个data.sql文件里,需要一并导入。
4.3 小程序开发者工具的请求域名配置与真机预览
后端跑起来后,打开微信开发者工具导入小程序源码。本地开发阶段最方便的做法是关闭域名校验:右上角"详情" -> "本地设置" -> 勾选"不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书"。然后在 config.js 里设置本机地址:
// config.js module.exports = { baseUrl: 'http://localhost:8080/order-mini' };注意,如果你用真机预览,这个localhost指向手机自己,必须改成电脑的局域网 IP,比如http://192.168.1.100:8080/order-mini。同时手机和电脑要连同一个 WiFi。但在真机上即使改成局域网 IP,微信依然会拦截不合法域名,除非你在小程序后台把域名配置为 HTTPS 且已备案。所以毕设演示一般分两种:一种是只在开发者工具里演示,另一种是用内网穿透工具把本地后端映射成一个 HTTPS 域名,然后把该域名加入小程序后台的 request 合法域名。这样在手机微信里打开体验版也能正常请求,这一招能让你的答辩演示看起来更像真实上线。
走一遍核心流程验证:编译小程序后,首页能看到分类和菜品,点击菜品加入购物车,购物车页面点击结算,提交订单后数据库order表增加一条记录,order_item表增加对应明细。只要这条链路通,你的项目就成功了一大半。
5. 微信点餐毕设避坑:SSM 版本冲突、MyBatis 映射失败等 5 个典型问题
5.1 接口 404 且后端无日志:SpringMVC 静态资源拦截
现象:小程序请求/api/category/list返回 404,但后端控制台没有任何请求日志,换一个页面路径也一样。
原因:DispatcherServlet 的url-pattern配置成/时,会拦截所有请求,包括静态资源。如果你的 SpringMVC 配置里没有放行静态资源,或者web.xml的 servlet-mapping 写的是*.do,那/api路径根本进不了 SpringMVC 的处理链。还有一种情况是项目部署 context path 和实际请求前缀不一致。
解决:先确认web.xml中 servlet-mapping 是/而不是*.do,然后在 SpringMVC 配置文件中加一段:
<mvc:default-servlet-handler/> <mvc:annotation-driven/>default-servlet-handler会把没有映射到的请求交给容器默认 servlet 处理,这样静态资源和非接口请求不会挡住接口路由。同时检查@Controller类上是否有多层@RequestMapping,比如类上写了/api,方法上又写了/api/category,拼出来就是/api/api/category。这类问题没有报错,只有把完整路径打印出来才能发现。
5.2 数据库连接失败:MySQL 8 驱动与 url 参数不配套
现象:启动 Tomcat 时后台报Communications link failure,或者Unable to load authentication plugin 'caching_sha2_password'。
原因:本地安装的是 MySQL 8.0,默认认证插件是caching_sha2_password,而项目 pom 里的mysql-connector-java还是 5.x 版本,无法识别这个插件。另外 MySQL 8 对时区更敏感,jdbc.url里没有serverTimezone直接抛异常。
解决:把依赖版本调整到 8.x,并在 URL 里加齐参数。pom 中可以这样改:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency>8.0.33是稳定版本,具体以你项目现有依赖为准。改完依赖后重新mvn clean package,重启 Tomcat。如果密码正确仍然报认证失败,检查用户插件类型,也可以在 MySQL 里执行ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码';,但毕业设计不建议为迁就旧代码去改数据库认证方式,升级驱动才是正路。
5.3 MyBatis 查不出数据但 Navicat 能查到:字段映射与空结果
现象:接口返回{"rows":[]},但是把 SQL 复制到 Navicat 里执行却有 10 条记录。
原因:最常见的是 Java 实体属性用的是驼峰命名categoryId,数据库字段是下划线category_id,而 MyBatis 默认不会自动做驼峰转换,结果每个字段都是 null,前端拿到的对象像空一样。另一个原因是 Mapper 接口的 namespace 与 XML 不匹配,导致 MyBatis 绑定了别的接口,执行了错误的 SQL。
解决:先检查mybatis-config.xml里是否开启驼峰映射:
<configuration> <settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> </configuration>如果开启后端和程序都正常,就保持这个配置。如果项目里用的是旧式<resultMap>,则确认每个<result column>的 column 是否是数据库真实列名。还要检查 Mapper 接口与 XML 的目录是否一致,接口在com.example.mapper.OrderMapper,XML 就必须放在mappers/OrderMapper.xml并且 namespace 完全一致,不能只靠文件名对。这个错误启动时不报,只有运行时才报Invalid bound statement或返回空数据,属于典型的黑匣子问题。
5.4 真机预览请求失败:小程序合法域名校验与局域网 IP 的问题
现象:微信开发者工具里页面一切正常,点"真机预览"后,所有接口请求失败,提示request:fail或url not in domain list。
原因:开发者工具本地设置里的"不校验合法域名"只对工具生效,真机上微信会强制检查 request 合法域名。如果后端是http://192.168.x.x:8080,这个地址既不是 HTTPS,也不是合法域名,必然被拦截。
解决:毕设答辩演示前,把所有接口地址改成你为这个项目注册的 HTTPS 域名,并在小程序后台"开发管理 -> 开发设置 -> 服务器域名"里加入 request 合法域名。没有备案域名的同学,可以临时使用内网穿透工具把本地 SSM 服务映射成一个 HTTPS 地址,然后把那个地址填进 BASE_URL。用这种方法演示时,注意穿透工具的免费域名经常会变,答辩前一天一定要重新确认域名还活着。如果只在学校机房演示,用开发者工具 + 模拟器就够了,没必要冒这个风险。
5.5 提交订单出现重复数据:前端重复点击与后端幂等缺失
现象:用户快速点击"提交订单"两次,数据库里出现两条一模一样的订单,购物车商品被扣掉两份。
原因:小程序按钮没有做防重复点击处理,网络慢时第二次点击照样发送请求。后端也没有幂等校验,订单表只靠自增主键,同一次请求的重放无法识别。
解决:先从最前端加一道保险:
// pages/order/confirm.js const app = getApp(); Page({ data: { submitting: false }, submitOrder() { if (this.data.submitting) return; this.setData({ submitting: true }); app.request('/api/order/submit', 'POST', this.data.order) .then(() => wx.showToast({ title: '下单成功' })) .finally(() => this.setData({ submitting: false })); } });submitting标志位可以在发送期间禁用后续点击,但真要防止后端收到重复请求,最好在前端生成一个traceId,传入后端。后端在order表加一个trace_id字段并且建唯一索引,事务里先查该traceId是否已存在,存在直接返回旧订单号,不存在才插入。这个方案在前端兜底失效时仍然有效,也是企业级接口幂等设计的常见做法。
6. 答辩前验证技巧:给接口列一张测试清单,再重导一次数据库
6.1 用接口测试清单过一遍核心流程
答辩前不要只点按钮,列一张接口测试清单对着过,能暴露"演示时才发现"的问题。比如获取分类列表、按分类拉菜品、加入购物车、修改购物车数量、提交订单、查询我的订单、取消订单,每项都记录请求路径、入参和预期结果。我用 Postman 或 Apifox 测一遍后端,再从小程序里走一遍同样流程,两边结果一致说明链路正常。这个方法最值钱的地方是:让答辩现场的每一步操作都有预期,而不是随机演示。
6.2 我答辩前的习惯:从空库开始重新跑一遍
我习惯在答辩前一天把数据库整个 drop 掉,重新导入初始化 SQL,清掉所有测试订单,然后从后端启动到小程序下单全流程重跑一遍。这样做会暴露很多"只有重启才能发现"的隐性问题,比如数据库连接缓存、订单号自增偏移、图片路径写死成了旧地址。顺带把开题报告里的系统架构图对应到代码位置,老师问"你的 Service 层在哪里",你就能直接指着service/impl目录回答。这个习惯救过我很多次,答辩现场的演示环境永远不会比本地干净,提前用空库跑通才算真的准备好。希望帮到你。
本文还有配套的精品资源,点击获取