
这些年校园、社区里的“共享书角”越来越多一个书架、几本旧书就能撑起一个角落但真正运营起来才发现问题一大堆谁借了哪本书、借了多久、该还了没有、书在哪个人手里全靠一本纸质登记本或者Excel表格管理员累借书的人也不方便。我自己就经历过帮社团管理图书角翻登记本翻到崩溃的时期所以才下定决心动手做一个真正能用的图书借还管理系统。这个项目就是用SpringBoot2 Vue3 MyBatis-Plus MySQL8.0这套主流技术栈从零开发的一套前后端分离的图书借还管理系统。管理员可以维护书库、管理读者、处理借书还书、查看逾期记录普通用户可以在线浏览书单、查询个人借阅历史。整个项目包括完整的源码、数据库脚本和配套文档拿来直接运行、二次开发或者当毕业设计、练手项目都非常合适。适合正在学Java Web的开发者、做课程设计的学生以及确实需要一套轻量图书管理工具的个人或小团体。1. “共享书角”到底在解决什么问题1.1 纸质登记模式的三宗罪先别急着聊技术你得先搞清楚这个系统服务的对象是谁。共享书角的特点是书不多几十到几百本场地随意走廊角落、活动室、咖啡吧都有可能管理员通常是兼职可能是学生会干事、前台小姐姐、社团负责人不可能天天盯着系统做复杂操作。纸质登记模式有几个致命痛点。第一借还记录不实时一本书被借走之后其他人根本不知道这本书当前在哪里很容易出现“书架上明明有位置但书没了”的情况。第二逾期不还全靠管理员记忆力借出去一个月的书没人催等想起来的时候书已经找不到了。第三盘点极痛苦年底想统计一下哪些书最受欢迎、哪些书丢失了对着登记本手工数基本等于重新整理一遍。这些问题落到技术上其实就是三个需求图书信息的数字化管理、借还流程的规范化记录、数据统计与逾期提醒的自动化。这也是“共享书角”管理系统最核心的价值所在。1.2 功能拆解从借书到统计的一条完整链路做系统最忌讳一上来就写代码我习惯先把用户故事走一遍。这套系统我从角色和动作两个维度拆解功能就非常清晰了。管理员侧的核心动作图书管理新增书籍、编辑信息、下架损坏或丢失的书、按书名/作者/ISBN检索读者管理录入读者信息、查看借阅历史、冻结违规账户借阅处理登记借书、登记还书、处理续借、标记逾期统计看板图书总量、借出数量、逾期数量、热门图书排行用户侧的核心动作浏览书单通过分类、关键字筛选想看的书查看详情图书简介、库存状态、可借数量个人中心自己的借阅记录、当前在借图书、历史记录、逾期状态这里面有一个容易被忽略但是很重要的设计点借书这个动作由谁来操作我在最初设计时纠结过“用户自助借书”还是“管理员代登记”。后来实际走访了几个书角的运营场景发现如果是开放式书角用户自助借书会导致极大的丢书率——人都有侥幸心理自己扫码登记万一漏了没人知道。所以最终方案是用户提交借阅申请管理员审核之后确认借出形成完整的闭环责任链。这个决策对系统的权限模型影响很大后面会细说。1.3 角色权限与数据模型的关系权限模型是这个系统最基础的地基。我用的方案是经典的RBAC基于角色的访问控制简化版不做细粒度的权限点只区分两个角色管理员和普通读者。用户表里用一个 role 字段区分角色比如 0 表示普通读者1 表示管理员。这个模型在初期完全够用而且实现成本极低。读者只能操作自己的借阅记录和个人信息管理员拥有全部业务操作权限。前端路由根据角色做动态过滤后端接口用拦截器做统一鉴权双重保证。这里分享一下权限设计的心得很多初学者喜欢一开始就上 Spring Security JWT RBAC 完整权限模型做了一大堆角色表、菜单表、权限表结果业务还没开始写光权限配置就劝退了自己。小项目就应该用小项目的做法先用一个简单的拦截器 用户角色字段搞定等系统真正需要多角色、细粒度权限时再升级。架构是为业务服务的不是为了炫技。2. 为什么坚持选这套技术栈2.1 SpringBoot2还是SpringBoot3要看你敢不敢冒险标题里写的是SpringBoot2很多人会问现在SpringBoot3都出来这么久了为什么还选2这个问题的答案其实很现实。SpringBoot3底层依赖的是JDK17而SpringBoot2.7是JDK8的最后一个主要版本。JDK8在企业的装机量依然恐怖很多学校的机房、公司老服务器上都还是JDK8。如果你的目标用户是学生做毕设、中小企业内部工具JDK8SpringBoot2的兼容性最稳妥部署到服务器上不会因为JDK版本不一致直接起不来。另外SpringBoot2.7本身已经是2.x的最终版本功能上非常成熟该踩的坑网上全都有答案。结合2.7内置的Spring Framework 5.3配合MyBatis-Plus、Druid连接池、Swagger文档生成这些老牌组件稳定性极高。对于图书借还这种并发量极低的管理系统SpringBoot2.7的性能余量绰绰有余。我这个项目用的是SpringBoot 2.7.x JDK8 Maven这也是目前国内Java Web项目最常见、最不容易出问题的一套组合。2.2 MyBatis-Plus凭什么叫“效率神器”MyBatis-Plus简称MP和原生MyBatis的区别一句话总结就是MyBatis让你自己写SQLMyBatis-Plus帮你把80%的增删改查都自动生成好了。你要是用过原生MyBatis一定写过类似的痛苦代码写一个Mapper接口再写对应的XML文件里面是一堆重复性极高的selectById、insert、updateById。一个图书模块就要写小几十行样板SQL三个模块下来全是体力活。MyBatis-Plus的BaseMapper接口直接内置了insert、deleteById、updateById、selectById、selectList、selectPage等常用方法。你的Mapper接口只需要继承BaseMapperCRUD就全有了。比如图书管理的Mapper核心代码就一行public interface BookMapper extends BaseMapperBook { }加上MyBatis-Plus的条件构造器QueryWrapper和LambdaQueryWrapper多条件查询、模糊搜索、排序、分页都变得非常优雅。比如按书名字段模糊搜索LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.hasText(keyword), Book::getTitle, keyword) .eq(Book::getStatus, 1) .orderByDesc(Book::getCreateTime); ListBook bookList bookMapper.selectList(wrapper);LambdaQueryWrapper的一大好处是类型安全字段名用方法引用Book::getTitle而不是字符串title编译阶段就能发现字段名拼写错误重构的时候也不容易漏改。这个项目里所有复杂查询我都优先用Lambda方式代码可读性和维护性都提高了不止一个档次。另外MyBatis-Plus的分页插件也很省心。配置一个PaginationInnerInterceptor然后用Page对象一包分页数据就出来了PageBook page new Page(current, size); IPageBook result bookMapper.selectPage(page, wrapper);返回结果里total、pages、records全都齐了前端分页组件直接对接。2.3 Vue3组合式API带来的前端体验升级前端我选的是Vue3 Vite Element Plus Pinia这套组合。Vue3和Vue2最核心的区别就是组合式APIComposition API的引入。在Vue2里写一个页面的逻辑你得把data、methods、computed、watch分散在不同选项里。功能少还好功能一多一个组件几百行代码你要看一个业务逻辑的完整流程得在四个选项之间来回跳非常痛苦。Vue3组合式API把同一业务的变量和方法聚在一起代码组织方式从“按类型分”变成了“按业务分”。比如图书列表中跟加载数据相关的逻辑可以写在一起const bookList ref([]) const loading ref(false) async function loadBookList() { loading.value true try { const res await getBookList({ current: page.value, size: pageSize.value }) bookList.value res.records total.value res.total } finally { loading.value false } }变量、函数、生命周期都集中在一起模块化程度更高。配合Vite的热更新开发体验和Vue2时代完全不是一个量级。前端状态管理用的Pinia是Vue3官方推荐的新一代状态管理库相比Vuex语法更简洁去掉了mutations概念直接可以在actions里同步修改state。配合setup store的写法写起来就跟写普通组合式函数一样自然。这个项目里主要用Pinia存储用户登录状态和角色信息export const useUserStore defineStore(user, () { const token ref() const userInfo ref({}) function setToken(value) { token.value value localStorage.setItem(token, value) } return { token, userInfo, setToken } })UI组件库用的Element Plus表格、表单、弹窗、分页、消息提示这些后台管理系统的高频组件都有现成的样式也统一改起来非常省事。2.4 MySQL8.0有哪些真正用得上的新特性数据库选MySQL8.0最直接的感受就是安装和使用的便利性提高了。MySQL8.0的安装包在Windows和Linux上都有很完善的向导流程默认字符集已经是utf8mb4不需要像5.7时代那样手动改配置文件支持emoji和生僻字。另外几个8.0特性在日常开发中很实用。窗口函数Window Function让排行榜、累计值这类查询变得异常简单。比如统计图书借阅排行按借阅次数排个名次一条SQL就搞定了SELECT title, borrow_count, RANK() OVER (ORDER BY borrow_count DESC) AS rank_no FROM book这在5.7里写起来就得用复杂的临时表或者子查询而在8.0里直接内置支持。事务方面8.0默认的事务隔离级别是REPEATABLE READ同时InnoDB的MVCC机制保证了读写不互相阻塞对于图书借还这种读多写少的场景完全够用。还一个被很多人忽略的点是MySQL8.0官方文档的质量很高遇到问题去查官方参考手册很多参数都有详细的行为说明排查问题效率高很多。3. 数据库设计与后端核心实现3.1 不绕弯子的四张核心表图书借还系统的数据结构不复杂核心就是四张表用户表、图书表、借阅记录表、分类表。我单独加上分类表是因为图书分类在共享书角场景下是强需求读者浏览时喜欢按分类筛选如果只靠书名搜索体验会很差。用户表sys_user核心字段CREATE TABLE sys_user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, username VARCHAR(50) NOT NULL UNIQUE COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT 密码BCrypt加密, real_name VARCHAR(50) COMMENT 姓名, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色0-读者 1-管理员, phone VARCHAR(20), status TINYINT DEFAULT 1 COMMENT 状态1-正常 0-冻结, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 系统用户表;图书表book核心字段CREATE TABLE book ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL COMMENT 书名, author VARCHAR(100) COMMENT 作者, isbn VARCHAR(30) COMMENT ISBN号, category_id BIGINT COMMENT 分类ID, publisher VARCHAR(100) COMMENT 出版社, total_count INT DEFAULT 1 COMMENT 总数量, available_count INT DEFAULT 1 COMMENT 可借数量, status TINYINT DEFAULT 1 COMMENT 1-在架 0-下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) COMMENT 图书表;借阅记录表borrow_record是整个系统的业务核心CREATE TABLE borrow_record ( id BIGINT PRIMARY KEY AUTO_INCREMENT, book_id BIGINT NOT NULL COMMENT 图书ID, user_id BIGINT NOT NULL COMMENT 借阅人ID, borrow_time DATETIME NOT NULL COMMENT 借出时间, due_time DATETIME NOT NULL COMMENT 应还时间, return_time DATETIME COMMENT 实际归还时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-借出中 1-已归还 2-逾期未还 3-已续借, renew_count INT DEFAULT 0 COMMENT 续借次数, operator_id BIGINT COMMENT 操作管理员ID ) COMMENT 借阅记录表;这里有个容易踩的坑图书的“总数量”和“可借数量”必须分开。因为共享书角的书经常存在同一本书多册的情况比如《三体》三部曲三册或者同一本书捐赠了两本。借出时 available_count 减一归还时加一同时要保证 available_count 永远大于等于0这个约束在后端业务代码里必须做校验。借阅记录表的status字段我设计了几个状态借出中、已归还、逾期未还、已续借。为什么把逾期未还单独作为一个状态而不是用借出中加逾期标记因为查询“当前有哪些书逾期了”是这个系统最高频的管理端操作之一独立状态字段可以直接走索引查询效率更高业务逻辑也更直白。3.2 登录鉴权的正确姿势拦截器JWT登录鉴权这块我用的是JWT Spring MVC拦截器方案没有引入Spring Security。理由前面说过小系统用简单方案降低理解和维护成本。前端在登录页输入账号密码后端验证通过后签发一个JWT包含用户ID、用户名、角色三样信息有效期24小时。前端拿到token存到localStorage每次请求通过axios拦截器自动加到请求头service.interceptors.request.use(config { const userStore useUserStore() if (userStore.token) { config.headers[Authorization] Bearer userStore.token } return config })后端写一个JwtInterceptor实现HandlerInterceptor接口在preHandle方法里校验tokenpublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (!(handler instanceof HandlerMethod)) { return true; } String token request.getHeader(Authorization); if (StringUtils.hasText(token) token.startsWith(Bearer )) { token token.substring(7); try { Claims claims JwtUtil.parseToken(token); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } catch (Exception e) { // token过期或无效返回401 } } response.setStatus(401); return false; }为什么用拦截器而不是过滤器因为拦截器可以获取HandlerMethod信息更方便地做接口级别的精细控制。另外还要在WebMvcConfigurer里配置拦截器的拦截路径和放行路径registry.addInterceptor(jwtInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/auth/register);这样一个简单可靠的鉴权链路就完成了。密码加密用的是BCrypt不是MD5。MD5加盐虽然也能用但BCrypt是一种自适应哈希算法通过内置随机盐和计算强度参数能有效抵抗彩虹表攻击而且Spring Security的BCryptPasswordEncoder可以直接拿来单独使用不需要引入整套安全框架。3.3 借阅和归还的核心事务逻辑借书和还书是整个系统业务逻辑最重的两个接口也是我必须写事务的地方。借书接口的逻辑是这样的校验读者状态是否正常校验图书是否存在且在架校验可借数量大于0检查该读者是否有未归还的同名图书防止恶意多借创建借阅记录借出时间now应还时间now30天图书可借数量减1如果读者借阅数量超过上限默认5本拒绝借出这些步骤必须包在同一个事务里任何一步失败都要整体回滚否则会出现“借阅记录创建了但库存没减”这种脏数据。用Transactional注解搞定Transactional(rollbackFor Exception.class) public void borrowBook(Long bookId, Long userId) { User user userMapper.selectById(userId); if (user null || user.getStatus() ! 1) { throw new BusinessException(读者不存在或已被冻结); } Book book bookMapper.selectById(bookId); if (book null || book.getStatus() ! 1) { throw new BusinessException(图书不存在或已下架); } if (book.getAvailableCount() 0) { throw new BusinessException(该图书暂无可借数量); } Long activeBorrowCount borrowRecordMapper.getActiveBorrowCount(userId); if (activeBorrowCount MAX_BORROW_LIMIT) { throw new BusinessException(已达到最大借阅数量); } // 同一本书只能借一本 Long sameBookCount borrowRecordMapper.getActiveBorrowCountByBook(userId, bookId); if (sameBookCount 0) { throw new BusinessException(您已借过这本书请先归还); } BorrowRecord record new BorrowRecord(); record.setBookId(bookId); record.setUserId(userId); record.setBorrowTime(new Date()); record.setDueTime(DateUtils.addDays(new Date(), 30)); record.setStatus(0); borrowRecordMapper.insert(record); book.setAvailableCount(book.getAvailableCount() - 1); bookMapper.updateById(book); }这里我单独封装了BusinessException作为业务异常配合全局异常处理器RestControllerAdvice可以统一返回前端友好的错误格式而不是一长串的堆栈信息。这个习惯建议所有Java Web开发者养成对接口联调和后期维护帮助巨大。还书逻辑相对简单但也要注意状态一致性只能归还“借出中”的记录归还时设置return_time和status1图书可借数量加1。同时要处理一种特殊情况——逾期归还也要正常走还书流程但系统需要额外记录一条逾期标记方便以后统计。最好的做法是还书时判断当前时间是否晚于due_time如果是在返回信息里携带一个overdue字段前端弹窗提示“已逾期X天”。4. 前端实现与联调要点4.1 前端工程搭建和目录结构前端用的是Vite Vue3。创建项目一条命令就搞定npm create vitelatest book-frontend -- --template vue然后安装项目依赖cd book-frontend npm install npm install element-plus element-plus/icons-vue npm install axios pinia vue-router npm install sass -D目录结构我习惯按模块分包而不是按文件类型分包。src下分api、router、stores、views、components五个目录。api目录按业务模块拆文件比如book.js、borrow.js、user.js每个文件导出对应的请求函数。views目录下的页面组件跟路由一一对应看一眼目录结构就知道有哪些页面。在实际开发中这种组织方式比按“components/xxx.vue”平铺要容易维护得多。找代码的时候先去views找页面页面里引用的接口去api目录找状态去stores找思路非常清晰。4.2 图书管理列表页的完整实现图书管理页面是整个前端最核心的页面涉及条件搜索、分页、表格展示、新增编辑弹窗、下架操作等完整功能。我用它来展示Vue3组合式API的组织方式。[[views/book/BookList.vue]]里先写列表加载逻辑script setup import { ref, onMounted } from vue import { getBookPage, deleteBook } from /api/book import { ElMessage, ElMessageBox } from element-plus const loading ref(false) const bookList ref([]) const total ref(0) const queryParams ref({ current: 1, size: 10, keyword: , categoryId: null, status: null }) async function loadBookList() { loading.value true try { const { data } await getBookPage(queryParams.value) bookList.value data.records total.value data.total } finally { loading.value false } } function handleSearch() { queryParams.value.current 1 loadBookList() } function handleReset() { queryParams.value { current: 1, size: 10, keyword: , categoryId: null, status: null } loadBookList() } onMounted(loadBookList) /script模板结构就是el-form搜索区、el-table数据展示区、el-pagination分页区三大块。这个页面涉及的组件交互比较多我把新增和编辑共用一个弹窗组件通过一个isEdit标志区分避免两个弹窗组件逻辑重复。这里要重点说一个实战技巧表格里的图书封面、状态标签、库存颜色这些信息在接口返回时最好让后端把展示文本一起返回。比如status字段返回数字的话前端需要自己维护一个映射对象const statusMap { 0: { text: 已下架, type: info }, 1: { text: 在架, type: success } }而更省心的方式是后端在VO视图对象里直接带statusName字段前端直接渲染。这两种方案我权衡过小项目其实都行但如果你希望前端代码尽量简单推荐后端多做一个字段映射。前端就变成了一行模板渲染el-tag :typerow.statusName 在架 ? success : info {{ row.statusName }} /el-tag4.3 前后端联调时的接口规范与跨域处理联调阶段接口规范越早定越好。我约定所有接口统一返回R对象结构{ code: 200, message: success, data: { ... } }前端axios响应拦截器统一处理code为200时直接返回data部分业务代码不需要每个接口都做一次解包code为401时跳转登录页code为500时ElMessage弹出后端返回的错误信息这样一来业务代码里拿到的直接就是有效数据大量重复的错误处理被收敛到拦截器里代码清爽非常多。跨域问题是前后端分离项目逃不掉的一关。前端开发时Vite服务器跑在5173端口后端接口在8080端口浏览器的同源策略会直接拦下请求。解决方式有两种后端加CORS全局配置或者前端用Vite代理。我推荐前端Vite代理方案因为开发时前端配置proxy后端完全不需要感知跨域问题生产环境通过Nginx做同源反代这是最标准的姿势。Vite配置// vite.config.js export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } })在这个配置下前端请求/api/xxx会被代理转发到后端8080端口浏览器看到的请求是同源的跨域问题直接消失。5. 环境搭建、部署与典型问题排查5.1 从安装到跑起来的全套步骤如果你从零开始搭这套环境我按实际操作的先后顺序给你列一遍第一步安装MySQL 8.0。Windows下用安装包一路点下一步就行安装时注意选择字符集为utf8mb4设置root密码最好单独创建一个普通用户供应用连接不要所有应用都拿root连接数据库。数据库创建之后导入项目中的init.sql脚本表结构和初始数据包含一个默认管理员账号就都有了。第二步安装JDK8和Maven。JDK8建议用官方版本或Adoptium的OpenJDK发行版配好JAVA_HOME环境变量。Maven用3.8.x版本settings.xml配置好阿里云镜像仓库否则依赖下载会等到怀疑人生。第三步后端启动。用IDEA打开后端项目等Maven导入依赖完成后修改application.yml里的数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/book_corner?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password运行主启动类BookCornerApplication看到Spring Boot启动成功的日志后用接口测试工具验证登录接口是否正常。第四步前端启动。进入book-frontend目录npm install安装依赖npm run dev启动开发服务器浏览器访问localhost:5173前端代理会把/api请求转发到8080后端。这里有一个特别提醒MySQL8.0默认的驱动类已经从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver连接URL需要显式带上serverTimezone参数否则会报时区相关的错误。另外SpringBoot2.7默认自带的是mysql-connector-j 8.0版本驱动不需要额外手动引入驱动依赖如果你在pom里看到旧版的mysql-connector-java最好去掉以免版本冲突。5.2 常见问题与排查技巧速查我整理了一份问题排查速查表这些问题全部来自实际开发中踩过的坑问题现象可能原因排查方法启动时报数据库连接失败数据库未启动、连接URL错误、密码不对先确保MySQL服务已启动用客户端工具测试连接登录接口返回401JWT生成和校验的盐值不一致、token过期检查application.yml中jwt配置确认前后端请求头字段名一致跨域请求被拦截未走Vite代理、Nginx未配置开发环境检查vite.config.js的proxy配置生产环境检查Nginx location配置中文乱码数据库字符集不是utf8mb4、连接URL缺少encoding参数检查建库语句和连接URL确保CHARACTER SET utf8mb4前端页面打不开Node版本过老或过新、npm install失败确认Node版本在16.x以上删除node_modules重新install返回的查询结果多了deleted字段MyBatis-Plus逻辑删除配置生效检查实体类是否有TableLogic注解符合预期则无需处理5.3 几个容易忽略但是很重要的坑第一个坑是MyBatis-Plus的更新操作自动填充策略。比如创建时间和更新时间很多人的做法是在插入时手动set但这样每个新增的地方都要写一遍少写一处就会出现空字段。正确做法是在实体类字段上使用TableField(fill FieldFill.INSERT)和TableField(fill FieldFill.INSERT_UPDATE)然后实现一个MetaObjectHandler处理器统一处理Component public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }第二个坑是数据库的关键字冲突。表名或者字段名如果用了MySQL的保留关键字会直接报SQL语法错误。我踩过的是把一张表命名为group结果所有查询都报错。解决办法是建表时用反引号包裹或者干脆起名时避开保留字。图书借还系统的表都比较安全但以后自己扩展字段时要有这个意识比如description、order、level这些词在MySQL里都有特殊含义使用时需要格外小心。第三个坑是前端路由使用history模式后刷新页面出现404。这是因为前端路由的路径是虚拟的比如/admin/books浏览器刷新时服务器找不到这个路径对应的静态文件。开发环境Vite已经处理过这个问题但生产环境如果用Nginx部署必须配置try_fileslocation / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; }这行配置的意思是当请求的路径在磁盘上找不到对应文件时统一返回index.html由前端路由接管。少了这行配置部署之后一刷新页面就白屏这是前端部署最常见的坑没有之一。5.4 功能扩展还能怎么玩这套系统的架构做完了后续扩展空间很大。我个人觉得最有价值的方向是图书预约功能当某本书的所有可借数量都被借出时读者可以排队预约有书归还时系统自动通知排在最前面的人。这个功能在现代图书管理系统中几乎是标配但在共享书角场景里尤其实用。第二个方向是数据可视化。MySQL8.0的窗口函数可以轻松统计借阅趋势、热门分类、读者活跃度等数据前端用ECharts画几个图表整个系统的档次就上去了。对于毕设项目或者简历上的亮点展示这个功能性价比极高。第三个方向是消息通知。可以引入简单的邮件发送或企业微信/钉钉机器人通知逾期自动提醒读者还书管理员也能收到催还汇总。不需要上消息队列直接用Spring的Scheduled定时任务扫一遍借阅记录表把逾期的记录聚合后批量通知即可。这些扩展方向都在现有表结构的能力范围内不会推倒重来这也是当初表设计时把状态字段分开的原因。这次从零做完这套共享书角图书借还管理系统我最大的感受是技术选型不是越新越好而是越适合越好。SpringBoot2 Vue3 MyBatis-Plus这套组合对中小型管理系统来说刚刚好开发效率高、社区资料全、部署门槛低。如果你也想搞一套类似的管理系统或者正在为课程设计、毕业设计发愁完全可以参考这个项目的思路先把业务梳理清楚、表设计合理再动手写代码你会发现在一套结构清晰的项目里加功能是一件无比爽快的事。