多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

SpringBoot+Vue图书管理系统源码实战:跑通、改造与部署指南

SpringBoot+Vue图书管理系统源码实战:跑通、改造与部署指南 1. 项目到底解决什么问题先直接说结论这是一套“带完整源码、可直接运行、能写进毕设/课设/简历”的图书管理系统技术栈是SpringBoot Vue MySQL MyBatis。标题里那个“html”其实指的是前端页面形态是 HTML 页面Vue 单页应用打包后就是一套 HTMLJSCSS并不是说前端就只有一个静态 html 文件。这类项目在 GitHub/Gitee 上一搜一大把但大多数版本的问题在于要么后端代码跑不起来要么前端依赖装不上要么 MySQL 版本不匹配要么 MyBatis 配置和表结构对不上。我拿到这套源码之后花了两天时间把它完整跑通并且梳理了从环境准备 → 数据库导入 → 后端启动 → 前端启动 → 功能联调的完整流程。这篇文章不是照抄 README而是以“实际动手做一遍”的角度把每一步的坑和为什么这么做的原因都写清楚。适合谁看正在做毕业设计、课程设计需要一套能答辩、能演示的图书管理系统的同学刚学完 SpringBoot 和 Vue想找个完整项目练手、看懂真实项目结构的人需要快速搭建一套图书管理后台评估这套源码能不能直接改造成自己业务的人如果你是零基础、连 SpringBoot 和 Vue 是什么都还没搞明白这篇文章也能看但建议先把我标注的“基础概念补充”部分读一遍否则容易卡在环境配置上。2. 整体设计与技术选型拆解2.1 为什么是 SpringBoot Vue而不是别的组合先看图浏览器Vue 页面 ↓ HTTP / JSON SpringBoot 后端Controller → Service → Mapper ↓ MyBatis 通过 JDBC MySQL 数据库在 2024 年做这类管理系统SpringBoot Vue 几乎算得上“最省心”的组合没有之一。原因有三点第一前后端分离后开发时两边互不干扰。后端同学只需要写好接口、返回 JSON前端同学只需要对着接口文档写页面。如果是在校生做毕设这种做法还能在论文里多写一章“前后端分离架构设计”凑字数也好、展示能力也罢都很方便。第二SpringBoot 把配置简化到了极致。传统 SSM 项目要写一堆 XML 配置、web.xml、spring-mvc.xmlSpringBoot 直接给你内置了 Tomcat搞一个application.yml就搞定大部分设置。对新手来说少配一项就少一个出错点。第三Vue 对新手极其友好。它的语法介于传统 HTMLJS 和现代前端框架之间就算你只会 jQuery看 Vue 的单文件组件也能猜个大概。再配合 Element UI 这类组件库做表格、弹窗、表单几乎不用自己写 CSS直接拿来用就行。那为什么不推荐 JSP Servlet 或者 Thymeleaf没有说那些不能做只是从“拿出去能找工作、能写进简历”的角度看Vue 的普适性更高。现在随便打开一个招聘网站搜“Java 开发”十有八九都要求会 Vue 或者至少了解一种前端框架。用这套源码跑一遍至少能背下来一套前后端交互的完整链路。2.2 后端分层的设计思路为什么代码不糊在一坨这套源码的后端包结构大概是这样的src/main/java/com/xxx/library/ ├── controller/ // 接收前端请求调用 service ├── service/ // 业务逻辑层接口 实现 ├── mapper/ // MyBatis 的 mapper 接口 ├── entity/ // 实体类对应数据库表 ├── config/ // 配置类比如 CORS 跨域配置 └── common/ // 通用返回结果、工具类很多新手拿到代码第一反应是这分层好麻烦啊我直接在 Controller 里写 SQL 不行吗当然可以但那样写出来的东西不叫系统叫“接口集合”。分层的目的不是让你多敲几行代码而是如果将来要改业务逻辑不需要动 Controller 和数据库层如果将来要换数据库只需要改 mapper 层如果将来要加权限验证只需要在 service 层统一处理。举个实际例子假设你要加一个“借书前检查该用户是否还有未归还的图书”的逻辑。在分层设计里你只需要在BorrowService里加一个判断方法然后去BookMapper或BorrowMapper写一条查询 SQL。前端完全不用改Controller 也基本不用动。如果你是全局在一个 Controller 里写 SQL那就得把整个方法重写测试风险高得多。这套源码的分层虽然简单但五脏俱全。Controller 负责参数接收和结果返回Service 负责业务判断Mapper 负责 SQL 操作。照着这个结构你可以很轻松地拓展新功能比如加一个“公告管理”模块那就是照抄图书模块的四层结构20 分钟能搞定。2.3 前端路由与页面组织Vue 是怎么把多个页面串起来的前端部分不是那种一个 HTML 文件里写死的单页而是用了 Vue Router 做前端路由。页面之间通过路由跳转像是在一个 HTML 外壳里动态替换内容。前端的主要目录结构src/ ├── main.js // 入口文件创建 Vue 实例 ├── router/ │ └── index.js // 路由表URL 路径对应哪个组件 ├── views/ │ ├── Login.vue // 登录页 │ ├── Layout.vue // 主布局顶栏侧边栏内容区 │ ├── BookManage.vue // 图书管理页 │ ├── BorrowManage.vue // 借阅管理页 │ └── UserManage.vue // 用户管理页 ├── api/ │ └── request.js // 封装 axios 请求登录之后进入 Layout左侧是导航菜单右侧是根据路由切换的内容区。这种设计的好处是新增页面不需要动其他页面只要在 router 里注册一个路由再写一个 Vue 文件即可。如果之前没接触过 Vue 的“组件化”概念可以这样类比把页面当成积木。Layout.vue是一个固定的拼图底板菜单和顶栏是底板的一部分中间的内容区域是个“插座”。每个路由组件比如BookManage.vue就是一块可以插进插座的积木切换路由时插座里的积木被换掉了但底板和菜单位置不动。这就是为什么前端常见的后台管理系统都长一个样——顶部栏和侧边栏几乎是固定的变的只是中间的业务页面。3. 基于源码的核心功能拆解与实操要点3.1 图书管理模块最核心的增删改查图书管理模块基本就是一套标准的“增删改查 分页 条件搜索”。实体类字段大体如下字段含义字段名类型说明图书IDidint自增主键书名book_namevarchar可模糊搜索ISBNisbnvarchar图书唯一标识可搜索作者authorvarchar可模糊搜索出版社publishervarchar普通字段库存量stockint借阅时会关联判断状态statusvarchar在馆/下架后端对应BookController里的几个接口大致是RestController RequestMapping(/api/book) public class BookController { GetMapping(/list) public Result page(BookQuery query) { ... } PostMapping(/add) public Result add(RequestBody Book book) { ... } PutMapping(/update) public Result update(RequestBody Book book) { ... } DeleteMapping(/delete/{id}) public Result delete(PathVariable Integer id) { ... } }实操时要注意几个点分页参数不要写死。前端会传pageNum和pageSize后端用 PageHelper 或者手写 LIMIT 做分页。如果你改成了LIMIT 10写死前端翻页就会失效。搜索条件用 Map 或者 Query 对象接收。别一个参数一个参数地接以后加查询条件会非常痛苦。删除图书要关联判断。如果有未归还的借阅记录关联着这本书直接删除会导致数据不完整。严谨的做法是删除前先查一下借阅表有没有status借出且book_id当前id的记录。我实际跑通之后发现这套源码在删除借阅中的图书时没有做拦截属于一个“功能完整但不够健壮”的小缺陷。如果是做毕设建议在BookServiceImpl.delete里加一段判断int count borrowMapper.countByBookIdAndStatus(bookId, 借出); if (count 0) { return Result.error(该书存在未归还的借阅记录无法删除); }就这一小段逻辑答辩时能成为你的加分项因为你展示的不是“会抄代码”而是“能发现问题并改进”。3.2 借阅管理模块最容易出 bug 的地方借阅模块的难度比图书管理高一档因为它涉及两张表的数据联动借阅记录表和图书表。借书流程拆开来看是这样前端提交借书请求携带参数图书ID、用户ID或读者证号后端先查图书是否存在、库存是否大于 0再查该用户是否有未归还的同名图书防止重复借同一本允许借出后图书表的库存减一在借阅表插入一条记录状态为“借出”记录借书时间设置应还时间一般是借书时间 30 天还书流程就是反过来查到这条借阅记录确认状态是“借出”将状态改为“已还”写入实际还书时间图书表的库存加一如果超期计算罚款金额有的系统有有的没有值得认真研究的是这个部分用了事务。在BorrowService的借书方法上会看到Transactional注解Transactional public Result borrowBook(BorrowDTO dto) { // 1. 校验库存 // 2. 扣减库存 // 3. 插入借阅记录 }为什么必须有Transactional因为**“扣库存”和“插入借阅记录”必须同时成功或同时失败**。如果扣了库存但插入借阅记录失败就会出现一本书凭空消失如果插入了记录但没扣库存就会出现库存对不上账。这个注解就是给这两个数据库操作上了一把锁要么全做完要么全回滚。类比一下你在支付宝转账扣款和收款是两个动作如果扣了款但对方没收到你肯定不干。事务就是保证这种“要么都成功要么都不成功”的机制。3.3 用户登录与权限控制这套系统提供了读者和管理员两类角色。登录成功后会返回一个 token有的版本是直接返回用户信息存到 localStorage前端根据角色控制菜单显示管理员能看到用户管理、图书管理等全部菜单普通读者只能看到图书查询、个人借阅记录等有限功能。这里有一个很常见的实务点后端接口不能只靠前端隐藏菜单来实现权限控制。什么意思就是说如果前端菜单里看不到“用户管理”但 someone 猜到/api/user/list这个接口地址直接拿浏览器或者 Postman 发一个请求后端如果没有任何拦截那就泄露了数据。正规的做法是在后端加一个拦截器HandlerInterceptor或者用一个轻量权限框架比如 Sa-Token、Shiro、Spring Security。但这套源码里大概率只是做了简单的登录校验没有做接口级别的权限控制这是很多“毕设货架项目”的通病。如果你想在答辩里展示“我思考过安全问题”可以给后端加一个简单的拦截器比如继承HandlerInterceptorpublic class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(token); if (token null || !TokenUtils.verify(token)) { response.setStatus(401); return false; } return true; } }然后在 WebMvcConfig 里注册拦截路径拦截所有 /api/** 的请求放行 /api/login这个改动不算大但能让你在答辩时说清楚“我的系统不是只靠前端控制后端的拦截器同样会校验登录状态”。3.4 数据库设计的核心表关系这套源码里的数据库至少有这几张表表名主要字段userid, username, password, role, nicknamebookid, book_name, isbn, author, publisher, stockborrow_recordid, user_id, book_id, borrow_time, return_time, status表之间的关系user和borrow_record一对多一个用户有多条借阅记录book和borrow_record一对多一本书被借多次但如果限定了同一本书同时只能被一个人借那就需要额外约束设计表的时候外键并不一定非要在数据库层面建逻辑外键通过代码关联在现在的企业项目中更常见因为这样可以减少数据库锁竞争、方便分库分表和备份恢复。但作为教学项目建议把外键建上至少在论文里可以写“通过外键保证数据的引用完整性”。实际操作时如果你发现源码里的 SQL 文件只有一个sql文件直接导入即可。导入前务必将数据库编码设置为utf8mb4否则中文会乱码。如果是用 Navicat 导入右键数据库 → 运行 SQL 文件不要用复制粘贴到查询窗口的方式否则容易因为编码问题产生“明明看着是对的但查询就是报错”的情况。4. 完整实操过程从零到跑通4.1 环境准备清单与版本避坑先列一份我当时使用的环境也是比较稳的组合组件推荐版本备注JDK1.8 或 11这版代码用的是 Java 8 语法太高也行Maven3.6用 IDEA 内置也可以MySQL5.7 或 8.08.0 需要改驱动名和连接参数Node.js14.x / 16.x别用太新的18 有时装依赖会报 openssl 错误Vue CLI4.x / 5.x具体看前端 package.json最大的坑往往是版本不匹配。如果前端项目是用 Vue CLI 4 初始化的Node 18 环境下安装依赖时十有八九会报Error: error:0308010C:digital envelope routines::unsupported。解决方式是修改package.json里的启动脚本scripts: { serve: NODE_OPTIONS--openssl-legacy-provider vue-cli-service serve }Windows 下这样写会出问题Windows 的 cmd 不支持这种写法需要改成serve: set NODE_OPTIONS--openssl-legacy-provider vue-cli-service serve或者更省事直接用 Node 16这个版本基本不会触发该报错。4.2 后端启动详细步骤第一步导入源码到 IDEA。选择pom.xml用 Maven 的 Import Project 方式等待依赖下载完成。这里有两个情况如果本地 Maven 仓库里缺依赖而你的网络又比较差等半小时是常事可以考虑切换阿里云镜像。如果 pom.xml 里的依赖版本特别老比如 SpringBoot 2.2.xIDEA 下载时可能会报一些警告不用管只要最终右侧 Maven 窗口不报红即可。第二步修改application.yml。重点检查这三个配置spring: datasource: url: jdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456一定要把数据库名、用户名、密码改成你自己的。useSSLfalse一定要加否则 MySQL 8.0 会报 SSL 连接错误。serverTimezone一定要加否则日期字段可能报时区错误。第三步确认 MyBatis 的 XML 文件路径。在application.yml里通常会看到mybatis: mapper-locations: classpath:mapper/*.xml对应地src/main/resources/mapper/目录下应该有BookMapper.xml、BorrowMapper.xml等文件。如果 XML 文件不在这个路径启动时会报Invalid bound statement (not found)。第四步运行LibraryApplication.java的 main 方法控制台显示类似Tomcat started on port(s): 8080然后访问http://localhost:8080/api/...看看是否能返回 JSON。如果直接 404别急先确认你访问的路径和 Controller 里的RequestMapping是不是一致比如/api/book/list。4.3 前端启动详细步骤第一步打开终端cd到前端目录一般是frontend或者vue-book。第二步安装依赖npm install如果你是在国内网络环境下建议用淘宝镜像npm config set registry https://registry.npmmirror.com npm install第三步配置代理。在vue.config.js里找到类似这样的配置module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }意思就是前端页面上请求/api/xxx时开发服务器会把请求转发给http://localhost:8080这个后端地址。如果你后端端口不是 8080这里要改。这是前后端分离开发时最常用的手段用来绕开跨域问题。第四步运行npm run serve浏览器打开http://localhost:3000看到登录页基本就成功了。输入测试账号README 里一般会有 admin/admin123有的版本是 admin/123456进去之后能看到首页统计和各个管理菜单。4.4 联调验证的关键接口跑通之后最快的验证方式是打开浏览器开发者工具F12切到 Network 面板随便点一下“图书列表”页面会看到类似这样的请求GET http://localhost:3000/api/book/list?pageNum1pageSize10对应返回的 JSON 结构大致是{ code: 200, message: success, data: { total: 25, list: [ { id: 1, bookName: Java编程思想, isbn: 9787111213826, author: Bruce Eckel, stock: 10 } ] } }如果看不到数据按这个顺序排查数据库有没有数据SELECT * FROM book后端接口直接访问能不能返回浏览器访问http://localhost:8080/api/book/list前端代理有没有生效Network 面板里请求的 URL 是不是http://localhost:3000/api/...还有一招比较省心如果你不想每次都用开发模式可以前端执行npm run build生成dist目录然后用 Nginx 托管前端、反向代理后端。这个操作更接近真实部署场景毕设或项目演示时也更有说服力。5. 常见问题与排查技巧实录值得收藏的一章5.1 后端启动失败“Invalid bound statement (not found)”这个报错十有八九是 MyBatis 的 XML 文件没被扫描到。排查三步确认application.yml里mapper-locations对应的路径是否正确比如classpath:mapper/*.xml那么 XML 文件必须放在src/main/resources/mapper/下。检查pom.xml里是否把 XML 文件排除掉了。有些模板的buildresources配置只打包了application.yml没包含resources/mapper需要加resources resource directorysrc/main/resources/directory includes include**/*.xml/include include**/*.yml/include /includes /resource /resources启动类上是否有MapperScan或者每个 Mapper 接口是否有Mapper注解。两选其一即可。5.2 MySQL 8.0 连接报错或时区问题如果你用的 MySQL 是 8.0驱动名要改成com.mysql.cj.jdbc.DriverURL 必须带上serverTimezoneAsia/Shanghai。MySQL 5.7 用com.mysql.jdbc.Driver即可。另外如果报Public Key Retrieval is not allowed在 URL 后面加allowPublicKeyRetrievaltrue。完整示例url: jdbc:mysql://localhost:3306/library_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue5.3 前端 npm install 报错最常见的两类一是网络问题。把 registry 切到淘宝镜像基本能解决如果还不行就清 npm 缓存npm cache clean --force。二是node-sass编译失败。因为node-sass需要在安装时下载二进制文件网络不稳定容易挂。解决方案是删除package-lock.json和node_modules然后执行npm install前先设置 SASS 镜像npm config set sass_binary_site https://npm.taobao.org/mirrors/node-sass/如果这版源码用的不是node-sass而是sassDart Sass安装起来就轻松多了基本上不会有这套问题。5.4 登录成功后跳转不到首页前端登录逻辑一般是登录接口返回 token → 存到 localStorage → 路由跳转到/layout或/home。如果一直停留登录页常见原因后端返回结构和前端约定不一致。比如前端期望{code: 200, token: xxx}后端返回却是{code: 200, data: {token: xxx}}前端里res.data.token取到 undefined登录判断失败。路由守卫里写了if (!token) redirect login但 token 存错 key 了。解决建议F12 看 Network 面板请求和响应确认后端到底返回了什么再去request.js和login.vue里比对取值逻辑。这种问题八成是字段名对不上。5.5 前端请求接口跨域报错如果是开发模式用vue.config.js的 proxy 最省事但注意页面上请求的地址应该是/api/xxx而不是http://localhost:8080/api/xxx。一旦写了完整的后端地址就走不到代理跨域报错就来了。如果是部署模式建议用 Nginx 统一解决server { listen 80; location /api/ { proxy_pass http://localhost:8080/api/; } location / { root /path/to/dist; index index.html; try_files $uri $uri/ /index.html; # 这个是 Vue Router history 模式必需的 } }5.6 时间字段显示不对或格式乱数据库里的datetime字段返回给前端时Jackson 默认可能序列化成时间戳一串数字前端看到的就是 1690000000000 这种。解决方式是在application.yml里加spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT85.7 如何改造成自己的业务比如“学生选课系统”这套图书管理系统的表结构和通用业务系统非常接近改造成“学生选课系统”其实只需要动几张表book→course课程ID、课程名称、教师、学分、人数上限borrow_record→select_record选课记录含学生ID、课程ID、选课时间、状态user不变学生/管理员两种角色后端实体类、Mapper XML 里的字段名全替换Controller 和 Service 逻辑基本不用动。前端页面把表格列名和数据字段换掉也能复用。这就是“模板化管理系统”的通用价值——你换换表和字段就能交付出一个新的管理后台。6. 部署上线时我踩过的几个坑6.1 后端打包要注意的配置后端打包命令是mvn clean package -DskipTests生成target/xxx.jar。但这个 jar 能不能跑取决于application.yml里的配置是不是生产环境配置。如果你打包时还是连本地的localhost:3306上传到服务器后自然会连接失败。所以部署前先确认数据库连接配置。还有一个常见错误如果把后端跑在云服务器上前端页面访问的是域名http://your-domain.com:8080/api/...还是 Nginx 代理的/api如果是服务器直接对外开放 8080需要注意安全组策略否则容易被扫端口。建议前端打包成静态文件交给 Nginx后端 jar 跑在 8080通过 Nginx 反向代理访问。6.2 服务器上如何把 SpringBoot 后台进程守住直接用java -jar xxx.jar启动一关终端进程就没了。推荐用nohupnohup java -jar xxx.jar log.log 21 更稳的做法是配 systemd 服务不过对于毕设或演示nohup已经够用了。建议每次启动时看一眼log.log确认端口有没有被占用、数据库连接是否正常。6.3 一个隐藏很深的坑前后端认证机制不一致如果后端的 token 校验用的是自定义拦截器前端却在每次请求时使用不同的 header 名比如后端读的是Authorization前端设置的是token那么即使你登录成功后续请求也会 401。建议统一约定所有经过登录鉴权的请求都在 axios 拦截器里统一设置 header。给一个 axios 拦截器的通用写法import axios from axios 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 }) export default request前端所有页面 api 请求都通过这个封装好的request对象发出避免各自的页面里手动加 header减少出错率。7. 最后再分享点实操体会说实话这种“图书管理系统”在技术上不算多难但它真的能帮你把知识串起来。很多初学者单独学 SpringBoot、单独学 Vue 时都觉得明白一看代码好像也都认识但真正缺的是那张“网”——前端点击按钮到后端查询数据库再把数据显示回页面上这一整条链路如果没有亲手跑通一遍就永远只是停留在“语法会了”的层面。我实际操作下来最大的心得是拿到一套源码后不要一上来就跑而是先花 20 分钟把目录结构看一遍理清前端和后端是怎么约定的接口路径、返回结构、token 传递方式再动手配置环境。这样就算后面出了问题你也能定位到大概哪一层而不是无头苍蝇一样乱试。另外这套源码毕竟不是企业级产品你在研究时保持两个心态第一能用但不够健壮很多边界条件没有处理第二这也恰恰是你能“二次加工”的地方。比如给系统加上借阅超期提醒、图书封面上传、Excel 导入导出这些功能都能真正锻炼你对整条技术链路的掌控力。如果遇到跑不起来的情况先别急着改代码按这个顺序排查数据库导入有没有成功 → 后端配置对不对 → 浏览器直接访问后端接口通不通 → 前端代理转发有没有生效。这四条链路通了系统自然就转起来了。祝你能顺利跑通也欢迎在实操过程中带着具体报错来找我讨论。
返回列表