
简介基于SpringBootVue的网上书城完整开发源码面向具备一定Java与前端基础的开发者或是课程设计、毕业设计需要完整项目参考的学生。系统围绕书籍浏览、搜索、购物车、在线支付、订单配送与售后评价等核心流程展开前后端分离架构清晰数据库表结构完整覆盖从用户登录鉴权到订单状态流转的主要业务场景。压缩包大小36.83MB内含前端工程、后端接口服务、数据库脚本及配套说明文档文档包括部署说明本地与远程服务器环境配置、系统介绍功能模块与技术栈梳理和源码解释逐行分析API设计、业务逻辑与实现技巧。读者可据此快速搭建运行环境跑通全栈项目理解SpringBoot整合Vue的开发模式并可将方案迁移至服装、家居等其他电商品类。已有415人学习下载适合希望深入掌握源码细节并在此基础上二次开发的SpringBoot/Vue爱好者。1. 网上书城源码为什么 SpringBootVue 这套组合值得照着跑一遍网上书城是课程设计和毕业设计里出现频率最高的题目之一但真正能把一套SpringBootVue的网上书城源码从压缩包变成能访问的页面至少拦下一半人。这套资源做的事情很简单后端用SpringBoot搭接口、前端用Vue做页面、MySQL存数据同时把部署说明、系统介绍和源码解释都放进压缩包答辩时最容易被追问的登录鉴权、购物车、订单状态都有对应代码可以讲。适合交课程设计的学生、准备毕设的应届生以及想快速看一套完整前后端分离项目怎么组织的人。下面按我实际拆解的顺序讲先把架构和数据表说清楚再一步步带你把服务跑起来。2. 从架构到数据表先看清书城源码的骨架再动手2.1 前后端分离架构与技术栈选型这套网上书城采用的是典型的前后端分离结构后端只提供JSON接口前端页面渲染全部交给Vue。我拆项目时第一件事不是跑代码而是先看pom.xml和package.json这两个文件基本决定了后面的部署流程怎么走。后端技术栈集中在SpringBoot生态里ORM层用MyBatis-Plus登录状态用JWT接口风格是RESTful数据源MySQL前端用Vue配合ElementUI和axios。这张表是我拆完这套源码后整理的技术栈清单拿到其他类似书城项目时也可以照着对照。技术项选型作用后端框架SpringBoot提供REST接口、控制反转、内嵌TomcatORMMyBatis-Plus单表增删改查不用手写SQL权限JWT登录后下发token无状态鉴权数据库MySQL存储用户、图书、购物车、订单数据前端框架Vue单页应用、组件化页面UI组件ElementUI表格、表单、消息提示等现成组件HTTP工具axios前端调用后端接口pom.xml里的依赖大致是这样一片。课程设计项目的依赖不会太复杂但每一类都有明确分工!-- 提供Controller接口和内嵌Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus简化单表增删改查 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.1/version /dependency !-- MySQL驱动版本号以资源里pom.xml为准 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- JWT生成与校验token -- dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt/artifactId version0.9.1/version /dependencyspring-boot-starter-web负责把Controller层跑起来内嵌Tomcat直接处理HTTP请求MyBatis-Plus 3.5.1对应SpringBoot 2.x这是课程设计项目里最常用的稳定组合别拿SpringBoot 3去硬套版本一高很多配置和依赖会跟着变JJWT专门用来生成和解析登录token这个量级的项目用它就够了不用上Spring Security全家桶。2.2 用户端和管理端功能拆解网上书城的功能拆开是两条线。一条给普通用户用注册登录、首页轮播、图书分类展示、图书详情、加入购物车、提交订单、模拟支付、查看个人订单另一条给管理员用管理员登录、图书信息增删改查、分类管理、订单处理、用户管理。每个功能背后都有对应的Controller和Service图书列表页调的是BookController的列表接口购物车页面调的是CartController的增删改接口。源码里通常带系统介绍文档里面有功能结构图但多数课程设计的源码并没有把所有功能都实现得很全拿到手先对照页面路由把功能点摸一遍确认哪些有实际代码支撑。管理端和接口的对应关系值得留意。图书管理页面的新增、编辑、下架最终都是通过BookController里的几个方法完成的订单管理里的发货、取消对应OrderController的状态流转接口。答辩时老师问某个按钮对应哪个接口能答出这一层关联就说明你真的看过代码。2.3 数据库表设计与关键字段数据表是这套项目的另一个核心课程设计阶段最常翻车的就是SQL文件导入报错或者表和表之间对不上。网上书城的表结构基本逃不开这几张用户表、图书表、分类表、购物车表、订单表、订单明细表。外键在课程设计里通常不用真正建靠逻辑关联就够了。CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARSET utf8mb4; USE bookstore; CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 登录名, password varchar(100) NOT NULL COMMENT 密码BCrypt或MD5加密, nickname varchar(50) DEFAULT NULL COMMENT 昵称, avatar varchar(255) DEFAULT NULL COMMENT 头像地址, email varchar(100) DEFAULT NULL COMMENT 邮箱, role tinyint NOT NULL DEFAULT 1 COMMENT 0管理员1普通用户, create_time datetime DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB COMMENT用户表;拿到源码后先看user表的password字段怎么存如果原文存储说明项目没有做加密处理如果是BCrypt登录校验时就要用同一个加密工具类比对。图书表里通常有一个cover字段存封面图路径这个路径是相对路径还是完整URL直接影响第5章要讲到的图片显示问题。订单表是状态字段最多的表一般用status字段标识订单走向0待付款、1待发货、2已发货、3已完成、4已取消。理解这个状态枚举后管理端的订单操作就好对应了——发货就是把它从1改成2取消就是把非完成状态的订单改成4。这些设计细节比记住实体类的代码更有用答辩或者面试问的就是这个订单状态怎么管理的。CREATE TABLE book ( id int NOT NULL AUTO_INCREMENT, book_name varchar(200) NOT NULL COMMENT 书名, author varchar(100) DEFAULT NULL COMMENT 作者, press varchar(100) DEFAULT NULL COMMENT 出版社, category_id int DEFAULT NULL COMMENT 分类ID, price decimal(10,2) DEFAULT NULL COMMENT 单价, stock int DEFAULT 0 COMMENT 库存, cover varchar(255) DEFAULT NULL COMMENT 封面图路径, description text COMMENT 图书简介, status tinyint DEFAULT 1 COMMENT 1上架0下架, PRIMARY KEY (id) ) ENGINEInnoDB COMMENT图书表;这张表里category_id关联分类表但代码查询时通常先用分类ID过滤再查不会在数据库层面建外键。如果图书表里带了库存下单时要扣减库存这就涉及事务问题第4章订单流程里会看到Transactional注解原理全串在这几张表上。提示很多网上书城源码的SQL文件里没有外键约束删除分类前要注意先处理分类下的图书或者给图书表用status下架字段做软删除否则容易出现数据不一致。3. 本地部署全流程从空环境到页面跑通的六个步骤空环境是多数人卡住的地方。我见过太多同学JDK、Node、MySQL都装好了却卡在不知道先点哪个按钮、先执行哪条命令。部署这套资源有个前提先把环境版本对齐这一步错了后面全是白忙。3.1 环境准备与版本选择部署这套SpringBootVue书城建议环境统一版本避免因为版本差异出现莫名其妙的报错。我常用的组合是JDK 1.8、Maven 3.6.3、MySQL 5.7或8.0、Node 14或16后端开发工具用IDEA 2022以上前端改代码用VSCode。先用命令确认版本版本不对的尽早调整java -version mvn -version node -v npm -v mysql --version四条命令分别检查Java、Maven、Node、npm和MySQL。如果Node版本是18甚至20npm install时很可能遇到node-sass报错建议直接切到Node 14后面避坑章节会单独讲。SpringBoot 2.x对JDK 8支持最稳当前这套书城源码按SpringBoot 2.x来跑是主流选择。3.2 导入数据库打开MySQL命令行或者Navicat先创建数据库再用脚本初始化。如果用命令行登录后执行CREATE DATABASE IF NOT EXISTS bookstore DEFAULT CHARSET utf8mb4; USE bookstore; SOURCE /你的路径/bookstore.sql;SOURCE命令会把bookstore.sql里的建表语句和初始化数据一次性执行。也可以用重定向方式mysql -uroot -p --default-character-setutf8mb4 bookstore bookstore.sql--default-character-setutf8mb4这个参数一定要带否则SQL文件里即便声明了utf8mb4终端传输过程中也可能因为客户端默认编码造成中文乱码。导入完成后进数据库敲SHOW TABLES看到user、book、orders、cart这些表就说明成功。3.3 修改配置文件并启动后端数据库导入只是第一步后端程序还要知道自己连哪个库。打开backend目录下的application.yml数据源配置改成你自己的MySQL账号server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/bookstore?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码 driver-class-name: com.mysql.cj.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 10MB jwt: secret: bookstore-secret-key expiration: 604800url里的serverTimezoneAsia/Shanghai解决MySQL 8时区导致的日期偏差不加的话连接时会报时区异常driver-class-name用com.mysql.cj.jdbc.DriverMySQL 5.7和8.0都认这个。jwt.secret是token签名密钥jwt.expiration单位是秒604800就是7天。密码改成你自己的不要保留默认的123456。启动后端有两种方式IDEA里找到主类直接Run或命令行进入项目根目录执行mvn spring-boot:run看到日志输出Started Application in xx seconds表示后端起来了。这期间Maven会下载大量依赖第一次等待时间比较长耐心等完不要中途CtrlC。3.4 启动前端后端起来了前端还要单独起一个开发服务器。进入frontend目录装依赖再启动cd frontend npm install npm run servenpm install会把package.json里声明的依赖全部装到node_modules装完没有红色报错就可以运行。启动后控制台会显示访问地址通常是http://localhost:8081。这时候打开浏览器直接访问8081页面大概率能看到但接口可能不返回数据因为前端代理还没配。vue.config.js里一般会有devServer配置把前端请求转发到后端module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这个配置的意思是前端8081端口收到的、路径以/api开头的请求全部转发到后端8080。changeOrigin设置为true是为了让后端看到请求来自8080而非8081避免一些服务器端校验来源的接口被挡掉。3.5 前后端联调验证两个服务都在本地跑起来后先不要急着点页面用最简单的方式验证链路是否通。浏览器访问http://localhost:8081按F12打开开发者工具切到Network面板然后注册一个测试账号或者登录管理员账号看请求的响应状态。也可以用curl直接打后端接口验证curl -X POST http://localhost:8080/api/user/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}正常返回里会有一个token字段说明后端、数据库、密钥配置都对了。如果返回401或者连接失败优先检查后端日志端口和密码问题在这一步暴露得最明显。3.6 打包与生产部署方式本地开发跑通后如果要把项目部署到服务器前后端要分别打包。后端打jar包mvn clean package -DskipTests java -jar target/bookstore-0.0.1-SNAPSHOT.jar-DskipTests跳过测试课程设计项目没有单测直接跳过省时间。前端打包npm run build生成dist目录里面是纯静态文件。常见做法是把dist放到nginx的root目录同时配置反向代理转发api请求如果嫌nginx麻烦也可以把dist里的文件复制到后端resources/static目录然后只启动后端一个服务两种方式都能跑。4. 核心代码导读登录鉴权、图书检索与订单提交的实现路径部署跑通只是第一步答辩和面试看的是代码。这一章把书城源码里最容易被问的三个点拆开讲登录鉴权怎么做的、图书检索接口长什么样、订单提交为什么用事务。4.1 JWT登录鉴权实现登录接口本身不复杂复杂的是token的生成和校验。Controller层接收用户名密码Service层校验通过后调用JwtUtil生成token返回给前端RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody User user) { User loginUser userService.login(user.getUsername(), user.getPassword()); if (loginUser ! null) { String token JwtUtil.generateToken(loginUser.getId(), loginUser.getUsername()); return Result.success(token); } return Result.error(用户名或密码错误); } }login方法会先按用户名查用户再比对密码。密码比对方式取决于user表的password字段是怎么加密的如果用的MD5Service里通常会有MD5工具类做一次加密后再比对如果用的BCrypt则调用BCryptPasswordEncoder的matches方法。token的生成核心在这段public static String generateToken(Long userId, String username) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .setExpiration(new Date(System.currentTimeMillis() 604800000L)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); }setSubject放用户名claim里放userId过期时间604800000毫秒对应application.yml里的604800秒必须两边一致。SECRET常量要和yml里的jwt.secret保持一致否则后端重启后所有已登录用户的token全部失效。后端用拦截器校验token常见做法是写一个JwtInterceptorpublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token ! null JwtUtil.verify(token)) { return true; } response.setStatus(401); return false; }拦截器注册到WebMvcConfigurer里排除掉登录、注册两个接口其余接口全部拦截。前端每次请求在axios拦截器里把token放到Authorization头里就可以做到无状态登录。4.2 图书分页检索接口图书列表页不可能一次性把几千本书全查出来源码里一般用MyBatis-Plus的分页查询。接口代码通常是这样的GetMapping(/api/book/page) public Result page(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword, RequestParam(required false) Integer categoryId) { PageBook page new Page(pageNum, pageSize); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(keyword), Book::getBookName, keyword) .eq(categoryId ! null, Book::getCategoryId, categoryId) .eq(Book::getStatus, 1) .orderByDesc(Book::getCreateTime); bookService.page(page, wrapper); return Result.success(page); }pageNum是页码从1开始pageSize是每页条数默认10keyword做模糊搜索categoryId按分类过滤。LambdaQueryWrapper的eq和like第一个参数是布尔条件条件为true才拼进SQL这样前端不传参数时不会出问题。orderByDesc按创建时间倒序新书排前面。返回的page对象里有records、total、pages、current字段前端分页组件直接绑定total和pages。要注意的是MyBatis-Plus分页需要配置分页拦截器否则page方法查询出来的total永远是0。源码里通常会有一处MybatisPlusInterceptor配置跑完数据不对就先去查这个。4.3 购物车与订单提交购物车逻辑的核心在于同一个用户添加同一本书时是新增记录还是累加数量。常见写法public void addCart(Long userId, Long bookId, Integer count) { Book book bookService.getById(bookId); if (book null || book.getStatus() ! 1) { throw new RuntimeException(图书不存在或已下架); } Cart cart cartMapper.selectOne(new LambdaQueryWrapperCart() .eq(Cart::getUserId, userId) .eq(Cart::getBookId, bookId)); if (cart ! null) { cart.setCount(cart.getCount() count); cartMapper.updateById(cart); } else { Cart newCart new Cart(); newCart.setUserId(userId); newCart.setBookId(bookId); newCart.setCount(count); cartMapper.insert(newCart); } }先查书是否存在且上架再查购物车里是否已有同样一本书有就累加数量没有就新增记录。这里的count参数前端传的是页面上用户输入的数量注意要做空值和范围校验否则负数也能进购物车后面的库存校验会跟着出问题。订单提交是整个书城最需要事务保护的操作因为涉及多张表。核心代码Transactional public Order submitOrder(Long userId, Long[] cartIds, String address) { if (cartIds null || cartIds.length 0) { throw new RuntimeException(请选择商品); } Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setStatus(0); order.setAddress(address); orderMapper.insert(order); BigDecimal total new BigDecimal(0); for (Long cartId : cartIds) { Cart cart cartMapper.selectById(cartId); Book book bookService.getById(cart.getBookId()); if (book.getStock() cart.getCount()) { throw new RuntimeException(图书库存不足); } OrderItem item new OrderItem(); item.setOrderId(order.getId()); item.setBookId(book.getId()); item.setPrice(book.getPrice()); item.setCount(cart.getCount()); orderItemMapper.insert(item); book.setStock(book.getStock() - cart.getCount()); bookService.updateById(book); cartMapper.deleteById(cartId); } order.setTotalAmount(total); orderMapper.updateById(order); return order; }Transactional保证这串操作要么全部成功要么全部回滚。假设订单插入成功、扣库存时却报了库存不足事务回滚会把刚才插入的订单和订单明细一并撤销不会出现订单存在但库存没扣的脏数据。generateOrderNo一般用时间戳加随机数生成用来给用户展示订单号。total金额累加每本书的单价乘数量价格以数据库book表里的price为准不能信任前端传过来的金额。4.4 前端路由与axios封装前端的核心工作集中在两个文件axios请求封装和路由守卫。axios封装决定每个接口怎么带token、401统一怎么处理import axios from axios import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers.Authorization token return config }) request.interceptors.response.use(response response.data, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(error) }) export default requestbaseURL写/api是因为开发环境代理和后端接口前缀都统一为/api这样代码里只需要写/page、/user/login这些相对路径。请求拦截器从localStorage取token放到Authorization头后端拦截器从这个头解析用户身份。响应拦截器统一取出response.data页面组件里就不用每次写res.data两层嵌套遇到401就清除token并跳回登录页实现登录过期自动踢回登录页。路由守卫控制哪些页面必须登录才能进router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next(/login) } else { next() } })在路由配置里给需要登录的页面加meta.requiresAuth: true比如购物车、订单列表、个人中心。管理员页面还要额外校验role字段只判断有没有token是不够的普通用户token也能通过这个守卫要对管理员路由单独判断用户角色。5. 避坑笔记部署这套书城源码最容易翻车的五个问题跑通一次容易但要把这套源码在你自己电脑上跑起来版本和配置的坑一个都不会少。这一章是我自己复现课程设计类书城项目时踩过、也帮别人解决过的高频问题每一条都是现象、原因、解决三步走照着排查比瞎试快得多。5.1 导入SQL时报编码错误启动后中文乱码现象mysql执行bookstore.sql时报Unknown character set或者直接报语法错勉强导入成功后页面图书名、用户名全是问号。原因SQL文件本身是utf8mb4编码但mysql客户端以latin1或gbk的默认编码传输另外书城源码的SQL文件可能是用Navicat导出的里面的禁用外键检查语句在命令行下执行会被视为特殊标记。解决命令行导入时强制指定字符集mysql -uroot -p --default-character-setutf8mb4 bookstore bookstore.sql导入前先确认存在bookstore库没有的话先建库再导入。如果已经导入了乱码数据把库删掉重新导一遍不要只改data里的字符表结构里已存的乱码不会自动恢复。5.2 后端启动失败端口被占用或驱动类找不到现象启动后端日志最后一行是Web server failed to startPort 8080 was already in use或者报ClassNotFoundException: com.mysql.jdbc.Driver。原因8080端口被其他程序占用很多本机服务默认也喜欢用8080驱动类报错则是application.yml里的driver-class-name写成了MySQL 5时代的老驱动com.mysql.jdbc.DriverMySQL 8环境需要新的com.mysql.cj.jdbc.Driver。解决先找占用者Windows下用netstat和taskkillmacOS和Linux用lsofnetstat -ano | findstr 8080 taskkill /F /PID 占用进程的PID端口杀掉之后重新启动。驱动类问题直接改application.ymlMySQL 8必须改成com.mysql.cj.jdbc.Driver。如果数据库密码里带着#这类特殊字符要检查yml里是不是被错误解析了必要时用单引号包起来。5.3 前端npm install 报错node-sass编译不过现象npm install执行到node-sass阶段报错提示找不到binding.node或者提示需要Python和C编译器。原因这套书城前端大概率是Vue 2项目依赖里带了node-sass。node-sass是一个需要在安装时下载对应Node版本二进制文件的库Node版本一高就找不到对应的预编译版本只能现场编译没有编译环境就直接失败。解决使用nvm把Node版本切到14或16再重装依赖这是最快的路径nvm install 14 nvm use 14 rm -rf node_modules package-lock.json npm install如果不想切版本也可以把package.json里的node-sass替换成sass同时把代码里的import改为use或者webpack配置里调整loader但改动比切版本大。我一般建议直接切Node 14Vue 2生态适配这个版本最稳。5.4 页面接口全部跨域登录成功后一刷新就401现象浏览器控制台报CORS policy错误前端8081请求后端8080被拦截或者登录成功、页面刷新后所有需要登录的接口都返回401。原因CORS是因为前端和后端在不同端口后端没有放开跨域401则是前端请求头没带token或者后端拦截器校验失败。刷新后401通常是因为路由模式用了history刷新时直接请求了后端路径而不是前端路由导致进入后端拦截器或者token存的是session而不是localStorage刷新后丢失。解决后端加一个CorsConfigConfiguration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) // 所有接口都放开跨域 .allowedOrigins(http://localhost:8081) // 前端地址不要用通配符 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true); } }allowedOrigins这里不要写成*因为allowCredentials(true)时浏览器不允许通配来源要明确写前端地址。前端vue.config.js配了proxy的话严格说不会产生跨域真正报CORS大概率是开发时直接访问了后端8080接口或者代理配置没生效。刷新401的排查方向是先看Network面板登录接口请求头里有没有Authorization字段再排查token存储位置。5.5 封面上传成功页面里图片却显示不出来现象管理员在后台成功上传图书封面数据库里也有路径但图书列表页的img标签要么404要么403图片就是打不开。原因图片被保存在本地磁盘路径下比如D:/upload但SpringBoot默认只映射classpath:/static磁盘路径不在静态资源范围内另外数据库里存的可能是绝对路径D:/upload/xxx.jpg这种路径直接拼到img的src里浏览器当然访问不到。解决在application.yml里扩展静态资源映射spring: web: resources: static-locations: classpath:/static/, file:D:/upload/数据库里存储的相对路径应该写成/uploads/xxx.jpg前面不要带盘符。如果还是显示不了检查上传接口返回的地址是不是完整路径很多源码返回http://localhost:8080/uploads/xxx.jpg这样的完整URL前端直接拿这个字符串当src就行。用完整URL时要注意后端端口不是默认8080的情况下URL会拼错把端口改成yml里配置的端口即可。注意改静态资源映射后一定要重启后端SpringBoot对yml配置不热更新改完不重启很容易让你误以为配置没生效。6. 改造与验证把课程设计源码升级成答辩能过的项目6.1 先用一张验收表把功能闭环跑通部署完成的下一步不是急着改功能而是把关键闭环完整走一遍。我习惯列一张验收表每个场景按顺序操作并记录结果验证场景操作步骤预期结果注册登录注册新用户、退出、重新登录登录后能访问个人中心商品浏览首页搜索数据库点进详情页返回匹配图书详情字段完整购物车加两本不同书到购物车修改数量购物车数量实时总额正确下单选中购物车商品提交订单库存扣减、购物车清空、生成订单号后台处理管理员登录订单点发货订单状态从待发货变为已发货如果某一步和后端日志对不上先用第5章的方法排查再继续后面的改造。6.2 低成本高答辩分的三个改造方向如果时间够我给课程设计项目优先加这三个功能改动都不大但效果明显。第一是图书检索增强在现有keyword分页基础上增加按出版社和价格区间的筛选前端只需要多传两个参数后端在LambdaQueryWrapper里再加两个条件第二是订单详情弹窗用户点击订单号能看到订单商品明细这个表order_item本来就有数据缺的只是展示接口第三是评论功能给book表关联一张comment表详情页展示评论区参加答辩时比单纯展示电商页面更能讲出业务深度。最后说句题外话。我当年第一次做课程设计部署跑通就开始洋洋得意结果答辩时老师问两个人同时买最后一本书怎么办我完全答不上来。从那以后我每次拿到SpringBootVue的源码都强制自己先跑一遍完整业务闭环再看一遍关键表关系最后才动代码。这套书城资源的部署说明和源码解释就是按这个路径配的照着做能少走很多弯路希望帮到你。本文还有配套的精品资源点击获取