简介:面向毕业设计或课程设计场景的Spring Boot与Vue.js前后端分离图书商城实战项目,适合需要完整可运行源码、论文及答辩材料的计算机相关专业学生。资源包约66.44MB,包含全套前后端源码、SQL数据库脚本、毕业设计论文与开题报告,以及部署调试服务与说明文档等文件,可直接用于本地环境搭建和功能演示。后端基于Spring Boot,借助Spring Data JPA完成商品、订单等数据的CRUD操作,并用Spring Security控制访问权限;前端采用Vue.js实现组件化页面,结合Vuex管理全局状态、Vue Router处理路由跳转,覆盖用户注册登录、图书浏览、购物车、订单结算等核心业务环节。SQL脚本预置了用户、图书、订单等核心表结构及关系索引,讲解文档则梳理了系统架构、设计理念、各功能模块实现方式以及常见排错思路。已有399人浏览学习,对希望快速掌握在线商城系统开发、理解前后端协作模式、提升毕业设计完成质量的同学,具有很高的参考价值。1. 在线图书商城毕业设计:这套 Spring Boot + Vue 源码到底能帮你省多少事如果你正在为毕业设计选题发愁,或者手头已经有了题目但不知道从哪里下手,那么基于 Spring Boot + Vue 的在线图书商城项目应该是你见过的最稳妥的选择之一。这类项目最大的价值在于:它不是一个教学性质的 Demo,而是一个前后端分离、功能模块齐全、可以直接运行并用于答辩的完整系统。我拆过不少类似的毕设资源,这套图书商城的特别之处在于它还附带了 SQL 脚本和论文/开题报告,意味着你不光能跑起来,还能把文档工作一并解决。在此之前先给你提个醒:很多同学拿到源码第一步就去配环境,结果被各种依赖和版本问题卡住,其实问题大多不在代码本身,而是对整体结构缺少预判。接下来的内容我会从项目分层、关键技术选型,到前端请求封装、后端接口设计、数据库脚本导入,再到你大概率会踩的坑,逐一拆开讲清楚。2. 先看懂系统的技术骨架:Spring Boot + Vue 是怎么协作的在动手敲命令之前,你得先清楚这个项目内部是怎么组织的。在线图书商城采用的是前后端完全分离的架构:后端只负责提供 RESTful API,前端通过 HTTP 请求拿到 JSON 数据,再渲染成页面。这种架构的好处是,你后面如果想换掉前端框架(比如改成 React),后端代码完全不用动。2.1 前端项目结构:Vue 单页应用的路由、视图和状态管理先说前端。Vue 这块一般会分成 views、router、api、components 这几个目录。views 里是一张张页面,比如图书列表页、购物车页、订单结算页、后台管理页;router 负责页面之间的跳转规则;api 目录则统一封装对后端接口的调用。实际的代码组织大致是这样的:// api/book.js import request from '@/utils/request' export function getBookList(params) { return request({ url: '/api/book/list', method: 'get', params }) } export function getBookDetail(id) { return request({ url: `/api/book/${id}`, method: 'get' }) }这段代码的核心作用是把后端接口地址集中管理,避免在每个页面里散落一堆 axios 请求。注意这里的request是从@/utils/request导入的,它是一个封装好的 axios 实例。你拿到源码后如果发现登录状态失效或者请求报跨域错误,问题一般就出在这个封装里。参数说明:params会以查询字符串的形式拼到 URL 后面,比如/api/book/list?pageNum=1&pageSize=10。2.2 后端项目结构:Controller、Service、Mapper 的分层准则后端用的是 Spring Boot 的标准三层结构。Controller 接收 HTTP 请求,但不处理业务逻辑;Service 层做真正的业务处理;Mapper(或者叫 DAO)层负责跟数据库打交道。这张分层图你务必记在脑子里,因为论文里的系统设计章节就是照着它写的。// BookController.java @RestController @RequestMapping("/api/book") public class BookController { @Autowired private BookService bookService; @GetMapping("/list") public Result getBookList(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { PageResult pageResult = bookService.getBookList(pageNum, pageSize); return Result.success(pageResult); } }这个 Controller 的写法你应该不陌生,它接收 pageNum 和 pageSize 两个分页参数,返回一个统一的结果封装类 Result。大多数毕设项目都会定义一个 Result 类,里面包含 code、message、data 三个字段。前端 request 封装里拦截响应的时候,也是先判断 code 是否为 200,否则统一弹出错误提示。参数说明:defaultValue = "1"表示不传分页参数时默认从第 1 页开始,pageSize默认是 10。2.3 数据库视角:从图书分类到订单状态的核心数据表设计图书商城系统的数据表一般不少于八张,其中最关键的是图书表(book)、用户表(user)、订单表(orders)和订单项表(order_item)。它们之间的关系是:用户下单后生成一个主订单,主订单里包含多个订单项,每个订单项对应一本具体的图书。订单状态在数据库里通常用数字表示,比如 0 代表待付款,1 代表已付款待发货,2 代表已发货,3 代表已完成,4 代表已取消。你拿到项目后第一件事,就是打开 SQL 脚本把注释里的状态枚举看清楚,不同的需求或论文会选用不同的状态流转方式,如果直接看代码可能会绕晕。3. 搭建运行环境:从 JDK 到 MySQL 的版本兼容性排查这大概是整个项目落地过程中最容易翻车的一步。很多人在改了代码、跑了一个小时编译错误之后,才发现问题不是代码逻辑,而是 JDK 和 Spring Boot 版本不匹配,或者 MySQL 驱动和数据库版本对不上。我建议你按照下面的顺序逐步安装,不要乱。3.1 JDK、Maven、Node.js 的版本组合推荐这个项目是基于 Spring Boot 2.x 的,那么 JDK 建议装 1.8 或者 11。如果你用了 JDK 17,一些 Spring Boot 2.x 的老版本会出现反射相关的兼容问题。Maven 用 3.6 以上基本没问题,配置国内镜像源可以大幅加快依赖下载速度。前端那边,Node.js 建议用 14 或 16。我之前在同一台机器上试过 Node 18 跑老项目,结果 node-sass 编译失败,换回 Node 14 后立竿见影。# 查看当前环境版本 java -version mvn -v node -v npm -v这四条命令是排查环境问题的第一步。如果 java 版本没问题而 mvn 报错,大概率是 Maven 的 JDK 配置冲突;如果 npm install 过程中卡住,优先检查是否使用了 taobao 镜像源。3.2 MySQL 与初始化脚本:导入 SQL 的三种方式和验证方法拿到 SQL 脚本之后,导入方式无外乎三种:命令行、Navicat 图形界面、或者直接用 Spring Boot 的 schema.sql 自动执行。命令行适合习惯终端操作的同学,Navicat 适合图形化党。mysql -u root -p < books.sql注意在执行导入前,你要先在数据库里创建对应的库。CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE bookstore;为什么我要强调 utf8mb4?因为图书名称、分类名称这类数据里如果包含特殊字符(比如 emoji),utf8 会出现乱码或插入失败。而后端连接串里的characterEncoding=utf8也要配套同步修改。导入之后你怎么确认脚本没问题?最直接的办法是打开后端配置文件,找到数据库账号密码,然后启动项目看它能不能连上并正常读取数据。如果报Table doesn't exist,基本可以断定是导错了数据库,脚本里的 INSERT 语句偶尔会带库名前缀,不同客户端对库名解析方式不同,这就会导致你看似执行成功,实际表落到了别的库。4. 运行前后端项目:配置文件的隐藏坑和启动参数调整环境准备好之后,真正的启动阶段就开始了。这一节是很多新手第一次感到“玄学”的地方:明明按照教程一步步来的,前端页面就是白屏,后端接口就是 404。别急,我把启动过程拆成两条线,分别说清。4.1 后端启动:application.yml 里必须改的三个关键配置打开后端的src/main/resources/application.yml,你会看到三处必须根据自己环境修改的值:数据库连接地址、数据库账号密码、Tomcat 启动端口。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/bookstore?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456serverTimezone=Asia/Shanghai是很多老项目跑不起来的元凶。如果数据库驱动是 mysql-connector-java 8.x,不设置时区会直接报 CST 和 UTC 换算错误。useSSL=false是为了避免本地连接时的 SSL 证书警告。修改完配置后,在后端项目根目录打开终端,执行:mvn spring-boot:run第一次执行会下载大量依赖,耐心等待。如果你看到Started Application in x.xxx seconds就代表后端启动成功。此时在浏览器地址栏输入http://localhost:8080/api/book/list,应该能返回一串 JSON 数据。4.2 前端启动:路由跳转后白屏的排查思路前端启动需要先安装依赖,再启动开发服务器。npm install npm run serve如果卡在 node-sass 安装阶段,多半是 Node 版本和 node-sass 版本不匹配,最省力的方案是删掉 node_modules 目录,切换到相应版本的 Node 后重新安装。rm -rf node_modules npm install前端默认会在 8081 或 9527 端口启动,打开页面后如果导航栏能看到,但内容区域是空白,很可能是前端调接口时跨域被拦了。此时打开开发者工具的 Network 面板,看请求是否返回了CORS error或者 401。跨域问题的普遍解法是在前端vue.config.js里配置 devServer 的代理。// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }这块就是很多人搞到深夜的“隐藏坑”:不配置代理,前端请求的 /api 地址会被解析到前端自己的地址,而后端的接口服务在 8080 端口,前后端互相找不到。配置代理后,前端开发服务器会把带有 /api 前缀的请求转发给后端,后端收到后正常响应,前端拿到数据并渲染。5. 避坑/常见问题排查:项目启动到答辩全周期容易踩的六个坑我不可能把每个你遇到的问题都预先想到,但下面这几条是拆过的图书商城项目中出现频率最高的,建议你一条一条对照排查。5.1 前端登录后刷新页面就退出现象:你登录成功,看到用户名没毛病,按 F5 刷新一下就回到了登录页。原因:登录状态用 JavaScript 变量保存,没有写入 localStorage 或 sessionStorage,刷新后内存里的数据被清空。解决:检查前端的登录逻辑,观察是否存在sessionStorage.setItem('userInfo', ...)。如果没有,需要把用户信息存到本地存储中,并在路由守卫里通过读取本地存储来判断登录状态。5.2 后端接口返回的 JSON 里字段名是 null现象:接口能通,但返回的数据全是 null,或者某个字段缺失。原因:后端实体类字段和前端传入的参数名对不上。常见场景是前端用 smallBookCover,而后端实体类叫 smallBookCover 以外的名字。解决:打开浏览器 Network 面板,对比请求 URL 的参数和 Controller 接收的参数是否一一对应。如果有一个字母大小写不一致,Spring Boot 默认是区分大小写的,传进来就对不上。5.3 数据库中文乱码现象:SQL 脚本导入后,前端页面上显示的图书名称全是问号。原因:数据库连接串或表结构字符集不是 utf8mb4。解决:检查 application.yml 里的characterEncoding=utf8,同时确认建表语句里的DEFAULT CHARSET是 utf8mb4。如果表已经建好了,可以直接修改表字符集。ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;5.4 修改了后端代码但重启后不生效现象:你改了 Controller 层的代码,重启后访问接口结果还是老逻辑。原因:Maven 项目增量编译没有命中,或者 Spring Boot DevTools 没有自动重启。解决:手动执行一次完整编译再启动。mvn clean compile mvn spring-boot:run注意clean会删除 target 目录下的所有编译产物,属于无损操作,别担心会删掉源码。5.5 npm install 报错 ERESOLVE现象:新版本 npm 安装依赖时抛出一堆依赖解析树冲突的报错。原因:npm 7 以上的版本对 peerDependencies 的校验非常严格,老项目的依赖树本身存在冲突。解决:使用--legacy-peer-deps参数跳过依赖冲突检查。npm install --legacy-peer-deps这个方式是毕设项目中最省事的解法,不会影响最终打包运行。5.6 打包部署后前端文件 404现象:执行npm run build生成了 dist 目录,把 dist 里的文件放到 Nginx 后,刷新页面就 404。原因:Vue Router 默认使用 history 模式,这种模式下服务器必须把所有路径都重定向到 index.html。解决:在 Nginx 配置中添加这段重定向规则。location / { try_files $uri $uri/ /index.html; }6. 三个进阶方向:从能跑通到答辩加分的改造思路如果你还有余力,想让自己在答辩环节从“把项目跑起来了”上升到“我理解并改进了项目”,下面这三个方向是我比较推荐、且改动成本不高的具体操作。第一个方向是把数据访问换成代码生成器。原项目可能是手写 Mapper 和 XML,你如果接受 MyBatis-Plus,把 Mapper 接口继承 BaseMapper,再配合 MyBatis-Plus 的分页插件,业务代码可以减少三成左右。不过要小心:MyBatis-Plus 的自动填充功能需要配置 MetaObjectHandler,否则 createTime 这类字段写入时会变成 null,属于常见翻车点。第二个方向是加入缓存。图书列表页的接口如果被频繁访问,每次都要打数据库显然不合理。为了减少数据库的访问压力,你可以引入 Redis,把热门图书列表缓存起来。// BookServiceImpl.java public PageResult getBookList(int pageNum, int pageSize) { String cacheKey = "book:list:" + pageNum + ":" + pageSize; // 先从缓存读取 Object cacheVal = redisTemplate.opsForValue().get(cacheKey); if (cacheVal != null) { return JSON.parseObject(cacheVal.toString(), PageResult.class); } // 缓存里没有,查数据库 PageResult result = bookMapper.selectPage(pageNum, pageSize); redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(result), 30, TimeUnit.MINUTES); return result; }这段代码的逻辑是典型的 Cache-Aside 模式:先读缓存,没命中就查数据库,再写回缓存。这里给缓存设置了一个 30 分钟的过期时间,避免数据永久不变。真实面试或答辩时,上面的缓存穿透、缓存雪崩、缓存击穿三个问题经常被追问,建议你把解决思路准备好:穿透用空值缓存或者布隆过滤器,雪崩用加随机过期时间,击穿用互斥锁或者热点数据永不过期。上述代码还有一个优化空间是商品详情页的缓存,只需要把入参换成图书 ID 即可,代码结构是一样的。第三个方向是权限控制加拦截器。原项目可能只在某些接口上判断登录状态,你可以在 Spring Boot 里写一个拦截器,对所有非登录接口统一做 token 校验。// AuthInterceptor.java public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(401); return false; } // 校验token有效性 return true; } }这套拦截器设计比在 Controller 里散落的判断更规范,也是项目答辩时一个可以被拿出来讲的亮点。你只需要注意放行登录接口和图书查询接口,避免用户没登录就连图书列表都看不了。做完一键登录和缓存之后,你再看这套源码的视角应该完全不一样了。它从一套“能跑的代码”变成了“可改造的系统骨架”,你能说清楚里面每一个模块为什么存在,也能指出它哪些地方还可以进一步优化。我刚开始拆这类毕设项目时,也犯过直接上手改代码的毛病,结果越改越乱,后来强制自己每次拿到新项目都先走一遍“数据库 → 后端接口 → 前端页面”的完整链路,确认整体结构后才动任何一行代码。希望你也能把这个习惯用在后面的开发过程里,希望帮到你。本文还有配套的精品资源,点击获取
简介:面向毕业设计或课程设计场景的Spring Boot与Vue.js前后端分离图书商城实战项目,适合需要完整可运行源码、论文及答辩材料的计算机相关专业学生。资源包约66.44MB,包含全套前后端源码、SQL数据库脚本、毕业设计论文与开题报告,以及部署调试服务与说明文档等文件,可直接用于本地环境搭建和功能演示。后端基于Spring Boot,借助Spring Data JPA完成商品、订单等数据的CRUD操作,并用Spring Security控制访问权限;前端采用Vue.js实现组件化页面,结合Vuex管理全局状态、Vue Router处理路由跳转,覆盖用户注册登录、图书浏览、购物车、订单结算等核心业务环节。SQL脚本预置了用户、图书、订单等核心表结构及关系索引,讲解文档则梳理了系统架构、设计理念、各功能模块实现方式以及常见排错思路。已有399人浏览学习,对希望快速掌握在线商城系统开发、理解前后端协作模式、提升毕业设计完成质量的同学,具有很高的参考价值。1. 在线图书商城毕业设计:这套 Spring Boot + Vue 源码到底能帮你省多少事如果你正在为毕业设计选题发愁,或者手头已经有了题目但不知道从哪里下手,那么基于 Spring Boot + Vue 的在线图书商城项目应该是你见过的最稳妥的选择之一。这类项目最大的价值在于:它不是一个教学性质的 Demo,而是一个前后端分离、功能模块齐全、可以直接运行并用于答辩的完整系统。我拆过不少类似的毕设资源,这套图书商城的特别之处在于它还附带了 SQL 脚本和论文/开题报告,意味着你不光能跑起来,还能把文档工作一并解决。在此之前先给你提个醒:很多同学拿到源码第一步就去配环境,结果被各种依赖和版本问题卡住,其实问题大多不在代码本身,而是对整体结构缺少预判。接下来的内容我会从项目分层、关键技术选型,到前端请求封装、后端接口设计、数据库脚本导入,再到你大概率会踩的坑,逐一拆开讲清楚。2. 先看懂系统的技术骨架:Spring Boot + Vue 是怎么协作的在动手敲命令之前,你得先清楚这个项目内部是怎么组织的。在线图书商城采用的是前后端完全分离的架构:后端只负责提供 RESTful API,前端通过 HTTP 请求拿到 JSON 数据,再渲染成页面。这种架构的好处是,你后面如果想换掉前端框架(比如改成 React),后端代码完全不用动。2.1 前端项目结构:Vue 单页应用的路由、视图和状态管理先说前端。Vue 这块一般会分成 views、router、api、components 这几个目录。views 里是一张张页面,比如图书列表页、购物车页、订单结算页、后台管理页;router 负责页面之间的跳转规则;api 目录则统一封装对后端接口的调用。实际的代码组织大致是这样的:// api/book.js import request from '@/utils/request' export function getBookList(params) { return request({ url: '/api/book/list', method: 'get', params }) } export function getBookDetail(id) { return request({ url: `/api/book/${id}`, method: 'get' }) }这段代码的核心作用是把后端接口地址集中管理,避免在每个页面里散落一堆 axios 请求。注意这里的request是从@/utils/request导入的,它是一个封装好的 axios 实例。你拿到源码后如果发现登录状态失效或者请求报跨域错误,问题一般就出在这个封装里。参数说明:params会以查询字符串的形式拼到 URL 后面,比如/api/book/list?pageNum=1&pageSize=10。2.2 后端项目结构:Controller、Service、Mapper 的分层准则后端用的是 Spring Boot 的标准三层结构。Controller 接收 HTTP 请求,但不处理业务逻辑;Service 层做真正的业务处理;Mapper(或者叫 DAO)层负责跟数据库打交道。这张分层图你务必记在脑子里,因为论文里的系统设计章节就是照着它写的。// BookController.java @RestController @RequestMapping("/api/book") public class BookController { @Autowired private BookService bookService; @GetMapping("/list") public Result getBookList(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize) { PageResult pageResult = bookService.getBookList(pageNum, pageSize); return Result.success(pageResult); } }这个 Controller 的写法你应该不陌生,它接收 pageNum 和 pageSize 两个分页参数,返回一个统一的结果封装类 Result。大多数毕设项目都会定义一个 Result 类,里面包含 code、message、data 三个字段。前端 request 封装里拦截响应的时候,也是先判断 code 是否为 200,否则统一弹出错误提示。参数说明:defaultValue = "1"表示不传分页参数时默认从第 1 页开始,pageSize默认是 10。2.3 数据库视角:从图书分类到订单状态的核心数据表设计图书商城系统的数据表一般不少于八张,其中最关键的是图书表(book)、用户表(user)、订单表(orders)和订单项表(order_item)。它们之间的关系是:用户下单后生成一个主订单,主订单里包含多个订单项,每个订单项对应一本具体的图书。订单状态在数据库里通常用数字表示,比如 0 代表待付款,1 代表已付款待发货,2 代表已发货,3 代表已完成,4 代表已取消。你拿到项目后第一件事,就是打开 SQL 脚本把注释里的状态枚举看清楚,不同的需求或论文会选用不同的状态流转方式,如果直接看代码可能会绕晕。3. 搭建运行环境:从 JDK 到 MySQL 的版本兼容性排查这大概是整个项目落地过程中最容易翻车的一步。很多人在改了代码、跑了一个小时编译错误之后,才发现问题不是代码逻辑,而是 JDK 和 Spring Boot 版本不匹配,或者 MySQL 驱动和数据库版本对不上。我建议你按照下面的顺序逐步安装,不要乱。3.1 JDK、Maven、Node.js 的版本组合推荐这个项目是基于 Spring Boot 2.x 的,那么 JDK 建议装 1.8 或者 11。如果你用了 JDK 17,一些 Spring Boot 2.x 的老版本会出现反射相关的兼容问题。Maven 用 3.6 以上基本没问题,配置国内镜像源可以大幅加快依赖下载速度。前端那边,Node.js 建议用 14 或 16。我之前在同一台机器上试过 Node 18 跑老项目,结果 node-sass 编译失败,换回 Node 14 后立竿见影。# 查看当前环境版本 java -version mvn -v node -v npm -v这四条命令是排查环境问题的第一步。如果 java 版本没问题而 mvn 报错,大概率是 Maven 的 JDK 配置冲突;如果 npm install 过程中卡住,优先检查是否使用了 taobao 镜像源。3.2 MySQL 与初始化脚本:导入 SQL 的三种方式和验证方法拿到 SQL 脚本之后,导入方式无外乎三种:命令行、Navicat 图形界面、或者直接用 Spring Boot 的 schema.sql 自动执行。命令行适合习惯终端操作的同学,Navicat 适合图形化党。mysql -u root -p < books.sql注意在执行导入前,你要先在数据库里创建对应的库。CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE bookstore;为什么我要强调 utf8mb4?因为图书名称、分类名称这类数据里如果包含特殊字符(比如 emoji),utf8 会出现乱码或插入失败。而后端连接串里的characterEncoding=utf8也要配套同步修改。导入之后你怎么确认脚本没问题?最直接的办法是打开后端配置文件,找到数据库账号密码,然后启动项目看它能不能连上并正常读取数据。如果报Table doesn't exist,基本可以断定是导错了数据库,脚本里的 INSERT 语句偶尔会带库名前缀,不同客户端对库名解析方式不同,这就会导致你看似执行成功,实际表落到了别的库。4. 运行前后端项目:配置文件的隐藏坑和启动参数调整环境准备好之后,真正的启动阶段就开始了。这一节是很多新手第一次感到“玄学”的地方:明明按照教程一步步来的,前端页面就是白屏,后端接口就是 404。别急,我把启动过程拆成两条线,分别说清。4.1 后端启动:application.yml 里必须改的三个关键配置打开后端的src/main/resources/application.yml,你会看到三处必须根据自己环境修改的值:数据库连接地址、数据库账号密码、Tomcat 启动端口。server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/bookstore?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false username: root password: 123456serverTimezone=Asia/Shanghai是很多老项目跑不起来的元凶。如果数据库驱动是 mysql-connector-java 8.x,不设置时区会直接报 CST 和 UTC 换算错误。useSSL=false是为了避免本地连接时的 SSL 证书警告。修改完配置后,在后端项目根目录打开终端,执行:mvn spring-boot:run第一次执行会下载大量依赖,耐心等待。如果你看到Started Application in x.xxx seconds就代表后端启动成功。此时在浏览器地址栏输入http://localhost:8080/api/book/list,应该能返回一串 JSON 数据。4.2 前端启动:路由跳转后白屏的排查思路前端启动需要先安装依赖,再启动开发服务器。npm install npm run serve如果卡在 node-sass 安装阶段,多半是 Node 版本和 node-sass 版本不匹配,最省力的方案是删掉 node_modules 目录,切换到相应版本的 Node 后重新安装。rm -rf node_modules npm install前端默认会在 8081 或 9527 端口启动,打开页面后如果导航栏能看到,但内容区域是空白,很可能是前端调接口时跨域被拦了。此时打开开发者工具的 Network 面板,看请求是否返回了CORS error或者 401。跨域问题的普遍解法是在前端vue.config.js里配置 devServer 的代理。// vue.config.js module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } }这块就是很多人搞到深夜的“隐藏坑”:不配置代理,前端请求的 /api 地址会被解析到前端自己的地址,而后端的接口服务在 8080 端口,前后端互相找不到。配置代理后,前端开发服务器会把带有 /api 前缀的请求转发给后端,后端收到后正常响应,前端拿到数据并渲染。5. 避坑/常见问题排查:项目启动到答辩全周期容易踩的六个坑我不可能把每个你遇到的问题都预先想到,但下面这几条是拆过的图书商城项目中出现频率最高的,建议你一条一条对照排查。5.1 前端登录后刷新页面就退出现象:你登录成功,看到用户名没毛病,按 F5 刷新一下就回到了登录页。原因:登录状态用 JavaScript 变量保存,没有写入 localStorage 或 sessionStorage,刷新后内存里的数据被清空。解决:检查前端的登录逻辑,观察是否存在sessionStorage.setItem('userInfo', ...)。如果没有,需要把用户信息存到本地存储中,并在路由守卫里通过读取本地存储来判断登录状态。5.2 后端接口返回的 JSON 里字段名是 null现象:接口能通,但返回的数据全是 null,或者某个字段缺失。原因:后端实体类字段和前端传入的参数名对不上。常见场景是前端用 smallBookCover,而后端实体类叫 smallBookCover 以外的名字。解决:打开浏览器 Network 面板,对比请求 URL 的参数和 Controller 接收的参数是否一一对应。如果有一个字母大小写不一致,Spring Boot 默认是区分大小写的,传进来就对不上。5.3 数据库中文乱码现象:SQL 脚本导入后,前端页面上显示的图书名称全是问号。原因:数据库连接串或表结构字符集不是 utf8mb4。解决:检查 application.yml 里的characterEncoding=utf8,同时确认建表语句里的DEFAULT CHARSET是 utf8mb4。如果表已经建好了,可以直接修改表字符集。ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;5.4 修改了后端代码但重启后不生效现象:你改了 Controller 层的代码,重启后访问接口结果还是老逻辑。原因:Maven 项目增量编译没有命中,或者 Spring Boot DevTools 没有自动重启。解决:手动执行一次完整编译再启动。mvn clean compile mvn spring-boot:run注意clean会删除 target 目录下的所有编译产物,属于无损操作,别担心会删掉源码。5.5 npm install 报错 ERESOLVE现象:新版本 npm 安装依赖时抛出一堆依赖解析树冲突的报错。原因:npm 7 以上的版本对 peerDependencies 的校验非常严格,老项目的依赖树本身存在冲突。解决:使用--legacy-peer-deps参数跳过依赖冲突检查。npm install --legacy-peer-deps这个方式是毕设项目中最省事的解法,不会影响最终打包运行。5.6 打包部署后前端文件 404现象:执行npm run build生成了 dist 目录,把 dist 里的文件放到 Nginx 后,刷新页面就 404。原因:Vue Router 默认使用 history 模式,这种模式下服务器必须把所有路径都重定向到 index.html。解决:在 Nginx 配置中添加这段重定向规则。location / { try_files $uri $uri/ /index.html; }6. 三个进阶方向:从能跑通到答辩加分的改造思路如果你还有余力,想让自己在答辩环节从“把项目跑起来了”上升到“我理解并改进了项目”,下面这三个方向是我比较推荐、且改动成本不高的具体操作。第一个方向是把数据访问换成代码生成器。原项目可能是手写 Mapper 和 XML,你如果接受 MyBatis-Plus,把 Mapper 接口继承 BaseMapper,再配合 MyBatis-Plus 的分页插件,业务代码可以减少三成左右。不过要小心:MyBatis-Plus 的自动填充功能需要配置 MetaObjectHandler,否则 createTime 这类字段写入时会变成 null,属于常见翻车点。第二个方向是加入缓存。图书列表页的接口如果被频繁访问,每次都要打数据库显然不合理。为了减少数据库的访问压力,你可以引入 Redis,把热门图书列表缓存起来。// BookServiceImpl.java public PageResult getBookList(int pageNum, int pageSize) { String cacheKey = "book:list:" + pageNum + ":" + pageSize; // 先从缓存读取 Object cacheVal = redisTemplate.opsForValue().get(cacheKey); if (cacheVal != null) { return JSON.parseObject(cacheVal.toString(), PageResult.class); } // 缓存里没有,查数据库 PageResult result = bookMapper.selectPage(pageNum, pageSize); redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(result), 30, TimeUnit.MINUTES); return result; }这段代码的逻辑是典型的 Cache-Aside 模式:先读缓存,没命中就查数据库,再写回缓存。这里给缓存设置了一个 30 分钟的过期时间,避免数据永久不变。真实面试或答辩时,上面的缓存穿透、缓存雪崩、缓存击穿三个问题经常被追问,建议你把解决思路准备好:穿透用空值缓存或者布隆过滤器,雪崩用加随机过期时间,击穿用互斥锁或者热点数据永不过期。上述代码还有一个优化空间是商品详情页的缓存,只需要把入参换成图书 ID 即可,代码结构是一样的。第三个方向是权限控制加拦截器。原项目可能只在某些接口上判断登录状态,你可以在 Spring Boot 里写一个拦截器,对所有非登录接口统一做 token 校验。// AuthInterceptor.java public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token == null || !token.startsWith("Bearer ")) { response.setStatus(401); return false; } // 校验token有效性 return true; } }这套拦截器设计比在 Controller 里散落的判断更规范,也是项目答辩时一个可以被拿出来讲的亮点。你只需要注意放行登录接口和图书查询接口,避免用户没登录就连图书列表都看不了。做完一键登录和缓存之后,你再看这套源码的视角应该完全不一样了。它从一套“能跑的代码”变成了“可改造的系统骨架”,你能说清楚里面每一个模块为什么存在,也能指出它哪些地方还可以进一步优化。我刚开始拆这类毕设项目时,也犯过直接上手改代码的毛病,结果越改越乱,后来强制自己每次拿到新项目都先走一遍“数据库 → 后端接口 → 前端页面”的完整链路,确认整体结构后才动任何一行代码。希望你也能把这个习惯用在后面的开发过程里,希望帮到你。本文还有配套的精品资源,点击获取