ARTICLE DETAIL

资讯详情

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

SpringBoot+Vue美食推荐系统毕设全攻略:从选题到答辩

SpringBoot+Vue美食推荐系统毕设全攻略:从选题到答辩 最近好几个准备毕设的同学来找我问的都是同一个方向想做一套 SpringBoot Vue 的美食信息推荐系统平台。说实话这个选题确实很聪明——它不像纯管理系统那样平淡又不像人工智能类题目那样容易把自己绕进去。美食推荐系统既有完整的业务闭环又有“推荐”这个可以讲出技术亮点的核心模块作为 Java Web 毕设来说属于那种“拿得出手、讲得清楚、演示效果好”的类型。今天这篇文章我就以这套完整项目源码 SQL 脚本 接口文档为线索从选题分析、技术栈选型、数据库设计、后端接口、前端实现到最后的部署运行和答辩思路完整过一遍。不管你是刚拿到项目还没头绪还是已经在自己写但卡在某一步这篇都能帮你省不少时间。1. 为什么选美食推荐系统做毕设选题逻辑与项目全景1.1 这题目好在哪业务清晰、亮点明确、工作量可控毕设选题最怕两种一种是题目太空比如“基于SpringBoot的系统设计”评委一眼看过去不知道你做了什么另一种是题目太窄比如“XX信息管理”功能就增删改查答辩时没内容可讲代码量也不够。美食信息推荐系统恰好避开了这两个极端。从业务上看它有用户、菜品、分类、收藏、评论、推荐这些实体前后端交互路径完整从技术亮点上看“推荐”是一个天然的加分项哪怕不用机器学习只做基于标签和偏好的匹配推荐也能讲出一套完整的思路。对于大多数Java Web方向的毕设来说这个题目的工作量刚好在一个学期内可控不会少到没东西写也不会多到做不完。另外还有一个很现实的因素这个题目的参考资料和开源项目非常多。你遇到的大部分坑别人都已经踩过并在网上留下了解决方案。作为毕设稳定跑通比什么都重要这个选题恰好能满足。1.2 系统角色划分与核心功能清单拿到项目源码后第一件事不是跑代码而是先搞清楚系统里有哪些角色、各自能做什么。这套美食推荐系统平台通常按两种角色划分普通用户注册登录、浏览菜品、按分类筛选、关键词搜索、查看菜品详情、收藏/取消收藏、选择个人偏好标签、获取推荐菜品列表、发布评论。管理员菜品分类管理、菜品信息维护、用户管理、推荐参数管理如果有、统计数据查看。有的项目还会区分“访客”角色未登录状态下允许浏览菜品但限制收藏和评论。这个设计很实用答辩时能顺带说明“接口层做了权限控制”。我把核心功能整理成一张表方便你对照源码逐个核对功能模块角色对应核心接口页面用户注册登录用户/api/auth/register、/api/auth/login登录页、注册页菜品分页浏览用户/访客/api/dish/page菜品列表页分类筛选用户/访客/api/dish/list?categoryId菜品列表页关键词搜索用户/访客/api/dish/search顶部搜索框菜品详情用户/访客/api/dish/detail/{id}菜品详情页收藏/取消收藏用户/api/favorite/add、/api/favorite/remove菜品详情页、收藏页偏好标签设置用户/api/preference/save我的偏好页推荐菜品列表用户/api/recommend/list推荐页评论管理用户/管理员/api/comment/add、/api/comment/list菜品详情页菜品管理管理员/api/admin/dish/save、/api/admin/dish/delete后台管理页确认完角色和功能你就能在源码里快速定位到对应模块后面不管是调试还是答辩心里都有一张完整地图。1.3 拿到源码后先看什么目录结构决定你的上手速度毕设项目的源码目录各不一样但合理的项目一定会把代码分得清楚。常见结构是food-recommend-system/ ├── backend/ # SpringBoot后端 │ ├── src/main/java │ ├── src/main/resources │ └── pom.xml ├── frontend/ # Vue前端 │ ├── src/ │ ├── package.json │ └── vue.config.js ├── sql/ # SQL脚本与初始化数据 │ └── food_db.sql └── docs/ # 接口文档 └── 接口文档.md我的建议是先打开sql/目录下的脚本把数据表全部看一遍通过表结构反推业务功能。比如看到favorite表里有user_id和dish_id就知道这是收藏功能的存储基础看到user_preference表就说明推荐逻辑大概率依赖用户偏好。这个“先看表、再看接口、最后看代码”的顺序能让你在半小时内对整个项目有概念性认识而不是一头扎进某个 Controller 里出不来。2. SpringBootVue技术选型为什么这套组合是毕设的省心之选2.1 后端用SpringBoot把时间花在业务而不是配置上现在做 Java Web 毕设SpringBoot 基本是默认选择。它最大的价值就在于自动配置和起步依赖解决了早期 SSM 项目里大量 XML 配置的问题。你只需要在pom.xml里引入相关 starter框架就会自动完成大多数配置内置的 Tomcat 也让发布变得非常简单——打一个 jar 包就能跑。有一点要特别提醒SpringBoot 版本不要贪新。早期很多毕设项目用的是 SpringBoot 2.x对应 JDK 1.8新一些的 3.x 版本要求 JDK 17而且包名从javax迁到了jakarta很多老代码直接复制过来会报错。如果你拿到的是成熟项目源码尽量保持原来的版本组合先跑通再说升级的事。Maven 方面项目构建用标准的mvn clean package即可。后端项目结构一般按功能分包com.example.fooddemo ├── controller/ # 接口层接收请求并返回结果 ├── service/ # 业务逻辑层处理推荐、权限等 ├── mapper/ # 数据访问层MyBatis-Plus 操作数据库 ├── entity/ # 实体类对应数据表 ├── common/ # 统一返回结果、全局异常处理 └── config/ # 跨域配置、拦截器配置等很多学生在写代码时喜欢把所有逻辑都堆在 Controller 里但毕设项目我建议按标准分层来写。一来代码可读性强二来答辩时老师问到“你的项目结构怎么设计的”你可以讲出一套合理的设计思路。2.2 前端选Vue组件化和开箱即用的生态Vue 在前端框架里是相对容易上手的。它的组件化开发模式让页面结构非常清晰——美食卡片、导航栏、推荐列表都可以抽成独立组件复用双向数据绑定省去了大量 DOM 操作配合 Vue Router 做页面跳转、Vuex/Pinia 做状态管理整个前端工程结构一目了然。如果你用的是 Vue 2 Vue CLI安装环境时注意 Node 版本不能太高推荐 Node 14 或 16太高可能会出现node-sass编译失败的问题。Vue 3 的项目则建议 Node 16。毕设环境里稳定优先哪个版本组合成熟就用哪个不要为了赶新而给自己挖坑。Vue 的目录结构一般是这样frontend/src/ ├── api/ # 封装axios请求 ├── assets/ # 静态资源 ├── components/ # 公共组件 ├── router/ # 路由配置 ├── store/ # 状态管理 ├── views/ # 页面组件 ├── App.vue └── main.js2.3 前后端分离架构与一次完整请求的数据流这个项目的通信方式是典型的前后端分离前端 Vue 页面通过 axios 发起 HTTP 请求后端 SpringBoot 的 Controller 接收并处理Service 层完成业务逻辑Mapper 层操作数据库结果以 JSON 格式返回给前端。一次完整的“用户浏览推荐菜品”请求路径是这样的用户进入推荐页Vue 组件onMounted里调用recommendApi.getList()axios 携带 Token 发起 GET 请求到/api/recommend/listSpringBoot 拦截器校验 Token合法则放行Controller 调用 Service 层Service 根据用户 ID 查询偏好标签、统计收藏行为计算推荐得分Mapper 执行 SQL 查询把符合条件的菜品列表返回后端统一包装成{ code, message, data }结构返回前端拿到数据渲染推荐卡片。这个链路几乎覆盖了项目所有核心点跨域、权限、业务逻辑、数据访问、统一返回格式。答辩时把这条链路讲清楚比背十遍项目介绍都管用。3. SQL脚本与数据库设计美食推荐系统的数据骨架3.1 核心数据表拆解一张张表看懂业务数据库是整套系统的地基。拿到 SQL 脚本后我建议你先把表关系画出来。这套美食推荐系统平台的核心表一般有下面几张表名作用关键字段user用户表id、username、password、nickname、role、avatarcategory菜品分类表id、name、sortdish菜品表id、name、category_id、image、price、tags、description、view_countfavorite收藏表id、user_id、dish_id、create_timecomment评论表id、user_id、dish_id、content、create_timeuser_preference用户偏好表id、user_id、tag偏好标签其中dish表的tags字段很关键。它是推荐算法的“原料”存储的是菜品的标签比如“辣”、“清淡”、“素食”、“高蛋白”、“甜口”等。用户注册后选择自己的口味偏好同样也落到user_preference表。推荐逻辑本质上就是在这两张表之间做标签匹配。favorite表也不能小看它是隐式反馈数据。一个用户收藏了哪些菜直接反映他的真实喜好比他自己选的标签还可靠。好的推荐逻辑一定会结合这两类数据。3.2 SQL脚本初始化执行顺序和版本差异是重灾区很多同学拿到源码后卡在第一步SQL 脚本导入报错。我总结了几个高频问题提示先在 MySQL 中创建数据库如CREATE DATABASE food_db DEFAULT CHARACTER SET utf8mb4;再执行source food_db.sql顺序不要反过来。字符集问题如果脚本里没有指定utf8mb4中文字段可能变成乱码。导入后用SHOW CREATE TABLE dish;检查一下表字符集。外键顺序部分项目脚本会先建子表再建父表如果启用了严格模式外键约束会导致建表失败。遇到这种情况可以拆开执行建表语句先父表后子表。MySQL 5.7 与 8.0 的兼容8.0 默认密码插件是caching_sha2_passwordSpringBoot 用的 MySQL 驱动版本如果太旧会报Public Key Retrieval is not allowed。解决方式是在 JDBC 连接串后加allowPublicKeyRetrievaltrueuseSSLfalse。时间字段默认值create_time建议用DEFAULT CURRENT_TIMESTAMP如果脚本里写的默认值是0000-00-00 00:00:00在 MySQL 严格模式下会直接报错。3.3 推荐系统需要的数据从哪来模拟数据也是项目的一部分我见过不少同学问“推荐系统不是需要大量用户数据吗我哪来那么多真实数据”答案很简单SQL 脚本里预置模拟数据。这也是这套项目设计比较聪明的地方——数据库脚本里除了表结构还会插入一批菜品数据、若干测试账号、一批收藏记录和用户偏好。比如插入 20 道菜品、5 个分类、3-5 个测试用户每个用户有若干收藏和偏好标签。别小看这批模拟数据。它在演示时非常关键你登录测试账号推荐页能立刻展示出符合该用户偏好的菜品这就是最好的效果验证。而且答辩时你可以说“针对冷启动问题系统预置了用户行为数据用于推荐效果验证”这句话本身就是一个加分点。4. 后端接口设计与推荐逻辑从Controller到推荐算法的完整链路4.1 接口文档先定义清楚再写代码接口文档是这套源码里的重要组成部分也是很多毕设项目容易忽略的部分。拿到项目后你要会读接口文档更重要的是能讲出为什么要定义这些接口。标准的接口文档应包含请求路径、请求方法GET/POST/PUT/DELETE、请求参数说明、返回结果示例。项目里的接口一般按业务模块分组认证模块/api/auth/login、/api/auth/register菜品模块/api/dish/page、/api/dish/detail/{id}推荐模块/api/recommend/list收藏模块/api/favorite/add、/api/favorite/list评论模块/api/comment/add、/api/comment/list管理模块/api/admin/dish/save、/api/admin/dish/delete统一返回结构长这样{ code: 200, message: 成功, data: { records: [...], total: 20 } }这个Result类在common包下是所有接口的返回模板。我见过不少同学自己写项目时每个接口返回格式都不一样前端处理起来非常痛苦。统一返回结构虽然是小细节但体现的是工程化意识答辩时可以说“所有接口采用统一返回约定方便前端统一处理异常”。4.2 推荐算法的落地实现标签匹配加收藏加权这是整个项目最核心的部分也是最能体现你工作量和技术水平的地方。我不建议在毕设里硬上协同过滤或深度学习因为数据量不够效果反而难讲清楚。基于内容的推荐逻辑朴素但完整非常适合毕设。我的推荐逻辑设计是这样的从user_preference表取出当前用户的偏好标签集合比如[辣, 川菜, 肉]从dish表查出所有菜品解析每道菜的tags字段计算菜品标签与用户偏好标签的匹配数量作为基础得分再根据用户收藏记录加权收藏过的菜品加 10 分同类标签的菜品按权重乘以 1.2综合考虑菜品浏览量和评分排序后返回前 N 条。Service 层核心代码思路如下public ListDish recommendForUser(Long userId, int limit) { ListString userTags userPreferenceMapper.selectTagsByUserId(userId); ListDish allDishes dishMapper.selectAll(); ListLong favoriteIds favoriteMapper.selectDishIdsByUserId(userId); // 为每道菜计算推荐得分 return allDishes.stream() .map(dish - { int score 0; for (String tag : dish.getTagList()) { if (userTags.contains(tag)) { score 5; } } if (favoriteIds.contains(dish.getId())) { score 10; } // 浏览量作为微弱信号加权 score Math.min(dish.getViewCount() / 10, 5); return new ScoreDish(dish, score); }) .sorted(Comparator.comparingInt(ScoreDish::getScore).reversed()) .limit(limit) .map(ScoreDish::getDish) .collect(Collectors.toList()); }这段逻辑并不复杂但每一步都有依据标签匹配是“显式偏好”收藏行为是“隐式反馈”浏览量是“热度加权”。答辩时把这三层讲清楚老师会认为你对推荐问题有真正的理解。4.3 拦截器权限控制与全局异常小项目也要有工程化味道除推荐逻辑外代码里最能加分的还有两点登录拦截器和全局异常处理。项目中的拦截器一般继承HandlerInterceptor在preHandle方法里校验请求头中的 TokenOverride public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (!StringUtils.hasText(token) || !tokenService.validateToken(token)) { response.setStatus(401); return false; } return true; }对所有需要登录的接口收藏、评论、推荐、个人中心通过 WebMvcConfigurer 注册拦截器并配置放行路径比如/api/auth/**、/api/dish/**列表和详情可公开。全局异常处理则是用ControllerAdvice捕获业务异常避免直接把堆栈信息暴露给前端。这样无论前端还是后端调用方收到的都是结构统一的错误信息。这些细节通常是“优秀毕设”和“普通毕设”的分水岭。5. Vue前端落地从登录页到美食卡片的完整实现5.1 前端项目结构与路由设计前端项目的核心在src目录。我按照页面维度拆分 viewsviews/ ├── Login.vue ├── Register.vue ├── Home.vue # 首页/推荐页 ├── DishList.vue # 菜品列表 ├── DishDetail.vue # 菜品详情 ├── FavoriteList.vue # 我的收藏 ├── UserPreference.vue # 偏好设置 ├── Admin/ │ ├── DishManage.vue # 菜品管理 │ ├── CategoryManage.vue # 分类管理 │ └── UserManage.vue # 用户管理路由配置要体现角色差异。使用路由守卫在进入页面之前判断本地存储中的role字段router.beforeEach((to, from, next) { const user JSON.parse(localStorage.getItem(userInfo) || {}); if (to.meta.requiresAuth !user.token) { next(/login); return; } if (to.meta.requiresAdmin user.role ! admin) { next(/); return; } next(); });Vue 路由有两个容易踩的坑一是动态路由传参比如跳转菜品详情页用router.push({ path:/dish/${id}})在目标页面要用this.$route.params.id接收二是路由模式如果用history模式打包部署到服务器后刷新页面容易 404用hash模式则没这个问题。毕设阶段用hash模式最省心。5.2 推荐页面和列表页的交互设计组件拆分与状态管理推荐页是所有页面里最需要花心思的。我建议这样拆顶部搜索框输入关键词后回车触发搜索接口推荐区展示recommend/list接口返回的结果用卡片网格渲染热门区调用/api/dish/page?sortviewCount展示浏览量最高的菜品分类筛选栏点击分类后切换菜品列表。在 axios 请求时要注意 URL 参数的拼接。我习惯把所有接口统一放在src/api下管理比如// src/api/recommend.js import request from /utils/request; export function getRecommendList() { return request({ url: /api/recommend/list, method: get }); }然后页面里调用逻辑就很干净import { getRecommendList } from /api/recommend; onMounted(async () { const res await getRecommendList(); recommendList.value res.data; });5.3 前端调接口的常见坑跨域、Token、刷新前后端分离项目最常见的三个问题我逐个说跨域。开发环境下前端跑在 8080 端口后端跑在 8081 端口浏览器会拦截跨域请求。最简单的解决方式是后端加 CORS 配置更推荐的方式是在vue.config.js中配置 devServer 代理module.exports { devServer: { proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } };这样前端请求/api/xxx时代理会自动转发到后端浏览器看到的是同源请求不会触发跨域问题。Token 过期。请求拦截器和响应拦截器可以在 axios 封装里统一处理。请求前从 localStorage 取 Token 加到 header响应返回 401 时清空本地登录状态并跳转登录页。这个机制能避免接口报错时前端表现得很突兀。打包后放不进 SpringBoot。不少人喜欢把前端npm run build后的dist目录复制到后端src/main/resources/static下让 SpringBoot 同时提供页面和接口。这个做法可行但要注意如果你后端有拦截器需要额外放行静态资源路径否则页面和 JS 文件会被权限拦截。另外路由模式如果是 history刷新时后端没有对应路由处理会返回 404这也是我推荐用 hash 模式的原因。6. 把项目跑起来的完整步骤从源码导入到SQL执行6.1 环境准备版本选对后面少折腾跑这套项目环境版本尽量与项目一致。我的建议如下组件推荐版本说明JDK1.8SpringBoot 2.x 项目的最佳搭档Maven3.6配置阿里云镜像加速依赖下载Node.js14 或 16与 Vue CLI 兼容性最好MySQL5.7 或 8.0注意数据库连接串参数IDEIDEA 2020自带 Maven 和 Node 支持Maven 依赖下载慢是国内开发环境的老问题。在maven/conf/settings.xml里加阿里云镜像mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror6.2 后端启动IDEA配置、数据库连接与端口问题后端启动步骤很简单但每一步都有坑。第一步IDEA 里File - Open选择后端目录等待 Maven 自动导入依赖。如果依赖没下载完整先执行mvn clean compile强制拉取。第二步修改application.yml。重点是数据库地址、用户名、密码server: port: 8081 spring: datasource: url: jdbc:mysql://localhost:3306/food_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: 123456第三步启动Application主类。如果端口被占用SpringBoot 会直接报Port 8081 was already in use解决方式要么改server.port要么杀掉占用进程。连接数据库报错是另一个高频问题常见的是Access denied for user说明账号密码不对或没有远程访问权限。SQL 脚本导入后先用命令行测试连接比如说mysql -uroot -p123456确认能连上再回来看应用。6.3 前端启动与打包从 npm install 到生产部署前端部分的操作相对机械但也是出错率最高的一块。进入frontend目录执行# 安装依赖换成国内源更快 npm config set registry https://registry.npmmirror.com npm install依赖装好后启动开发服务器npm run serve默认访问http://localhost:8080。如果 Node 版本过高导致node-sass报错通常有两个办法一是换成sass版本安装sass并兼容 Vue CLI 5二是降低 Node 版本。稳妥起见建议按项目 package.json 里锁定的版本走。生产部署打包npm run build生成dist目录后可以直接扔给 Nginx 托管也可以复制到后端静态目录。前者更贴近真实项目后者适合毕设演示环境。你选哪种都可以但答辩时一定要说明你用的是哪种方式以及为什么。7. 答辩与二次开发真正拉分的是你能讲清楚什么7.1 答辩演示时怎么讲项目用一条主线串起整个系统很多同学答辩时喜欢从登录页开始一页页点讲到哪算哪这样效果很差。我建议的演示顺序是先花 20 秒讲清楚系统定位和角色划分登录管理员账号展示菜品管理和分类管理说明数据结构切换到普通用户账号展示浏览、搜索、收藏重头戏登录一个已设置好偏好标签的测试账号进入推荐页展示推荐结果并和前一步的普通浏览形成对比讲推荐算法的三层逻辑标签匹配、收藏加权、热度补充展示数据库里预置的数据量说明效果验证过程。这条主线里推荐逻辑是核心前面的管理功能只是铺垫。老师最想听到的“你做了什么”和“你怎么做的”全在推荐逻辑这一段。7.2 评审老师常问的问题与回答思路我根据经验整理了高频提问提前准备好这些答案答辩会从容很多“为什么不用SSM”SpringBoot 简化了配置和部署内置 Tomcat更适合快速开发和前后端分离架构SSM 适合理解底层原理但项目开发效率低。“推荐算法为什么不用协同过滤”协同过滤依赖大量用户行为数据当前模拟数据规模有限基于内容和标签的推荐在小规模数据下效果更稳定、解释性更强后续可以引入协同过滤作为扩展。“用户偏好和菜品标签怎么维护”用户注册时选择偏好标签也可以在个人中心修改菜品标签由管理员在后台维护未来可集成分词工具自动提取标签。“系统安全性如何保证”登录采用 Token 认证拦截器统一校验数据库密码加密存储前端路由守卫控制页面访问。“并发量大了怎么办”可以引入 Redis 缓存热门菜品使用 Nginx 做负载均衡数据库加索引优化查询。这些问题不用答得多深但一定要能接住千万不能说“我没想过”。7.3 项目后续可以扩展的方向哪些升级性价比最高如果时间充裕或者你想在毕设里再加一点与众不同的东西我推荐按性价比排序做这些扩展给热门菜品加 Redis 缓存这是最容易演示的优化带上缓存后响应速度对比也能直观展示把推荐算法升级为“基于用户的协同过滤”从收藏记录中找相似用户推荐他们喜欢的菜品代码量不大但理论价值高接入分词工具对菜品描述做标签自动提取比如对“香辣鸡腿堡香辣酥脆”自动打上“辣”、“香酥”、“鸡肉”标签体现自动化能力增加用户浏览历史把浏览行为也纳入推荐权重推荐结果会更个性化。我个人在实际操作中的体会是毕设项目真正的竞争力不在于代码量多少而在于你是否能把一条完整的技术链路讲清楚。美食推荐系统这套源码从数据库到推荐算法再到前端呈现是一整条可以自洽的链路。你把它跑通是第一步把它讲透才是拿高分的关键。很多同学拿到的项目其实都一样冷静拆解、逐个模块吃透你的项目就会变成你自己的作品。答辩前最后再分享一个小技巧找朋友当观众完整地走一遍演示流程他会帮你发现你自己感觉不到的“逻辑断层”和“卡壳点”比闷头背稿子有效得多。
返回列表