news 2026/9/14 14:13:45

SpringBoot+Vue美食评测系统全栈实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue美食评测系统全栈实践指南

简介:本资源是一套面向Java全栈开发初学者与课程设计者的美食评测系统完整实践方案,聚焦人工智能在信息化管理中的落地应用,解决餐饮领域用户评价、菜品检索与个性化推荐等典型业务问题。压缩包共667个文件,含190个Java后端逻辑代码、133个Vue前端组件、161个SVG图标资源及68个PNG界面素材,辅以MyBatis持久层配置(XML)、Spring Boot启动脚本(bat)和论文配套静态资源,整体48.43MB,结构清晰、模块解耦度高。已有65人学习下载,适合用于毕业设计参考、Spring Boot+Vue工程化教学或信息管理系统实战训练。读者可直接运行前后端分离架构,复现用户登录、美食搜索、多维度评分、个人中心等核心功能,并结合13000字论文深入理解需求分析、数据库ER建模、RESTful接口设计及潜在AI推荐模块的扩展思路。

1. 为什么一个「美食评测系统」要选 SpringBoot + Vue?它真能跑通从登录到写评、查店、看热榜的完整链路?

你手头拿到的这个.zip文件,表面看是「源代码+数据库+13000字论文」三件套,但背后是一套被高校课程设计、毕设答辩和中小餐饮SaaS原型反复验证过的全栈技术组合:后端用 SpringBoot 做 RESTful 接口与事务控制,前端用 Vue 实现响应式交互与路由跳转,MySQL 存储用户、店铺、菜品、评分、图片路径等结构化数据。它不是玩具 Demo——真实部署时能支撑 500+ 用户并发提交评测、按口味标签筛选餐厅、查看近7天热门菜品TOP10;也不是过度工程——没有引入 Kafka 或 Redis 集群,所有核心功能(注册/登录、发布评测、上传图片、分页查询、模糊搜索)都落在 SpringBoot 的@RestController和 Vue 的vue-router+axios调用链上。适合两类人:一是计算机专业大三学生,正卡在「怎么把数据库表变成可点击的网页」这一步;二是刚转前端的开发者,想通过一个有真实业务逻辑(非 TodoList)的项目,搞懂 Vue 组件如何接收后端 JSON 并渲染成带星级评分的卡片。它不教你怎么写论文,但论文里所有系统截图、ER图、接口文档、测试用例,都来自你本地跑起来的这套代码。

2. 搭建环境:从解压 ZIP 到前后端同时启动的最小可行路径

2.1 解压后目录结构必须确认的 5 个关键位置

拿到.zip后不要直接双击运行!先解压并检查以下路径是否存在,这是后续所有操作的前提:

food-review-system/ ├── backend/ ← SpringBoot 项目根目录(含 pom.xml) │ ├── src/main/java/com/example/foodreview/ │ └── src/main/resources/application.yml ├── frontend/ ← Vue 项目根目录(含 package.json) │ ├── src/ │ │ ├── api/ ← 封装 axios 请求的目录(重点看 index.js) │ │ ├── router/ ← 路由配置(index.js 中应有 /login, /home, /detail/:id) │ │ └── views/ ← 页面组件(Login.vue, Home.vue, ReviewForm.vue 等) │ └── vue.config.js ← 必须存在,用于配置跨域代理(见 2.3) ├── database/ ← SQL 脚本目录(food_review.sql 是建库建表语句) ├── docs/ ← 论文 PDF 和 Word 版(非运行必需) └── README.md ← 作者写的快速启动说明(优先读它)

提示:如果frontend/src/api/index.js里 baseURL 写的是http://localhost:8080,而你的后端端口是8081,必须立刻修改,否则所有请求 404。Vue 项目本身不处理跨域,靠vue.config.jsdevServer.proxy转发到后端。

2.2 后端启动:SpringBoot 项目必须调的 3 个参数

进入backend/目录,用 IntelliJ IDEA 或 VS Code 打开,不要直接点绿色三角运行。先检查src/main/resources/application.yml中以下三项:

spring: datasource: url: jdbc:mysql://localhost:3306/food_review?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 # ← 这里必须是你本地 MySQL 的真实密码 jpa: hibernate: ddl-auto: update # ← 关键!设为 'update' 可自动建表;设为 'validate' 则只校验不建表 show-sql: true properties: hibernate: format_sql: true
  • url中的food_review是数据库名,必须与database/food_review.sqlCREATE DATABASE food_review;一致;
  • username/password必须匹配你本机 MySQL 服务的登录凭证(若用 Docker 运行 MySQL,请确认容器端口映射为3306);
  • ddl-auto: update是开发阶段最安全的选项:首次启动时自动创建user,restaurant,review等表;后续加字段会自动新增列,不会删数据。

启动命令(终端执行):

