资讯详情

资讯详情

前后端分离图书管理系统:SpringBoot+Vue完整实现与部署实战

“前后端分离图书管理系统”这个话题网上源码其实不少但真正能直接跑起来、有完整登录鉴权、有借还书业务流程、还附带部署说明的并不算多。我最近把一套 SpringBoot Vue MyBatis MySQL 的实现完整整理了一遍包含后端接口、前端页面、数据库建表脚本和部署笔记。这篇文章就是把这套系统的设计思路、关键代码和最容易踩的坑一股脑写出来给打算拿它做课程设计、入职练手项目或者想从零接触前后端分离项目的朋友做个参考。这套系统麻雀虽小五脏俱全。读者端和管理员端分开权限靠 JWT 控制图书的增删改查、分页搜索、借书还书、超期判断都有完整实现前端用 Vue 配合 Element UI后端是标准的 SpringBoot 三层结构。我尽量把那些文档里不会写、但实际开发一定会遇到的细节也一并交代清楚比如跨域配置为什么只在开发环境需要、Linux 部署时前端路由刷新为什么会 404、MySQL 8 的时区报错怎么解决。下面直接进入正题。1. 从“有源码”到“能跑通”这套系统的整体架构与功能拆解1.1 为什么前后端分离项目更需要“完整”很多人在网上找图书管理系统下载下来才发现是老掉牙的 JSP Servlet或者是只有一个 CRUD 页面的半成品。前后端分离的项目则更容易出现只给前端或只给后端的情况要么接口文档不全要么前端根本跑不起来。这套系统从一开始就是按能跑通的标准整理的前端、后端、数据库三部分是一套完整的闭环。前后端分离的真正难点不是技术新而是联调。前端需要知道后端返回什么结构后端需要知道前端传什么参数。这套系统把接口返回统一成了{ code, message, data }格式前端 axios 封装里也做了统一处理联调阶段省掉了很多不必要的扯皮。1.2 系统模块划分与核心功能清单系统按角色分成两端读者端和管理员端共用同一套后端接口只是不同接口需要的权限不同。读者可以注册登录、查询图书、借书还书、查看自己的借阅记录管理员除了图书管理还能管理分类、用户、借阅记录并在首页看到简单的统计信息。模块读者端功能管理员端功能登录注册注册、登录、修改密码登录、退出图书查询按书名/作者/ISBN 搜索、分页浏览图书增删改、上下架、库存调整分类管理按分类筛选分类的增删改借阅管理借书、还书、查看个人借阅历史查看全部借阅记录、处理超期用户管理查看个人资料用户禁用/启用、角色调整功能清单看着常规但每一项背后都有对应的数据表设计和接口实现。图书库存扣减和借阅记录新增必须放在一个事务里否则会出现书借出去了但库存没减或者库存减了但记录没生成的问题这类细节我在后面专门讲。2. 数据库设计六张表如何支撑借阅流程与权限体系2.1 核心表的字段清单这套系统的数据库一共六张表用户表、图书表、分类表、借阅记录表、管理员操作日志表以及一张用于保存前端上传图片的附件表。这里重点说前四张它们的结构直接决定了业务逻辑怎么写。用户表主要字段包括主键、用户名、密码、真实姓名、角色标识和状态。密码我建议不要存明文后面会讲怎么用 BCrypt 加密。角色用user和admin区分前端路由守卫根据这个字段决定能不能进入管理页面。用户状态字段status用来做禁用被禁用的账号即使 token 没过期后端拦截器也会拒绝请求这个逻辑比前端隐藏按钮靠谱得多。图书表是系统里字段最多的表除了书名、作者、出版社、ISBN 这些基础信息还有分类外键、库存总量、当前可借数量、封面图地址和简介。ISBN 建议加唯一索引因为同一本书的不同版本虽然书名一样但 ISBN 一定不同而真正做图书管理系统时书名重复是常态靠书名去重根本不现实。借阅记录表的字段比较讲究它包含用户外键、图书外键、借书时间、应还时间、实际归还时间和状态。应还时间不能靠查询时临时计算而是借书动作发生时就把时间写进去一般是借书时间加 30 天或者 60 天这个阈值可以在配置里改。状态字段用数字表示0 表示借阅中1 表示已归还2 表示超期未还查询超期图书时直接where status 0 and due_time now()就行。2.2 为什么借阅记录和图书库存要分开维护初学的时候很容易犯一个错误把当前可借数量直接算成库存总量减去借阅记录数。这个思路在数据量小的时候没问题但一旦有图书被删除、借阅记录被清理或者出现异常数据统计就会出错。所以这套系统在图书表里直接维护一个available字段每次借书成功就减一还书成功就加一同时把操作日志写进借阅记录表。这种设计相当于用空间换一致性。查询可借数量走索引命中不需要临时 count 大表。另一个好处是支持人工修正库存如果实体书盘点发现数量不对管理员可以直接调整可用数量而不需要改历史借阅记录。在实际项目中历史数据是动不得的可借数量则属于可变业务数据两者分开是合理的。建表脚本里还需要注意字符集。MySQL 8 默认字符集已经比较合理但如果你用的是 MySQL 5.7建表一定要显式指定utf8mb4否则书名里的生僻字或特殊符号存进去就变成问号。排序规则用utf8mb4_general_ci就够不需要上unicode_ci。3. 后端SpringBoot实现JWT鉴权、动态SQL和借书还书的事务问题3.1 工程目录与应用配置后端工程是标准的 Maven 单模块结构按controller / service / mapper / entity / common五层分包。common里放统一返回结果类、全局异常处理器、JWT 工具类和拦截器配置。启动类上加MapperScan扫描 Mapper 接口这是 MyBatis 整合里最常见的遗漏点之一——很多人 SpringBoot 启动就报找不到 Bean十有八九是忘了这行注解。application.yml里有几个关键配置值得单独说明。数据库连接串一定要带serverTimezoneAsia/Shanghai和characterEncodingutf8前者解决 MySQL 8 的时区报错后者解决中文乱码。Jackson 的日期格式建议统一成yyyy-MM-dd HH:mm:ss否则前端拿到的是时间戳数字还得自己写格式化函数。MyBatis 的驼峰映射也在这里配。数据库字段往往是create_timeJava 属性是createTime如果不开启map-underscore-to-camel-case: true查询结果里这些字段全是 null而且这种 bug 很难定位因为它不影响程序运行只是数据莫名丢失。3.2 JWT登录鉴权的最小实现登录鉴权是这套系统里最有复用价值的部分。用户提交用户名密码后后端校验通过就用 JWT 工具类生成一个 token 返回给前端。token 里只放用户 id 和角色不塞敏感信息有效期设成 2 小时。前端把 token 存到 localStorage每次请求在 axios 拦截器里加到 Header 的Authorization字段。拦截器这边做一个HandlerInterceptor在preHandle里从请求头取 token、解析、查用户状态。如果 token 解析失败直接返回 401用户状态为禁用则返回 403。这里有个细节很容易踩坑addInterceptors注册拦截器时必须用excludePathPatterns把登录接口、注册接口和静态资源路径排除掉否则前端还没登录就被拦截登录页面自己都进不去。实际项目中如果只有 JWT 还不够token 一旦签发在过期前是没法主动作废的。所以被禁用的用户虽然会被拦截器拦下但那要等他下一次发请求。这也是为什么用户表里要有status字段——权限控制不能只依赖 token 本身。3.3 MyBatis动态SQL与PageHelper分页图书查询是这套系统里查询逻辑最复杂的一块支持按书名模糊搜索、按分类筛选、按 ISBN 精确匹配还要分页。这种场景最适合用 MyBatis 的动态 SQL也就是where加if标签组合条件而不是拼字符串。分页用的是 PageHelper 插件。用法很简单查询前调用PageHelper.startPage(pageNum, pageSize)紧接着执行查询语句插件会自动给 SQL 加 limit 并生成 count 查询。这里有个必须记住的规矩startPage后面跟的第一条查询语句才会被分页中间不能插入其他查询或赋值操作否则分页效果会跑到别人身上。PageHelper 的返回值不要直接返回Page对象而是把它转成自定义的分页结果结构包含总记录数、总页数、当前页数据和页码。这样前端分页组件才能拿到total来渲染页码。接口返回结构统一为{ code: 200, message: success, data: { list: [], total: 100 } }前端写起来非常省事。3.4 借书还书时的事务与并发控制借书接口是整个系统里对数据一致性要求最高的地方。一次借书操作要干两件事往借阅记录表插入一条记录同时把图书表的available字段减一。这两步必须放在同一个事务里我用Transactional注解搞定。但光有事务还不够并发情况下会有超借问题。两个用户同时借同一本书的最后库存各自读到available 1都执行减一结果库存变成 -1。解决方式是在查询图书时加上for update行锁。MyBatis 的查询语句写成select ... from book where book_id #{id} for update事务提交前这行记录会一直锁住后到的请求会等前一个事务结束才继续执行。还书接口的逻辑也类似更新借阅记录状态为已归还、写实际归还时间、图书可用数量加一同样在一个事务里。注意还书时要判断这本书是否超期如果超期可以在返回数据里带一个标记前端提示该书已超期 X 天。后端只管记录事实超期罚款这类业务规则可以再扩展系统的核心流程不受影响。4. 前端Vue页面与接口联调axios封装、路由守卫和借阅交互4.1 前端依赖选型Vue 2还是Vue 3这套系统的前端我用的是 Vue 2.6 Element UI 的组合原因很实际兼容性最稳Element UI 的中文文档和示例代码最多遇到问题一搜就有答案。如果你熟悉 Vue 3换成 Vue 3 Element Plus 也完全可以接口和页面逻辑基本一致主要区别在响应式 API 和组件引入方式。工程结构按页面划分views目录下分成admin和reader两块router里配置路由和权限api目录里按业务模块封装接口请求函数utils里放 axios 实例和 token 操作工具。这个划分方式对中小型系统足够清晰等业务复杂了再按 feature 拆分也不迟。4.2 axios请求封装Token自动携带与401处理axios 封装是前后端联调的核心枢纽。我在utils/request.js里创建了一个 axios 实例设置基础路径和超时时间然后在请求拦截器里从 localStorage 取 token拼到请求头。响应拦截器里统一处理返回结构如果 code 不是 200弹错误提示如果 HTTP 状态码是 401清掉本地 token 并跳转登录页。401 跳转这里有个细节要做防抖处理。如果同时发出三个请求都返回 401会触发三次跳转。加一个标记变量跳转只在第一次触发时执行后面直接 return。不然用户会被反复踢回登录页体验很差。接口函数按模块拆分比如api/book.js里导出getBookList(params)、addBook(data)、updateBook(data)、deleteBook(id)这些函数。组件里只需要引入对应的函数调用不用关心 axios 实例怎么创建的这样后端接口路径变了只需要改一个文件。4.3 路由守卫与页面刷新防404前端路由用 Vue Router 的全局前置守卫做权限控制。在beforeEach里判断目标路由是否标记了requiresAuth如果需要登录就检查本地有没有 token如果路由标记了requiresAdmin还要额外校验用户的角色信息。角色信息在登录成功后从后端接口返回存到 localStorage刷新页面后从本地读取。这里有个坑必须提醒用history模式的路由部署到 Nginx 后刷新页面会 404因为 Nginx 找不到对应的物理路径。解决办法是配置try_files $uri $uri/ /index.html;让所有未匹配到文件的请求都回退到入口页面。开发环境不会遇到这个问题生产环境才会很多人在本地跑得好好的一上服务器就翻车九成是这个原因。4.4 列表页、弹窗表单和借阅交互的实现逻辑图书列表页是前端最复杂的页面包含搜索表单、数据表格和分页组件。搜索按钮触发查询时把表单里的条件参数传给getBookList接口拿到数据后塞进表格的data属性。分页组件绑定当前页和总条数页码变化时重新请求数据。新增和编辑图书用的是同一个弹窗表单组件通过editMode区分场景。弹窗打开时如果是编辑模式先调用接口拿到图书详情回填表单提交时根据模式调新增或更新接口。删除操作加上$confirm二次确认避免误删。封面图上传使用 Element UI 的el-upload组件上传成功拿回附件 id表单里存储的是附件 url。读者端的借书交互在图书详情卡片上放一个借书按钮点击后调借阅接口。后端返回成功就刷新列表数据并提示成功失败则直接展示后端的错误消息常见情况是库存不足或者已经借过这本书未还。每个读者在借阅记录页能看到自己当前借了哪些书、什么时候该还状态一目了然。5. Windows和Linux两套部署流程从编译打包到Nginx反向代理5.1 环境版本对照与准备事项部署这套系统的环境要求如下表按这个版本组合测试兼容性问题最少。组件版本要求备注JDK1.8 及以上推荐 OpenJDK 8兼容性最好Maven3.6后端编译打包工具Node.js14 及以上前端构建工具建议 14 或 16MySQL5.7 或 8.0两种版本都测过Nginx1.18 以上生产环境前端部署动手前先把两件事做了一是创建数据库并执行建表脚本二是修改后端application.yml里的数据库账号密码和 JWT 密钥。JWT 密钥默认是写死的一串字符正式使用务必换掉不然任何人都能拿同样的密钥签发 token。5.2 Windows本地一键启动Windows 下部署适合本地开发调试。先启动 MySQL用命令行或客户端工具导入建表脚本。后端起服务在工程根目录执行mvn clean package -DskipTests然后java -jar target/xxx.jar。看到 Spring 启动成功的日志就说明后端起来了默认端口 8080。前端在frontend目录下执行npm install安装依赖然后npm run serve启动开发服务器默认端口 8081。开发环境需要配代理在vue.config.js里把/api开头的请求转发到http://localhost:8080这样前端开发服务器就解决了跨域问题不需要后端额外开放跨域。5.3 Linux服务器发布与Nginx配置Linux 部署的生产模式是后端打 jar 包用nohup后台运行前端npm run build生成dist目录交给 Nginx 托管。前端构建完后把dist里的文件上传到服务器某个目录下然后配置一个 Nginx 站点root 指到那个目录。接口请求通过 Nginx 反向代理转发到后端端口。配置里location /api段用proxy_pass指向http://127.0.0.1:8080注意proxy_pass后面加不带 URI 的地址时请求会保留原始路径转发过去如果带了斜杠路径会被截断。这个细节导致的问题很隐蔽接口全部 404 但前端页面正常。后端 jar 包的启动命令建议写成nohup java -jar xxx.jar app.log 21 日志输出到文件而不是终端方便排查问题。改完代码重新部署时先杀掉旧进程再启动新的避免端口被占用报错。5.4 部署后必查的三个点第一件事访问前端页面登录一遍确认接口能通。第二件事随便找一个列表页刷新几次看是不是每次都正常排查 history 路由的 404 问题。第三件事检查 Nginx 错误日志和后端 app.log很多部署问题都藏在日志里日志不报错不代表没报错。数据库这边也要留意。MySQL 8 默认的认证插件是caching_sha2_password旧版本的 JDBC 驱动可能连不上。处理办法是升级驱动到com.mysql.cj.jdbc.Driver对应版本或者在创建用户时指定mysql_native_password插件。本地开发用的 MySQL 5.7 不会遇到这个问题一上 8.0 就冒出来。6. 复盘与扩展排错清单、安全增强和下一步演进方向6.1 高频踩雷清单这个项目从开发到部署我整理了一份高频问题清单每一条都是实际遇到过的。现象根本原因解决办法查询结果 createTime 等字段为 null没开 MyBatis 驼峰映射配置 map-underscore-to-camel-case前端请求接口 404路由模式 Nginx 未配 try_files添加 fallback 配置接口 401 循环跳转多个并发请求同时收到 401加跳转防抖标记中文乱码数据库连接串缺少编码参数加 characterEncodingutf8数据库时区报错MySQL 8 时区配置连接串加 serverTimezoneAsia/Shanghai分页不生效startPage 后不是紧邻的查询调整代码顺序借书超出库存并发下没有行锁查询加 for update6.2 代码级安全增强密码加密与参数校验目前系统里登录密码直接存的 MD5这在个人开发里勉强够用但拿得出手的项目至少要换成 BCrypt。Spring Security 框架里就有BCryptPasswordEncoder可以只引这个工具类而不引入完整的安全框架改动量不大把注册和登录两个接口的密码处理替换掉就行。参数校验也是值得加强的地方。后端 Controller 里现在靠手动判断参数是否为空项目大了以后很容易漏。引入javax.validation的注解在实体类字段上标注NotBlank、Size等Controller 方法参数加Valid由全局异常处理器统一返回校验失败消息。这样每个接口的参数校验代码能删掉大半可维护性提升明显。6.3 从单体到工程化缓存、权限框架与容器化如果这套系统要继续演进第一步是引入 Redis 缓存热点数据。图书列表是典型的热点查询可以把首页数据缓存到 Redis设置 5 分钟过期数据库压力能降不少。借阅记录查询也适合做缓存但要注意缓存更新策略借书还书后必须删掉对应用户的缓存。权限这块JWT 手写拦截器虽然简单但功能有限。如果后续要细分权限比如分类管理员只能管理分类不能动图书建议换成 Sa-Token 或 Spring Security注解方式控制接口权限开发效率高很多。最后一步是容器化部署写一个 docker-compose 文件把 MySQL、后端、前端容器编排起来服务器迁移只需要一条命令。我之前在另一台服务器上手动部署这套环境花了近一个小时换成容器编排后十分钟搞定。图书管理系统看起来是个老生常谈的项目但把登录鉴权、权限区分、借阅事务、数据库设计、前端联调、生产部署这一整条链路走通价值并不比一个花哨的 Demo 小。这套源码和教程的价值在于你拿到的不只是一堆代码文件而是一条从零到可运行、可演示、可部署的完整经验路径。如果你正卡在前后端分离项目的第一步或者需要一个稳定可跑的练手项目把上面的内容吃透系统的每一行代码都会变成你自己的东西。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →