SpringBoot+Vue校园二手交易平台:前后端分离毕业设计实战解析
发布时间:2026/9/26 2:06:10来源:尧图网络
简介这是一份基于SpringBootVue的校园二手交易平台毕业设计项目主要面向计算机专业学生适用于毕业设计、期末课程大作业或全栈项目实战。资源已经严格调试评审分在95分以上能够稳定运行可放心下载使用。压缩包共69个文件整体大小约1.34MB其中包含55个Java源码文件覆盖后端控制器、服务层、实体类与工具类2个YAML和2个properties配置文件用于多环境参数设置1个SQL脚本用于初始化数据库表结构及示例数据另有VueElementUI前端工程、jks安全证书、jar依赖包、xml配置以及使用说明等前后端目录划分清晰便于快速部署和二次开发。通过学习这份源码可以系统掌握SpringBoot与Vue的前后端分离开发模式、RESTful接口设计、用户认证与证书配置、MyBatis或JPA的数据持久化写法以及二手交易场景中的商品发布、分类检索、下单购买、订单管理和留言评价等核心业务实现。目前该项目已有482人学习浏览参考价值高适合需要完整可运行毕业设计源码或想提升全栈开发能力的读者下载用于学习与借鉴。1. 校园二手交易平台一套能跑通完整交易闭环的 SpringBoot Vue 毕业设计每年到课程设计或毕设交付的时候最怕的不是不会写代码而是手里没有一套能当场跑起来的完整项目。这套校园二手交易平台的源码和数据库就是冲这个来的后端用 SpringBoot 提供接口前端用 Vue 渲染页面MySQL 存业务数据从注册登录、发布闲置、浏览商品到下单购买一整条交易闭环都在里面。拿到手之后先导入数据库再启动后端和前端改一个数据库连接密码就能跑起来适合 Java 方向的毕设和期末大作业直接复现也适合想搞懂前后端分离项目怎么串起来的人当作参考骨架。2. 先看懂系统骨架数据表设计、接口约定与 SpringBoot 分层2.1 为什么是 SpringBoot Vue MySQL选型理由与边界答辩时老师最常问的第一个问题就是「为什么用这套技术栈」所以得先把选型理由讲清楚。SpringBoot 解决了后端环境配置的痛内嵌 Tomcat不要求你单独装服务器打包成 jar 用 java -jar 就能跑Vue 负责前端交互组件化写页面比 JSP 时代的模板渲染维护成本低MySQL 是关系型数据库里最普及的选择学校机房里基本都装好了答辩演示时不容易被环境卡住。这三者的边界也要心里有数。它是典型的单体前后端分离项目用户登录用的是接口鉴权而不是独立认证服务商品搜索是数据库 LIKE 而不是 Elasticsearch也没有引入 Redis 做缓存。这些「没有」不是缺点反而说明这套系统的复杂度刚好贴合课设要求。你答辩时主动讲一句「单体架构能满足校园二手交易这个规模引入分布式组件反而增加部署成本」比空泛地说「我的系统很强大」更有说服力。版本搭配上常见课设模板是 SpringBoot 2.x 配合 JDK 8 或 11、MySQL 5.7 或 8.0前端 Vue 2 配 vue-cli或者 Vue 3 配 Vite。这套资源里后端依赖集中在 pom.xml前端依赖在 package.json启动前先确认本机 JDK 与 Node 版本能对上后面会少踩很多坑。2.2 数据库设计四张核心表的字段与约束先看数据库脚本这是理解整个系统的入口。整个平台围绕「撮合闲置交易」展开四张表覆盖了核心业务用户表存账号与角色商品表存发布信息与在售状态订单表记录买卖双方与成交价留言表存商品下的咨询内容。CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 加密后的密码, nickname VARCHAR(50) DEFAULT NULL COMMENT 昵称, phone VARCHAR(11) DEFAULT NULL COMMENT 手机号, avatar VARCHAR(255) DEFAULT NULL COMMENT 头像路径, role TINYINT DEFAULT 0 COMMENT 角色0 普通用户 1 管理员, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;这里有几个值得注意的设计点。password 字段建议存 BCrypt 加密后的结果不要明文入库答辩时能算一个安全加分点。username 加唯一索引注册接口并发调用时数据库层兜底防止同一个账号被重复创建。create_time 用 DEFAULT CURRENT_TIMESTAMP插入时不用手动填时间。再看商品表和订单表CREATE TABLE goods ( id INT NOT NULL AUTO_INCREMENT, user_id INT NOT NULL COMMENT 发布人 ID, title VARCHAR(100) NOT NULL COMMENT 标题, description TEXT COMMENT 详细描述, price DECIMAL(10,2) NOT NULL COMMENT 售价, original_price DECIMAL(10,2) DEFAULT NULL COMMENT 原价, category VARCHAR(20) DEFAULT 其他 COMMENT 分类, status TINYINT DEFAULT 0 COMMENT 状态0 在售 1 下架 2 已售, image VARCHAR(255) DEFAULT NULL COMMENT 商品图路径, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; CREATE TABLE orders ( id INT NOT NULL AUTO_INCREMENT, goods_id INT NOT NULL COMMENT 商品 ID, seller_id INT NOT NULL COMMENT 卖家 ID, buyer_id INT NOT NULL COMMENT 买家 ID, price DECIMAL(10,2) NOT NULL COMMENT 成交价, status TINYINT DEFAULT 0 COMMENT 状态0 待付款 1 待发货 2 待收货 3 已完成 4 已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME DEFAULT NULL COMMENT 付款时间, PRIMARY KEY (id), KEY idx_seller_id (seller_id), KEY idx_buyer_id (buyer_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;价格字段用 DECIMAL(10,2) 而不是 FLOAT这是交易类系统的常识浮点数精度会翻车对账时金额对不上特别尴尬。status 用 TINYINT 存枚举值接口层判断数字比字符串比对应付字符串大小写问题更稳。orders 表里同时冗余了 seller_id 和 buyer_id查询「我买到的」和「我卖出的」各自走独立索引避免每次都要 join 商品表再反查用户。留言表结构类似核心字段是商品 ID、留言人 ID、接收人 ID 和内容这里就不展开贴 SQL 了导入资源里的 campus_secondhand.sql 会自动建出全部表。注意所有表都指定了 utf8mb4 字符集因为 MySQL 5.7 以下的 utf8 编码存不了 emoji商品描述里出现表情符号会直接报错。2.3 后端结构Controller、Service、Mapper 分层与统一返回体看完数据库再回到代码。后端目录按 SpringBoot 标准三层拆Controller 接收请求、Service 写业务逻辑、Mapper 操作数据库。实体类对应表结构DTO 用于接口传参VO 用于向前端返回展示数据这几层分清楚之后改接口字段不需要动表结构是这套代码里值得学习的组织方式。接口层有一个统一的返回体前端不看 HTTP 状态码而是看返回体里的 code 字段public class ResultT { private Integer code; // 200 成功 400 参数错误 401 未登录 500 服务异常 private String message; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.message success; r.data data; return r; } public static T ResultT error(Integer code, String message) { ResultT r new Result(); r.code code; r.message message; return r; } }为什么强调统一返回体前端 axios 响应拦截器里只需要判断 res.data.code 是否等于 200等于就走成功分支不等于就统一弹错误提示每个页面不用单独写异常处理。登录过期返回 401前端收到后自动跳转登录页这也是套路化的做法。接口路径上商品相关的请求统一挂在 /api/goods 下GET 列列表、POST 发商品、PUT 改信息、DELETE 下架方法语义和 HTTP 动词对齐。下单走 /api/orders留言走 /api/message。前后端联调时前端代理只认 /api 前缀这个约定后面部署环节会反复用到。3. 本地跑起来环境匹配、数据库初始化与前后端联调3.1 版本匹配JDK、Maven、Node、MySQL 怎么选启动这套项目之前先检查本机的环境。后端是 Maven 工程需要 JDK 和 Maven前端是 Vue 工程需要 Node 和 npm数据库需要 MySQL然后用 Navicat 或命令行导入 SQL 脚本。这四个组件版本不对最容易出现「别人能跑我跑不起来」的问题。组件建议版本说明JDK8 或 11与 SpringBoot 2.x 匹配最稳定Maven3.6 以上后端依赖管理MySQL5.7 或 8.08.0 需要配置时区参数Node14 以上前端构建与开发服务器我一般会这样匹配JDK 8 或 11 配 SpringBoot 2.xMaven 3.6 以上MySQL 5.7 或 8.0 都行但 8.0 要在连接串里加时区参数Node 14 以上npm 跟着 Node 走。如果你的机器装的是 JDK 17SpringBoot 2.x 也能跑但某些旧版本内嵌 Tomcat 会有字节码版本问题优先换 JDK 8 最省事。3.2 数据库初始化建库、导入 SQL、核对账号权限第一步是建库。打开命令行或 Navicat执行mysql -u root -p CREATE DATABASE campus_secondhand DEFAULT CHARSET utf8mb4; USE campus_secondhand; SOURCE /你的路径/campus_secondhand.sql;这里有两个细节建库时显式指定 utf8mb4避免继承 MySQL 实例默认的 latin1 字符集SOURCE 后面用绝对路径相对路径在部分终端下会找不到文件。导入成功后用 SHOW TABLES; 确认四张核心表是否都在顺便看一眼表名和字段名后面改配置要用到数据库名。提示MySQL 8.0 下如果连接串没有 serverTimezone驱动会直接抛错先把这个参数改了再排查其他问题。然后打开后端工程里的 application.yml这是 SpringBoot 配置的大本营server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/campus_secondhand?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 10 minimum-idle: 5 servlet: multipart: max-file-size: 10MB max-request-size: 20MBurl 里的 serverTimezoneAsia/Shanghai 是 MySQL 8.0 的必填项不写会在启动时报 CST 与 UTC 的时区错误characterEncodingutf8 保证中文写入不乱码。password 改成你本机 MySQL 的实际密码这是整份资源里唯一必须动的地方。hikari 段是连接池参数maximum-pool-size 控制最大连接数课设场景 10 个完全够用不用调太大。3.3 后端启动Maven 打包与运行数据库就绪后在工程根目录下执行mvn clean package -DskipTests java -jar target/campus-secondhand-0.0.1-SNAPSHOT.jar第一次执行 mvn clean package 会下载大量依赖如果卡在 downloading 不动去看第 5 章的 Maven 镜像配置。启动成功的标志是控制台出现 Tomcat started on port(s): 8080以及 SpringBoot 的启动横幅。如果出现 APPLICATION FAILED TO START大概率是数据库连不上回到上一步核对账号密码和 url。调试阶段其实不需要每次都打包直接 mvn spring-boot:run 也可以效果等价于上面两条命令。区别在于 java -jar 打的是生产包改了代码要重新打包spring-boot:run 适合边改边跑。答辩演示建议用打包好的 jar启动更快也更稳定。3.4 前端启动Vue 依赖安装与代理配置后端跑起来后前端工程在另一个目录。打开终端进入前端目录执行npm install npm run servenpm install 装完后npm run serve 会启动 Vue 开发服务器默认端口根据 vue.config.js 里的配置而定。这里有个关键文件vue.config.js 里的 devServer.proxy 设置它决定前端请求怎么打到后端module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } };port 3000 是浏览器访问的地址proxy 的意思是把所有以 /api 开头的请求转给 target 指向的 8080。changeOrigin: true 会改写请求头里的 Host避免后端拿到的来源信息和预期不一致。这样前端页面和后端接口不在同一个端口也不会触发浏览器跨域拦截因为浏览器看到的请求是同源的。3.5 联调验证一条交易链路走到底前后端都启动后打开浏览器访问 http://localhost:3000。按顺序做一遍注册一个新账号、登录、发布一件商品、去首页找到它、发起下单。每一步都在 F12 的 Network 面板里确认前端请求了哪个 /api 接口、后端返回的 code 是不是 200。只要这条链路走通说明环境、数据库、代理、鉴权全部正常可以进入下一步改代码或准备答辩了。4. 核心模块实现思路登录鉴权、商品发布与订单状态流转4.1 登录鉴权拦截器 Token 方案登录模块是答辩时的高频考点。这套系统的做法是用户登录成功后后端生成一个 tokenuuid 或 JWT把用户 ID 和 token 的映射关系存起来前端在请求头里带 token后端用拦截器统一校验。先看拦截器核心逻辑Component public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 预检请求直接放行 if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(token); if (token null || token.isEmpty()) { response.setStatus(401); return false; } Long userId tokenService.getUserId(token); if (userId null) { // token 无效或已过期 response.setStatus(401); return false; } request.setAttribute(userId, userId); return true; } }预检请求放行那一行不能省浏览器跨域时会先发 OPTIONS 请求不放行会导致前端报「请求被拦截」。校验通过后把 userId 塞进 request后续 Controller 里直接取不需要再解析一次 token。拦截器需要注册并且要放行一部分公开接口Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns(/api/**) .excludePathPatterns(/api/user/login, /api/user/register, /api/goods/list); } }addPathPatterns 表示拦截所有 /api 开头请求excludePathPatterns 放行登录注册和商品列表。这个放行名单很有讲究商品列表如果不放行游客打开首页连商品都看不到如果这个名单写漏了前端就会集体 401这是调试时最容易遇到的鉴权问题。4.2 商品发布与图片上传两个容易出错的点商品发布模块涉及前端表单、后端校验、图片上传三块。图片上传是课设里最容易翻车的地方前端提交 multipart 文件后端存到本地磁盘返回相对路径给前端商品表里只存这个路径。后端的静态资源映射是配套必须写的Configuration public class UploadConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: System.getProperty(user.dir) /upload/); } }addResourceHandler 里的 /upload/** 是 URL 访问路径addResourceLocations 是磁盘上的真实目录。System.getProperty(user.dir) 是当前工作目录打 jar 跑时它是 jar 所在目录IDE 里跑时是工程根目录同一个项目搬到不同环境图片路径会变这是部署后图片 404 的常见根源。上传时还要限制文件类型和后缀常见做法是只允许 jpg、png、webp并且用 UUID 重命名防止用户传 exe 或者覆盖别人的文件。商品发布接口的业务校验也要看标题不能为空、价格必须大于 0、分类要落在预设集合里。价格解析用 BigDecimal 而不是 Double因为 Double 的 0.1 在二进制里存不精确入库前就可能出现 9.9999 这种值。4.3 订单状态流转状态机设计与并发控制订单是交易系统的核心状态不能乱跳。这套代码里订单状态是 0 待付款、1 待发货、2 待收货、3 已完成、4 已取消合法的流转路径是买家下单生成待付款付款后变待发货卖家发货后变待收货买家确认后变已完成取消只允许发生在待付款和待发货阶段。创建订单的逻辑里有一个并发控制的经典写法public Order createOrder(Long goodsId, Long buyerId) { Goods goods goodsService.getForUpdate(goodsId); if (goods null || goods.getStatus() ! 0) { throw new BizException(商品不存在或已不在售); } // 查出来 status0 也不一定安全加行锁才能挡住并发下单 if (goods.getUserId().equals(buyerId)) { throw new BizException(不能购买自己发布的商品); } Order order new Order(); order.setGoodsId(goodsId); order.setSellerId(goods.getUserId()); order.setBuyerId(buyerId); order.setPrice(goods.getPrice()); order.setStatus(0); orderService.save(order); goods.setStatus(2); goodsService.updateById(goods); return order; }getForUpdate 走的是 SELECT ... FOR UPDATE把这件商品的行锁住直到事务提交才释放。不加这一句的话两个人同时点「立即购买」都能查到 status0就会卖出两单商品却只有一件。数据库行锁是这层兜底业务上还要在事务里先改商品状态再生成订单顺序反了会放大锁的持有时间接口响应变慢。这套代码把「下单即锁商品」的意图放在 Service 层比在 Controller 里写要规范答辩时可以把这段单独拿出来讲。5. 避坑指南跑这套源码最常见的五个坑这一章是把实际跑这套 SpringBoot Vue 源码时大概率会遇到的五个问题按「现象 → 原因 → 解决」列出来。每一条都是先看到什么现象再说根因最后给操作。5.1 后端启动报数据库连接失败现象控制台抛 Communications link failure 或 Access denied for userSpringBoot 直接退出提示 APPLICATION FAILED TO START。原因两个来源占多数。一是 MySQL 8.0 的连接串里没写 serverTimezone驱动不知道用哪个时区二是 application.yml 里的用户名密码与 MySQL 实际账号不匹配root 密码被改过而配置没同步。解决先看连接串确认 url 末尾带 serverTimezoneAsia/Shanghai 和 characterEncodingutf8再用 mysql -u root -p 手动登录验证密码登录成功后把 yml 里的 password 改成同一个。改完重启不要只刷新页面SpringBoot 的配置在启动时读取。5.2 前端页面能打开但接口全部 404现象npm run serve 正常浏览器能显示首页布局但商品列表空、F12 里所有 /api 请求标红响应状态是 404。原因前端代理配置的路径和后端实际的接口前缀对不上。比如后端 Controller 的 RequestMapping 是 /api/goods代理里只写了 /goods请求根本转发不到正确路径或者 vue.config.js 改了之后没重启 devServer代理不生效。解决打开后端代码看接口统一前缀是 /api 还是空再把 vue.config.js 的 proxy 键名改成一致保存后重启 npm run serve。验证代理是否生效可以直接在浏览器访问 http://localhost:8080/api/goods能返回 JSON 就说明后端没问题问题只在前端转发。5.3 图片上传提示成功页面却显示不出来现象商品发布接口返回 200数据库里也有了图片路径但前端 标签加载出来是裂图请求图片地址返回 404。原因图片保存到了本地磁盘后端没有配置静态资源映射浏览器无法通过 URL 访问磁盘文件。也可能是映射配置里的磁盘路径写死成了某个目录实际 jar 运行目录不一致。解决确认工程里有 UploadConfig 这类实现了 WebMvcConfigurer 的配置类addResourceLocations 必须带 file: 前缀Windows 下写 file:D:/upload/Linux 下写 file:/home/user/upload/。图片访问路径在前端应当是以 /upload 开头的相对路径不要存成带 8080 端口的绝对路径否则环境一变地址就失效。5.4 端口被占用前端或后端起不来现象执行 java -jar 或 npm run serve 时报 Port 8080 was already in use 或 Port 3000 is already in use。原因上次运行的后端进程没退出或者开发调试时开了多个终端没有关闭。Windows 下常见的是 Java 进程假死任务管理器里 Java 占了内存但窗口已经关了。解决macOS 或 Linux 用 lsof -i :8080 查 PID然后 kill -9 Windows 用 netstat -ano | findstr :8080 拿到 PID再 taskkill /F /PID 。图省事也可以直接把 server.port 改成 8081前端代理的 target 同步改但这种「换端口绕过」的方式建议只用于临时调试。5.5 Maven 依赖下载慢或直接失败现象mvn clean package 长时间卡在 Downloading偶尔报 Could not transfer artifact 或者 Cannot resolve symbol打包中断。原因默认走 Maven 中央仓库国内访问不稳定也可能是 IDE 里缓存了旧的坏依赖。解决在 Maven 的 settings.xml 里配置阿里云镜像仓库配置方式是在 节点加一个 mirrormirror idaliyun/id mirrorOfcentral/mirrorOf namealiyun maven/name urlhttps://maven.aliyun.com/repository/public/url /mirror配完重新加载项目依赖会从国内镜像拉取。如果改了 settings.xml 还在报错删掉本地仓库里对应的残留目录再重新拉一次。npm install 慢的同理把 registry 指到 npmmirror 镜像能快很多。6. 答辩演示技巧预置数据、固定路径与现场兜底6.1 给首页灌一批「有说服力」的数据空首页演示是很多课设翻车的起点。资源里的 SQL 通常带少量预置数据但数量不够撑起「平台氛围」。我一般会在演示前一天额外补一批商品和留言INSERT INTO goods (user_id, title, description, price, original_price, category, status, image) VALUES (1, 九成新自行车, 骑行一年通勤用链条保养过座位无破损, 180.00, 400.00, 交通工具, 0, /upload/bike.jpg), (1, 高数教材全套, 同济版高数上下册有笔记适合期末复习, 25.00, 80.00, 教材, 0, /upload/math.jpg);补数据的核心原则是「真实感」价格要有零头描述要有细节分类不要全部挤在一个类目。演示时评委会看列表页的丰富度数据填得用心系统看起来就像真的被用过。6.2 把演示路径固定成一条主干流程演示环节不要临场发挥。我建议的顺序是登录管理员账号展示后台数据统计 → 切换普通用户发布一件商品 → 从首页找到该商品并下单 → 切换到卖家视角发货 → 买家确认收货。这一条链路覆盖了登录鉴权、文件上传、订单状态机三大核心模块每个考点都在路线上不会出现「老师想看下单我还在找入口」的尴尬。6.3 一个我常用的演示兜底技巧上讲台前把后端、前端、数据库全部启动好浏览器提前停在首页但不要提前点进任何页面。正式演示的「开场动作」是刷新首页让商品列表从空到满的加载过程完整展示一次这比打开一个已经渲染好的页面更有「系统在工作」的说服力。如果现场网络或环境出问题优先用手机开热点避免共用答辩教室的 Wi-Fi 导致 npm run serve 重新编译页面卡顿。还有一条血泪经验演示前 30 分钟强制走一遍完整的注册→发布→下单→确认收货绝不带一个没验证过的状态上台。那次我为了省时间直接登录预置账号演示结果预置账号的密码被我之前测试时改掉了现场密码错误只好当着老师的面重置数据。从那以后我每次答辩都先自测主链路再预置数据最后才上讲台。希望帮到你这套源码跑通之后剩下的就是把它讲成一个「有人用过的平台」了。本文还有配套的精品资源点击获取
网站建设高端定制企业官网