cd backend mvn spring-boot:run -Dspring.profiles.active=dev

看到Tomcat started on port(s): 8080 (http)即表示后端就绪。

2.3 前端启动:Vue 依赖安装与跨域代理配置

进入frontend/目录,执行:

npm install # 若报错 node-sass,改用: npm install --legacy-peer-deps # 启动前检查 vue.config.js 是否包含以下代理配置:

vue.config.js必须有如下内容(否则浏览器控制台将出现 CORS 错误):

module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', // ← 必须与后端实际端口一致 changeOrigin: true, pathRewrite: { '^/api': '' // ← 把前端请求的 /api/login 映射为后端的 /login } } } } }

然后启动:

npm run serve

访问http://localhost:8080(注意:Vue 默认端口是8080,但此时已被后端占用,所以实际是http://localhost:8081—— 查看终端输出确认)。若页面显示「欢迎来到美食评测系统」且无控制台红字,说明前后端通信成功。

2.4 数据库初始化:3 步执行 SQL 脚本,避免「表不存在」异常

不要用 IDEA 自带的 Database 工具直接拖入.sql文件——它可能忽略CREATE DATABASE语句。推荐用命令行:

# 1. 登录 MySQL mysql -u root -p # 2. 执行建库建表(假设 SQL 文件路径为 /path/to/food_review.sql) mysql> source /path/to/food_review.sql; # 3. 验证表是否生成 mysql> USE food_review; mysql> SHOW TABLES; -- 应看到:review, restaurant, user, tag, review_tag 等至少 5 张表

food_review.sql中的关键约束必须存在:

  • review.user_id外键关联user.id
  • review.restaurant_id外键关联restaurant.id
  • review.score字段类型为TINYINT CHECK (score BETWEEN 1 AND 5)—— 这是评分合法性的数据库层保障。

注意:如果执行source报错Unknown database 'food_review',说明CREATE DATABASE food_review;语句未被执行,需手动执行该语句后再重试。

3. 核心功能落地:从用户注册到发布一条带图评测的完整代码链

3.1 后端接口:ReviewController如何接收 Vue 提交的 JSON

backend/src/main/java/com/example/foodreview/controller/ReviewController.java中,发布评测的核心方法是:

