ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

微信点餐小程序毕设:SSM+MySQL订单表设计与避坑指南

2026/10/6 2:56:28 拓冰建站 浏览量
微信点餐小程序毕设:SSM+MySQL订单表设计与避坑指南 简介一套完整的微信点餐小程序毕业设计资料包基于微信小程序与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 事务订单提交为什么不能少 TransactionalSSM 是 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 namespacecom.example.mapper.OrderMapper resultMap idOrderMap typecom.example.entity.Order id columnorder_id propertyorderId/ result columnuser_id propertyuserId/ result columntotal_amount propertytotalAmount/ result columnorder_status propertyorderStatus/ /resultMap select idselectByUserId resultMapOrderMap 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_urlcategory菜品分类category_id, name, sortdish菜品基础信息dish_id, category_id, name, price, image_url, statuscart购物车记录cart_id, user_id, dish_id, quantityorder订单主表order_id, user_id, total_amount, order_status, create_timeorder_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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE category ( category_id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(32) NOT NULL, sort INT DEFAULT 0 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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下架 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; 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 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;注意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.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/wx_order_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456com.mysql.cj.jdbc.Driver是 8.x 之后的驱动类旧版写的是com.mysql.jdbc.Driver。如果驱动类不匹配启动时会出现ClassNotFoundException。serverTimezoneAsia/Shanghai必须加否则 MySQL 8 会报时区错误。allowPublicKeyRetrievaltrue是为了解决连接时出现Public Key Retrieval is not allowed的问题这在 MySQL 8 默认加密插件下很容易触发。useSSLfalse代表本地开发不启用 SSL 连接减轻握手开销。如果你的源码里没有 properties 文件而是直接在 Spring XML 里写的property namejdbcUrl value.../也正常。毕业设计里不必强行拆成多环境但至少不要把数据库密码硬编码在 Java 类里。我一般把jdbc.properties放在src/main/resources下在 Spring 配置文件里用context:property-placeholder locationclasspath: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 或 9JDK 版本要匹配否则启动时可能报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-setutf8mb4 wx_order_db wx_order_db.sql--default-character-setutf8mb4这个参数在 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 groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency8.0.33是稳定版本具体以你项目现有依赖为准。改完依赖后重新mvn clean package重启 Tomcat。如果密码正确仍然报认证失败检查用户插件类型也可以在 MySQL 里执行ALTER USER rootlocalhost 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 namemapUnderscoreToCamelCase valuetrue/ /settings /configuration如果开启后端和程序都正常就保持这个配置。如果项目里用的是旧式resultMap则确认每个result column的 column 是否是数据库真实列名。还要检查 Mapper 接口与 XML 的目录是否一致接口在com.example.mapper.OrderMapperXML 就必须放在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目录回答。这个习惯救过我很多次答辩现场的演示环境永远不会比本地干净提前用空库跑通才算真的准备好。希望帮到你。本文还有配套的精品资源点击获取