简介:这是一份基于Vue和Spring Boot的Java校园二手交易平台毕业设计源码与数据库,面向计算机相关专业正在做毕业设计、课程设计的学生,也适合需要完整项目练习的Java学习者。项目由导师指导、助教审定,评审分98分,源码均经过本地编译调试,可直接运行并二次开发,可作为完整课题方案或课程设计参考。压缩包共363个文件,大小约13.43MB,其中113个Java文件承担后端业务接口,18个Vue与10个JavaScript文件构建前端页面交互,9个SQL脚本负责数据库初始化。32个XML及properties、json等文件保障项目配置,图片素材辅助预览界面效果,整体结构清晰,导入IDE后可快速理解前后端分离开发流程。目前已有263人学习下载,借助这套代码可还原校园闲置物品发布、浏览、交易管理等主流功能,为学习Spring Boot与Vue整合开发提供可运行的落地样例。
1. Java校园二手交易平台:SpringBoot+Vue毕业设计源码到底值不值得下
做毕业设计的人最怕两件事:一是选题太大做不完,二是网上找的源码跑不起来。这套SpringBoot+Vue的校园二手交易平台源码,属于典型的"刚好够用"的毕业设计项目:后端是SpringBoot,前端是Vue,数据库用MySQL,功能覆盖了用户注册登录、商品发布、商品浏览搜索、订单管理、个人中心这些核心模块,既满足任务书要求,又不至于把工作量铺得太开。
为什么我推荐这类前后端分离的项目作为毕业设计?因为答辩时老师问得最多的就是"这个项目架构是什么、你做了哪些工作",前后端分离的项目天然能把这两个问题讲清楚。后端SpringBoot管接口,前端Vue管页面,中间走JSON数据交互,这条链路本身就是一套完整的知识体系。适合Java基础一般、想用现成源码二次开发、或者时间只剩两三个月需要快速出成果的同学。下面我从后端、前端、数据库到部署跑通,把这份源码怎么拆、怎么改、怎么避开常见的坑完整走一遍。
2. 后端SpringBoot源码拆解:目录结构、登录鉴权与商品接口
2.1 拿到源码先看什么:后端项目结构是项目的骨架
解压源码包后,后端一般是maven结构的标准SpringBoot工程。我建议不要急着点运行,先花十分钟把src/main/java下的包结构理清楚。典型的划分是controller、service、mapper、entity、config这几个包:controller层暴露REST接口,service层写业务逻辑,mapper层通过MyBatis操作数据库,entity是实体类,config放跨域配置和拦截器配置。
/src/main/java/com/example/secondhand/ ├── controller/ │ ├── UserController.java │ ├── GoodsController.java │ ├── OrderController.java │ └── AdminController.java ├── service/ │ ├── UserService.java │ ├── GoodsService.java │ └── OrderService.java ├── mapper/ │ ├── UserMapper.java │ ├── GoodsMapper.java │ └── OrderMapper.java ├── entity/ │ ├── User.java │ ├── Goods.java │ ├── Order.java │ └── Category.java ├── config/ │ └── CorsConfig.java └── Application.java我见过不少同学拿到源码第一件事就改数据库连接、点启动,结果报一堆错,其实就是没看配置。最优先要看的是application.yml或application.properties里的三样东西:端口号、数据库连接串、MyBatis的mapper-locations路径。数据源配置和MyBatis扫描路径是最容易翻车的位置,后面避坑章节会展开。
2.2 登录鉴权怎么实现:JWT还是Session
校园二手平台这类项目,登录鉴权一般有两种做法:Session+拦截器,或者JWT+拦截器。这套源码我拆过,走的是JWT方案:用户登录成功后后端签发一个token返回给前端,前端存到localStorage里,后续请求在Header里带上token,后端拦截器解析token拿到用户信息。
public class TokenInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token = request.getHeader("Authorization"); if (token != null && !token.isEmpty()) { String userId = JwtUtil.parseToken(token); if (userId != null) { request.setAttribute("userId", userId); return true; } } response.setStatus(401); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":401,\"msg\":\"登录状态已过期\"}"); return false; } }逻辑不复杂:preHandle里从请求头取Authorization字段,调JwtUtil里的parseToken方法解析,解析成功就把userId放到request属性里,继续放行;失败直接返回401状态码。这里注意拦截器只拦需要登录的接口,像登录、注册、商品列表这些接口得通过excludePathPatterns放行,否则前端还没拿到token就被拦了。
JwtUtil类里封装了两个核心方法:createToken和parseToken。createToken会用用户的id和过期时间生成一个token,签名用的密钥是写死在配置文件里的。做毕业设计不需要搞多复杂的JWT校验,有这两个方法就够了。参数说明:过期时间我习惯设成24小时,太短的话用户用着用着就要重新登录,答辩演示时很尴尬。
2.3 商品模块的核心接口:发布、列表、详情与搜索
商品是这个项目的核心业务,接口设计的好坏直接决定前端好不好对接。后端GoodsController里至少要有这几个接口:发布商品、分页获取商品列表、获取商品详情、按条件搜索商品、下架商品。分页这块用的是MyBatis的PageHelper插件,接口接收pageNum和pageSize两个参数,返回JSON里带总条数total。
// 前端调用商品列表接口示例 axios.get('/api/goods', { params: { pageNum: 1, pageSize: 10, keyword: this.searchKeyword, categoryId: this.currentCategoryId, sort: 'createTime', order: 'desc' }, headers: { 'Authorization': localStorage.getItem('token') } }).then(res => { this.goodsList = res.data.rows; this.total = res.data.total; })前端传参逻辑:pageNum和pageSize控制分页;keyword是搜索关键字,对应后端的模糊查询;categoryId是分类筛选;sort和order决定排序方式,按发布时间倒序排是列表页的默认行为。后端查询时PageHelper.startPage(pageNum, pageSize)紧跟的一句就会被分页,当然后面的查询SQL必须是Mapper里定义好的查询语句,不能是多表拼接出来的临时对象。
发布商品接口有个细节值得注意:图片上传。生产环境一般用OSS对象存储,但毕业设计没有服务器条件,最常见做法是后端接口接收文件后保存到本地磁盘的upload目录,然后把文件路径存到数据库。源码里一般会有FileController处理上传,application.yml里配置上传路径。做答辩演示时,建议提前把图片准备好,避免现场上传大图超时。
3. Vue前端工程解析:路由配置、Axios封装与页面交互
3.1 前端工程结构:Vue2还是Vue3,项目怎么初始化
这套源码前端部分是标准的Vue工程,用Vue CLI或Vite初始化的。先看package.json里的依赖版本,确定用的是Vue2还是Vue3,两个版本的写法差别很大。如果是Vue2,组件里用Options API写,data、methods、mounted是标配;如果是Vue3且用了组合式API,代码风格就完全不一样了。毕业设计项目用Vue2的占多数,主要是生态成熟、模板多、踩坑资料好搜。
# 前端依赖安装(在工程根目录下执行) npm install # 开发模式启动,默认端口一般是8080 npm run servenpm install执行时间取决于网络状况,国内网络建议先给npm配淘宝镜像源,否则依赖下载可能卡半小时。配镜像源的方式是执行npm config set registry https://registry.npmmirror.com。安装完成后npm run serve启动开发服务器,Vue CLI默认监听8080端口,如果被占用就换端口。
vue.config.js里一般会配一个proxy代理,解决开发环境跨域问题:前端请求/api开头的接口,代理转发到后端的8080端口。这个配置非常重要,没有它前端页面调接口就报跨域错。
module.exports = { devServer: { port: 8080, proxy: { '/api': { target: 'http://localhost:8081', changeOrigin: true } } } }这个配置的意思是:前端跑在8080端口,页面里发的所有/api开头的请求,都会被代理到后端8081端口。changeOrigin设为true,后端拿到的Host头就是代理目标的地址,避免后端某些基于Host的判断逻辑出错。不同源码里后端端口可能不一样,以实际application.yml里配置的端口为准。
3.2 Axios封装:拦截器、Token注入与错误处理
前端请求后端离不开Axios,这套源码一般会在src/utils目录下有一个request.js文件,对Axios做了统一封装。封装的目的有两个:一是统一注入token,二是统一处理后端返回的响应码和错误提示。不封装的话,每个页面写一遍axios.get,token手动加,错误码手动判断,代码会很散。
import axios from 'axios' const service = axios.create({ baseURL: '/api', timeout: 10000 }) // 请求拦截器:自动带上token service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = token } return config }) // 响应拦截器:统一处理状态码 service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { this.$message.error(res.msg || '请求失败') return Promise.reject(new Error(res.msg)) } return res }, error => { if (error.response && error.response.status === 401) { this.$router.push('/login') } return Promise.reject(error) } )拦截器逻辑分两块:请求出去之前,从localStorage拿token塞到请求头,后端拦截器才能通过鉴权;响应回来之后,先判断业务状态码code,200才往下走,非200弹出错误提示并reject。注意后端返回的数据结构一般包一层,比如{code: 200, data: {...}},拿数据时要取res.data。401状态码统一跳转登录页,这个设计在答辩时可以主动讲,属于架构上的加分项。
3.3 核心页面交互:商品列表、商品详情与发布表单
商品列表页是前端最复杂的页面,因为涉及分页、筛选、搜索三个维度的状态联动。常见实现是data里维护searchKeyword、currentCategoryId、pagination三个变量,任何一项变化都触发loadData方法重新请求接口。分类菜单用的是el-menu或van-sidebar,点击分类项时更新currentCategoryId并重置分页页码。
商品详情页的交互相对简单:路由跳转到详情组件,组件在mounted钩子里从this.$route.params.id或query里取出商品id,然后调接口获取商品详情数据渲染到页面上。发布商品页涉及的交互是表单校验和图片上传。Element UI的Upload组件的action属性可以直接指向后台上传接口,但别忘了在headers里带上token,否则图片上传接口被拦截器拦下来,返回401,图片传不上去。
图片上传这是毕业设计里出现频率最高的翻车现场之一。很多同学改了半天发现图片传不上去,一查network,上传请求报401,就是因为Upload组件没带token。解决方式是在Upload组件的headers属性里手动加。
4. 数据库设计与初始化:从建表SQL到测试数据入库
4.1 核心表结构分析:用户表、商品表、订单表
数据库设计是毕业设计答辩时老师必问的部分。这套源码的数据库一般包含三张核心表:用户表、商品表、订单表,加上分类表和收藏表。先看建表SQL,理解每个字段的用途和表之间的关联关系。
-- 用户表 CREATE TABLE `user` ( `id` INT NOT NULL AUTO_INCREMENT, `username` VARCHAR(32) NOT NULL COMMENT '用户名', `password` VARCHAR(64) NOT NULL COMMENT '密码(MD5加密)', `nickname` VARCHAR(32) DEFAULT NULL COMMENT '昵称', `avatar` VARCHAR(255) DEFAULT NULL COMMENT '头像路径', `phone` VARCHAR(11) DEFAULT NULL COMMENT '手机号', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 商品表 CREATE TABLE `goods` ( `id` INT NOT NULL AUTO_INCREMENT, `user_id` INT NOT NULL COMMENT '发布者id', `category_id` INT DEFAULT NULL COMMENT '分类id', `title` VARCHAR(64) NOT NULL COMMENT '商品标题', `description` TEXT COMMENT '商品描述', `price` DECIMAL(10,2) NOT NULL COMMENT '价格', `image` VARCHAR(255) DEFAULT NULL COMMENT '封面图路径', `status` TINYINT DEFAULT 1 COMMENT '1在售 2已下架 3已卖出', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '发布时间', PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;用户表里username加了唯一索引,防止重复注册;商品表里user_id和category_id建了普通索引,因为列表页和搜索页会频繁按这两个字段筛选。price用DECIMAL(10,2)而不是float,是因为浮点数在数据库里做比较会有精度问题,价格这种字段必须用定点数。
订单表的核心字段是buyer_id、seller_id、goods_id和status。买家、卖家各存一个字段,是因为查询"我买到的"和"我卖出的"订单时,只需要根据当前登录用户的id去匹配对应字段即可,不用联表查用户信息。
4.2 初始化数据库:导入SQL文件的完整步骤
拿到源码后第一步不是启动后端,而是先建库。数据库连不上、表结构缺字段、中文字符集乱码,这三个问题占了毕业设计运行失败原因的一半。正确顺序是:打开Navicat或命令行,先创建数据库,再导入源码包里提供的xxx.sql文件。
# 命令行导入SQL文件 mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS secondhand DEFAULT CHARSET utf8mb4;" mysql -u root -p secondhand < /path/to/sql/secondhand.sql第一条命令创建数据库,指定utf8mb4字符集,确保中文能正常存储。第二条命令把SQL文件导入到这个库里。用Navicat操作更直观:右键点击连接->新建数据库->数据库名secondhand、字符集选utf8mb4,然后选中这个库右键运行SQL文件,选择源码包里的secondhand.sql执行。
数据库导完以后,打开application.yml检查url里的数据库名、用户名、密码是否匹配。常见的翻车情况是:源码默认用户root密码123456,而你自己本地的MySQL密码不是这个。改完数据库配置,后端才能启动成功。
4.3 测试数据的重要性:为什么不要从空表开始演示
源码自带的SQL文件里一般带了一部分测试数据,包括几个测试用户、十几条商品记录和几条订单记录。这些数据不建议清空,答辩演示时直接展示效果比自己重新录入效率高得多。
尤其注意商品表的image字段,对应的是图片路径。有的源码测试数据的image路径指向的是演示环境下的图片地址,换到本地环境后路径不对,图片显示不出来。这种情况要么把图片下载到本地resources下补上对应路径,要么重新上传几张图片,数据库路径跟着变。
密码字段默认是MD5加密后的值。测试用户的具体账号密码一般写在SQL文件的注释或者项目README里,常见的是admin/123456和user/123456。如果你不知道密码,直接看SQL文件里insert语句的password字段,然后去在线MD5解密的网站反查原值。
5. 部署与避坑:从本地跑通到常见问题排查
5.1 本地开发环境启动的完整顺序
跑通整个项目有一个标准顺序,顺序错了就会遇到"前端调不通后端"这种问题。正确操作是:先启动MySQL,确认数据库连得上;再启动后端,看控制台日志有没有正常打印SpringBoot启动信息;最后启动前端,浏览器访问页面。每一步启动成功后再进入下一步,不要在还没确认后端正常的情况下就去点前端页面。
后端启动时如果端口被占用会直接报错。SpringBoot默认端口是8080,前端vue项目默认也是8080,两个撞在一起的情况非常常见。解决方式是把后端的端口改成8081,或者把前端的vue.config.js里的端口改掉。具体改哪个看你习惯,关键是两边要错开,同时前端代理的target要按照实际后端端口改。
启动后端的最快验证方式:浏览器访问后端的Swagger或Knife4j文档地址,如果能打开接口文档,说明后端已经完全正常。没集成Swagger的话,就用Postman调用登录接口,能拿到token就算成功。
5.2 避坑:部署运行时最常见的8个问题
问题1:后端启动报错——数据库连接失败现象:控制台抛出java.sql.SQLException和Communications link failure。 原因:绝大多数情况是三种,MySQL服务没启动、application.yml里的数据库ip/端口写错、用户名密码不对。 解决:先确认MySQL服务已启动,再逐项核对url、username、password三个配置。测试数据库连接可以用Navicat先连一下,排除掉账号密码问题再启动后端。
问题2:前端npm run serve启动失败现象:控制台报Module not found或ERESOLVE依赖版本错误。 原因:node版本和依赖版本不兼容,或者npm install没装完整。 解决:删除node_modules目录和package-lock.json,重新npm install。Node版本建议不要超过Vue CLI要求的版本上限,Vue2项目用Node 14或16比较稳。
问题3:前端页面能打开但接口全部报404现象:浏览器F12看Network,所有请求的URL都是404。 原因:代理配置里的target端口和后端实际端口不一致。 解决:打开vue.config.js,确认target端口和后端一致。改完代理配置必须重启npm run serve,否则不生效。
问题4:登录接口报401现象:前端登录页提交账号密码后,提示"登录已过期"或其他未授权信息。 原因:用了错误账号,或者刚注册的账号还没被后台审核通过。部分源码给注册用户加了状态字段,新用户status默认为0,需要管理员在后台改成1才能登录。 解决:先用SQL文件里自带的测试账号登录,确认登录流程没问题后再排查其他账号。
问题5:图片上传成功但页面显示不出来现象:上传接口返回200,但img标签的src是404。 原因:后端保存的图片路径和前端访问的路径不一致。后端保存的是本地绝对路径,但前端请求时拿到的URL访问不到这个路径。 解决:把后端的上传目录配置成resources/static/upload,这样图片的访问URL就是http://localhost:8081/upload/xxx.jpg,前端直接用相对路径就能访问。
问题6:创建订单时库存或状态判断错误现象:同一个商品被下单多次,或者已下架的商品还能被下单。 原因:后端创建订单时没有判断商品status状态。 解决:在OrderService的创建订单方法里,先查一次商品状态,只有status=1才能下单,并且下单后把商品status改成3(已卖出),这个逻辑要在事务里执行。
问题7:MyBatis的XML文件找不到现象:启动报Invalid bound statement (not found)。 原因:mapper接口的方法没有在对应的XML文件里定义。 解决:检查application.yml里的mapper-locations配置,确认指向正确位置,另外XML文件里的namespace必须和Mapper接口的全限定名一致。
问题8:数据库中文乱码现象:前端页面显示的中文全是问号或乱码。 原因:数据库字符集不是utf8mb4,或者JDBC连接串没指定characterEncoding。 解决:把数据库和表的字符集改成utf8mb4,同时JDBC连接串加上useUnicode=true&characterEncoding=utf8mb4。
6. 验证与进阶:把毕业设计做成能经得起答辩的项目
项目跑通只是第一步,答辩时要能讲清楚设计思路,还要展示几个值得说的亮点。我建议拿到源码后,至少做三件事:第一,把项目的功能清单整理成表格,对照着逐项确认哪些能用、哪些有坑;第二,挑一个自己真正理解的模块,改一行代码或加一个小功能,答辩时被问"这个功能是你做的吗"时能理直气壮;第三,把后端接口用Postman跑一遍,画出接口清单,这比背源码更有效。
可以做的进阶功能,我按难度从小到大列几个:搜索历史记录,在本地存储存最近十条搜索关键字;商品收藏功能,新增一张收藏表,一个接口收藏、一个接口取消、一个接口查列表;数据统计图表,管理员后台用ECharts展示每天发布商品数量、成交订单数量;图片懒加载,列表页用v-lazy指令替代img的src直接加载。这几个功能代码量都在100行以内,但对答辩的加分效果非常明显。
验证项目是否真正可用,我一般用这样的步骤:清空浏览器缓存,重新走一遍用户完整流程——注册新账号、登录、发布一件商品(带图片)、浏览商品列表、搜索关键字、点击商品详情、下单购买、查看订单记录、退出登录、再登录。这套流程走完,说明项目的核心链路是通的。另一条链路是管理员操作:登录管理员账号、查看用户列表、审核商品上下架、查看订单列表。两条链路都通,项目就可以交出去了。
如果是换了一个全新的环境部署,注意JDK版本和Node版本的兼容性。SpringBoot 2.x配JDK 8是最稳的组合,前端Vue2配Node 14或16,这套组合我实测踩坑最少。数据库用MySQL 5.7或8.0都行,但要注意连接串的区别,MySQL 8.0需要显式加driver-class-name为com.mysql.cj.jdbc.Driver。把那以后我拿到任何一套SpringBoot+Vue的源码,都强制自己先走一遍数据库导入、后端起服务、前端联调的完整流程,再开始改代码,这样省下来的时间远比直接上手香。希望帮到你。
本文还有配套的精品资源,点击获取