本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可运行的图书管理全栈项目,后端用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_recordbook_iduser_id 两个外键,MyBatis里 <resultMap> 标签直接定义 bookNameuserName 字段怎么从关联查询中取值,学生看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(已归还),方便学生测试“归还”按钮的显隐逻辑;用户表里学号 20230012023002 的借阅记录时间跨度覆盖上周和今天,支撑“按时间范围查询”功能验证。这些数据让抽象的业务规则瞬间变得可触摸、可验证。

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,全部用组件内 dataprops 传递。只有全局配置(如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.3npm -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; 应返回 12SELECT 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 已打包好。启动步骤:

  1. 打开终端,进入 BookManagementSystemJava 目录
  2. 执行 ./mvnw spring-boot:run(Linux/Mac)或 mvnw.cmd spring-boot:run(Windows)
  3. 观察控制台输出,直到出现 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)。然后:

  1. 打开新终端,进入 vueproject 目录
  2. 执行 npm install 安装依赖(首次需几分钟)
  3. 执行 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找不到数据库连接配置。排查步骤:

  1. 检查 application.yml 路径:确保文件在 src/main/resources/ 下,而非 src/main/java/ 或其他目录。IDEA中右键 resources → “Mark Directory as” → “Resources Root” 可修复路径问题。
  2. 检查缩进格式:YAML对空格敏感。spring:datasource: 必须顶格,url:username: 必须缩进2个空格。用在线YAML校验工具(如 https://yamlchecker.com/)粘贴内容验证。
  3. 检查MySQL服务状态:执行 netstat -ano | findstr :3306(Windows)或 lsof -i :3306(Mac/Linux),确认MySQL进程在运行。若无输出,启动MySQL服务。
  4. 检查密码特殊字符:如果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.jschunk-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.vuesubmitBorrow() 方法中:

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.ymljdbc: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),但默认放行所有路径。要启用登录,只需三步:

  1. 创建 SecurityConfig.java,配置 http.authorizeRequests(),放行 /login/api/public/**,保护 /api/**
  2. 新增 LoginController,接收用户名密码,校验后生成JWT令牌(用 jjwt-api 依赖);
  3. 前端登录页提交后,将JWT存入 localStorage,后续所有 $http 请求在 headers 中添加 Authorization: Bearer xxx

这样,学生就从“无认证系统”升级到“标准Token认证系统”,为后续开发企业级应用打下基础。

6.3 如何部署到服务器:Nginx + Linux 生产环境

本地开发用 npm run servemvnw,生产环境必须用正式部署:

  • 后端:执行 ./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流畅完成借书、查记录、导出报表时,通过率直接拉满。因为这证明系统不是“只能在自己电脑跑”,而是具备真实可用性。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:直接可运行的图书管理全栈项目,后端用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),开箱即用,适合教学演示、课程设计、毕设参考或快速原型验证。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

更多推荐