资讯动态

基于SpringBoot与Vue的美食推荐系统开发实战:AI算法与协同过滤实现

发布时间:2026/10/20 8:51:40 来源:尧图企业网站定制
做美食推荐系统这个方向我前后加起来也折腾过不少时日。SpringBootVue这套组合在校园设计和中小型项目里实在太常见但真正把AI推荐那一层做出效果、还能配上完整文档和搭建视频的其实是少数。这篇文章我按自己的实际开发路径写从技术选型、数据库设计、推荐算法的落地写法到前端交互、文档组织、视频录制再到部署和排查问题一次性讲透。想自己动手复现一个带AI的美食推荐系统照着这条线走能少踩很多坑。1. 项目定位与技术选型拆解1.1 为什么是SpringBootVue而不是其他组合先聊聊选型。很多人一上来就纠结框架其实这类管理系统推荐服务的项目选SpringBootVue不是因为它最新而是因为它最适合这种“管理后台用户交互推荐逻辑”混合的场景。SpringBoot在后端的优势不用多说Java生态成熟、社区资料多、部署简单内置Tomcat能让打包后的jar直接跑起来对后面录制搭建视频特别友好。你总不希望学生在演示环境还要手动装Web服务器吧SpringBoot的main方法一行启动这是很大的工程体验优势。Vue这边因为推荐系统的一大核心是“用户与页面高频交互”需要实时展示菜品、点击收藏、切换偏好标签Vue的响应式机制让这些动效和交互状态维护成本大幅下降。Element UI这类组件库能让页面在短时间内达到不错的视觉效果不用耗时抠CSS。再补充一点这套技术栈的资料密度极高不管是校园博文、技术社区还是视频站点SpringBootVue推荐系统的实战案例都多到看不完遇到问题很容易搜到解决方案。如果换成别的冷门组合可能某次环境报错就卡你半天。项目要落地选型的第一标准永远是“能顺利做完”。1.2 美食推荐场景的AI需求拆解所谓“带AI”很多项目是挂个名字真正要落地无非是几类做法基于内容的推荐给每个菜品打标签根据用户历史行为建立偏好画像计算用户画像与菜品标签之间的相似度。协同过滤推荐分用户协同过滤和物品协同过滤两种本质是靠“相似用户”或“相似菜品”做推荐。混合推荐把上面两种结果加权融合或者按场景切换。美食推荐这个领域有个很有趣的特征用户对菜品的偏好分为两类一类是长期稳定的口味偏好比如偏爱辣、偏爱粤菜另一类是短期的尝鲜需求比如看大家都在吃某道菜。所以我在设计时没有只用一种算法而是做了一个混合推荐服务日常默认走基于内容的推荐从用户收藏和点击历史中提取偏好同时后台定期用协同过滤计算“相似菜品集合”作为补充候选池。对毕设或者中小型系统来说混合推荐看似复杂其实代码量并不大关键是要把数据层的用户行为记录做扎实否则算法喂进去的都是空数据。这部分建议项目一开始就先设计好埋点不然后面补非常痛苦。1.3 功能模块边界划分整个系统我按使用角色拆成三端用户端注册登录、浏览菜品、搜索分类、查看详情、收藏菜品、提交评论、个人中心、推荐页。商家端可选菜品管理、分类管理、订单处理。美食推荐系统如果只做菜品展示会显得单薄加上商家端的内容管理逻辑会更完整。管理端用户管理、菜品审核、评论管理、推荐日志查看、系统参数配置。再加上一个推荐服务模块独立于普通CRUD逻辑专门负责用户画像构建、推荐候选生成和结果过滤。这个模块划分有很多好处首先是开发时可以并行文档和视频也能按模块组织一层层讲清楚其次推荐服务单独拆出来意味着你可以在不破坏业务逻辑的前提下持续迭代推荐算法本身后期换模型、调参数都不会影响主流程。2. 数据库设计与推荐数据的底层支撑2.1 核心表结构与设计思路数据库设计是推荐系统最大的隐藏难点。很多人的表结构是零散拍脑袋写的结果做到推荐模块时发现历史数据根本不够用。我按“业务数据行为数据”两层来设计。业务层表用户表主键、用户名、密码加密存储、昵称、头像、注册时间、状态。菜品表主键、菜名、描述、图片URL、分类ID、价格、辣度、口味标签、商家ID、上架状态。分类表主键、分类名、排序。商家表主键、商家名、地址、联系电话、评分。评论表主键、用户ID、菜品ID、评分、内容、创建时间。行为层表用户行为表主键、用户ID、行为类型浏览/收藏/评论/搜索、目标菜品ID、行为发生时间、行为权重。推荐日志表主键、用户ID、推荐日期、推荐菜品集合、推荐策略标识、是否点击。行为层是AI推荐的数据基础表结构一定要设计得“写多读也合适”。浏览、收藏、评论都是高频操作写入频率高所以要给行为时间字段建索引。用户行为表里我额外加了一个行为权重字段收藏权重远大于浏览权重评论权重大于收藏这样在计算偏好时可以直接取值不需要在代码里硬编码权重。2.2 菜品标签体系的设计基于内容的推荐非常依赖标签体系。我给每道菜设计了三种类型的标签并存放在菜品标签表中菜系标签川菜、粤菜、苏菜、湘菜等。口味标签微辣、中辣、特辣、清淡、酸甜、咸鲜。场景标签午餐、晚餐、夜宵、聚会、一人食、健身餐。标签表结构很简单菜品ID、标签类型、标签名、标签权重。标签权重用于表达菜品对该标签的归属度比如“水煮肉片”对“中辣”标签权重0.8对“聚会”场景标签权重0.6。这样在处理偏好计算时每个标签携带力度信息结果比纯0/1的标签更细腻。给菜品打标签是前期最耗时的工作。我的经验是不要一次性全量打标先给热门菜品打标让推荐系统先跑起来然后逐步补全更多菜品。还可以做一个简单的标签推荐功能后台根据菜品名称自动匹配预置标签词库比如名称含“辣”或“椒”字的自动关联口味标签人工确认后入库。2.3 用户行为数据采集的踩坑提醒行为数据的采集时机很关键。收藏、评论这类显性行为好办接口里顺手记录就行。最容易漏的是浏览行为很多人在前端页面里压根没有给菜品详情页埋点什么“当前用户看了这道菜”的请求导致推荐模块永远只有收藏和评论的数据稀疏得很。我的做法是用户点击菜品详情时前端发一条浏览行为记录请求后端异步写入行为表。同一个用户同一道菜在10分钟内的重复浏览会被合并更新避免同一道菜反复叠加浏览权重。这一个细节救了推荐效果——数据稀疏度大幅下降冷启动问题缓解不少。另外搜索行为也应该记录。用户搜索“火锅”说明此刻有火锅需求更新用户偏好画像时给火锅相关标签临时增加权重这是短期兴趣过期后要自动衰减。3. AI推荐算法的实现细节3.1 基于内容的推荐用户画像与余弦相似度这一部分是整个系统技术含量最高的地方也是文档和答辩时最能展示亮点的地方。我先说核心思路要推荐就得知道用户喜欢什么。用户画像是用用户所有历史行为的加权标签向量表示的。每个用户经过一段时间的行为积累会得到形如这样一个向量用户画像向量: {川菜: 0.82, 微辣: 0.74, 聚会: 0.53, 粤菜: 0.31}每个值代表对应标签的偏好强度。计算方法是用户在该标签下的所有行为加权求和再经过归一化处理。权重安排为浏览行为权重1搜索行为权重2收藏行为权重4评论行为权重3下单行为权重如果有订单模块5这里我做了一个时间衰减函数近期行为权重更高远期行为权重递减。例如30天前的行为权重乘以0.57天前的行为权重乘以0.9。这个时间衰减是影响推荐新鲜感的核心因素。菜品向量则是菜品标签表中对应标签及权重的组合。然后计算用户画像向量与每个菜品向量的余弦相似度取TopN作为推荐结果。余弦相似度的公式是cosine_similarity (A · B) / (||A|| × ||B||)其中A是用户画像向量B是菜品向量在Java里的实现也不复杂用一个Map表示稀疏向量就能算。核心代码大致是这样public class CosineSimilarityUtil { public static double calculate(MapString, Double userVector, MapString, Double itemVector) { if (userVector.isEmpty() || itemVector.isEmpty()) { return 0.0; } double dotProduct 0.0; double normA 0.0; double normB 0.0; for (Map.EntryString, Double entry : userVector.entrySet()) { Double itemValue itemVector.get(entry.getKey()); if (itemValue ! null) { dotProduct entry.getValue() * itemValue; } normA entry.getValue() * entry.getValue(); } for (Double value : itemVector.values()) { normB value * value; } if (normA 0 || normB 0) { return 0.0; } return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB)); } }我在文档里详细解释了这个工具类的设计思路它没有用任何推荐算法框架而是纯手写的相似度计算逻辑这样在答辩时能清清楚楚讲明白原理也方便后续改造扩展。3.2 协同过滤相似用户的挖掘与降级处理单纯使用基于内容的推荐有一个明显问题推荐结果过于同质化。如果用户只收藏过川菜系统会一直只推荐川菜用户想换口味时系统完全无感。所以要叠加协同过滤。我这里采用的是物品协同过滤ItemCF因为对于美食推荐场景用户数量往往比菜品数量大得多物品协同过滤的实时性和稳定性更好。ItemCF的流程是离线计算菜品之间的相似度矩阵依据是“被同一个用户共同收藏/评论的菜品往往相似”。在线推荐时找到用户历史交互过的菜品集合根据相似度矩阵取出最近邻菜品。对候选菜品按加权相似度排序与基于内容的推荐结果按比例融合。举个例子用户A曾经收藏了“宫保鸡丁”和“水煮牛肉”。在相似度矩阵中“宫保鸡丁”与“干煸四季豆”的相似度是0.7“水煮牛肉”与“毛血旺”的相似度是0.6那么“干煸四季豆”和“毛血旺”就会被推荐给A用户。物品相似度矩阵的计算我放在定时任务里做每天凌晨2点跑一次用新的用户行为数据刷新矩阵。这样既保证推荐结果有更新又不会影响在线接口的响应速度。协同过滤在数据稀疏时会出现冷启动问题——新用户没有任何行为算不出相似物品。我的降级方案是如果用户行为不足3个直接采用热门榜推荐同时在前端引导用户填写“口味偏好问卷”用问卷结果初始化用户画像加速冷启动。3.3 推荐服务的接口设计与性能优化推荐服务对外暴露一个核心接口GET /api/recommend?userIdxxxpage1size10后端流程是加载用户画像 - 生成内容推荐候选 - 生成协同过滤候选 - 过滤已下架菜品和用户近期已浏览菜品 - 按融合规则排序 - 缓存结果 - 返回。性能优化是这里的关键。我强烈建议把推荐结果做成缓存因为完整计算一次推荐的耗时大约在200-500毫秒在线接口直接计算会影响体验。我用Redis缓存每个用户的推荐结果key设计为“recommend:userId:page”缓存有效期1小时。用户再次刷新推荐页时直接从缓存拿结果响应时间会降到50毫秒以内。这里有个细节缓存的推荐结果要设置合理的过期时间不然用户行为发生变化后推荐结果迟迟不更新会显得系统很笨。我设置的是1小时过期另外在用户产生收藏或评论行为时主动删除该用户的推荐缓存这样用户下一次访问推荐页就会触发重新计算。4. 后端SpringBoot核心模块实现4.1 项目结构划分与分层思想后端项目我按标准的分层结构组织com.example.foodrecommend ├── controller // 接口入口层 ├── service // 业务逻辑层 ├── dao // 数据访问层 ├── entity // 实体类 ├── dto // 数据传输对象 ├── common // 通用工具、结果封装、异常处理 ├── config // 配置跨域、MyBatis、Redis等 ├── task // 定时任务协同过滤矩阵刷新 └── recommend // 推荐算法模块推荐算法模块单独放在recommend包下和普通业务代码隔离。这样做最大的好处是算法的输入输出非常清晰输入是用户行为数据和菜品标签数据输出是推荐结果列表。隔离之后后续无论是改进算法还是调试错误都不会影响周边业务接口对维护体验的提升很明显。4.2 登录认证与安全设计登录这块我采用JWT无状态认证方案。用户登录成功后后端生成一个包含用户ID和过期时间的Token前端在后续请求的Header中携带Token。JWT的好处是不需要在服务端保存会话状态部署到云服务器多开实例时无需考虑Session共享问题。密码存储绝对不允许明文。我使用的是BCrypt加密Spring Security框架里自带这个工具类。BCrypt的特性是每次加密同一个密码得到的结果都不同但校验时都能通过这是因为每次加密时生成随机盐并拼接进加密结果中。这种机制可以有效对抗彩虹表攻击。接口安全上我用拦截器统一校验Token直接调用需登录接口时没有Token或Token过期都返回401状态码。管理端接口还需要额外校验角色标识确保普通用户无法触达管理员功能。4.3 菜品、评论、收藏等CRUD流程菜品模块主要是后端管理维护菜品信息包括菜品图片上传到本地磁盘或云存储、菜品上下架状态切换、按分类分页查询等。分页查询我用了MyBatis-Plus的分页插件前端传页码和每页条数即可。评论模块需要注意评论列表的排序策略我的做法是“置顶时间倒序”商家可以后台设置置顶评论普通评论按时间倒序排列。每条评论后面要统计当前用户是否已经评论过前端需要展示“已评论”状态时后端返回当前用户的评论信息。收藏模块是一个典型的关联表操作。用户点击收藏时先判断记录是否存在存在则取消收藏不存在则新增收藏记录同时更新用户行为表。收藏列表要能分页查询并且关联查询出菜品的最新价格和上架状态防止下架菜品出现在收藏列表中。4.4 异步任务与定时任务的应用异步任务在行为记录场景中非常实用。浏览行为产生频率高如果同步写入数据库会拖慢菜品详情的响应速度。我的做法是在浏览行为接口中加入Spring的Async注解让行为写入在异步线程执行接口立即返回用户无感知。定时任务我用Spring自带的Scheduled注解主要跑两个任务物品相似度矩阵刷新读取近30天用户行为数据重新计算菜品相似度矩阵。冷门菜品清理持续30天无任何行为的菜品自动标记为“较少推荐”。定时任务有个容易忽视的点默认是单线程调度如果有多个任务串行执行会导致后面的任务延迟。我配置了自定义调度线程池让不同任务跑在独立线程中。5. 前端Vue页面与交互实现5.1 前端工程架构与路由设计前端使用的Vue框架搭配Vue Router和Vuex/Pinia状态管理。工程通过Vue CLI或Vite初始化生成。路由设计上分成用户端、管理端两个独立布局用户端布局包含导航栏、侧边栏、推荐页路由、菜品列表路由、详情页路由。管理端布局单独走一套布局组件包含菜品管理、用户管理、评论管理等路由。路由守卫用于权限控制需要登录的路由未登录时统一跳转登录页管理员路由还需要校验本地存储的用户角色字段。Vite在开发环境下的代理配置很关键前后端联调时直接把“/api”前缀的请求代理到后端的服务器地址避免开发过程中遇到跨域问题。上线时则通过Nginx反向代理转发让前后端同一域名访问进一步避免跨域困难。5.2 首页推荐流的技术实现首页推荐流是整个项目视觉和使用体验的第一印象。我在前端使用卡片流布局卡片展示菜品图片、名称、标签、价格、收藏按钮和评分。数据来源是“/api/recommend”接口。为了给用户“每次打开都不一样”的感觉前端在推荐接口中带一个随机偏移量参数。后端拿到偏移量后在推荐结果列表中按偏移量截取不同片段返回。这个设计能大幅提升推荐页的新鲜感用户在测试时会觉得系统确实在“做个性化推荐”。推荐流采用滚动加载前端监听滚动到底部即触发下一页请求。关键点在于防重复请求我用一个“isLoading”标志位控制接口响应前不允许发起下一次加载。5.3 菜品详情页与用户行为埋点菜品详情页展示菜品完整信息、标签列表、所属商家、评论区以及“相似菜品推荐”模块。相似菜品推荐调用后端接口依据是物品相似度矩阵数据结构直接返回菜品列表。埋点逻辑放在详情页的生命周期钩子里页面加载后延时1秒触发浏览行为记录请求。为什么要延时因为如果用户只是滑动列表时误点了详情再立刻返回这类误触浏览会污染数据。延时1秒能在很大程度上过滤掉误触。收藏按钮的交互反馈要好用户点击后立即改变按钮状态同时提示成功或因未登录跳转登录页。评论功能是弹窗或者跳转评论区锚点评分采用星级组件。5.4 用户偏好画像的可视化展示这个功能是我个人很喜欢的部分在个人中心页面展示我的美食偏好画像。后端提供“/api/user/profile”接口返回用户已生成的偏好标签及权重。前端用词云或柱状图展示权重最高的若干标签比如“你的口味关键词川菜 85% 微辣 78% 火锅 62%”。这个功能虽然实现难度不大但“AI存在感”立刻增强文档和演示效果都能上一个档次。用户也能直观看到自己的行为如何影响推荐结果形成使用闭环。可视化工具我用的ECharts饼图或雷达图都适合展示偏好分布。雷达图能清晰呈现多种口味的偏重程度做演示时最加分。6. 项目文档与指导搭建视频的组织方法6.1 文档结构设计从设计文档到部署手册这部分标题里有“文档”二字其实是很多人的薄弱项。一份合格的课程或毕业项目文档我的建议是以下几章结构第一章 项目概述说明项目背景、解决问题、使用意义。 第二章 需求分析划分角色列出每个角色的功能需求和非功能性需求性能、安全、响应时间。 第三章 技术选型说明为什么选SpringBoot、Vue、MySQL、Redis每项技术解决什么问题。 第四章 系统设计给出完整的数据库设计表字段、关系、接口设计清单路径、入参、出参、推荐算法设计原理、流程、公式。 第五章 系统实现按模块展示核心代码片段和界面截图每一块都配上说明。 第六章 系统测试测试用例设计、测试结果分析、性能测试数据。 第七章 部署指南本地运行步骤、云服务器部署步骤、环境安装要点。数据库设计这一章建议画出实体关系描述不需要复杂的图表工具直接用文字加表格列出每个表的关键字段、类型、说明即可。6.2 指导搭建视频的分段录制思路指导搭建视频是这个项目里最花时间的部分但效果也最好。我的视频总时长控制在90分钟以内分六段第一段10分钟项目演示把系统所有功能过一遍让观众先知道成品是什么样的。录这一段的目的是建立目标感观众能判断是不是自己要的东西。第二段15分钟环境准备Java、Maven、Node.js、MySQL的安装和验证。第三段20分钟后端启动导入数据库脚本、修改配置文件、启动SpringBoot、接口测试。第四段20分钟前端启动依赖安装、接口地址修改、开发环境启动、页面效果确认。第五段15分钟数据初始化推荐系统如何填入测试数据需要多少用户和菜品才能让推荐效果明显。第六段10分钟常见错误解决汇总端口占用、数据库连接失败、前端白屏等高频问题。录制视频时务必用固定分辨率和清晰的编程字体操作步骤在屏幕上要有光标识。关键配置文件修改的地方最好用红框或高亮标注一下。我录的时候每段都会先写脚本录完再剪辑不然返工会浪费大量时间。6.3 数据初始化脚本的重要性做美食推荐系统最尴尬的情况是项目能跑但没有任何数据推荐页空荡荡。我建议写一个完整的数据初始化SQL脚本包含10个分类、100道菜品、30个测试用户、2000条左右的随机用户行为数据。有了这批数据推荐效果才能真实展现。随机行为数据的生成我用了一段Python脚本也可以写Java批量插入程序逻辑是随机选用户、随机选菜品、随机行为类型、随机时间构造出接近真实使用的数据分布。脚本要在文档中说明方便使用者生成更多样的测试数据。7. 上线部署与常见问题排查7.1 本地与云服务器的部署流程本地部署比较简单后端打包成jar前端构建成dist静态文件。上线我推荐用Nginx挂载前端静态文件并反向代理后端接口大致配置如下server { listen 80; server_name your-domain.com; root /var/www/food-recommend/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }前端打包时要把接口基础地址改成相对路径“/api”这样走Nginx代理时不用在客户端硬编码IP方便迁移部署。云服务器还要考虑MySQL和Redis的远程访问安全不要把端口直接对公网开放用防火墙只允许服务器的本机访问或者限制来源IP。密码要设置复杂一点机关项目千万别用什么弱口令。7.2 常见问题实录与解决办法我把实际开发中遇到过的高频问题整理成表格现象原因解决方案前端请求接口报跨域错误开发环境未配置代理或后端未开放跨域开发用Vite代理上线用Nginx同域转发菜品图片加载不出来图片上传路径与部署环境不一致配置文件使用相对路径上线后统一修改为服务器绝对路径推荐接口返回空列表用户行为数据过少使用初始化脚本生成测试数据或调用冷启动热门推荐登录后Token丢失JWT过期时间过短或前端未存储Token合理设置过期时间前端使用本地存储持久化Token定时任务没有执行未开启SpringBoot的定时任务注解扫描在启动类或配置类加入“EnableScheduling”注解评分数据缺少某道菜相似度矩阵未包含新菜品新菜品需打标签并等下次定时任务刷新矩阵7.3 性能优化与安全加固性能方面推荐接口是核心必须走Redis缓存。菜品列表查询用MySQL索引重点关注分类字段和行为时间字段的索引设计。分页深度大时会有性能问题我的做法是限制最大页码禁止一次跨太多页。安全方面除了JWT验证还应该对管理端接口做请求频率限制防止恶意调用。可以用拦截器配合本地计数Map实现简单的限流或者引入现成的限流组件。文件上传要校验文件类型和大小防止上传非法内容到服务器。8. 这段开发经历给我的几个真实体会最后说几句掏心窝的话。做这类全栈项目最容易轻视的有三点一是算法模块的代码可读性二是测试数据的丰富度三是文档与视频的分层介绍能力。关于第一点建议记住“先把协同过滤和内容推荐的接口设计得足够干净再加功能”。我在初期把代码揉在一起后面每次改参数都觉得如履薄冰重构之后算法模块变成了标准的“输入-计算-输出”结构调试和答辩都轻松多了。关于第二点建议在项目一开始就准备数据生成脚本。如果你等到功能全部写完再补数据往往会因为表结构对不上而大量返工。我就是中途加的行为权重字段导致之前脚本全部重写白白折腾了一天。关于第三点写文档和录制视频要趁着开发的热度记忆做不要等项目全部结束再补。开发过程中每完成一个模块顺手记录遇到的问题和截图最后汇总质量远高于一次性赶工。这个项目的后续扩展空间也有不少。比如接入真实位置服务按照用户所在地推荐附近的特色美食或者在菜品详情中加入营养标签基于用户健康目标做更细颗粒度的推荐。我自己后续就打算把行为数据做成小时级增量更新让推荐实时性更强。系统的骨架已经打好了后续的优化和扩展都只是时间问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