基于Spring Boot的高校食堂点餐系统实战:从需求到部署全解析 每年一到毕设季我的私信就会被同一类问题轰炸学长基于Spring Boot的高校食堂点餐系统能不能做好不好通过有没有现成源码问的人多了我干脆把之前完整做完并成功答辩的项目经验整理成这篇长文。这篇文章不是把代码贴一遍就完事而是尽量还原我从需求梳理、数据库设计、核心代码编写到服务器部署、文档包装的完整过程顺便把那些只在实战里才会踩到的坑一起说清楚。这题目很适合Java方向的学生因为它既有互联网项目该有的业务复杂度又不至于像电商秒杀系统那样做到一半心态崩掉。高校食堂点餐系统本质上就是一个带库存、订单、支付的餐饮交易系统放在校园场景下用户角色清晰、业务流程固定非常适合用来展示Spring Boot、MySQL、Redis、MyBatis-Plus这些主流技术。无论你是打算自己从零写还是手里已经有一份源码准备二次开发这篇文章都能帮你搞清楚系统到底应该包含什么、代码为什么要这么写、以及答辩时老师最可能追问哪些点。1. 为什么食堂点餐系统适合做Java毕设从需求到工作量的一次评估很多人选毕设题目只看“是不是热门”或者“网上有没有源码”却忽略了最重要的一件事题目能不能在三个月内做完、做完之后能不能讲清楚。食堂点餐系统恰恰在“工作量适中”和“业务完整”之间找到了很好的平衡点。它不是纯增删改查的管理系统也不是需要高并发支撑的大厂级应用而是恰好能让一个应届生把主流后端技术都串起来练一遍的题目。1.1 高校场景下的需求到底有哪些聊需求之前先明确一个核心逻辑食堂点餐的真实场景是高峰期排队太久、菜品信息不透明、堂食和打包混在一起。所以系统的核心价值不是“能点餐”这么简单而是把选餐、下单、支付、窗口取餐这个链路搬到线上同时让食堂管理者能看清什么菜卖得好、什么菜剩得多。按用户角色拆分系统的需求大致如下学生端浏览食堂和窗口、查看菜品及价格、加入购物车、提交订单、在线支付、查看订单状态待支付/制作中/待取餐/已完成/已取消、余额充值、历史订单和评价。食堂商家端管理自己窗口的菜品上架、下架、改价格、调库存、查看订单并标记制作状态、处理评价。平台管理端食堂和窗口管理、用户管理、订单查询和统计、菜品分类管理、公告管理。有些同学想把系统做成大而全加上骑手配送、优惠券、会员等级这里我要泼一盆冷水毕设的核心是“结构合理、功能闭环”不是功能多。一个能完整走通“用户下单-商家接单-支付成功-订单完成”闭环的项目比一个功能堆了十几个但每个都做得半吊子的项目更容易拿高分。配送这个需求在食堂场景下本来就不合理除非你是做校外外卖平台否则不要给自己加戏。1.2 技术覆盖面与难度平衡点Spring Boot MyBatis-Plus MySQL 是毕设铁三角这套组合的好处是资料多、社区活跃、碰到问题随便一搜就有答案。在此基础上我建议按自己的能力决定是否引入Redis和Vue。我这里给出的经验是如果时间充裕引入Redis做购物车缓存和订单编号生成再写一个简单的拦截器做登录校验技术亮点就已经够用了。如果时间紧张完全可以不用Redis用MySQL表存购物车数据登录校验用Filter或者Spring Boot拦截器实现一样能通过。最怕的是项目里引了一堆技术栈但说不清为什么用它们答辩时被老师一句“你为什么不用HashMap存”问得说不出话。难度上我把它分成三档难度技术方案适合人群低Spring Boot Thymeleaf模板 MySQL前端基础较弱、追求快速做完中Spring Boot Vue前后端分离 MySQL想展示前后端分离能力大部分人选这个高Spring Boot Vue Redis 微信小程序有一定编程经验想冲优秀毕设我自己的选择是中间偏高的方案Spring Boot 2.7 Vue 3 MySQL Redis但支付模块没有接真实的微信支付理由很简单个人开发者申请微信支付商户号需要营业执照大多数在校学生没有这个条件所以系统里用“模拟支付”走完流程同时在文档里写明真实支付场景下的接口替换方案。这一点在答辩时反而是加分项因为它说明你想过工程落地问题。1.3 三个月排期怎么安排把整个项目拆开看合理的时间分配大概是这样的第1-2周需求分析、建表、搭建前后端工程骨架。第3-4周完成用户登录注册、食堂窗口和菜品管理模块。第5-7周完成购物车、下单、支付、订单状态流转核心流程。第8-9周前端页面补全、接口联调、处理异常情况。第10周部署到云服务器整理测试用例和项目文档。第11-12周写论文、做PPT、准备答辩演示。这个排期有一个关键前提需求不要再中途膨胀。我见过太多人做着做着突然觉得“加个评论功能也不错”“搞个数据可视化图表吧”结果越加越多最后每个模块都是半成品。记住毕设的完成度和稳定性永远比功能数量重要。2. 技术选型与工程初始化Spring Boot版本、ORM和前端框架怎么定技术选型直接决定了后面两个月你过得舒不舒服。很多人上来就选最新版Spring Boot 3.x结果发现Java version要求17原来的针对JDK8的教程统统不适用连很多第三方starter都没跟上自己又不会看报错硬生生把时间耗在环境问题上。这里我先给出一套相对稳妥的组合。2.1 版本选型思路到底用Spring Boot 2.7还是3.x如果项目的主语言是Java 8那就老老实实用Spring Boot 2.7.x这是官方最后一个支持JDK8的稳定的主线版本网上生态资料最全MyBatis-Plus、Shiro、JWT等工具的全家桶版本都是对齐Spring Boot 2.x的。如果你的电脑已经装了JDK 17想体验Spring Boot 3.x也没问题但必须把MyBatis-Plus切换到3.5.3否则启动时会遇到兼容性报错。我自己选的组合如下组件版本备注JDK1.8稳定绝大多数云服务器默认Spring Boot2.7.12支持JDK8社区资料最多MyBatis-Plus3.5.3避免手写大量CRUD SQLMySQL5.7 或 8.0本地开发用8.0服务器可用5.7Redis2.x用spring-boot-starter-data-redisVue3.x配Element Plus组件库Maven3.8依赖管理这里专门说一句Spring Boot版本的事情。想理解Spring Boot自动装配原理需要对SpringBootApplication、EnableAutoConfiguration、META-INF/spring.factories或AutoConfiguration.imports这些机制有基本认知。答辩时老师很爱问“Spring Boot为什么能自动配置”如果你是照着一个模板工程改出来的建议把这段源码阅读一下启动类上三个注解分别干什么、自动配置类的条件注解ConditionalOnClass和ConditionalOnMissingBean是什么作用。能把这几点讲清楚基本上就能证明代码不完全是复制粘贴的。2.2 工程结构按业务模块拆分别按三层架构硬劈很多教程喜欢把项目分成controller、service、mapper、entity、config这种纯三层架构但在一个真实业务系统里这样分包会让文件越来越乱。比如订单相关的控制器、服务、实体散落在各个包里后期找一个类的精力比写它还多。更推荐的模块划分是垂直切片按业务域拆分com.canteen ├── common // 通用返回结果、异常处理、工具类 ├── config // WebMvc、Redis、Cors等配置 ├── controller // 接口层 ├── service // 业务层接口与实现 ├── mapper // MyBatis-Plus的Mapper层 ├── entity // 数据库实体类 ├── dto // 接收前端参数的传输对象 ├── vo // 返回给前端的数据视图对象 └── interceptor // 登录拦截器、权限拦截器前端用Vue的话工程上建议直接分成vue-admin管理端和vue-user用户端两个独立项目或者用同一个项目通过路由权限区分角色。千万别把管理端和用户端页面混在一起因为两个端的视觉风格、组件类型、接口权限都不同硬塞在一起会互相影响。2.3 Maven依赖管理中的常见坑Spring Boot项目用Maven管理依赖最难受的问题就是jar包冲突。启动项目报ClassNotFoundException或者NoSuchMethodError八成是同一个依赖被引入了不同版本。解决方案是用mvn dependency:tree查看依赖树排除掉冲突的那一个。另外不要所有依赖都用最新版比如lombok版本跟JDK版本不匹配时编译期报错能把你逼疯这时在pom里明确指定lombok版本就能解决。还有一点经常被人忽略热部署依赖spring-boot-devtools在打包时一定要设为optional或者直接用spring-boot-maven-plugin打包。我之前见过同学把devtools也打进了生产jar包结果服务器上动不动自动重启接口响应还越来越慢。2.4 统一返回结果与全局异常处理要提前做好这套系统前后端联调时需要大量接口如果每个接口返回的JSON格式都不一样有的返回data有的直接返回字符串前端写起来就特别痛苦所以工程的第一个任务就是把统一响应体写好。定义一个Result 类包含code、message、data三个字段所有接口都返回这个结构。配合RestControllerAdvice全局异常处理业务异常、参数校验异常、兜底异常分别返回不同的code和message。这个机制虽然写起来只要半小时但它让全项目的接口风格保持一致是一个特别能体现工程意识的设计。在部署到服务器后全局异常处理也帮了大忙——前端拿到code不等于200时就能弹出提示不至于把一大段堆栈信息糊到用户脸上。3. 核心功能与数据模型订单、菜品、窗口与用户状态怎么设计一场逻辑正常的项目复盘实体关系设计一定是重头戏。食堂点餐系统的数据库虽然不复杂但表与表之间的关系如果理不清楚后面写业务会非常痛苦。我自己第一版数据库时把订单条目和订单表塞成一张表结果一个订单有5个菜就要存5行记录查询订单详情时还得用GROUP_CONCAT又慢又难维护后来才拆成两张表。3.1 角色与权限模型三张主表加一张关联表的通用套路用户相关表标准做法是用户表(user)、角色表(role)、用户角色关联表(user_role)。尽管食堂点餐系统只有学生、商家、管理员三种角色直接用一张user表加role字段似乎也行但我仍然建议做成标准的三表或四表结构原因有二第一Spring Security或Sa-Token等权限框架的数据模型就是这种结构将来扩展比较顺第二论文里写“基于RBAC的权限模型设计”会比“用一个字段区分角色”听起来专业一个档次。用户表设计的字段可以包括id、username、passwordBCrypt加密存储nickname、avatar、phonebalance账户余额用于模拟支付扣款role_id或直接用role_codestatus启用/禁用/锁定create_time、update_time如果不想引入过于复杂的权限框架用Spring Boot拦截器判断role_code就能满足需求。管理端接口设置成/admin/**前缀商家端接口设置成/merchant/**前缀然后在WebMvcConfig里针对这些前缀做角色校验写起来简单、逻辑清晰答辩也容易解释。3.2 核心业务表结构从分类到订单详情的落地方案我最终设计的核心表有这些表名作用关键字段canteen食堂表名称、位置、营业时间、状态window窗口表所属食堂id、窗口名称、排序dish菜品表所属窗口id、名称、价格、图片、月销量、库存cart购物车表用户id、菜品id、数量orders订单主表订单号、用户id、窗口id、金额、状态、支付方式order_item订单明细表订单id、菜品id、菜品名称、单价、数量comment评论表订单id、用户id、评分、内容recharge_record充值记录表用户id、金额、方式、时间菜品表里的库存这个字段值得单独说明。食堂的真实业务中有些菜如红烧肉每日限量有些菜如米饭基本不限量所以在dish表里设置stock和sold字段下单时扣减库存、支付成功后累加销量能很好地模拟真实供需关系。对于库存字段用Integer还是Long并不重要关键是扣减逻辑要正确这一点我来在后面详细展开。订单主表的状态字段我建议用Integer而不是String枚举理由很简单排序和范围查询更方便前后端各维护一张状态映射表——0待支付、1制作中、2待取餐、3已完成、4已取消。在数据库里给status建普通索引管理后台按状态查询时就不会因为数据量增长而变慢。3.3 订单状态机先想清楚状态流转再写代码订单状态是这类系统的灵魂。我在写下单接口之前先在草稿纸上画了一遍完整的状态流转路径虽然不能用渲染图直接放进博客但你可以把它理解为一条线性迁移链待支付-(用户支付成功)-制作中-(商家确认完成)-待取餐-(用户确认收货/超时自动完成)-已完成同时还要考虑两个分支待支付-(用户超时未支付/主动取消)-已取消制作中-(商家拒绝接单/用户申请退款)-已取消。把这些分支在代码里统一封装到一个OrderStateService里每个状态变更都调用changeState(orderId, fromStatus, toStatus)方法里面校验当前状态是否符合预期避免出现“已取消的订单变成待取餐”这种脏数据问题。超时未支付自动取消是一个很常见的需求。最靠谱的实现方案是订单创建后把订单号写入Redis并设置过期时间比如10分钟同时用Redis的key过期事件监听过期后触发一个关闭订单的方法。如果不想用Redis也可以写一个Spring定时任务每分钟扫描一次待支付订单把超时订单批量改成已取消。两种方案各有利弊前者实时性好后者逻辑直白、不依赖Redis我采用的是定时任务方案因为部署更简单而且毕设场景下10分钟级的延迟完全可以接受。3.4 菜品与窗口的数据联动菜品和窗口之间是明显的多对一关系菜品表里存window_id作为外键。查询菜单时前端先选食堂再选窗口然后加载该窗口下的菜品列表。这里要注意一个细节下架或售罄的菜品前端列表要带标识不能等用户点“加入购物车”时才提示失败。所以我给菜单查询接口返回的数据里加了两个字段stock和status前端根据这两个字段做置灰处理体验会好很多。还有一个容易被忽略的关系是餐品规格。比如“加辣/不辣”“大份/小份”如果毕设不想做太复杂建议直接把这些选项作为字符串存到菜品的规格字段里下单时将所选规格原样写入订单明细。如果你非要做成独立的sku表工作量会多出一个量级而且对食堂点餐这种系统来讲意义不大。毕设评审老师更关心的是业务闭环是否完整而不是你有没有做过复杂的sku扩展。4. 后端关键逻辑实现并发下单、扣库存、支付回调与数据统计系统里最容易写崩的就是下单和支付。很多同学的代码风格是“前端传来一个菜品id和数量后端直接Service层更新数据库”听起来没什么问题但两个人同时点最后一份红烧肉时库存会变成负数。这就是典型的并发问题也是我在实际开发中花最多时间调优的地方。4.1 用户下单流程代码级拆解创建订单的完整流程建议按以下顺序执行校验用户登录态从Token中解析userId。接收下单请求参数可以是购物车ids也可以是直接下单的菜品id数量。遍历购物车条目查询最新菜品信息校验状态为上架、库存充足。计算订单总金额。校验用户余额是否充足。扣减菜品库存。创建订单主表和订单明细表。调用模拟支付服务扣减用户余额。更新订单状态为制作中。返回订单号给前端前端跳转到订单详情页。这个流程里最核心的步骤是第6步和第8步因为它们牵涉到数据一致性。如果第6步扣了库存第8步支付失败就需要有补偿机制把库存加回去。更合理的做法是先创建订单待支付再扣库存但给库存加上标记性的锁定数量等支付成功后再真正落地。不过这个方案对毕设来说稍微复杂我采用的是先扣库存、支付失败返还会话的简化方案同时在代码注释里注明真实场景应该用分布式事务或本地消息表答辩时再展开说明。4.2 扣库存为什么不能“先查再改”最自然的写法是先从数据库查出库存再判断库存是否足够然后执行UPDATE扣减。但在高并发下这段代码会有严重的并发问题两个请求同时查出stock1都判断“足够”然后都去扣减最终stock变成-1。正确的做法是用数据库行锁或原子更新完成扣减UPDATE dish SET stock stock - 1 WHERE id #{dishId} AND stock 1这条SQL的巧妙之处在于它把“判断库存足够”和“扣减”合并成了一个原子操作。MyBatis-Plus中这样写UpdateWrapperDish wrapper new UpdateWrapper(); wrapper.eq(id, dishId).ge(stock, quantity).setSql(stock stock - quantity); int rows dishMapper.update(null, wrapper); if (rows 0) { throw new BizException(库存不足); }注意这里UPDATE返回的影响行数。如果影响行数是0说明要么菜品不存在要么库存不足需要中断下单流程。我在实际测试中专门用JMeter模拟了50个并发请求同时购买同一道库存只有3份的菜最终只有3个请求成功库存没有变成负数这就验证了方案的可靠性。4.3 支付模块的模拟与真实回调设计真实支付系统的核心特征是异步回调。用户扫码付款后微信或支付宝服务器会异步请求咱们的后台接口携带支付结果后台需要根据回调结果更新订单状态。在毕设中接真实支付不现实但完全可以用“模拟支付”实现同款逻辑。我的做法是这样的用户点击“确认支付”时前端调用后端/pay/mock接口后端做如下事情校验订单属于当前用户且状态为待支付。校验余额充足。扣减用户余额。调用OrderStateService把订单从待支付改为制作中。返回支付成功。发送一个内部消息到消息处理器模拟异步回调通知更新支付流水表状态。虽然扣余额和更新订单不是在一个数据库事务里也能跑通但强烈建议加上Transactional把扣余额、更新订单、插入支付流水放在同一个事务中。只要有一个失败全部回滚从根本上避免“钱扣了但订单还是待支付”的数据不一致问题。4.4 销售统计与菜品销量更新管理端需要一个简单的统计页展示每个窗口或者菜品的销量排行。这块最烦人的点在于销量不能每次都SUM订单明细表因为数据量一大查询就会慢。我的方案是菜品表里维护一个sales字段支付成功后异步递增销量。前端展示菜单的时候销售数据直接读这个字段不需要联表聚合。用Spring的Async注解把销量递增和订单明细插入解耦让用户支付请求不用等额外API调用。这里出现的坑是Async在同一个类内部调用时不生效因为它基于代理实现自调用不会经过代理对象。我一开始就是把异步方法写在同一个Service里怎么调都不生效后来独立成一个AsyncTaskService才正常这个细节值得记到笔记里。统计接口方面按天统计营收和订单数SELECT DATE(create_time) AS day, COUNT(*) AS orderCount, SUM(amount) AS totalAmount FROM orders WHERE status ! 4 AND create_time BETWEEN #{start} AND #{end} GROUP BY DATE(create_time) ORDER BY day用MyBatis-Plus写自定义SQL或者Select注解都行只要记得日期参数用LocalDateTime类型避免字符串格式问题。5. 前端与接口联调Vue页面开发中最容易拖进度的实际问题后端接口写得再漂亮联调阶段前端报错一样会让你想掀桌子。这里说的前端不一定是你亲自写可能是你的队友或者另一份源码但只要前后端职责分不清联调就会变成灾难现场。5.1 统一接口返回体每个页面都要处理三个状态我开发前端时先封装了一个request.js工具基于axios统一设置baseURL、请求头、Token注入和响应拦截。响应拦截器拿到后端Result对象后依次做三件事code等于200直接返回data给业务页面。code等于401跳到登录页并清除本地登录态。code等于其他值弹出Element Plus的ElMessage提示错误信息。这样做的好处是页面里不用在每个接口后面都写错误弹窗业务代码干净很多。但注意一个问题文件上传接口的Content-Type是multipart/form-data请求拦截器里不能无条件给所有请求加application/json否则后端会解析不了参数。我就在这个问题上卡了一个下午最后用axios实例分离的方式解决普通请求和文件上传请求各用一个instance配置不同请求头。5.2 下单页的防重复提交不只是按钮置灰“重复下单”是食堂点餐系统里最容易被老师当面演示翻车的功能。用户点了“确认支付”后端处理需要几百毫秒如果前端按钮没有做防抖用户手快点了两下就会产生两个订单。解决方案至少要两层前端层面点击后立即把按钮设为loading同时用一个本地布尔变量拦截第二次点击请求完成后恢复。后端层面利用数据库和Redis做幂等前端下单请求携带一个前端生成的requestId后端在Redis里用setIfAbsent命令写入requestId只有第一次写入成功才继续处理重复请求直接返回“正在处理中”。这样即使前端防抖失效、重复请求绕过前端后端也能拦截。5.3 跨域问题要在一开始就解决而不是等到联调才发现开发阶段前端跑在5173端口后端跑在8080端口浏览器默认会拦截跨域请求。解决方式有两种后端提供CORS配置类或者前端用Vite的proxy代理。我建议两边都做开发时用前端代理部署时由Nginx把/api路径转发到服务端后端同时保留CORS配置作为兜底。这里有一个安全相关的细节CORS配置的allowedOrigins不要写成*要明确指定前端域名尤其是系统里有登录功能时否则会有CSRF风险。虽然毕设系统面对的攻击面不大但养成好习惯总没错。6. 从本地调试到服务器部署那些只在生产环境才暴露的坑毕设做完是一回事能在答辩现场把系统演示起来又是另一回事。很多同学在本地运行一切正常一到教室用自己电脑演示就连不上数据库或者后端启动失败。所以部署环节至少要在答辩前一周完成别拖到最后一天。6.1 本地联调时最容易混淆的配置来源本地联调最典型的坑是application.yml里的数据库地址还在用localhostRedis也在本机但前端配置的后端地址写的是服务器IP导致数据对不上。我的习惯是本地和服务器各维护一套配置# application-dev.yml 本地环境 server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/canteen?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai redis: host: localhost服务器上则通过启动参数指定环境java -jar canteen.jar --spring.profiles.activeprod。另外数据库账号密码不要用root/123456这种默认组合尽可能单独创建一个业务数据库用户给足对canteen库的权限即可。这样万一数据库连接串泄露也不至于被直接拿到服务器最高权限。6.2 打包与部署完整命令流程在项目根目录执行Maven打包mvn clean package -DskipTests打包完成后目标目录下会生成canteen.jar。部署到服务器推荐用systemd管理进程先创建service文件[Unit] DescriptionCanteen Order System Afternetwork.target mysql.service redis.service [Service] Userdeploy WorkingDirectory/opt/canteen ExecStart/usr/bin/java -jar /opt/canteen/canteen.jar --spring.profiles.activeprod Restarton-failure RestartSec5 [Install] WantedBymulti-user.target然后执行systemctl daemon-reload和systemctl enable canteen开机自启。Nginx方面配置一个server块把/api/路径反向代理到本机8080端口同时托管前端dist静态文件。这个部署结构是当前最通用的Java单体项目落地方式面试时也能拿出来讲。6.3 上线后必须检查的几个安全项不能因为它是毕设就完全放弃安全性至少要检查四件事登录接口是否在短时间内被高频请求如果没有做任何防护需要在网关层或拦截器里加简单的接口限流。比如用Redis对用户IP做计数器一分钟内超过30次就拒绝请求。数据库密码和Redis密码不能为空Redis不要监听0.0.0.0且无密码。管理端接口不能只靠前端隐藏按钮来做权限控制必须由后端拦截器校验角色。文件上传接口必须限定文件类型和大小否则有人上传jsp或webshell就麻烦了。这些检查未必每个都会实际遇到但在文档里写清楚你的安全设计答辩时提到“我对上传文件做了类型校验和大小限制”能让老师认为你的工程素养在线。6.4 演示环境的三条红线现场演示最怕三件事演示到一半数据库崩了、Redis没启动、手机端页面调不起键盘。我的建议是答辩前把所有服务重启一遍清空脏数据保证列表页数据整洁。准备一个演示账号密码一定要短比如admin/123456但只用于演示环境。提前把关键页面截图保存到PPT中万一现场网络出故障还能靠截图推进讲解。另外不要用现场用户真实下单测试余额和支付流程万一支付成功但订单没生成场面会非常尴尬。提前准备几份已完成的订单数据在库里演示时直接展示列表和详情即可。7. 文档整理与答辩亮点怎么把20分的工作量讲出80分的效果最后这部分可能是很多同学最不重视但最拉分的环节。同一个系统有的人答辩完老师说“工作量挺足”有的人答辩五分钟就被老师打断。差别往往不在代码而在你会不会讲述。7.1 从源码到写论文文档里必须有哪些图论文或文档里有几张图是必备的系统架构图、功能模块图、数据库ER图、核心业务时序图。这些图不需要画得多花哨也不用非要UML工具关键是把逻辑讲通。画数据库ER图时别把每个表所有字段都画上去只画核心字段和表间关系否则一屏看下来全是字段名评审老师反而抓不住重点。功能模块图要按角色划分从学生端、商家端、管理端三个维度展开不要画成一张所有功能平铺的大杂烩。7.2 测试用例怎么写得像模像样测试用例是很多毕设文档里露怯的地方。有些同学只写“测试登录功能正常”这种描述扔给老师等于没写。正确的写法是包含明确的测试步骤、输入数据和预期结果用例编号测试功能前置条件操作步骤预期结果实际结果TC-001用户登录已注册账号输入正确账号密码点击登录返回token并跳转首页通过TC-002库存不足下单菜品库存为1添加2份加入购物车并下单提示库存不足下单失败通过TC-003重复支付请求订单待支付连续发送两次支付请求第二次请求返回幂等提示通过像TC-003这种测试用例直接和之前设计的幂等机制呼应一看就是真做过并发思考的。测试截图建议每张都配上文字说明不要只贴一张绿油油的全通过截图。7.3 答辩时老师最爱问的几个点以及回答方向根据我带过的几十个学生的反馈食堂点餐系统答辩时高频问题无非这几个为什么用Spring Boot答它简化了Spring配置内嵌Tomcat自动装配机制让我能专注于业务而不是框架配置。顺势展开自动装配条件注解的原理。你是怎么防止库存超卖的答不是先查再改而是用UPDATE的原子性配合库存条件判断影响行数为0就判定失败。如何保证订单和支付数据一致答核心流程放在一个事务里涉及外部系统时用本地事务加状态回滚机制并提及真实场景下的幂等设计。你的系统有哪些可以优化的地方答可以把模拟支付替换为真实微信支付引入消息队列削峰用读写分离提升查询性能。注意不要主动说“系统很完善没有缺点”这种回答等于给自己挖坑。回答这些问题的策略就一句话你写在代码里的每一个特殊处理都要准备好一个能讲清楚“为什么”的故事。代码可以通过学习源码来理解但把思路组织成语言才是答辩的关键。7.4 如果拿到别人的源码二次开发前必须做的几件事网上确实有很多基于Spring Boot的高校食堂点餐系统源码质量参差不齐。如果是拿来改造成自己的项目我建议按以下顺序处理先跑起来能启动、能登录、能购物、能下单、能支付。全局搜索数据库密码、Redis密码等敏感配置全部替换。删除无用的演示数据和测试类。逐条核对需求看哪些页面根本没连通后端哪些接口是写死的假数据。重点检查支付和下单逻辑确认没有数据一致性问题。把关键模块的代码读一遍改掉类名里的残留信息避免答辩时被发现是模板项目。有些源码表面功能齐全实际前端调的是静态Mock数据后端接口完全是空壳。这种项目如果不检测就提交答辩时一改密码或者新增一条记录就会露馅。拿到任何现成源码第一步永远是在真实环境完整走一遍核心业务不要急着写论文。最后分享一个我个人的习惯项目做完之后我会把整个调试过程中踩过的所有坑记录成一个Markdown笔记每条注明“现象-原因-解决方式-如何避免”。这份笔记在写论文的“问题与解决”章节时帮助极大里面全是真实素材比网上复制来的技术问答有说服力得多。做Java毕设这事真正值钱的从来不是那一堆代码而是你解决问题的整个思考过程这恰恰是工作面试里最想看到的东西。