【SpringBoot + Vue 】美食推荐系统(源码)
目录
一、项目背景
在移动互联网与社交媒体高度发达的今天,“吃”已不再仅仅是满足生理需求的日常行为,更演变为一种重要的社交体验与生活方式。大众点评、小红书、抖音等平台上,探店分享、美食打卡等内容持续火爆,折射出用户对发现优质美食体验的强烈渴望。然而,面对海量的美食信息,用户往往陷入“选择困难症”:平台上餐厅评价褒贬不一、推荐内容同质化严重、算法偏好过度集中于热门商家,导致真正符合个人口味偏好或具有地方特色的小众美食难以被发现。与此同时,传统美食推荐模式多依赖编辑人工筛选或简单按热度排序,缺乏对用户个性化需求的深度理解与动态适配。
从技术应用现状来看,现有主流美食平台虽已引入推荐算法,但普遍存在“信息茧房”问题——系统过度推送用户已浏览过的同类餐厅,且冷启动阶段对新用户缺乏有效的个性化引导。此外,许多小型餐饮资讯网站或校园实践项目仍采用老旧的技术架构,前后端耦合严重、响应速度慢、移动端适配性差,难以提供流畅的用户体验。这为开发一套轻量级、可扩展、具备智能推荐能力的美食推荐系统提供了现实需求。
基于上述背景,本课题拟设计并实现一套基于Spring Boot 3 + Vue 3架构的美食推荐系统。后端采用Spring Boot 3构建RESTful API,结合MyBatis-Plus简化数据访问层开发,使用MySQL存储餐厅信息、用户行为及菜品数据;前端利用Vue 3 + Element Plus构建响应式用户界面,适配PC与移动端访问。系统将涵盖餐厅浏览与搜索、用户评分与评论、个性化推荐(基于协同过滤或内容基础的简单推荐算法)、美食收藏与分享等核心功能,并引入JWT实现用户认证。通过该系统,用户可便捷发现符合个人口味偏好的餐厅,商家或管理员也能获取数据可视化运营洞察。此外,本课题也是对前后端分离架构与推荐算法工程化落地的一次完整实践,可为同类O2O生活服务类系统的开发提供参考。
二、技术介绍
本项目采用前后端分离架构,后端基于 Spring Boot 2.6.7 构建,利用其自动配置与启动器依赖特性,快速搭建RESTful API服务。Spring Security结合JWT实现基于Token的用户认证与接口授权,保障系统访问安全;Spring Mail用于发送注册验证、密码找回等邮件通知。持久层选用MyBatis 2.2.2,通过XML或注解方式编写SQL语句,灵活处理复杂的多表关联查询;MySQL 8.0作为关系型数据库存储用户信息、餐厅数据、评分记录及评论内容。Lombok通过注解简化实体类代码,减少getter/setter等样板代码的编写。
前端采用 Vue 2.6.11 框架,结合Vuex进行全局状态管理(如用户登录状态、购物车数据),利用Vue Router实现页面路由与导航守卫。Element UI 2.15.6作为UI组件库,提供表格、表单、弹窗等丰富组件,快速搭建后台管理界面。网络请求统一由Axios封装,支持请求/响应拦截与Token自动注入。ECharts 6.0用于生成餐厅评分趋势、菜品分类统计等可视化图表,为运营决策提供数据支撑;高德地图API集成地点搜索、位置展示与路线规划功能,增强用户体验。SASS作为CSS预处理器,支持变量、嵌套等特性,提升样式代码的可维护性。整体技术栈兼顾了开发效率与系统性能,为美食推荐系统的稳定运行提供了坚实保障。
三、功能介绍
三、核心和亮点功能
智能推荐算法 - 协同过滤+内容推荐的混合推荐策略,根据用户行为数据精准推荐美食
个性化推荐 - 基于浏览历史、收藏记录、评价信息构建用户画像
美食打卡 - 支持图片上传、地理位置定位(集成高德地图)、心情标签
评价系统 - 用户评分、图文评价、点赞互动、评价管理
AI聊天助手 - 集成智谱AI,提供智能美食咨询和多轮对话
标签筛选 - 多维度标签(口味、场景、健康)精准筛选美食
数据可视化 - ECharts实时统计分析,数据看板展示系统运营数据
双端系统 - 用户端(浏览/收藏/评价/打卡)+ 管理端(美食管理/数据统计/用户管理)
四、技术亮点
邮件验证 - Spring Mail实现注册验证码、密码找回功能
密码加密 - BCrypt加密算法保障用户密码安全
推荐算法 - 基于用户行为的协同过滤算法,计算用户相似度和物品相似度
地图集成 - 高德地图API,支持地理位置定位和地址选择
日志管理 - Logback日志框架,分级别记录系统运行日志
热部署 - Spring DevTools支持,提升开发效率
四、代码实现
@Service
public class RecommendServiceImpl implements RecommendService {
@Autowired
private RatingMapper ratingMapper;
@Override
public List<Restaurant> getUserBasedRecommendations(Long userId) {
// 1. 获取所有用户对餐厅的评分数据
List<Rating> allRatings = ratingMapper.selectAll();
// 2. 构建用户-餐厅评分矩阵
Map<Long, Map<Long, Double>> userRestRatingMap = buildUserRestRatingMap(allRatings);
// 3. 计算目标用户与其他用户的皮尔逊相似度
Map<Long, Double> similarityMap = new HashMap<>();
Map<Long, Double> targetUserRatings = userRestRatingMap.get(userId);
for (Map.Entry<Long, Map<Long, Double>> entry : userRestRatingMap.entrySet()) {
Long otherUserId = entry.getKey();
if (otherUserId.equals(userId)) continue;
double similarity = pearsonCorrelation(targetUserRatings, entry.getValue());
if (similarity > 0.3) { // 相似度阈值
similarityMap.put(otherUserId, similarity);
}
}
// 4. 加权预测未评分餐厅的得分,取Top-N推荐
return predictAndRecommend(userId, targetUserRatings, similarityMap);
}
}
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private JwtUtils jwtUtils;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
String userId = jwtUtils.getUserIdFromToken(token);
// 从Redis获取用户权限信息
String redisKey = "LOGIN_USER:" + userId;
LoginUser loginUser = (LoginUser) redisTemplate.opsForValue().get(redisKey);
if (loginUser != null && jwtUtils.validateToken(token, loginUser.getUsername())) {
// 将用户信息存入SecurityContext
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
}
五、系统实现








AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)