@PostMapping("/reviews") public ResponseEntity<Map<String, Object>> createReview(@RequestBody ReviewDTO reviewDTO) { // 1. 校验 DTO:@NotBlank, @Min(1), @Max(5) 注解已写在 ReviewDTO 类中 if (!reviewDTO.isValid()) { Map<String, Object> error = new HashMap<>(); error.put("code", 400); error.put("message", "评分必须为1-5,评论不能为空"); return ResponseEntity.badRequest().body(error); } // 2. 保存主表 review Review review = new Review(); review.setUserId(reviewDTO.getUserId()); review.setRestaurantId(reviewDTO.getRestaurantId()); review.setScore(reviewDTO.getScore()); review.setContent(reviewDTO.getContent()); review.setCreatedAt(LocalDateTime.now()); reviewRepository.save(review); // ← JPA 自动生成 INSERT 语句 // 3. 处理多对多标签(如「辣」「甜」「适合约会」) for (String tagName : reviewDTO.getTags()) { Tag tag = tagService.findOrCreate(tagName); // 先查后建,避免重复 reviewTagRepository.save(new ReviewTag(review.getId(), tag.getId())); } Map<String, Object> result = new HashMap<>(); result.put("code", 200); result.put("message", "评测发布成功"); result.put("reviewId", review.getId()); return ResponseEntity.ok(result); }
  • @RequestBody ReviewDTO表明 SpringBoot 会自动将前端 POST 的 JSON 解析为 Java 对象;
  • reviewDTO.isValid()是自定义校验逻辑,比单纯用@Valid更灵活(例如判断content.length() < 500);
  • reviewTagRepository.save(...)使用复合主键实体ReviewTag,对应数据库中的关联表review_tag

3.2 前端调用:Vue 组件中如何组装 FormData 并上传图片

frontend/src/views/ReviewForm.vue中,用户填写表单并点击「发布」时,关键逻辑在submitReview方法:

methods: { async submitReview() { const formData = new FormData(); formData.append('userId', this.userId); formData.append('restaurantId', this.restaurantId); formData.append('score', this.score); formData.append('content', this.content); // 图片文件必须用 File 对象 append,不能转 base64 if (this.imageFile) { formData.append('image', this.imageFile); // ← 后端 Controller 需用 @RequestParam 接收 } // 标签数组转为字符串,用逗号分隔 formData.append('tags', this.selectedTags.join(',')); try { const res = await api.post('/reviews', formData, { headers: { 'Content-Type': 'multipart/form-data' } // ← 必须显式设置 }); this.$message.success('评测发布成功!'); this.$router.push(`/detail/${res.data.reviewId}`); } catch (err) { this.$message.error('发布失败:' + err.response?.data?.message || '网络错误'); } } }
  • FormData是浏览器原生 API,专为文件上传设计;若用JSON.stringify()发送图片,后端收到的是[object File]字符串;
  • headers: { 'Content-Type': 'multipart/form-data' }不能手动设置——浏览器会自动添加边界(boundary),手动设置反而导致 400;
  • api.post()调用的是frontend/src/api/index.js中封装的 axios 实例,其 baseURL 已设为/api,因此最终请求 URL 是http://localhost:8081/api/reviews,经vue.config.js代理到http://localhost:8080/reviews

3.3 数据库同步:当 Vue 提交「川菜」「火锅」两个标签时,MySQL 怎么存?

food_review.sqltag表结构为:

CREATE TABLE tag ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE, created_at DATETIME DEFAULT CURRENT_TIMESTAMP );

review_tag关联表为:

CREATE TABLE review_tag ( review_id BIGINT NOT NULL, tag_id BIGINT NOT NULL, PRIMARY KEY (review_id, tag_id), FOREIGN KEY (review_id) REFERENCES review(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tag(id) ON DELETE CASCADE );

当用户提交['川菜','火锅']时,后端执行:

  1. 查询SELECT id FROM tag WHERE name = '川菜'→ 若无则INSERT INTO tag(name) VALUES('川菜')
  2. 同理处理'火锅'
  3. 插入两条记录到review_tag(1001, 201)(1001, 202),其中1001是新评测 ID,201/202是标签 ID。

提示:这种设计避免了在review表中用VARCHAR存逗号分隔的标签(违反第一范式),也避免了每次查「带川菜标签的评测」时用LIKE '%川菜%'导致全表扫描。

4. 论文写作支撑:从系统截图到 ER 图,哪些内容可直接复用?

4.1 论文中必须出现的 4 类系统截图及生成方式

你的 13000 字论文不是空谈架构,所有图表必须来自你本地运行的系统。以下是可一键截取的高质量配图:

截图类型访问路径截图要点说明是否需登录
系统首页http://localhost:8081/显示顶部导航栏(首页/发现/我的)、轮播图(3 张美食图)、热门餐厅卡片(带星级和评分)
评测详情页http://localhost:8081/detail/1URL 中1为任意存在的 review.id;截图需包含:菜品图、评分星标、评论文字、发布时间、点赞按钮
后台管理页http://localhost:8081/admin需先用管理员账号(如admin/123456)登录;截图展示「审核评测」「禁用用户」两个操作按钮及表格数据
移动端适配效果Chrome 开发者工具 → Toggle device toolbar → 选 iPhone 12截图显示:导航栏折叠为汉堡菜单、卡片宽度自适应、字体大小未缩放过度

注意:所有截图必须关闭浏览器地址栏和开发者工具面板,使用系统自带截图工具(Win+Shift+S / Cmd+Shift+4),保证边框干净。不要用 QQ 截图,其水印会被学校查重系统识别。

4.2 ER 图绘制:用 MySQL Workbench 逆向工程生成标准图

不要手动画 ER 图!用官方工具保证学术规范性:

  1. 打开 MySQL Workbench;
  2. 连接本地food_review数据库;
  3. 菜单栏Database → Reverse Engineer...
  4. 勾选全部表(review,user,restaurant,tag,review_tag);
  5. 完成后右键 Diagram →Export as PNG
  6. 在论文中插入时,图标题写为「图 3.1 美食评测系统实体关系图」,并在正文中说明:「review_tag表实现reviewtag的多对多关系,符合第三范式要求」。

生成的 ER 图中,review_tag表必须显示为菱形(关系实体),两端连线标注1..*,这是数据库课程设计得分关键点。

4.3 接口文档表格:从 Swagger UI 直接导出可复制的 Markdown

后端项目已集成 SpringDoc OpenAPI(Swagger 3),启动后访问http://localhost:8080/swagger-ui.html

  • 点击ReviewController下的POST /reviews
  • 展开Example Value,复制 JSON 示例;
  • 点击Model查看ReviewDTO字段说明;
  • 整理为论文中的「表 4.2 评测发布接口参数说明」:
参数名类型必填说明示例值
userIdLong用户ID,从登录态获取1001
restaurantIdLong餐厅ID,由前端列表页传入2001
scoreInteger评分,范围 1-54
contentString评论正文,长度 10-500 字「锅底香而不腻…」
tagsString[]标签数组,前端转为逗号分隔字符串["麻辣","适合聚餐"]
imageFile图片文件,MIME 类型为 image/*(二进制流)

提示:此表格直接决定论文「系统设计」章节的严谨性。若表格中缺少image参数或未注明 MIME 类型,在答辩时会被质疑「文件上传功能未实现」。

5. 常见故障排查:当「页面空白」「404」「图片不显示」时,按顺序查这 5 个点

5.1 页面完全空白:90% 是 Vue 路由模式与后端静态资源冲突

现象:访问http://localhost:8081/显示白屏,控制台无报错,Network 标签页中index.html返回 200 但app.js404。

原因:Vue CLI 默认用history模式,但后端未配置 fallback。当用户直接访问/detail/1时,请求被发给后端,而 SpringBoot 没有@GetMapping("/detail/{id}"),返回 404。

解决:
方案一(推荐):改用hash模式,在frontend/src/router/index.js中:

const router = new VueRouter({ mode: 'hash', // ← 改这里 routes: [ /* ... */ ] });

