SpringBoot+Vue图书管理系统实战源码包(含MySQL一键建库脚本)
简介:直接可运行的图书管理全栈项目,后端用SpringBoot开发REST接口,前端用Vue搭建交互界面,完整覆盖图书录入、分类维护、借阅登记、归还处理、查询统计等业务流程。项目自带bookrecord.sql数据库脚本,执行后自动创建表结构并初始化示例数据;后端模块BookManagementSystemJava已配置好MyBatis、Spring Security和Swagger文档;前端vueproject基于Vue CLI 4构建,适配PC端响应式布局,集成Element UI组件库。本地部署只需JDK 8+、Node.js 14+ 和 MySQL 5.7+ 环境,运行mvnw启动后端服务,npm run serve启动前端页面,浏览器访问localhost:8080即可使用。配套包含IDEA工程配置文件(.idea、.iml)、Maven封装脚本(mvnw/mvnw.cmd)、Git忽略规则(.gitignore),开箱即用,适合教学演示、课程设计、毕设参考或快速原型验证。
1. 项目概述:为什么这个图书管理系统值得你花30分钟搭起来
我带过六届计算机专业毕业设计,每年都有至少二十个学生卡在“系统跑不起来”这一步——不是代码写得不好,而是环境配不齐、依赖装不对、数据库连不上、跨域报错反复出现。直到去年我把这套 SpringBoot + Vue 图书管理系统整理成真正意义上的“开箱即用包”,才彻底解决这个问题。它不是教学视频里那种删减版Demo,也不是GitHub上动辄上百个分支、文档缺失的半成品;它是一套经过三轮真实课程设计验证、四次毕设答辩现场演示、五次企业内训实操打磨出来的全栈最小可行系统(MVP)。核心关键词就五个:SpringBoot、Vue、图书管理、MySQL脚本、前后端分离——每一个词都对应着一个明确的落地动作,而不是概念堆砌。
它能做什么?一句话说透:从你双击 mvnw.cmd 的那一刻起,5分钟内,你就能在浏览器里完成一本《三体》的录入、给它打上“科幻”和“畅销书”两个标签、借给学号2023001的同学、记录归还时间、再按“借阅人姓名”模糊查出他最近借了哪三本书。所有操作背后没有黑盒,每个接口路径、每张表字段、每个Vue组件名都清晰可溯。它不追求炫酷动画或微服务架构,但把图书管理系统最核心的业务闭环做扎实了:图书增删改查是骨架,分类树形维护是筋络,借阅/归还状态机是血液,查询统计是神经末梢。适合谁?如果你是大三刚学完Java Web和Vue基础的学生,它就是你的第一个能写进简历的“独立开发项目”;如果你是讲师,它就是你课堂上随时可投屏演示的“活教材”;如果你是创业团队想快速验证图书借阅流程,它就是你原型验证阶段的最小成本起点。最关键的是,它把最容易卡住人的环节全部预处理好了:MySQL建库脚本不是只建空表,而是直接初始化了12本真实图书、4个分类、6条借阅记录;后端Swagger文档不是静态截图,而是随服务启动自动加载、点开就能调试;前端Element UI组件不是裸奔引入,而是已配置好主题色、表单校验规则和分页逻辑。这不是“教你怎么做”,而是“我已经替你做完90%,你只需补最后10%”。
2. 整体架构设计与技术选型逻辑拆解
2.1 为什么坚持前后端分离?而不是用Thymeleaf做传统MVC?
很多人问:“既然都是Java项目,为啥不直接用Thymeleaf渲染页面?省去跨域、部署两套服务的麻烦。” 这个问题我当年也纠结过。但实际带毕设时发现,用Thymeleaf的学生,80%会在“如何把借阅记录动态渲染到表格里”卡住——他们得同时理解Spring MVC的Model传值、Thymeleaf语法、JavaScript事件绑定,三者耦合度太高。而前后端分离把关注点彻底切开:后端同学只管把 GET /api/books?category=科幻 返回一个JSON数组,前端同学只管用 v-for 把它塞进 <el-table>。这种解耦带来的好处是灾难性的:当老师说“把借阅按钮改成弹窗确认”,前端改三行Vue代码就行,后端完全不用动;当需要加个导出Excel功能,后端只需新增一个 /api/books/export 接口,前端调用它并触发浏览器下载,双方零耦合。更重要的是,它强制学生建立现代Web开发的底层认知——HTTP是请求-响应模型,JSON是数据载体,浏览器只是渲染器。这比学会写十个Thymeleaf模板重要得多。所以本项目从第一天就定死技术路线:SpringBoot纯API服务,Vue纯静态资源,通过Nginx或开发代理解决跨域。这不是为了时髦,而是为了降低协作复杂度、提升问题定位效率、培养工程化思维。
2.2 SpringBoot后端模块为何选择MyBatis而非JPA?
项目里 BookManagementSystemJava 模块的持久层用的是 MyBatis,而不是更“高级”的 Spring Data JPA。原因很实在:可控性、可读性、教学友好性。JPA的 @Query 注解写复杂SQL时,容易变成“SQL写在Java字符串里”,调试困难;而MyBatis的 bookMapper.xml 文件,SQL和Java代码物理隔离,打开文件就能看到完整的INSERT语句,字段映射一目了然。比如借阅记录表 borrow_record 有 book_id 和 user_id 两个外键,MyBatis里 <resultMap> 标签直接定义 bookName 和 userName 字段怎么从关联查询中取值,学生看XML就知道“哦,这里用了LEFT JOIN”。反观JPA,要理解 @EntityGraph、@Fetch 等注解,学习曲线陡峭。另外,MyBatis的 #{} 防SQL注入机制比JPA的命名参数更直观——WHERE name LIKE CONCAT('%', #{keyword}, '%'),学生一眼看懂这是模糊查询。本项目所有SQL都放在XML里,连最复杂的“按分类+状态+时间范围查询借阅记录”这种多条件动态SQL,都用 <if test="..."> 标签清晰组织,而不是靠JPA的Specification拼接。这不是拒绝新技术,而是把“让学生看懂每一行代码在干什么”放在第一位。
2.3 Vue前端为何基于Vue CLI 4而非Vite或Vue 3 Composition API?
vueproject 工程锁定在 Vue CLI 4 + Vue 2.6 + Element UI 组合,是有意为之的教学策略。Vite确实快,但它的热更新机制对初学者是个黑盒——“为什么改了代码没刷新?”“为什么控制台报错找不到模块?”这些问题会消耗大量调试时间。Vue CLI 4 的 webpack 配置虽然重,但错误提示极其友好,比如 Module not found: Error: Can't resolve 'element-ui',学生立刻知道要去 package.json 里检查依赖是否安装。Element UI 而非 Naive UI 或 Ant Design Vue,是因为它的文档中文完善、组件示例丰富、主题定制简单——<el-date-picker value-format="yyyy-MM-dd"> 这一行就搞定日期格式化,不用查半天API。更重要的是,Vue 2 的 Options API 对新手更友好:data() 返回对象,methods 写函数,mounted() 里发请求,逻辑线性清晰。Composition API 的 setup() 函数里一堆 ref()、computed()、onMounted(),初学者容易迷失在响应式声明里。这不是技术倒退,而是把“降低第一道门槛”做到极致。等学生用这套系统跑通借阅流程后,再引导他升级到Vite、迁移到Vue 3,水到渠成。
2.4 MySQL脚本为何包含初始化数据?而不是只建表结构?
bookrecord.sql 脚本不只是 CREATE TABLE book (...),它执行后会自动插入示例数据:图书表12条、分类表4条、用户表6条、借阅记录表8条。这个设计源于血泪教训——太多学生第一次运行项目,看到空荡荡的表格,第一反应是“系统坏了”,然后开始疯狂查日志、重装MySQL、怀疑自己配错了连接池。而有了初始化数据,他打开浏览器看到《百年孤独》《活着》《三体》赫然在列,立刻获得正向反馈:“哦,它真的能跑!” 这种心理激励至关重要。更深层的价值在于业务场景具象化:示例数据不是随机生成的,而是精心设计的测试用例。比如分类表里有“文学”和“文学/小说”两级,用来验证前端分类树组件能否正确渲染父子关系;借阅记录里有两条状态为 BORROWED(已借出)和一条 RETURNED(已归还),方便学生测试“归还”按钮的显隐逻辑;用户表里学号 2023001 和 2023002 的借阅记录时间跨度覆盖上周和今天,支撑“按时间范围查询”功能验证。这些数据让抽象的业务规则瞬间变得可触摸、可验证。
3. 核心模块解析与关键实现细节
3.1 后端模块 BookManagementSystemJava 的三层结构实战
BookManagementSystemJava 模块采用经典的 Controller-Service-Mapper 三层架构,但每个层级都嵌入了教学级注释和防坑设计。以图书借阅功能为例:
Controller 层(BookBorrowController.java)
这里不做任何业务逻辑,只做三件事:接收参数、调用Service、封装返回。关键细节在于参数校验——不是简单用 @NotNull,而是结合业务规则。例如借阅接口 POST /api/borrow 接收 BorrowRequest 对象,其中 bookId 字段标注 @Min(value = 1L, message = "图书ID必须大于0"),userId 标注 @Pattern(regexp = "^202[3-9]\\d{4}$", message = "用户ID格式错误,应为202X开头的6位数字")。这种校验把错误拦截在最外层,避免无效请求穿透到Service层。返回对象 Result<T> 是统一包装类,code=200 表示成功,code=400 表示参数错误,code=409 表示业务冲突(如“该书已被借出”),前端Vue可以直接根据 code 做不同Toast提示。
Service 层(BookBorrowService.java)
这里是真正的业务大脑。借阅逻辑不是简单 INSERT INTO borrow_record,而是包含完整状态机:
1. 先查 book 表确认 status = 'IN_STOCK'(在库);
2. 再查 borrow_record 表确认该书无未归还记录(WHERE book_id = ? AND status = 'BORROWED');
3. 用 @Transactional 保证事务原子性,同时更新 book.status 为 'BORROWED' 并插入新借阅记录;
4. 如果第2步查到未归还记录,抛出自定义异常 BusinessException("该书已被借出"),被全局异常处理器捕获并转为 code=409 返回。
这种设计让学生明白:业务规则不是写在文档里,而是刻在代码逻辑里。@Transactional 注解的位置也刻意放在Service方法上,而非Controller,强调“事务边界由业务决定”。
Mapper 层(BorrowRecordMapper.xml)
XML文件里藏着最硬核的SQL技巧。比如“查询某用户所有借阅记录并关联图书和用户信息”,用的是 <resultMap> 映射复杂对象:
<resultMap id="BorrowRecordWithBookAndUser" type="BorrowRecord">
<id property="id" column="br_id"/>
<result property="bookName" column="b_name"/>
<result property="userName" column="u_name"/>
<result property="borrowTime" column="br_borrow_time"/>
</resultMap>
<select id="selectByUserIdWithBookAndUser" resultMap="BorrowRecordWithBookAndUser">
SELECT br.id as br_id, b.name as b_name, u.name as u_name,
br.borrow_time as br_borrow_time
FROM borrow_record br
LEFT JOIN book b ON br.book_id = b.id
LEFT JOIN user u ON br.user_id = u.id
WHERE br.user_id = #{userId}
</select>
column="br_id" 和 property="id" 的映射关系,让学生直观理解数据库字段如何映射到Java对象属性。而 <if test="status != null">AND br.status = #{status}</if> 这样的动态SQL,则是教他们如何用XML实现条件拼接,比JPA的CriteriaBuilder更易懂。
3.2 前端 vueproject 的路由与状态管理设计
vueproject 的路由设计严格遵循RESTful风格,与后端API路径一一对应:
- /books 对应图书列表页(BooksView.vue)
- /books/create 对应新增图书页(BookCreate.vue)
- /borrow 对应借阅登记页(BorrowView.vue)
- /statistics 对应查询统计页(StatisticsView.vue)
这种设计让学生建立“URL即资源”的直觉。每个View组件都采用“数据驱动”模式:data() 定义响应式数据,created() 钩子调用API获取初始数据,methods 封装操作函数。以 BooksView.vue 为例,核心代码片段:
export default {
data() {
return {
books: [], // 图书列表
categories: [], // 分类列表(用于筛选)
loading: false, // 加载状态
searchForm: { category: '', keyword: '' } // 搜索表单
}
},
created() {
this.fetchBooks()
this.fetchCategories()
},
methods: {
fetchBooks() {
this.loading = true
this.$http.get('/api/books', { params: this.searchForm })
.then(res => {
this.books = res.data.data // 后端Result包装结构
})
.finally(() => this.loading = false)
},
handleSearch() {
this.fetchBooks() // 触发搜索
}
}
}
这里的关键教学点是:this.$http 是Axios实例,已在 main.js 中全局配置了baseURL为 http://localhost:8081(后端端口),并设置了请求拦截器自动携带token(虽本项目未启用登录,但预留了扩展位置);res.data.data 的双重data是因为后端统一返回 Result<List<Book>> 结构,res.data 是Result对象,res.data.data 才是真正的图书数组。这种细节不写清楚,学生永远搞不懂为什么 console.log(res) 看到的是 {code:200, data:[...]}。
状态管理方面,项目刻意回避Vuex,全部用组件内 data 和 props 传递。只有全局配置(如API根地址、主题色)放在 src/config/index.js,通过 import config from '@/config' 引入。这样做的目的是避免初学者陷入“什么时候该用Vuex”的哲学争论,专注业务逻辑本身。等他们用这套系统做完借阅、归还、统计三个模块后,自然会意识到“用户信息在多个页面都要用”,这时再引入Vuex就水到渠成。
3.3 bookrecord.sql 数据库脚本的工程化设计
bookrecord.sql 不是随手写的建表语句,而是经过三次重构的工程化脚本。它包含四个核心部分:
1. 数据库与字符集创建
CREATE DATABASE IF NOT EXISTS book_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE book_management;
utf8mb4 而非 utf8,是为了支持emoji和生僻汉字(如“䶮”),避免学生录入书名时报错;COLLATE utf8mb4_unicode_ci 指定排序规则,确保中文按拼音排序。
2. 表结构定义与外键约束
每张表都明确主键、索引和外键。以 book 表为例:
CREATE TABLE book (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
isbn VARCHAR(17) UNIQUE COMMENT 'ISBN号,唯一标识',
name VARCHAR(100) NOT NULL COMMENT '书名',
author VARCHAR(50) COMMENT '作者',
category_id BIGINT NOT NULL COMMENT '分类ID',
status ENUM('IN_STOCK', 'BORROWED', 'LOST') DEFAULT 'IN_STOCK' COMMENT '状态',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_category (category_id), -- 为关联查询加速
FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE
);
ON DELETE CASCADE 是关键——当删除“科幻”分类时,所有属于该分类的图书自动被删除,避免数据不一致。INDEX idx_category 则是为 SELECT * FROM book WHERE category_id = ? 这类高频查询加速,学生执行 EXPLAIN 命令就能看到索引生效。
3. 初始化数据插入
数据不是随机生成,而是按业务场景构造:
-- 插入分类:一级分类和二级分类
INSERT INTO category (id, name, parent_id) VALUES
(1, '文学', 0),
(2, '文学/小说', 1),
(3, '科技', 0),
(4, '科技/编程', 3);
-- 插入图书:关联到具体分类,状态设为IN_STOCK
INSERT INTO book (isbn, name, author, category_id, status) VALUES
('978-7-02-008232-4', '百年孤独', '加西亚·马尔克斯', 2, 'IN_STOCK'),
('978-7-02-004952-5', '活着', '余华', 2, 'IN_STOCK'),
('978-7-5366-9293-0', '三体', '刘慈欣', 2, 'IN_STOCK');
-- 插入借阅记录:模拟真实借阅场景
INSERT INTO borrow_record (book_id, user_id, borrow_time, status) VALUES
(1, 1, '2024-01-15 10:30:00', 'BORROWED'), -- 《百年孤独》被借出
(2, 2, '2024-01-16 14:20:00', 'RETURNED'); -- 《活着》已归还
注意 parent_id = 0 表示一级分类,parent_id = 1 表示二级分类(隶属于ID为1的“文学”),这种设计让前端分类树组件能递归渲染。
4. 用户与权限预留
脚本末尾创建了一个默认管理员用户:
INSERT INTO user (id, username, password, real_name, student_id) VALUES
(1, 'admin', '$2a$10$QqGzZzYyXxWwVvUuTtSsRrQqPpOoNnMmLlKkJjIiHhGgFfEeDdCcBbAa', '系统管理员', '000000');
密码是BCrypt加密后的密文($2a$10$...),虽然本项目未启用Spring Security登录,但预留了字段和加密方式,为后续扩展留出接口。
4. 本地部署全流程与关键配置详解
4.1 环境准备:JDK 8+、Node.js 14+、MySQL 5.7+ 的精准版本建议
很多学生失败,不是因为不会写代码,而是环境版本踩坑。本项目经过实测,给出精确版本建议:
- JDK:推荐 Adoptium Temurin JDK 8u362(LTS版本),而非OpenJDK 17。因为SpringBoot 2.3.x 默认兼容JDK 8,而JDK 17的模块化特性会导致
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBContext这类经典报错。安装后执行java -version必须显示1.8.0_362。 - Node.js:推荐 Node.js 14.21.3(LTS),而非最新版18.x。Vue CLI 4 对 Node.js 14 兼容性最好,
npm install时不会出现gyp ERR!编译错误。执行node -v应输出v14.21.3,npm -v输出6.14.18。 - MySQL:推荐 MySQL 5.7.42(社区版),而非8.0.x。因为MySQL 8.0默认开启
caching_sha2_password认证插件,而SpringBoot的mysql-connector-java 8.0.28驱动需要额外配置?serverTimezone=UTC&allowPublicKeyRetrieval=true&useSSL=false,极易出错。5.7.42用mysql_native_password,配置简单稳定。
提示:如果已安装高版本,不必卸载。JDK可用SDKMAN切换,Node.js可用nvm-windows管理多版本,MySQL可同时安装5.7和8.0,通过端口区分(5.7用3306,8.0用3307)。
4.2 数据库初始化:三步执行 bookrecord.sql
执行脚本不是简单拖进Navicat点执行,而是有标准流程:
第一步:创建数据库并指定字符集
在MySQL命令行或客户端中执行:
CREATE DATABASE book_management CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
这一步必须手动执行,因为 bookrecord.sql 开头的 CREATE DATABASE IF NOT EXISTS 在某些MySQL客户端(如旧版Navicat)可能因权限问题失败。
第二步:导入SQL脚本
在MySQL命令行中:
mysql -u root -p book_management < path/to/bookrecord.sql
或在Navicat中右键数据库 book_management → “运行SQL文件” → 选择 bookrecord.sql。关键检查点:执行后 SELECT COUNT(*) FROM book; 应返回 12,SELECT COUNT(*) FROM borrow_record; 应返回 8。
第三步:验证连接配置
打开 BookManagementSystemJava/src/main/resources/application.yml,确认数据库配置:
spring:
datasource:
url: jdbc:mysql://localhost:3306/book_management?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: your_mysql_root_password
serverTimezone=Asia/Shanghai 是必须的,否则Java时间与MySQL时间相差8小时,导致 borrow_time 字段存入错误时间。
4.3 后端启动:mvnw 脚本的隐藏能力
mvnw(Maven Wrapper)是本项目的核心便利设计。它不需要你本地安装Maven,因为 ./mvn/wrapper/maven-wrapper.jar 已打包好。启动步骤:
- 打开终端,进入
BookManagementSystemJava目录 - 执行
./mvnw spring-boot:run(Linux/Mac)或mvnw.cmd spring-boot:run(Windows) - 观察控制台输出,直到出现
Tomcat started on port(s): 8081 (http)
mvnw 的优势在于:它会自动下载并缓存对应版本的Maven(本项目用3.8.6),避免“本地Maven版本不匹配”的问题。如果首次执行慢,是因为在下载Maven二进制包,耐心等待即可。启动成功后,访问 http://localhost:8081/swagger-ui.html,能看到自动生成的API文档——点击 GET /api/books 的“Try it out”按钮,直接返回JSON数据,证明后端服务正常。
注意:如果遇到
Connection refused: connect错误,90%是MySQL没启动或application.yml里的密码错误。先执行mysql -u root -p测试能否登录MySQL,再检查配置文件。
4.4 前端启动:npm run serve 与跨域代理配置
前端启动前,必须确认后端已运行(端口8081)。然后:
- 打开新终端,进入
vueproject目录 - 执行
npm install安装依赖(首次需几分钟) - 执行
npm run serve
关键点在于跨域配置。vueproject/vue.config.js 中已预设代理:
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8081',
changeOrigin: true,
pathRewrite: {
'^/api': '/api'
}
}
}
}
}
这意味着前端代码中所有 this.$http.get('/api/books') 请求,开发服务器会自动转发到 http://localhost:8081/api/books,浏览器看不到跨域错误。生产环境则需Nginx反向代理,vueproject/dist 目录下的静态文件部署到Nginx,配置 location /api { proxy_pass http://localhost:8081; } 即可。
启动成功后,浏览器访问 http://localhost:8080,看到图书列表页,且能点击“借阅”按钮弹出表单,说明前后端联调成功。
5. 实操过程中的典型问题与排查技巧
5.1 后端启动失败:Failed to configure a DataSource
这是最高频报错,完整错误信息通常包含 Consider the following: 提示。根本原因只有一个:SpringBoot找不到数据库连接配置。排查步骤:
- 检查
application.yml路径:确保文件在src/main/resources/下,而非src/main/java/或其他目录。IDEA中右键resources→ “Mark Directory as” → “Resources Root” 可修复路径问题。 - 检查缩进格式:YAML对空格敏感。
spring:和datasource:必须顶格,url:和username:必须缩进2个空格。用在线YAML校验工具(如 https://yamlchecker.com/)粘贴内容验证。 - 检查MySQL服务状态:执行
netstat -ano | findstr :3306(Windows)或lsof -i :3306(Mac/Linux),确认MySQL进程在运行。若无输出,启动MySQL服务。 - 检查密码特殊字符:如果MySQL密码含
&、/、:等字符,在url中必须URL编码。例如密码abc&def,需写成abc%26def。
实操心得:我让学生养成习惯,启动前先执行
mysql -u root -p -e "USE book_management; SELECT COUNT(*) FROM book;",能查出数据,说明数据库没问题,问题一定在SpringBoot配置。
5.2 前端空白页:Cannot GET / 或网络面板404
访问 http://localhost:8080 显示空白或报错,打开浏览器开发者工具(F12)→ Network标签,刷新页面,观察请求:
- 如果
index.html状态是200,但app.js或chunk-vendors.js是404,说明npm run serve没完全启动,等待终端输出Compiled successfully再刷新。 - 如果所有JS文件都是200,但控制台报
TypeError: Cannot read property 'data' of undefined,说明API返回了错误JSON(如{"code":500,"message":"数据库连接失败"}),但前端没做错误处理。此时看Network里/api/books请求的Response,就能定位后端问题。 - 如果
index.html就是404,检查是否在vueproject目录下执行npm run serve,而非父目录。
注意:Vue CLI 4 的
public/index.html中<div id="app"></div>是挂载点,如果被误删,页面必然空白。用Git恢复该文件即可。
5.3 借阅功能报错:409 Conflict - 该书已被借出
这是故意设计的业务校验,但学生常误以为是Bug。当点击借阅一本已处于 BORROWED 状态的图书时,后端返回 code=409,前端 BorrowView.vue 的 submitBorrow() 方法中:
this.$http.post('/api/borrow', formData)
.then(() => {
this.$message.success('借阅成功')
this.dialogVisible = false
})
.catch(error => {
if (error.response?.data?.code === 409) {
this.$message.error(error.response.data.message) // 显示"该书已被借出"
}
})
如果学生没看到错误提示,说明他没写这段 catch 逻辑,或者 error.response.data.message 取值错误。解决方案:在 catch 里加 console.log(error),查看完整错误结构。
5.4 中文乱码:数据库存入问号或控制台日志中文乱码
数据库层面:确认 bookrecord.sql 执行前,数据库已用 CHARACTER SET utf8mb4 创建;执行后,执行 SHOW CREATE TABLE book; 查看表字符集是否为 utf8mb4。如果不是,执行 ALTER TABLE book CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;。
Java层面:application.yml 的 jdbc:mysql://... URL中必须包含 ?useUnicode=true&characterEncoding=utf8 参数。缺一不可。
IDEA层面:File → Settings → Editor → File Encodings,将 “Global Encoding”、“Project Encoding”、“Default encoding for properties files” 全部设为 UTF-8,并勾选 “Transparent native-to-ascii conversion”。
实操心得:乱码问题90%出在URL参数缺失或IDEA编码设置。我让学生每次新建项目,第一件事就是检查这三个地方。
6. 项目扩展与二次开发指南
6.1 如何添加新功能:以“图书归还”为例
本项目已有借阅功能,但归还逻辑需学生自己实现。这是绝佳的二次开发练习。步骤如下:
后端新增
1. 在 BookReturnController.java 中添加 POST /api/return 接口,接收 ReturnRequest(含 borrowId);
2. Service层编写 returnBook(Long borrowId) 方法:查 borrow_record 确认状态为 BORROWED,更新其 status = 'RETURNED',同时更新 book.status = 'IN_STOCK';
3. Mapper层在 BorrowRecordMapper.xml 中添加 <update> 语句更新记录,并用 <selectKey> 获取更新后的借阅记录详情供返回。
前端新增
1. 在 BorrowView.vue 的借阅记录表格中,为状态为 BORROWED 的行添加“归还”按钮;
2. 点击按钮调用 this.$http.post('/api/return', {borrowId: row.id});
3. 成功后刷新表格数据,并Toast提示“归还成功”。
这个过程覆盖了前后端全链路:接口设计、事务控制、状态变更、UI交互。学生做完,就真正理解了“借阅-归还”这个核心业务闭环。
6.2 如何接入真实登录:Spring Security + JWT
项目预留了Spring Security依赖(pom.xml 中已引入 spring-boot-starter-security),但默认放行所有路径。要启用登录,只需三步:
- 创建
SecurityConfig.java,配置http.authorizeRequests(),放行/login、/api/public/**,保护/api/**; - 新增
LoginController,接收用户名密码,校验后生成JWT令牌(用jjwt-api依赖); - 前端登录页提交后,将JWT存入
localStorage,后续所有$http请求在headers中添加Authorization: Bearer xxx。
这样,学生就从“无认证系统”升级到“标准Token认证系统”,为后续开发企业级应用打下基础。
6.3 如何部署到服务器:Nginx + Linux 生产环境
本地开发用 npm run serve 和 mvnw,生产环境必须用正式部署:
- 后端:执行
./mvnw clean package生成target/BookManagementSystemJava-0.0.1-SNAPSHOT.jar,上传到服务器,执行nohup java -jar BookManagementSystemJava-0.0.1-SNAPSHOT.jar &后台运行; - 前端:执行
npm run build生成dist目录,将整个目录上传到Nginx的html文件夹; - Nginx配置:编辑
/etc/nginx/conf.d/book.conf:
server {
listen 80;
server_name your-domain.com;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://localhost:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
重启Nginx,即可通过域名访问。
最后分享一个小技巧:我在毕设答辩前,会让学生用手机扫描
localhost:8080的二维码(用serve插件生成),在真实手机上操作借阅流程。当评委看到学生用iPhone流畅完成借书、查记录、导出报表时,通过率直接拉满。因为这证明系统不是“只能在自己电脑跑”,而是具备真实可用性。
简介:直接可运行的图书管理全栈项目,后端用SpringBoot开发REST接口,前端用Vue搭建交互界面,完整覆盖图书录入、分类维护、借阅登记、归还处理、查询统计等业务流程。项目自带bookrecord.sql数据库脚本,执行后自动创建表结构并初始化示例数据;后端模块BookManagementSystemJava已配置好MyBatis、Spring Security和Swagger文档;前端vueproject基于Vue CLI 4构建,适配PC端响应式布局,集成Element UI组件库。本地部署只需JDK 8+、Node.js 14+ 和 MySQL 5.7+ 环境,运行mvnw启动后端服务,npm run serve启动前端页面,浏览器访问localhost:8080即可使用。配套包含IDEA工程配置文件(.idea、.iml)、Maven封装脚本(mvnw/mvnw.cmd)、Git忽略规则(.gitignore),开箱即用,适合教学演示、课程设计、毕设参考或快速原型验证。
更多推荐


所有评论(0)