ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

SpringBoot+Vue图书管理系统源码详解:从环境搭建到核心功能实现

SpringBoot+Vue图书管理系统源码详解:从环境搭建到核心功能实现 1. 项目概述与选题背景1.1 为什么图书管理系统成了Java Web毕设的“常青树”每年毕业季我都会收到大量学弟学妹的私信问的第一个问题几乎都是“学长毕设做什么题目比较好”我给出的答案里图书管理系统出现的频率极高。原因很简单它麻雀虽小五脏俱全——用户管理、角色权限、图书信息维护、借阅归还流程、统计报表这些模块恰好覆盖了Java Web开发最核心的知识点。更重要的是图书管理系统的业务逻辑足够清晰不像电商系统需要处理复杂的订单状态机也不像社交平台需要考虑高并发下的消息推送。一个在校学生只要认真做完这个系统基本就能把SpringBoot、Vue、MySQL、MyBatis-Plus这套技术栈吃透应付答辩绰绰有余。我为什么特别推荐SpringBoot Vue这套组合因为它是目前国内中小型公司使用最广泛的技术栈组合之一。前端用Vue做单页应用后端用SpringBoot提供RESTful接口前后端分离的开发模式也正是企业里最主流的协作方式。把这个项目做出来不只是完成一个毕设更是提前熟悉了真实的工作环境。1.2 这套源码到底包含哪些东西这套完整项目包含三部分核心资产项目源码前端Vue 后端SpringBoot、SQL脚本、接口文档。很多人拿到源码后不知道从哪儿下手或者运行起来全是报错根本原因就是没有理解这三部分之间的关系。先说源码。前端部分是基于Vue 2也有部分是Vue 3 Vite后面我会对比说明搭建的单页应用使用了Element UI作为组件库配合Axios做网络请求Vue Router做前端路由。后端部分是基于SpringBoot 2.x的Java项目使用了MyBatis-Plus做持久层框架Spring Security做权限认证JWT做无状态登录态管理。SQL脚本是项目的“地基”。里面包含了建库语句、建表语句和初始化数据。建表语句定义了用户表、图书表、借阅记录表、分类表等核心表结构初始化数据则预置了管理员账号和一批测试图书保证项目第一次启动就有内容可以看。接口文档则是前后端之间的“契约”。它定义了每一个RESTful接口的请求方式、请求参数、响应格式。比如GET请求获取图书列表、POST请求添加图书、PUT请求修改图书信息、DELETE请求删除图书。我见过太多人做毕设时前后端沟通靠口头描述接口路径写错、参数名对不上联调时浪费大量时间。这份文档就是用来避免这种问题的。1.3 什么人适合拿这套项目如果你是计算机相关专业的毕业生正在为选题发愁这个项目是很好的参考。如果你已经选了类似题目但不知道如何下手这份源码可以帮你理清思路。如果你是非科班转行想通过一个完整项目来学习业务系统的开发流程这套项目同样适用。但有一点我必须说清楚千万不要直接拿源码去交。毕设答辩时导师最反感的就是照抄代码。正确用法是把这套源码当作“参考答案”先读懂它的架构设计再尝试自己从零写一遍遇到不会的地方再回来参考。这样你才能真正掌握里面的技术点答辩时也能对答如流。2. 核心技术拆解与关键选型逻辑2.1 前端技术栈为什么是Vue 2 Element UI我在整理这套源码时前端部分大部分是基于Vue 2 Element UI完成的。这里要解释一下为什么选择Vue 2而不是Vue 3。最主要的原因是生态稳定性。Element UI是Vue 2时代最成熟的桌面端组件库网上教程多、踩坑记录全、遇到任何问题基本都能搜到答案。而Vue 3对应的Element Plus虽然已经发布很久了但早期版本的API变动比较大很多老教程里的代码直接搬过来会报错。对于做毕设的学生来说能顺利跑起来比什么都重要。另外一个原因是学校教材的滞后性。我看了很多高校的课程大纲大部分学校教的还是Vue 2的语法学生更熟悉v-model、$emit、vuex这套写法。如果毕设里强行上Vue 3 Composition API Pinia虽然更“新”但要额外花时间学习新语法最后反而影响进度。当然如果你本身已经熟练Vue 3完全可以把前端改造成Vue 3 Vite Element Plus后面我会专门讲兼容性处理的方案。前端核心依赖清单如下表依赖版本用途vue2.6.x核心框架vue-router3.x前端路由vuex3.x状态管理axios0.21.xHTTP请求库element-ui2.15.xUI组件库echarts5.x数据可视化图表sass1.xCSS预处理器core-js3.x兼容性垫片2.2 后端技术栈SpringBoot MyBatis-Plus的黄金组合后端选择SpringBoot 2.x这几乎是目前Java Web领域最没有争议的选择。SpringBoot最大的价值在于“约定大于配置”它把Spring家族里复杂的XML配置全部封装起来你只需要在application.yml里写上几行配置就能快速启动一个Web服务。MyBatis-Plus则是MyBatis的增强工具它最大的特点是不用写SQL就能完成单表CRUD操作。比如你要查询图书列表只需要定义一个BookMapper接口继承BaseMapperBook就能直接调用selectList、selectById、insert、updateById这些现成方法。对于简单的增删改查再也不用写大段XML了。这里我想解释一下MyBatis-Plus的分页实现原理。它通过MybatisPlusInterceptor配置分页拦截器当执行查询时拦截器会自动拦截SQL在原有SQL基础上拼装LIMIT语句生成分页同时执行COUNT查询获取总记录数。所以你在Service层只需要写PageBook page new Page(current, size); bookMapper.selectPage(page, queryWrapper);就能完成分页非常方便。后端核心依赖清单如下表依赖版本用途spring-boot-starter-web2.5.xWeb基础mybatis-plus-boot-starter3.4.x持久层框架mysql-connector-java8.0.xMySQL驱动spring-boot-starter-security2.5.x安全认证jjwt0.9.1JWT令牌生成与校验lombok1.18.x简化实体类代码hutool5.7.x常用工具类库spring-boot-starter-validation2.5.x参数校验knife4j2.0.x接口文档增强2.3 数据库设计图书管理系统到底需要几张表很多同学写毕设时一上来就急着写代码结果数据库表设计得一塌糊涂后面改起来痛不欲生。数据库表是整个系统的地基地基打歪了上面的代码再漂亮也没用。这套源码的数据库一共设计了几张核心表我先画一下整体结构用文字描述不依赖图表工具sys_user表用户表存管理员和普通读者的账号信息。字段包括user_id、username、passwordMD5加密存储、real_name、phone、email、avatar、role角色标识admin/reader、status账号状态1正常、0禁用、create_time、update_time。sys_role表角色表存角色定义。字段包括role_id、role_name、role_code、description。sys_user_role表用户角色关联表多对多关系一个用户可以拥有多个角色一个角色可以被多个用户拥有。字段就是user_id和role_id两个外键。book表图书表存图书基础信息。字段包括book_id、isbn国际标准书号、book_name、category_id所属分类、author、publisher、publish_date、book_desc、cover_path封面图片路径、stock库存数量。book_category表图书分类表字段包括category_id、category_name、description。比如计算机类、文学类、历史类这些都是固定的分类。borrow_record表借阅记录表记录每一次借还操作。字段包括record_id、user_id、book_id、borrow_time、return_time、due_time应还时间、status0借出中、1已归还、2逾期。这套表设计遵循了三个原则第一业务数据都有关联字段通过外键逻辑保持一致性第二状态字段用数字或字符串做标记不直接用中文方便后端判断逻辑第三必要的逻辑删除字段和创建更新时间字段都预留了方便后期扩展。有一点我必须提醒密码千万不能明文存储。这套系统用的是MD5加盐的方式实际生产环境推荐使用BCrypt加盐哈希强度更高。但毕设答辩时老师一般不会深究加密算法本身所以MD5盐已经够用重点是你要能说出“为什么不能明文存密码”这个常识。3. 环境准备与项目初始化的完整操作3.1 本地环境版本选型JDK、Node、MySQL一个都不能错运行这套项目之前环境的搭建是最容易出问题的一步。我遇到过太多人代码完全没问题结果因为JDK版本太高、Node版本太老项目死活起不来。推荐版本组合如下工具推荐版本说明JDK1.8 或 11SpringBoot 2.x用这两个版本最稳不建议上17Maven3.6.x用IDEA自带的就行重点看仓库配置Node.js14.x 或 16.xVue 2 Element UI用这两个版本最稳Node 18可能有编译兼容性问题MySQL5.7 或 8.05.7轻量8.0性能更好注意驱动版本要和数据库匹配IDEIDEA 2020.3后端用IDEA前端WebStorm或IDEA都可以这里说两个容易踩的坑第一个坑是JDK版本太高。之前有个同学装的是JDK 17结果SpringBoot 2.5.6项目运行时报错java.lang.reflect.InaccessibleObjectException其实是因为高版本JDK限制了反射访问导致SpringBoot内部代理机制失效。解决办法要么降级到JDK 8要么升级到SpringBoot 2.7以上并添加--add-opens参数但后者复杂度高不如直接换JDK省事。第二个坑是MySQL 8.0和驱动不匹配。如果你的SpringBoot项目里用的是5.x版本的mysql-connector-java驱动连接MySQL 8.0时会报Public Key Retrieval is not allowed。想用MySQL 8.0务必要把驱动升级到mysql-connector-java 8.0.x并且在JDBC连接串后面加上allowPublicKeyRetrievaltrueuseSSLfalse。3.2 SQL脚本的执行方式命令行和Navicat两种方案拿到SQL脚本后很多人不知道往哪里放、怎么执行。这里我给出两种方案任选其一即可。方案一命令行执行不依赖任何图形化工具# 先进入MySQL mysql -u root -p # 输入密码后创建数据库 CREATE DATABASE IF NOT EXISTS library_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 退出后用source命令执行脚本 mysql -u root -p library_system /path/to/library.sql方案二Navicat图形化执行打开Navicat先创建一个名为library_system的数据库字符集选择utf8mb4。然后右键该数据库选择“运行SQL文件”选中你下载的library.sql脚本运行完成后再刷新一下表列表就能看到所有的表了。执行完之后我有几个检查建议确认建表是否成功看一下表列表是否包含sys_user、book这类核心表表数量是否符合预期。确认初始数据是否导入打开sys_user表看是否有一条管理员账号默认密码通常是admin123。确认外键关系是否合理展开表结构看一下借阅记录表有没有正确关联到用户表和图书表。3.3 后端项目的导入与配置后端源码导入IDEA的步骤比较固定我按顺序写一下。打开IDEA选择File - Open选中后端项目所在的文件夹通常叫backend或server根目录下有pom.xml文件。IDEA会自动识别为Maven项目。等待Maven依赖下载。这一步是最容易卡住的地方因为国内访问Maven中央仓库速度不稳定建议在Maven的settings.xml中配置阿里云镜像。修改application.yml中的数据库连接信息重点核对三处url里的IP和端口、用户名密码、数据库名。我的application.yml里面数据库配置长这样你可以直接拿去改成自己的server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/library_system?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalseallowPublicKeyRetrievaltrue username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0Maven阿里云镜像配置在Maven安装目录的settings.xml中的mirrors节点里加上mirror idaliyunmaven/id mirrorOfcentral/mirrorOf name阿里云公共仓库/name urlhttps://maven.aliyun.com/repository/public/url /mirror启动后端前建议先把测试代码跑一遍。在test目录下通常有一个LibraryApplicationTests类右键运行如果能够看到Spring Boot的启动日志说明环境没问题。如果报错90%的概率是数据库连接没配好。3.4 前端项目的安装与启动前端项目通常叫frontend或web。进入项目根目录依次执行# 安装依赖 npm install # 如果npm install太慢可以使用以下方式 npm install --registryhttps://registry.npmmirror.com # 启动开发服务器 npm run dev启动成功后终端会提示前端服务运行在哪个端口通常是http://localhost:8081。这时候不要急着操作先检查前端有没有启动页面能看到登录页说明一切正常。关于npm install有几点要提前做好心理准备首次安装Element UI、Echarts这些大组件依赖数量通常在2000个以上需要几分钟时间。如果node_modules中途破坏导致安装失败不要硬着头皮重试先把node_modules文件夹删掉再重新执行npm install。Vue 2项目用Node 18运行时可能会出现digital envelope routines::unsupported的报错这是OpenSSL版本兼容性导致的。有两种解法一是降级Node到16二是执行一次export NODE_OPTIONS--openssl-legacy-provider然后重新启动。我推荐第一种因为第二种每次启动都要设环境变量太麻烦了。3.5 前后端联调打通登录这条主链路前后端都启动之后最关键的一步就是验证前后端是否能正常通信。最简单的测试就是登录功能。在浏览器里打开前端登录页输入管理员账号密码点击登录。如果跳转到系统首页说明整条链路是通的。如果卡在登录页没有任何反应或者报错Request failed with status code 401那就需要检查以下几点第一确认前端代理配置是否正确。查看vue.config.js看devServer里的proxy配置target地址是不是指向后端的8080端口。比如module.exports { devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }第二确认后端是否放行了跨域请求。在SpringBoot后端你需要一个CorsConfig类来配置跨域策略。如果前后端通过proxy代理可以直接打通后端也需要加一个允许跨域的配置否则前端直接访问后端接口还是会报跨域错误。第三确认登录接口的请求路径看前端axios的请求地址是否和后端Controller的RequestMapping路径匹配。比如前端请求的是/api/auth/login后端Controller上就要有RequestMapping(/api/auth)和PostMapping(/login)。很多同学在这步卡住其实不用慌。打开浏览器的开发者工具F12切到Network面板点击登录观察请求发的对不对、后端返回了什么错误信息绝大多数问题都能在这里找到根因。4. 核心功能实现与业务逻辑深度解析4.1 登录认证JWT 拦截器的完整实现思路登录认证是整个系统的安全底线。我采用的是JWT 后端拦截器HandlerInterceptor的组合方案这也是目前Java Web项目里非常常见的做法。业务流程是这样的用户在登录页输入用户名密码前端调用/api/auth/login接口。后端收到请求调用userService.login(userName, password)先根据用户名查出用户再用MD5盐的方式校验密码。密码校验通过后后端用JWT工具类生成一个tokentoken里封装了userId和role信息设置过期时间一般是12小时或24小时然后返回给前端。前端拿到token后存到localStorage或vuex里并在后续每个请求的header里加上Authorization: Bearer xxx。后端有一个AuthInterceptor拦截器对所有需要登录的接口进行token校验token有效才会放行。Token生成的核心代码大概长这样public String generateToken(Integer userId, String username, String role) { return Jwts.builder() .setSubject(username) .claim(userId, userId) .claim(role, role) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() 24 * 60 * 60 * 1000)) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器校验token的核心代码public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String header request.getHeader(Authorization); if (header ! null header.startsWith(Bearer )) { String token header.substring(7); try { Claims claims Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody(); request.setAttribute(userId, claims.get(userId)); return true; } catch (Exception e) { response.setStatus(401); return false; } } response.setStatus(401); return false; }这里我想强调一个容易被忽视的小细节拦截器只拦需要登录的接口登录接口本身不能拦截。否则连登录请求都被拦截下来系统就进不去了。所以需要配置一下放行路径比如/api/auth/login和/api/auth/register要显式排除在拦截路径之外。4.2 图书模块从列表展示到条件检索图书管理模块是系统的核心业务模块包含图书列表分页查询、添加图书、编辑图书信息、删除图书逻辑删除和按条件检索几个功能点。图书列表和检索通常合在一起。前端搜索栏里有图书名称、ISBN编号、图书分类三个筛选项传递到后端时变成三个可选参数。后端用MyBatis-Plus的QueryWrapper来构造查询条件public PageBook queryBookPage(int pageNum, int pageSize, String keyword, Long categoryId) { PageBook page new Page(pageNum, pageSize); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); if (StrUtil.isNotBlank(keyword)) { wrapper.like(Book::getBookName, keyword) .or().like(Book::getAuthor, keyword) .or().like(Book::getIsbn, keyword); } if (categoryId ! null) { wrapper.eq(Book::getCategoryId, categoryId); } wrapper.orderByDesc(Book::getCreateTime); return bookMapper.selectPage(page, wrapper); }注意这里的一个技巧如果用or()进行多字段模糊匹配并且后面还有eq条件最好用and()把多个or条件包起来避免SQL条件逻辑混乱。建议这样写wrapper.and(w - w.like(Book::getBookName, keyword) .or().like(Book::getAuthor, keyword) .or().like(Book::getIsbn, keyword)); if (categoryId ! null) { wrapper.eq(Book::getCategoryId, categoryId); }图书封面处理是一个很容易让新手卡住的问题。前端上传图片到后端后端需要把图片保存到本地的某个目录再把访问URL返回给前端。最简单的做法是在resources/static下建一个upload目录把图片写进去然后前端用http://localhost:8080/upload/xxx.jpg去访问。但要注意SpringBoot的静态资源默认映射了/static、/public等路径。如果你把图片传到/static/upload里那么访问地址就是http://localhost:8080/upload/xxx.jpg。不要直接存到项目的target目录下因为每次重新编译target会被清空上传的图片就全丢了。正确做法是配置一个本地绝对路径目录upload: path: D:/library-upload/然后用配置类做静态资源映射Configuration public class WebConfig implements WebMvcConfigurer { Value(${upload.path}) private String uploadPath; Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: uploadPath); } }这样图片就保存在项目之外的固定目录即使重新打包也不会丢失。4.3 借阅模块状态流转与超期判断借阅功能是图书管理系统里业务逻辑最复杂的一个模块。它涉及两个核心状态图书的库存状态和借阅记录的状态。这两者必须保持联动。借书操作发生在用户在图书详情页点击“借阅”时。后端接收userId和bookId进行以下步骤查图书检查库存是否大于0。如果库存为0返回“图书已借完”。查询该用户是否已经借了这本书且未归还。如果有返回“您已借过此书请先归还”。扣减图书库存数量即stock - 1。插入一条借阅记录状态设为“借出中”应还时间设置为当前时间加30天。还书操作相反先查借阅记录确认该记录状态是“借出中”然后把状态改为“已归还”设置归还时间再把图书库存加回去。超期判断这个逻辑很多初学者会想用定时任务每天扫描所有借阅记录看是否到了应还时间。但更优雅的做法是每次查看借阅记录时实时计算当前时间是否超过了dueTime。如果超过且状态仍然是“借出中”就在查询时把状态显示为“已逾期”同时允许用户在这个状态下执行还书操作。这样不需要任何定时任务逻辑也更简单。借阅记录列表查询时前端可能会展示用户姓名、图书名称这些关联数据。这里需要做一次关联查询。我的方案是分两步先用分页查询出借阅记录列表然后根据userId列表批量查用户信息、根据bookId列表批量查图书信息最后在内存中组装。这种写法SQL简单也避免了在XML里写大型子查询的麻烦。4.4 分类统计与可视化ECharts报表如何接入很多图书管理系统的毕设都要求有一个“统计分析”页面展示图书分类占比、借阅量排行之类的图表。这套源码里用ECharts来实现。ECharts的数据来源是后端的统计接口。比如“各分类图书数量统计”这个接口后端需要做一次SQL分组查询public ListMapString, Object countBookByCategory() { ListMapString, Object list bookMapper.countByCategory(); // 返回格式[{categoryName:计算机,count:12}, {categoryName:文学,count:8}] return list; }前端接收后把categoryName映射成xAxis的类目把count映射成series的data就能画出饼图或柱状图。前端ECharts的基本使用流程是在HTML中准备一个固定宽高的div设置ref属性方便通过this.$refs访问。import * as echarts from echarts在mounted里初始化图表let chart echarts.init(this.$refs.chartRef)调用接口获取数据后设置option并执行chart.setOption(option)。有一个高频小坑图表容器div在初始化时如果没有高度ECharts会渲染出空白。建议给div设置一个明确的高度比如height: 400px。另外切到别的Tab再切回来时图表可能没有自适应宽高记得调用chart.resize()。4.5 管理员与用户的权限边界这个系统的角色分为管理员和普通用户权限边界要划分清楚。管理员可以管理图书增删改、管理分类、查看所有借阅记录、管理用户账号。普通用户只能浏览图书、借书、还书、查看自己的借阅记录。权限控制最直接的方式是通过路由和菜单做前端控制后端再做一层接口校验。前端在路由配置里通过meta.roles字段限定哪些角色可以进入该路由。后端在接口上通过Spring Security的PreAuthorize(hasRole(ROLE_ADMIN))做二次校验。前端控制是为了用户体验后端控制才是真正的安全边界。前端路由守卫代码一般长这样router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (!token to.path ! /login) { next(/login) } else if (token to.meta.roles) { const userInfo JSON.parse(localStorage.getItem(userInfo)) if (to.meta.roles.includes(userInfo.role)) { next() } else { next(/403) } } else { next() } })这份路由守卫逻辑本身不复杂但很多同学会写成next()死循环建议在判断分支里确保每个分支都有明确的next目标。5. 接口文档与常见问题实战5.1 后端接口文档规范从零读懂Knife4j生成的API说明这套项目中接口文档采用Knife4j基于Swagger的增强工具生成。启动后端项目后在浏览器访问http://localhost:8080/doc.html就能看到可视化的接口列表。每个Controller里通过注解描述接口用途、参数含义和返回结构。常见的注解包括Api(tags 图书管理) RestController RequestMapping(/api/book) public class BookController { ApiOperation(分页查询图书列表) GetMapping(/page) public ResultPageBook page( ApiParam(value 页码, example 1) RequestParam int pageNum, ApiParam(value 每页条数, example 10) RequestParam int pageSize ) { return Result.success(bookService.queryBookPage(pageNum, pageSize)); } }统一返回结果Result类是整个项目里使用频率最高的封装类。它包含了三个字段code状态码、msg提示信息、data业务数据。比如code200表示成功code500表示业务异常code401表示未登录或token失效。这样前端可以统一处理不用为每个接口单独做异常判断。5.2 接口文档对前端联调的价值接口文档最大的价值不只是“给人看的”更是“给前端看的”。前端同学拿到文档后不需要问后端“你这个接口怎么调”直接看文档就能知道请求路径、请求参数和返回结构。在调试阶段前端最常用的方式是直接在axios请求中打印返回结果前端拿到返回的data后要注意返回结构是{code:200, data:{records: [], total: 32}}不要直接拿response.data.data当数组要先解开外面两层的封装再进行数据处理。很多联调问题都是因为数据结构没有解对导致的。5.3 常见问题与排查技巧整理我在实际调试这套项目的过程中总结了下面这些最常见的报错和解决思路整理成表格方便你做排查索引。报错信息可能原因解决方案501 Not Implemented前端请求的接口在后端不存在或路径拼错F12 Network里查看实际请求URL和后端Controller路径逐一对比401 Unauthorizedtoken缺失或过期登录接口也被拦截检查拦截器配置确认登录路径是否放行检查token是否在Header里正确携带403 Forbidden用户没有对应接口的访问权限检查角色权限配置确认该用户角色是否有接口权限Field ‘xxx’ doesn’t have a default value插入数据时某个非空字段没传值检查实体类和前端表单字段的对应关系确认必填字段都有值传入Unknown column ‘xxx’ in ‘field list’实体类属性和数据库表字段名不一致检查实体类的TableField注解或驼峰映射配置Whitelabel Error Page后端接口报错但页面没显示详情查看后端控制台完整堆栈日志通常能看到具体SQL或空指针错误Connection refused: connect后端没启动或端口被占用确认后端启动成功netstat查看端口占用情况Error updating database. Cause: Data too long for column插入数据超出字段长度限制修改数据库字段长度或限制前端输入长度Cannot resolve symbol ‘UserMapper’Maven依赖没有加载完成执行mvn clean compile刷新Maven依赖缓存Failed to bind properties under ‘spring.datasource’application.yml配置项的缩进错误检查YAML文件缩进最通用的坑就是空格数量不对5.4 两个惨痛教训看完能少走很多弯路第一个教训是关于数据库密码。曾有位同学把数据库密码设置成了root123在Navicat里连得好好的跑后端就报Access denied for user ‘root’‘localhost’。查了很久终于发现他把密码写到了application.yml里但字符串前面多了一个空格密码变成了“ root123”。这类问题排查起来非常隐蔽建议所有数据库连接配置都直接在编辑器里检查是否有首尾空格然后先用一个最简单的main方法测试数据库连接能不能通再启动项目。第二个教训是关于前端环境的。有个同学的电脑上同时装了Node 14和Node 18全局npm默认指向了Node 18npm run dev一直报OpenSSL的错误。当时我在远程帮他排查发现他根本没有意识到自己有多个Node版本。最后通过nvm use 16切换成功后项目秒启动。所以我建议大家在跑Vue 2项目时先执行node -v确认当前生效的Node版本再决定要不要装nvm来管理多版本。5.5 调试利器三条实用经验调试这类前后端分离的项目应该养成“先后端、后前端”的排查顺序。大多数人遇到接口报错第一反应是去前端改代码但问题的根源往往在后端。我自己的调试习惯是这样的所有后端接口先用Postman或Apifox直接测如果Postman能返回正确数据再判断问题在前端。如果Postman也报错就直接去IDE里打断点看Service层哪一步出的错。还有一个经验是关于日志的。把日志级别调成DEBUG比如logging.level.com.example.mapper: debugMyBatis-Plus会打印完整的SQL语句。一旦前端传参有问题你在日志里看到实际执行的SQL就能秒懂是哪段条件没生效。6. 部署上线与成品整理6.1 前端构建与后端打包毕设通常需要提供可运行的程序最省事的方式是前后端分别构建然后用Nginx反向代理到一起。前端构建在项目根目录执行npm run build构建完成后项目根目录下会生成一个dist文件夹里面是所有压缩好的HTML、CSS、JS文件。把dist里所有文件拷贝到Nginx的html目录下即可。后端打包在项目根目录执行mvn clean package -DskipTests执行完成后target目录下会生成一个library-system-0.0.1-SNAPSHOT.jar文件。然后就可以用命令启动java -jar library-system-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod如果需要改端口或数据库配置可以直接在启动命令后面追加参数比如java -jar library-system-0.0.1-SNAPSHOT.jar --server.port9090 --spring.datasource.password1234566.2 同一端口部署把前端静态资源交给SpringBoot托管有人问过我是否可以把前端打包后的dist文件夹直接放进SpringBoot里只用一个端口启动项目这样演示的时候就不用同时开两个服务了。这个方案完全可行。做法分两步先把前端dist里的static和index.html文件复制到后端项目的src/main/resources/static目录下。然后启动后端访问http://localhost:8080就能直接看到前端页面前端接口请求改为相对路径去掉http://localhost:8080前缀由SpringBoot自己转发到Service层。这种做法很适合那些需要给老师在单机上做演示的场景省去了配置Nginx的麻烦。但要注意如果后期前端代码改动需要重新构建并覆盖到static目录不然看到的还是旧页面。6.3 参考建议拿到源码后如何真正利用它最后我给所有想利用这套源码的读者三个建议。建议一先复现再理解最后改造。不要一上来就逐行读代码先照着文档把项目完整跑通。跑通之后挑你最不熟悉的功能模块比如JWT认证或图书借阅流程从头到尾读一遍画出调用链。最后尝试改一个需求比如给图书模块加“出版社模糊搜索”改完之后你就真正学会了这套体系。建议二答辩时一定要能讲清楚“为什么这么设计”。老师最常问的问题不是“这个功能怎么做”而是“为什么用这张表”“为什么用JWT”“为什么库存要加锁”。所以建议你把每个主要模块的设计理由在笔记里写一遍形成自己的理解。建议三适当增加论文的分量。毕设不只是写代码还得写论文。这套源码里附带的接口文档和数据库设计可以直接作为论文里“系统设计”章节的素材。如果你能补充一些需求分析的结果、用例图和时序图论文会更有可读性。7. 写在最后从这套源码里能带走什么我整理这套SpringBoot Vue图书管理系统已经很久了每次带着新同学跑一遍项目都会听到类似的感叹“原来前后端是这样连通的”“原来JWT是这个意思”“原来MyBatis-Plus能省这么多事”。这些感叹恰恰说明书本上的知识点和真正把项目跑起来之间隔着一道巨大的实践鸿沟。这道鸿沟只有自己亲手敲一遍代码、调一遍故障才能填平。这套源码的价值不在一行行代码里而在于它提供了一个把Java Web技术栈从理论变成现实的完整闭环。我始终觉得做完一个项目不一定要用上最高版本的技术但一定要搞清楚最核心的原理。你弄懂了JWT的生成和校验换任何语言都能写出对应的认证逻辑你弄懂了MyBatis-Plus的查询构造器再去学MyBatis、学JPA都能很快上手。这些东西比版本号重要得多。希望你在跑这个项目的过程中能踩到一些坑解决一些问题然后从这个项目里获得真正的成就感。后面如果把它扩展成带预约座位、带图书荐购功能的完整系统那就是完全不同的境界了。祝顺利。
返回列表