重启npm run serve,URL 变为http://localhost:8081/#/detail/1,问题消失。

方案二:后端加静态资源映射(需改backend/src/main/java/com/example/foodreview/config/WebConfig.java):

@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:/static/"); }

并在backend/src/main/resources/static/下放index.html(Vue 打包后的文件),但此方案增加部署复杂度。

5.2 图片上传后不显示:检查后端application.yml的文件路径配置

现象:评测发布成功,但详情页图片区域为空白,浏览器 Network 中图片请求返回 404。

原因:Vue 前端渲染的<img :src="review.imageUrl" />imageUrl是相对路径(如/uploads/20240510/abc.jpg),但 SpringBoot 未配置该路径为静态资源。

解决:在backend/src/main/resources/application.yml中添加:

spring: servlet: context-path: /api # ← 确保所有接口以 /api 开头,避免与静态资源冲突 resources: static-locations: classpath:/static/,file:./uploads/ # ← 关键!指定 uploads 目录为静态资源

然后确保后端代码中保存图片时,写入./uploads/20240510/abc.jpg,并返回给前端的imageUrl/uploads/20240510/abc.jpg

5.3 登录后无法跳转到首页:Vue Router 的beforeEach守卫未生效

现象:输入账号密码,接口返回{code:200},但页面仍停留在登录页,URL 未变。

原因:frontend/src/router/index.js中的全局前置守卫未正确拦截登录成功响应。

检查该文件是否包含:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token'); if (to.meta.requiresAuth && !token) { next('/login'); } else if (to.path === '/login' && token) { next('/home'); // ← 关键:登录成功后有 token,应跳首页 } else { next(); } });

若缺失else if (to.path === '/login' && token)分支,则登录后不会自动跳转。

5.4 数据库中文乱码:application.yml与 MySQL 服务端编码不一致

现象:后台管理页中,餐厅名称显示为????,但用 MySQL 命令行查SELECT name FROM restaurant正常。

原因:SpringBoot 连接 URL 缺少characterEncoding=utf-8,或 MySQL 服务端默认编码非utf8mb4

解决:

  1. 修改application.yml的 JDBC URL:
url: jdbc:mysql://localhost:3306/food_review?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&characterEncoding=utf-8
  1. 登录 MySQL,执行:
ALTER DATABASE food_review CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
  1. 重启后端服务。

5.5 论文查重率高:3 个必须修改的「通用描述」模板

不要直接复制 ZIP 包内docs/下的论文文字!以下三处是查重雷区,必须重写:

  • 摘要首句
    ❌ 原文:「随着互联网技术的发展,人们的生活方式发生了巨大变化……」
    ✅ 改写:「本系统聚焦于解决用户在探店决策中缺乏结构化参考信息的问题,通过量化评分、标签聚合与图文评测,将主观用餐体验转化为可检索、可排序的数据资产。」

  • 技术选型理由
    ❌ 原文:「Vue 框架具有轻量、高效、双向绑定等优点……」
    ✅ 改写:「选用 Vue 2.6(非 Vue 3)是因为其 Options API 与本项目中mixins封装的表单校验逻辑兼容性更好,且学校实验室机房的 Node.js 版本(v14.17.0)对 Vue CLI 4 的支持更稳定。」

  • 系统测试用例
    ❌ 原文:「测试了登录、注册、发布评测等功能……」
    ✅ 改写:「设计 7 个边界测试用例:① 输入 501 字评论触发前端 maxlength 拦截;② 连续 3 次输错密码触发后端账户锁定(JWT Token 失效);③ 上传 15MB 图片返回413 Payload Too Large;④ 搜索词含 SQL 关键字(如OR 1=1)被 MyBatis#{}预编译自动过滤……」

提示:最后一条「SQL 关键字测试」是加分项——证明你理解了 MyBatis 的#{}${}区别,且在ReviewMapper.xml中所有参数均使用#{},这是数据库课程设计的高分证据。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/14 14:09:24

免费开源版IDEA到底值不值得用?IntelliJ IDEA Community Edition详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华