每年到这个时候总要接到一堆学弟学妹的求助消息毕业设计选什么题、Spring Boot 和 Vue 前后端分离到底怎么联调、数据库表怎么设计才不会被答辩老师挑毛病、论文里“系统设计”那一章到底写点什么才像真的做过。这套基于 SpringbootVue 框架的网上订餐系统就是我从选题到答辩一路整理出来的完整实战项目你把源码跑起来之后再拿着论文一页页比对会发现整个流程特别顺。这套系统适合三类人一是准备做 Java 毕设但还没定题目的同学网上订餐系统的业务场景贴近日常生活理解成本低功能又能做得很完整二是想从 SSM 老项目跳出来、学一学前后端分离开发方式的初学者Spring Boot 作为后端框架在简历上的出镜率相当高前端配合 Vue 基本是中小型项目的标配三是时间紧张的上班族需要一套能快速跑通、讲解逻辑清楚、能改能扩展的源码做二次开发。它能帮你解决的问题也很直接一套前后端分离、带完整权限体系、有支付模拟流程、界面能看的全栈项目外加一篇能顺藤摸瓜讲清楚的毕业论文。我先说结论这套项目不是那种为了凑字数堆功能的“教学玩具”而是照着真实外卖平台上最核心的链路做的用户选菜、加购物车、下单、支付商家接单、发货管理员管用户管数据整条链路跑得通代码也不是一坨乱麻。接下来说说项目里的设计思路、核心代码、踩坑记录以及论文和答辩的准备细节你照着复现节省的不止一个星期。1. 项目概览与技术选型1.1 核心需求解析这系统到底在做什么网上订餐系统的业务逻辑其实不复杂核心是三个角色各自完成自己的工作流。普通用户在平台上浏览菜品、按分类筛选、把菜品加入购物车、下订单、模拟支付、然后等待商家处理。商家登录后台可以维护自己的菜品信息、上下架菜品、处理用户下单进来的订单。管理员则负责平台层的管理比如管理所有注册用户、管理商家的入驻和审核、查看整站的订单数据。这三个角色不是简单在同一套页面上切来切去而是有明确的权限边界。普通用户在页面上看到的是完整的点餐体验商家看到的是店铺、菜品、订单这些管理界面管理员看到的又是另一套后台。权限控制这块如果你不做答辩老师一定会问所以项目里用了 JWT 做登录令牌再配合前端路由守卫和后端拦截器做双重控制具体的实现我放到后面单独讲。做毕设的时候最容易犯的一个毛病就是漫天铺功能一会儿做个优惠券一会儿做个会员积分最后每个功能都残缺不全。这套系统的思路是“纵向打通横向选做”。纵向的主链路一定是完整的浏览、购物车、下单、支付、发货、收货、评价。横向的附加功能如果你有精力再去补优惠券、报表统计这些但不要让附加功能冲淡了主链路的演示效果。1.2 为什么选 Spring Boot Vue 这套组合这个组合在这几年已经是 Java 毕设的绝对主流了原因很实在。后端用 Spring Boot最直接的好处是免去了传统 SSM 项目里一大波繁琐的 XML 配置你不用为了一个applicationContext.xml折腾半天Maven 依赖一引配置一写项目就能跑起来。Spring 的依赖注入、面向切面编程这些经典特性都在但使用门槛低了很多。而且 Spring Boot 自带的 Spring MVC 处理 RESTful 接口非常顺手配合spring-boot-starter-validation做参数校验、spring-boot-starter-security或者自己写拦截器做权限控制都是很成熟的路子。前端用 Vue最核心的优势是组件化开发。一个页面拆成几个组件后开发思路会清晰很多而且 Vue 的双向绑定特性让你在处理表单、购物车这种频繁交互的场景时不用像 jQuery 时代那样手动操作 DOM。Element UI 组件库又是专门为 Vue 设计的表格、表单、弹窗、分页这些管理系统里最常见的界面元素都能直接拿来用改改字段就能上视觉效果远超手写 HTML 页面。另外前后端分离这个架构本身就值得你在论文里大写一笔。前端项目和后端项目可以独立开发、独立部署前端通过 HTTP 接口请求后端数据。这种架构是当前企业里做项目的主流模式你把它写在论文里答辩的时候就有内容可以聊而不是只说“我写了个增删改查的页面”。1.3 技术栈清单与环境基础整套项目跑起来需要准备的东西我列一下。后端主要用 Spring Boot 2.7.xJava 8 环境完全可以跑Java 11 也没问题。持久层框架我推荐用 MyBatis-Plus而不是纯 MyBatis原因是它内置了常见的单表 CRUD 方法可以直接省掉大部分 Mapper XML 里的基础 SQL你只需要把多表联查和复杂统计自己写就行。数据库用 MySQL 8.x如果机器上装的是 5.7 也能跑但要注意驱动版本和连接参数的差异。前端用 Vue 2.6 加 Element UI 2.x。Vue 3 也出了很久了有同学纠结要不要直接用 Vue 3我的建议是如果你的毕业设计时间比较紧Vue 2.6 的生态最稳网上能查到的坑基本都被踩过一遍了找资料非常容易。Vue 3 的 Composition API 当然更现代但如果你对 Vue 不熟学习成本会高一些而且 Element Plus 虽然兼容 Vue 3总会遇到一些组件版本的坑考虑到论文和答辩的核心还是系统功能的完整性选更稳的方案性价比更高。技术维度选用方案选型理由前端框架Vue 2.6 Element UI 2.x生态成熟、组件丰富、资料多前端构建工具Vue CLI 4.x / 5.x脚手架稳定配置简单自带代理能力后端框架Spring Boot 2.7.x配置简化、自动装配、社区资料丰富持久层MyBatis-Plus 3.5.x内置单表CRUD、分页插件、代码生成数据库MySQL 8.x主流关系型数据库安装维护简单鉴权方案JWT 自定义拦截器前后端分离场景下无状态认证实现清晰项目构建MavenJava 项目标准构建工具依赖管理方便环境方面你还需要一个 IDEA后端开发基本都用它前端开发可以用 IDEA 写也可以用 VSCode。数据库管理工具我用的是 Navicat免费的 DBeaver 也能用选顺手的就可以。这套组合的兼容性很好跟着后面章节的步骤走基本不会出现环境问题。2. 从需求到数据库设计表结构才是系统的地基2.1 需求分析先画流程图再写代码很多同学拿到题目就急着建项目、写代码结果写到一半发现这里缺个字段那里少张表。我个人的习惯是动手之前先在纸上把系统的核心流程画出来。就这个订餐系统而言核心流程是用户注册登录 - 浏览菜品加购物车 - 提交订单 - 模拟支付 - 商家接单 - 商家发货 - 用户确认收货 - 用户评价。从这个流程里你就能推导出最少需要哪些数据表。首先是用户表这个不用多说系统里所有角色都在这张表里靠一个role字段区分是普通用户、商家还是管理员。然后是菜品分类表和菜品表菜品表里至少要包括菜品的名称、图片、价格、分类 ID、销量、状态状态用来做上下架。用户下了订单之后需要订单主表和订单明细表订单主表记录一单的总体信息比如订单编号、下单用户、订单总金额、订单状态、下单时间订单明细表记录这个订单里具体买了哪几道菜、每道菜多少钱、数量多少。购物车表看情况如果允许用户把菜品先放进购物车再统一结算就需要单独建一张表否则也可以在 Redis 里用哈希结构维护但毕设项目里建一张表更直观论文里也好画 ER 图。最后是评论表用户在确认收货之后可以对菜品进行评价这对完善系统功能闭环很重要答辩的时候你可以说这是“提升用户互动性和平台数据沉淀的功能”这些话术在论文里很加分。2.2 数据库设计的核心原则表别贪多关系要对设计表的时候有一个非常重要的原则字段宁缺毋滥但关系必须对。一张表里的字段如果是后面用不上的就不要为了显得“专业”硬加进去。你的表结构是要在论文里放 ER 图的也是答辩老师重点看的东西每张表的每个字段都应该能在系统功能里找到出处不然老师问到这个字段是干嘛的你说半天也解释不清楚反而扣分。外键这个事我也单独说一下。实际企业项目里互联网场景下大部分都不建物理外键因为外键会影响插入和删除的性能而且分布式环境下外键约束根本没有办法生效。但是在毕设论文里你需要体现出表与表之间的逻辑关联。我的做法是在实体设计上体现外键关联关系比如订单表里有user_id和dish_id但数据库层面不设置物理外键约束而是在论文的数据库设计章节里明确说明逻辑外键和物理外键的区别以及为什么采用逻辑外键。这一点做好了答辩的时候反而是一个亮点。订单表和订单明细表的拆分是很多初学者容易忽略的地方。一个订单里可能包含多个菜品如果你把菜品直接作为一个字段用一个逗号分隔的字符串存在订单表里后面要统计销量、要做订单汇总的时候就会非常痛苦。拆成两张表之后订单主表管整体状态订单明细表管具体菜品通过order_id关联这是很标准的关系型数据库设计也是你论文里“数据库规范化”理论的最好实践案例。2.3 核心建表 SQL 参考我把项目中最重要的几张表的结构和关键字段说明列出来你可以直接拿去做参考。用户表的设计上因为角色不复杂就直接用role字段区分不需要单独建角色表和权限表。如果你想把权限做得更复杂那是另一个项目的事了对于这个订餐系统角色字段足够用。CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(50) NOT NULL COMMENT 用户名, password varchar(255) NOT NULL COMMENT 密码BCrypt加密, nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL COMMENT 手机号, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, role tinyint NOT NULL DEFAULT 0 COMMENT 角色0用户 1商家 2管理员, status tinyint NOT NULL DEFAULT 1 COMMENT 状态1正常 0禁用, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;菜品表的设计要注意分类 ID 这个字段它关联菜品分类表查询菜品列表的时候通常要带着分类名称一起返回。我建议在返回给前端的数据结构里冗余一个分类名称字段这样前端展示的时候不用再去查一次分类表减少一次请求。CREATE TABLE dishes ( id bigint NOT NULL AUTO_INCREMENT COMMENT 菜品ID, dish_name varchar(100) NOT NULL COMMENT 菜品名称, dish_image varchar(255) DEFAULT NULL COMMENT 菜品图片URL, price decimal(10,2) NOT NULL COMMENT 价格, category_id bigint DEFAULT NULL COMMENT 分类ID, description varchar(500) DEFAULT NULL COMMENT 描述, sales int DEFAULT 0 COMMENT 销量, status tinyint NOT NULL DEFAULT 1 COMMENT 状态1上架 0下架, create_time datetime DEFAULT NULL COMMENT 创建时间, update_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT菜品表;订单主表和订单明细表是一对多的关系订单号建议自己生成不要直接用数据库自增 ID因为自增 ID 暴露了订单总量而且不好看。可以用时间戳加随机数的方式生成一个唯一的订单号论文里你还可以给这个订单号生成规则写一小段程序逻辑这个细节同样能成为答辩时的加分项。CREATE TABLE orders ( id bigint NOT NULL AUTO_INCREMENT COMMENT 订单ID, order_no varchar(32) NOT NULL COMMENT 订单编号, user_id bigint NOT NULL COMMENT 下单用户ID, total_amount decimal(10,2) NOT NULL COMMENT 订单总金额, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待支付 1待接单 2待发货 3配送中 4已完成 5已取消, remark varchar(255) DEFAULT NULL COMMENT 备注, create_time datetime DEFAULT NULL COMMENT 下单时间, update_time datetime DEFAULT NULL COMMENT 更新时间, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; CREATE TABLE order_details ( id bigint NOT NULL AUTO_INCREMENT COMMENT 明细ID, order_id bigint NOT NULL COMMENT 订单ID, dish_id bigint NOT NULL COMMENT 菜品ID, dish_name varchar(100) NOT NULL COMMENT 菜品名称快照, dish_image varchar(255) DEFAULT NULL COMMENT 菜品图片快照, price decimal(10,2) NOT NULL COMMENT 下单时价格快照, quantity int NOT NULL COMMENT 数量, subtotal decimal(10,2) NOT NULL COMMENT 小计金额, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;2.4 表设计中的实际经验建表的时候有几个细节都是我自己踩过坑之后总结出来的。第一个是时间字段的默认值直接在数据库层面用DEFAULT CURRENT_TIMESTAMP会省很多事情如果实体里有时间字段插入的时候不用特意去赋值数据库会自动填上。不过要注意的是如果项目部署的服务器和数据库不在一个时区可能出现时间错乱所以连接参数里尽量加上serverTimezoneAsia/Shanghai这个坑下面会单独提到。第二个是金额字段一定用decimal不要用float或者double。浮点数在计算机里存储有精度问题比如 0.1 加 0.2 可能等于 0.30000000000000004钱如果算错了一个订单差一分钱都是大事。用decimal(10,2)这样的定点数存储就不会有这个问题。这个知识点在论文的数据库设计章节里也可以提显得你考虑到了细节。第三个是共用逻辑外键的命名规范。我的习惯是统一叫xxx_id比如category_id、user_id、order_id这样在写 SQL 联查的时候一眼就能看出关联关系也非常方便用 MyBatis-Plus 的TableField注解做映射。表名和字段名统一用小写下划线格式Java 实体类用驼峰命名MyBatis-Plus 里开启map-underscore-to-camel-case: true之后就能自动映射。3. 后端核心设计与关键实现3.1 项目结构包名按功能模块拆别按技术类型堆后端项目的包结构我推荐按“业务模块 通用能力”的思路来拆。比如controller、service、mapper、entity、dto、vo、common、config、utils、exception这些包。其中entity放数据库实体类dto放接收前端参数的传输对象vo放返回给前端的视图对象common放统一返回结果类、异常类这类通用组件。很多同学喜欢把所有的类都堆在一个包下面项目一旦大起来就非常乱改一个功能要翻半天目录。前后端分离项目中接口返回的数据结构一定要统一。我在项目里定义了一个Result类所有的接口不管成功失败都返回这个结构里面包含code、message、data三个字段。前端拿到响应之后先看code是不是 200再决定后续逻辑。这个习惯非常重要它让你前端调用的代码逻辑变得非常简单而且全局异常处理器可以直接把异常也包装成这个格式返回前端连错误处理都可以统一做。登录模块用 JWT 实现后端在用户登录成功后生成一个 token 返回给前端。这个 token 里面包含了用户 ID 和角色信息前端在每次请求的请求头Authorization字段里带上这个 token。后端写一个拦截器拦截所有需要登录的接口从 token 里解析出用户信息如果 token 无效或过期就直接返回 401。密码使用 BCrypt 加密存储不存明文。在论文里你可以这么写系统采用无状态 JWT 认证机制配合 Spring 拦截器完成接口访问鉴权使用 BCrypt 算法对用户密码进行不可逆加密保障账号安全。3.2 下单接口的实现思路与事务控制下单是整个系统最核心的业务逻辑也是答辩老师大概率会问的一个功能。下单的过程不是往订单表插入一条数据那么简单它涉及多个步骤校验菜品是否上架、计算订单总金额、扣减库存或更新销量、创建订单主记录、批量创建订单明细、清空购物车。这一连串操作必须保证要么全部成功要么全部失败否则就会出现订单创建了但明细没插入或者用户购物车没清空但订单已经生成这种脏数据。所以下单方法上必须加Transactional事务注解。Spring 的事务管理会把方法内所有数据库操作绑定在同一个数据库事务里任何一步抛出异常都会整体回滚。这个知识点几乎是 Java 面试里必问的所以你不仅要会用还要能说清楚Transactional的propagation属性、rollbackFor属性这些细节。比如默认情况下它只在RuntimeException时回滚如果你在业务方法里捕获了异常而没有重新抛出来事务是不会回滚的这叫做“事务失效”。我把这个作为避坑点写在后面一节。订单状态的流转要设计清楚。我在订单表里定义了几个状态待支付、待接单、待发货、配送中、已完成、已取消。用户提交订单后是待支付模拟支付完成后变成待接单商家接单后变成待发货点击发货后变成配送中用户确认收货后变成已完成。这个状态机看起来简单但你要保证每个状态之间的流转只能走规定的路径不能让用户直接把一个待支付的订单改成已完成这个控制既要在前端按钮上体现也要在后端接口做校验。3.3 分页查询与多条件筛选的实现管理端和后端的列表页面几乎全是分页查询。MyBatis-Plus 提供了一个现成的分页插件只要在配置类里注册一个PaginationInnerInterceptor然后在 Mapper 层调用selectPage方法分页就自动完成了。前端把pageNum和pageSize作为请求参数传过来后端在翻页查询时同时返回总记录数和当前页数据。有一个细节要注意分页插件默认是物理分页也就是 SQL 层做了 LIMIT不会一次性把全表的数据都查出来这点在论文里也值得提一句体现你不是只会调接口。多条件筛选也不难比如商家要查询某个状态下的订单、按订单号模糊搜索、按时间范围筛选这些都是通过构造QueryWrapper或LambdaQueryWrapper来实现的。用 Lambda 方式的好处是字段名用的是 Java 方法引用编译期就能发现字段名拼写错误不会等运行时候爆出 SQL 异常。这个细节你在代码里体现出来答辩的时候说一句“我用的是 LambdaWrapper可以避免 SQL 注入风险”老师就知道你真的写过。3.4 图片上传与文件存储的处理菜品需要上传图片项目里需要一个统一的文件上传接口。我的做法是后端接收MultipartFile把文件保存到本地的upload目录然后把文件的访问 URL 返回给前端。需要注意保存文件的路径和访问路径的映射关系。Spring Boot 里可以通过自定义资源映射把/upload/**路径映射到本地磁盘目录这样才能在浏览器直接通过 URL 访问到图片。如果你不做这个映射图片上传成功了但前端显示不出来这个坑特别常见。文件上传还有其他几个细节限制上传文件的大小在application.yml里配置spring.servlet.multipart.max-file-size给保存的文件名加一个 UUID 前缀避免文件名冲突和脏字符问题图片访问接口最好做一个简单的校验避免任意文件上传漏洞。虽然这是毕设项目这些安全细节能加都加上论文里的安全设计章节也有内容可以写。4. 前端 Vue 项目的搭建与联调4.1 前端项目初始化和页面结构前端项目用 Vue CLI 初始化vue create命令跑完后你会得到一个标准的 Vue 2 项目。我建议你先把项目里几个目录弄清楚src/api放所有对接后端接口的请求方法src/router放路由配置src/store放 Vuex 全局状态管理src/views放页面组件src/components放公共组件。很多初学者的代码里接口请求、业务逻辑、页面渲染全挤在一个文件里看着都头疼。页面结构是经典的后台管理系统布局左侧菜单栏、顶部用户信息栏、右侧主内容区。菜单按角色动态渲染普通用户登录进去看到的是首页、菜品分类、购物车、我的订单商家登录进去看到的则是菜品管理、订单。管理端可以设置不同页面按需展示对应的权限信息。4.2 Axios 封装与前端拦截器前端调用后端接口必须封装 Axios不要每个页面里直接写 axios.post。我在项目里建了一个request.js统一配置了请求超时时间、基础 URL、请求拦截器和响应拦截器。请求拦截器里做的事情很简单从 localStorage 里取出 token放到请求头里。响应拦截器里做的事情稍微多一点如果返回的 code 是 401说明登录过期跳转回登录页并清空本地用户信息如果返回的业务 code 不是 200直接弹出一个错误提示。路由守卫也是必须做的。用户没登录的时候不能访问购物车、订单这些需要身份的页面。Vue Router 的beforeEach路由守卫里读取 localStorage 里的用户信息没有就跳转到登录页。这里需要注意的是只有前端做校验是不够的后端接口也需要做权限拦截否则别人直接拿接口工具调用你的后端接口就可以绕过前端限制。前后端双重校验这个话题在论文系统实现章节里可以好好写一写。// request.js 核心拦截器逻辑 service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config }) service.interceptors.response.use(response { const res response.data if (res.code 401) { localStorage.clear() router.push(/login) return Promise.reject(new Error(登录过期)) } return res })4.3 购物车与订单确认页的实现细节购物车状态我用 Vuex 管理这样不同页面之间共享购物车数据不需要在组件之间事件传值。用户点“加入购物车”的时候调用 Vuex 的 action把菜品数据加入购物车数组。同一个菜品如果已经在购物车里就增加数量而不是新增一条记录这个逻辑要在 action 里做判断。订单确认页做三件事展示用户从购物车里选中的菜品列表和总价让用户填写收货地址和备注点击提交订单后跳转到模拟支付页。提交订单时前端把购物车的数据转换成订单参数传给后端后端返回订单 ID 和订单号然后前端跳转到支付确认页面。支付按钮其实是模拟的点击之后调用后端“支付接口”后端把订单状态改成待接单。这个模拟流程足够应对答辩你也可以在论文里写“系统预留接入第三方支付接口的能力”。4.4 前后端联调跨域与代理配置前后端分离开发环境最麻烦的就是跨域问题。你的前端地址是localhost:8080Vue CLI 启动的后端接口地址是localhost:9090Spring Boot 启动的浏览器会直接拒绝前端请求后端的接口。解决方案有两种一种是在后端加CrossOrigin注解或者全局 CORS 配置类另一种是在前端利用 Vue CLI 的 devServer 代理把/api开头的请求转发到后端接口地址。我推荐用第二种方案配置一次之后就不用管了而且上线部署的时候前端通过 Nginx 反向代理同样可以把/api转发到后端服务开发环境和生产环境的配置方式保持一致。配置文件就在vue.config.js里关键配置如下module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:9090, changeOrigin: true } } } }这里要注意路径前缀要前后端对齐。我在后端所有接口的访问路径都加上了/api前缀比如/api/user/login、/api/orders/list。前端请求的地址也是/api/...代理监听/api前缀的请求转发到后端。前端在request.js里配置的baseURL必须是空字符串因为代理已经帮它转发了不要再写一遍后端地址否则等于是在 8080 上另一个位置直接请求 9090还是会有跨域问题。5. 论文写作思路与答辩准备5.1 论文大纲结构怎么搭才能拿高分很多同学代码写得挺不错论文里却流水账一样从头文件介绍写到配置文件说明老师看一眼就不想往下翻。一篇合格的毕设论文结构应该是完整的软件开发文档体系。摘要部分写清楚系统的研究背景和意义、使用的技术栈、系统实现了哪些功能200 到 300 字要精炼。目录后面是需求分析章节把系统的目标和用户人群写清楚使用用例图描述功能需求。然后是系统设计章节这部分是重点要画出系统总体架构图、功能模块图、数据库 ER 图把每张表的设计理由写清楚。系统实现章节配合项目里的实际运行截图一步一步讲解核心功能的实现过程。测试章节放上测试用例表和测试结果截图黑盒测试为主。这些章节中系统设计章节是拉开分差的地方。架构图用前后端分离的经典结构浏览器端 Vue 应用通过网络层向后端 Spring Boot 服务发送请求后端通过 Mapper 层操作数据库。功能模块图要按角色划分出功能树。数据库 ER 图用工具画然后放到论文里。这些图你要是自己不会画推荐用 ProcessOn 这类工具画完之后再根据自己系统的实际情况调整文字不要直接使用网上的模板图名字都对不上答辩一眼就会被看穿。论文里还有一个容易被忽视的地方是核心代码的贴出。不要从 controller 到 mapper 全贴每段只贴关键代码比如下单事务那段、JWT 拦截器那段、前端 Axios 封装那段。正确的做法是每段代码贴出来后紧跟一段文字解释这段代码解决了什么问题、关键方法的作用是什么这样的论文才像是自己写出来的也能帮助你在答辩前快速回顾系统逻辑。5.2 答辩演示的标准操作流程答辩现场演示系统的流程要提前跑顺我给出的建议是走一条主线用户注册或登录 - 浏览菜品 - 加入购物车 - 下单 - 模拟支付 - 切换商家角色登录 - 接单发货 - 切换管理员角色查看订单。这条主线的每一步都对应一个核心功能模块演示过程中你顺手就能把每个模块的功能给老师讲一遍。演示用的是自己的电脑还是机房电脑这个要提前确认。如果是机房电脑环境没有配置好你是没有时间重新搭建的。有一个稳妥的做法就是把项目的部署文档带上里面写清楚所有环境变量、数据库初始脚本、启动命令。如果现场要求必须在他那边跑你照着文档走一遍还来得及。另外无论是演示还是答辩过程都要提前把项目打包成可运行的产物。后端打成 jar 包前端用npm run build打成静态文件然后用 Nginx 把两者整合起来这样在任何一台装有 JDK 和 MySQL 的电脑上都能快速启动和访问相对用 IDEA 启动要稳妥得多。5.3 答辩老师最常问的问题怎么答我收集了几个答辩时高频出现的问题把要点写在这里你需要会答。第一个问题是“你这个系统如何解决并发下单问题”很多人会说加锁但你要能说出具体的方案使用数据库事务配合乐观锁或悲观锁最关键的还在于数据库表设计上使用唯一索引避免同一个用户重复提交同一订单。这个问题重点考查的是你对业务场景的理解答的时候要有层次先描述问题再给方案。第二个问题是“数据库为什么不用外键”这个问题前面提到过回答要点是外键维护成本高操作受约束影响业务操作效率和系统扩展系统中用应用层逻辑维护数据完整性和一致性。回答时再加一句“对于互联网高并发场景分库分表后外键无法使用”就能体现出深度。第三个问题是“JWT 和 Session 有什么区别为什么选 JWT”核心答案是 Session 是服务端状态存储JWT 是无状态认证适合前后端分离和分布式部署。能够把这个对比讲清楚说明你已经理解了架构层面的东西。第四个问题是“你的项目中哪些地方体现了面向对象设计思想”这个问题就要回到你的代码里找答案比如抽取公共的BaseEntity使用接口定义 Service 层行为使用策略模式处理订单状态流转等等。开题阶段就要把这些问题准备好答辩时才不会慌张。6. 常见问题排查与避坑指南6.1 启动报错的高频问题与处理方案项目拿到手后第一步就是跑起来但很多问题恰恰出在启动阶段。我把最常见的三个列一下。第一个是 MySQL 连接失败典型的报错是Access denied for user rootlocalhost或者Communications link failure。前者是用户名或密码不对检查后端application.yml里的spring.datasource.password是否真的和你本机 MySQL 一致。后者多半是端口或地址不对MySQL 默认端口 3306检查 URL 里的地址写的是不是localhost:3306。还有一种隐蔽情况MySQL 如果是以服务方式安装的可能本机防火墙拦了 3306 端口暂时关闭防火墙再测试。注意数据库连接 URL 里建议加上useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8这几个参数分别避免 SSL 握手报错、处理时区问题和避免中文乱码。第二个是前端依赖安装慢或安装失败。npm install的时候如果你使用默认源网络不好就可能挂掉。直接用淘宝镜像源替代在项目根目录建.npmrc文件内容写registryhttps://registry.npmmirror.com然后重新npm install。装完依赖启动时如果提示端口被占用在vue.config.js里改 port或者找到占用端口的进程把它结束掉。第三个是后端启动时提示端口被占用。Spring Boot 默认 8080 端口可能被前端服务或者其他程序占用了在后端application.yml里把server.port改成 9090 或者 8081 就行。改完之后要注意和前端代理配置保持一致这个同步很容易被忘记。6.2 线上部署时需要处理好的细节部署这块我踩过不少坑把核心要点整理出来。生产环境不要用 IDEA 启动了后端项目用 Maven 打包成 jar 包在项目根目录执行mvn clean package -DskipTests生成的 jar 在target目录下然后通过java -jar启动。第一次打包的时候Maven 可能会下载大量依赖到本地仓库耐心等一会儿如果网络不稳定建议配阿里云的 Maven 镜像在settings.xml里加上镜像地址。前端打包成静态文件之后要用一个 Web 服务器才能跑起来。Nginx 是最合适的。部署上线的核心配置有两段一段是把静态文件目录指向前端dist目录一段是做反向代理把/api的请求转发给后端服务。把 Nginx 配置写进部署文档到哪儿都能快速部署。后端打包的时候有一个特别常见的坑Spring Boot 自带的可执行 jar 和依赖 jar 是嵌套结构如果在部署时提示找不到主类或缺失依赖检查pom.xml里是否配置了spring-boot-maven-plugin。使用内置 Tomcat 时provided范围的依赖不能打进 jar 包如果用到外部 Tomcat 部署才需要把scope改成provided这两者的差别不大但踩坑的人不少。6.3 容易埋下隐患的隐蔽问题盘点有几个问题不是启动时暴露的但开发到后期会突然冒出来提前知道就能省很多时间。第一个是 LocalDateTime 序列化成 JSON 时显示为数组格式的问题。如果你直接返回 LocalDateTime 实体给前端前端拿到的一串数字而不是可读的时间。解决办法是在实体时间字段上加上JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)注解或者全局配置 Jackson 序列化格式。第二个是 MyBatis-Plus 更新操作时字段没更新。使用updateById方法时MyBatis-Plus 默认只更新非 null 字段如果你想把某个字段置为 null直接传 null 是更新不掉的需要额外用UpdateWrapper的set方法手动指定。这个坑很隐蔽尤其在“取消订单时清空备注”这种场景上会遇到。第三个是事务不生效的问题。Transactional要在 Spring 容器管理的 bean 上才生效如果你自己new了一个 Service 对象那事务注解就无效了。另外方法内部调用同类中的另一个方法时事务同样不会生效因为事务是通过代理对象拦截的同类内部调用不会经过代理。解决办法是不要同类方法直接调用或者把事务方法提取到另一个 Service 类中。这个知识点在面试题里也经常出现理解了它你就能避免 80% 的事务失效问题。第四个是数据库表字段如果使用order、status、desc这类 MySQL 关键字会直接报 SQL 语法错误。所以建表时字段名尽量避免和 SQL 关键字冲突如果已经建了查询时必须用反引号包裹。注意这里的反引号是 MySQL 环境里使用的标识符号不同类型的数据库其转义符不同不能混在一起用。最稳妥的做法就是建表之前先查一遍字段名是不是关键字。6.4 答辩前一周的检查清单最后分享一个我在答辩前一周必做的检查清单。第一重新跑一遍整个系统所有核心功能从注册登录到下单支付到发货确认每一步都截图保存这些截图就是论文和 PPT 里的素材。第二把数据库初始化脚本导出确保任何一台电脑上执行这份脚本都能得到一样的初始数据。第三模拟一遍老师可能会问的问题把你的项目讲解控制在五分钟内凡是讲到的功能必须提前验证能跑通。第四准备一个 travel note 文本文件把关键配置、端口号、账号密码、启动顺序记录下来放在项目根目录里无论是你自己上机还是答辩现场都要用。这套系统的后端和前端我都把源码整理在了一起的压缩包里论文也有对应的模板你拿到的不是一个简单的代码包而是可以直接上手、可以改、可以讲的完整项目。最后再给一个小建议拿到项目之后不要为了“看起来很忙”去大改代码先花半天时间把项目跑通再花一天时间把所有代码从头到尾读一遍搞清楚每个请求串联起来的完整流程这个步骤很重要因为你只有自己理解了答辩的时候才能从容地回答老师的提问。之后的扩展方向也有很多比如增加菜品维度的销量统计图表、引入 Redis 缓存热点菜品、对接真正的微信支付这些都能成为你在论文里写的“后期展望”也让整个项目有了再往上走的空间。