ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SpringBoot+Vue+MySQL图书管理系统从部署到二次开发

SpringBoot+Vue+MySQL图书管理系统从部署到二次开发 拿到一套源码最怕的不是代码看不懂而是环境搭不起来数据库连不上、端口冲突、前端 npm install 报一堆错忙活一下午连个登录页都看不见。这套 SpringBoot Vue MySQL 的图书管理系统源码正好属于那种“业务典型、结构清晰、标着可直接运行”的全栈项目但我实测下来如果你对这套技术栈还不够熟直接双击源码照样会踩不少坑。这篇文章就是我把这套图书管理系统从零跑通的全过程环境怎么装、数据库脚本怎么导、后端和前端各自怎么启动、哪些位置特别容易翻车都会拆开讲清楚。适合三类人准备做课设/毕设的同学、刚学完三大框架想研究真实项目的新手、以及想快速拿一套可运行系统来做二次开发的 Java 开发。1. 项目全景业务范围与技术选型逻辑1.1 别把“图书管理”当成简单的增删改查很多同学拿到这类源码第一反应是“不就是一个图书信息的增删改查吗”真把代码打开之后才发现光业务就分了好几条线。第一块是图书资产线。管理员要做图书录入书名、ISBN、出版社、作者、馆藏数量、所属分类一个都不能少图书本身还有状态概念是在架、被借走、还是下架/丢失。第二块是借阅流水线。读者登录后检索图书、发起借阅、到期归还每一次操作都要落到一条借阅记录上关联到具体的人和具体的书如果超期没还还要能算出逾期天数。第三块是统计维度哪些书借得最多、哪些读者手上还有没还的书、馆藏库存在什么分布这些都会以列表或简易图表的形式出现在管理端。所以表面上是个“管理系统”实际上是一个“图书资产 借阅业务 统计看板”的完整闭环。代码量不大但表与表之间的关联、接口与接口之间的调用、前端页面与后端接口的对应关系全都体现出来的。这也是为什么毕业设计和课程设计里图书管理系统经久不衰——它复杂度适中又能把一套标准 Web 项目该有的东西全部覆盖。1.2 为什么是 SpringBoot Vue MySQL 这套组合技术选型不是玄学这套组合能成为“默认套餐”背后都是实打实的理由。SpringBoot 的价值在于开箱即用。它是“约定优于配置”的典型代表内嵌了 Tomcat不用单独部署外部服务器mvn spring-boot:run或者直接运行 main 方法就能把后端拉起来。起步依赖帮我们把 JAR 版本管好省去了 SSM 时代手写一坨 XML 配置的繁琐过程。Vue 做前端体验很合适。组件化开发让页面结构清晰数据双向绑定让表单操作非常顺手路由和状态管理的生态也成熟。对后端出身的同学来说Vue 的学习曲线在主流前端框架里算友好的模板语法接近 HTML改起来直观。MySQL 则是最不会出幺蛾子的选择。社区版免费Windows、Linux 都能装Navicat、DBeaver 这些可视化工具都是傻瓜式连接教学场景和小团队项目里几乎霸榜。为什么不选 JSP Servlet因为前后端耦合太严重改个样式都得重启页面现在企业内部新项目已经很少这么写了。为什么不选 Django 或 Flask能选Python 生态做后台效率也不低但如果你以后的目标是 Java 开发岗花时间在这套 SpringBoot Vue 的组合上技能基本都能迁移到企业项目里。2. 源码结构拆解目录里到底藏了什么2.1 后端工程三层架构是这类源码的通用骨架打开后端目录你会发现大多数毕业设计项目的结构都长一个样核心就是 Controller、Service、Mapper 三个层级library-backend ├── pom.xml # Maven 依赖与打包配置 ├── src/main/java/com/example/library │ ├── LibraryApplication.java # SpringBoot 启动入口 │ ├── config/ # 跨域配置、登录拦截器配置 │ ├── controller/ # 接口层接收请求、返回结果 │ ├── service/ # 业务逻辑层处理核心规则 │ ├── mapper/ # 数据访问层与数据库打交道 │ ├── entity/ # 数据库表对应的实体类 │ └── common/ # 统一返回结果 Result、全局异常处理 └── src/main/resources ├── application.yml # 数据源、端口等核心配置 └── sql/init.sql # 数据库初始化脚本Controller 只负责接收参数、调用 Service、把结果包一层返回Service 里写业务规则比如借书前先查库存Mapper 直接对应 SQL 操作。实体类一般会用TableName(book_info)标注和哪张表映射用 MyBatis-Plus 的话连 XML 都能少写很多。拿到源码后先别急着改代码我习惯先把application.yml和pom.xml看一遍搞清楚项目用了什么版本、数据库怎么配的、启动在哪个端口。这样即便后面出问题排查时也大概有方向。2.2 前端工程页面、路由、请求是怎么串起来的前端目录结构同样很固定本质上就是几件事创建 Vue 实例、配置路由、封装请求、写页面library-frontend ├── package.json # 依赖清单与启动脚本 ├── vue.config.js # 开发服务器与代理配置 ├── src/ │ ├── main.js # Vue 应用入口挂载 Router │ ├── router/index.js # 路由表与路由守卫 │ ├── store/ # 全局状态通常存登录用户信息 │ ├── views/ # 页面组件Login.vue、BookManage.vue │ ├── api/ # 按模块封装的接口请求 │ └── utils/request.js # axios 实例与 token 拦截器router/index.js里的配置决定访问哪个路径渲染哪个页面登录路由守卫一般写在这里。utils/request.js封装了 axios拦截器的核心作用就是每次请求自动把 token 塞进请求头。views/下面每个.vue文件对应一个功能页面这类源码的页面设计基本都是一行长表格加顶部搜索栏结构很规整。一个小判断技巧如果代码里大量出现require、module.exports大概率是 Vue2 Element-UI 项目如果出现importcreateApp那就是 Vue3 Element-Plus。两种项目的依赖命令不同但整体启动逻辑一致。2.3 数据库表设计撑起借阅业务的核心是关系数据库是这个系统真正的核心我建议先看表关系再碰代码。典型图书管理系统至少得有四张表表名职责核心字段sys_user管理员与读者账号id, username, password, role, statusbook_category图书分类id, category_namebook_info图书基础信息与库存id, book_name, isbn, author, publisher, total_count, stock, statusborrow_record借阅流水id, user_id, book_id, borrow_date, should_return_date, return_date, statusbook_info表里的stock字段是整个借阅业务的关键借书时它要减一还书时它要加一能不能借得出去就看它。borrow_record必须独立成表是因为“用户”和“图书”本质上是多对多关系一个人可以借多本书一本书也会被不同的人借走中间需要一个关系表来记录每一次借还行为。没有这张表你连“某位读者现在手上还有几本书没还”都查不出来。分类表可以单独建也可以把分类名称直接存在图书表里。单独建表的做法更规范也方便以后做分类统计。这套系统的表数量不多但表与表之间的关联刚好把一个典型的业务关系模型演示清楚了。3. 让源码在你的电脑上真正跑起来3.1 环境清单与版本选择很多源码跑不起来不是代码问题是版本问题。我总结了一份比较稳的版本组合组件推荐版本说明JDK8 或 11多数毕设源码基于 SpringBoot 2.x对应 JDK8/11 最稳Maven3.6.x ~ 3.8.x3.9 也能用太低版本可能解析不了部分依赖MySQL5.7 或 8.05.7 兼容性最好8.0 需要注意驱动和时区Node.js14 或 16 LTSVue2 项目在 Node18 上安装依赖经常报错数据库工具Navicat / DBeaver两者任选能执行 SQL 脚本就行IDEIDEA VSCode后端用 IDEA前端用 VSCode两个窗口一起开反复强调版本是因为我真见过有人把 JDK 升到 17、Node 升到 20然后被莫名其妙的编译错误折腾一晚上。源码作者写的时候用的什么版本你就用什么版本“能用不折腾”是第一原则。Windows 上装 MySQL 推荐用 msi 安装版安装时选 Server only记住 root 密码别装到带空格的目录里后面连接会省很多事。3.2 数据库初始化SQL 脚本这样导入最稳妥运行系统之前先得把数据准备好。第一件事是确认 MySQL 服务已经启动Windows 上可以在服务管理器里看 MySQL 状态没启动就手动启动。然后用 Navicat 或 DBeaver 连上本地 MySQL执行CREATE DATABASE IF NOT EXISTS library DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE library;注意用utf8mb4而不是老旧的utf8。utf8mb4是完整版的 UTF-8 编码中文不会乱码还能兼容表情字符。建完库之后把源码自带的 init.sql 导入。Navicat 里直接右键数据库选“运行 SQL 文件”命令行的话就用source /path/to/init.sql。导入完成后刷新表列表确认看到四张核心表。这一步做完可以先翻一翻 init.sql 里的 INSERT 语句默认管理员账号密码通常就藏在里面后面登录要用。3.3 后端启动改配置、导依赖、跑起来用 IDEA 打开后端工程后第一步是等 Maven 自动下载依赖。国内网络条件下这一步经常卡住解决方案是给 Maven 配置阿里云镜像。找到 Maven 安装目录下的conf/settings.xml在 mirrors 节点里加mirror idaliyunmaven/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirror依赖拉完之后打开application.yml把数据源改成你自己的配置server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/library?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse username: root password: 你本机的MySQL密码这里有两个 URL 参数特别重要。serverTimezoneAsia/Shanghai是解决数据库时间差 8 小时问题的useSSLfalse是避免本地连接时出现 SSL 连接错误的很多人报 SSL 相关异常就是因为少了这个参数。改完配置直接运行LibraryApplication的 main 方法控制台输出Tomcat started on port(s): 8080说明启动成功。这时候可以先访问http://localhost:8080或者用 Postman 调一下登录接口能看到 JSON 返回就说明接口层通了。3.4 前端启动把页面和接口接起来前端启动分两步。第一步装依赖在终端里进入前端目录执行npm install如果速度慢先换成国内镜像npm config set registry https://registry.npmmirror.com装完依赖后还要处理一个关键问题前端页面和后端接口不在同一个端口浏览器会认为它们是跨域请求。解决办法是在前端工程根目录的vue.config.js里配置代理module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }这个代理的意思很直白前端页面跑在 3000 端口凡是以/api开头的请求开发服务器都会转发给 8080 的后端。浏览器只看到同源的请求跨域问题自然消失。这是很多初学者最容易忽略的一环——接口地址看了代码也改了就是忘了配代理结果页面一直报 CORS 错误。配置完成后执行启动命令npm run dev看到App running at localhost:3000之后浏览器打开http://localhost:3000用 init.sql 里写的默认账号登录。如果 README 里没写一般就是 admin/admin123 这类组合找不到就直接在数据库的 sys_user 表里查一下。4. 核心业务逻辑与代码实操心法4.1 登录与 Token 鉴权后端必须自己验证身份这类管理系统的登录流程基本都是同一个套路前端把用户名密码传给后端后端查库校验通过后签发一个 token前端把 token 存到 localStorage之后每个请求都在请求头带上它。后端登录接口的简化逻辑长这样RestController RequestMapping(/api/auth) public class LoginController { Autowired private UserMapper userMapper; PostMapping(/login) public Result login(RequestBody LoginVO loginVO) { User user userMapper.selectByUsername(loginVO.getUsername()); if (user null || !user.getPassword().equals(encrypt(loginVO.getPassword()))) { return Result.error(账号或密码错误); } // 生成 token包裹当前用户 id 和角色 String token JWT.create() .withClaim(userId, user.getId()) .withClaim(role, user.getRole()) .sign(SecretKey); return Result.ok(token); } }密码不能明文存储常见的方案是 MD5 或者 BCrypt。无论源码用哪种核心思路都一样数据库里存的不是用户输入的明文而是加密后的字符串登录时把输入内容加密后再比对。有了 token 还不够后端必须做一道拦截校验。否则别人绕过前端直接调接口你根本不知道请求来自谁public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (isWhitelist(request.getRequestURI())) { return true; // 登录接口、静态资源放行 } String token request.getHeader(Authorization); try { JWT.verify(token); return true; } catch (Exception e) { response.setStatus(401); return false; } } }这里有个坑要提醒前端路由守卫只是在界面层做限制隐藏了页面而已。真正决定数据安不安全的是后端这道拦截器。如果拦截器白名单配置错了把/api/book/list这种接口也放行那登录功能就形同虚设。4.2 借书还书状态流转必须用事务兜底借书和还书是这个系统里最核心的业务也是最容易出现并发问题的地方。借书的流程是读者发起借阅 → 校验图书存在且库存大于 0 → 库存减一 → 插入一条借阅记录。如果不用事务就会出现极端情况两个用户同时借最后一本书两个请求都查到库存是 1都执行了减库存最后记录插了两条库存变成负数。解决方法是加事务注解让“查库存、扣库存、插记录”这三步操作要么全部成功要么全部回滚Transactional(rollbackFor Exception.class) public void borrow(Integer userId, Integer bookId) { Book book bookMapper.selectById(bookId); if (book null || book.getStock() 0) { throw new RuntimeException(图书不存在或库存不足); } bookMapper.decreaseStock(bookId); borrowRecordMapper.insert(new BorrowRecord( userId, bookId, LocalDate.now(), LocalDate.now().plusDays(30), 0 )); }还书的流程就是反向操作查借阅记录 → 确认记录存在且为未还状态 → 判断是否逾期 → 还书日期写入 → 库存加回Transactional public void returnBook(Integer recordId) { BorrowRecord record borrowRecordMapper.selectById(recordId); if (record null || record.getStatus() ! 0) { throw new RuntimeException(借阅记录不存在或已归还); } LocalDate today LocalDate.now(); if (today.isAfter(record.getShouldReturnDate())) { long days ChronoUnit.DAYS.between(record.getShouldReturnDate(), today); // 按 0.5 元/天计算滞纳金可以写入记录或单独生成罚金单 } record.setReturnDate(today); record.setStatus(1); borrowRecordMapper.updateById(record); bookMapper.increaseStock(record.getBookId()); }这套逻辑里最重要的是状态字段的取值约定。很多项目会在注释里说明0未还 1已还这个约定必须在后端代码里严格统一。实战中出问题最多的就是前端传了错误的 status 值或者后端状态判断条件写反导致一本已经还掉的书还能被再还一次。如果项目要考虑高并发光靠查库存再更新还不够稳更严谨的做法是直接在 SQL 层面用条件更新UPDATE book_info SET stock stock - 1 WHERE id #{bookId} AND stock 0这种写法的好处是数据库自己保证“库存大于 0 时才扣减”就算并发请求再多也不会超卖。4.3 列表分页与条件查询管理系统的“门面”逻辑管理系统里 80% 的页面都是表格列表所以分页查询是读者浏览频率最高的逻辑。后端用 MyBatis-Plus 写起来很简洁PageBook page new Page(pageNum, pageSize); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(bookName), Book::getBookName, bookName); page bookMapper.selectPage(page, wrapper);like的第一个参数是条件判断只有当前端传了bookName才拼接模糊查询条件。这样就不会出现输入框留空时把所有代码 search 出来的问题。前端配合 Element UI 的表格和分页组件流程就是切换页码时带上pageNum和pageSize重新请求接口拿到新的数据后刷新表格。要注意接口路径和参数名必须对齐后端pageNum从 1 开始前端el-pagination的 current-page 也就从 1 开始这两个数字的一致性最容易搞混。搜索条件除了书名还可以按作者、出版社、ISBN 模糊查询甚至按库存数量排序。排序在 SQL 里很好实现ORDER BY stock DESC前端对应的就是表格表头的排序事件。这里我多说一句条件查询字段越多后端 Service 层就会越长遇到这种情况别慌它只是把多个wrapper.like串在一起而已本质上和写一个复杂 SQL 是一个意思。5. 源码运行排查实录与二次开发建议5.1 六个高频报错的排查思路我在跑这套源码的过程中也遇到了不少问题。把最高频的几个整理成一份自查清单报错现象大概率原因快速解决方案Access denied for user rootlocalhost数据库账号或密码不对重新核对 application.yml 中的 username 和 passwordCommunications link failureMySQL 服务没启动或端口不是 3306启动 MySQL 服务确认端口一致Server returns invalid timezone时区未指定JDBC URL 追加serverTimezoneAsia/Shanghainpm ERR! code ERESOLVENode 版本太高切换到 Node 14 或 16重新 install前端请求接口报 401token 没被带到请求头检查 request.js 里的 axios 拦截器Maven 依赖下载失败仓库访问慢或证书问题配置阿里云镜像逐一展开说。Communications link failure是最常见的新手第一反应是改代码其实大多数时候就是 MySQL 服务压根没启动。Windows 下打开服务管理器找到 MySQL 服务手动启动即可Linux 下执行sudo systemctl start mysqld。401 的问题我也见过很多次。前端已经登录成功了localStorage 里也有 token但请求接口时 axios 没有把 token 放进 Header。排查方法很简单打开浏览器开发者工具切到 Network 面板点开任意一个接口请求看请求头里有没有Authorization。没有的话就检查utils/request.js看拦截器里是否从 localStorage 取 token 并设置了 header。前端 npm 安装报 ERESOLVE 基本可以断定是 Node 版本问题。Vue2 的项目依赖关系比较敏感Node18 版本的依赖解析算法更严格会把之前能装的环境当成冲突。稳妥的做法是直接装一个 Node16 LTS或者用nvm切换版本比去改 npm 配置快很多。5.2 从“能跑”到“随便改”拿到源码后的正确姿势源码跑通只算完成第一步。我的建议是别急着改功能先把系统完整过一遍。第一遍原样启动把所有页面和按钮都点一遍登录、增删改查、借书、还书、分页、搜索搞清楚每个功能在业务上是什么形态。第二遍打开浏览器开发者工具切到 Network 面板看着每个请求的 URL 和返回跟着请求链路去后端源码里找对应的 Controller 和 Service自己画一张“页面 → 接口 → 数据表”的对应关系图。第三遍才开始做你自己的改动。常见的起步改动有几个。想改包名在 IDEA 里右键包名选 Refactor → Rename顺便把pom.xml里的groupId和artifactId一起改掉。想换前端标题和 Logo找到public/index.html里的 title还有 layout 组件里的文字全局搜一下就都能定位到。想改数据库连接就改application.yml。这些都是低风险、适合练手的操作。如果想把这个系统做成一个可以部署的产物前端构建之后把dist目录里的静态文件复制到后端的src/main/resources/static下再用 Maven 打包成一个 jar就能实现前后端一体部署。这也是一种很常见的 SpringBoot Vue 项目打包方式。5.3 我自己跑这套代码的一些体会最后分享一点个人经验。我下载过不少网上的管理类源码最怕的不是代码写得烂而是作者把默认账号藏得深、SQL 脚本和代码对不上、前端代理配得稀碎。这套图书管理系统在这几个方面做得算完整的init.sql 里能看到初始数据前端代理也给你留好了位置。但我还是要提醒一句网上下载的源码如果你要用于毕业设计或正式项目一定要自己过一遍安全项默认密码必须改测试数据和真实业务数据要分开。毕竟能跑只是起点清楚每一行关键代码在做什么才是你把这套项目变成自己项目的前提。按这个流程走下来从拿到源码到页面亮起来二十分钟左右是正常的。过程中不管卡在哪一步先对照表格自查一遍绝大多数问题都能在十分钟内定位到根因。这套系统我跑通之后给不少学弟学妹做过演示大家问得最多的还是配置问题。其实配置类问题本质就是个排查顺序问题先看数据库有没有数据再看后端有没有启动最后看前端有没有把请求发对按这个顺序一层层查翻车概率会小很多。
返回列表