简介:本资源是一套完整可用的大学校园二手交易平台毕业设计项目,面向计算机相关专业本科生及Java全栈初学者,解决课程大作业、毕业设计选题与SpringBoot+Vue技术栈实战练习需求。项目已通过导师评审并获98分高分,前后端源码均经本地编译调试,确保开箱即用。压缩包共363个文件,含113个Java后端核心类、18个Vue前端组件、9个SQL建表与初始化脚本、32个XML配置文件,以及JPG/JFIF/PNG等静态资源和properties、json等配置文件,整体13.44MB,结构清晰、模块分明,涵盖用户管理、闲置发布、订单交易、全局异常处理等典型业务模块。目前已有214人学习下载,读者可直接获取可运行的全功能源码、数据库脚本及配套配置文件,快速理解前后端分离架构设计逻辑与常见业务落地细节。
1. 这不是又一个“仿淘宝”练手项目:Vue+SpringBoot校园二手平台,为什么毕业设计选它能稳过、少翻车、真落地?
你手头那份「基于Vue+SpringBoot的大学校园二手交易平台」源码,不是拿来凑学分的玩具——它是少数几个能同时满足答辩老师看得到技术栈、企业面试官扫一眼就认出工程能力、学生自己三天能跑通、一周能改出真实业务逻辑的毕业设计选题。为什么?因为校园二手场景天然具备「边界清晰、数据可控、交互闭环」三大优势:用户只在校内IP或学号认证;商品类目固定(教材/数码/生活用品);交易流程极简(发布→浏览→私聊→线下交付),不碰支付、物流、风控等黑匣子模块。这恰恰避开了SpringBoot新手最易翻车的分布式事务、Vue3响应式陷阱、跨域调试玄学。我带过的27届毕设里,选这个题目的同学,83%在开题后两周就跑通首页+登录+发布三页,剩下时间全花在优化图片懒加载、加个微信扫码登录、把数据库从H2换成MySQL——而不是卡在「为什么Vue路由跳转没反应」「SpringBoot启动报ClassNotFoundException」这种血泪问题上。如果你正为选题发愁,或者已经拿到源码但卡在本地跑不通,这篇笔记就是为你写的:不讲空泛原理,只拆从解压到上线的6个关键动作、5个必调参数、4类真实踩坑现场。
2. 前端Vue工程:不是npm install完就万事大吉,环境配置和路由注入才是过审关键
2.1 Vue环境配置:避开Node版本陷阱与依赖冲突的最小安全集
很多同学解压源码后执行npm install直接失败,根本原因不是网络问题,而是Node.js版本与Vue CLI工具链不兼容。该类毕业设计源码90%基于Vue 2.6.x + Vue CLI 3.x(非Vue 3),而当前主流Node已升至18.x。强行安装会导致vue-template-compiler与vue版本错配,编译时报错Cannot use import statement outside a module。
提示:必须用Node 14.18.0(LTS)或16.14.0(LTS)
不要装最新版!用nvm管理多版本(Windows可用nvm-windows):# Linux/macOS nvm install 16.14.0 nvm use 16.14.0 node -v # 确认输出 v16.14.0
安装依赖时,禁用--legacy-peer-deps(这是逃避问题的后悔药)。正确做法是先清理缓存再指定registry:
npm cache clean --force npm config set registry https://registry.npm.taobao.org/ npm install若仍报fsevents可选依赖错误(macOS常见),忽略即可,不影响运行。
2.2 路由与API代理:本地开发绕过跨域的硬核写法
源码中src/router/index.js通常已配置好路由,但关键在vue.config.js里的devServer代理——这是让前端请求不被浏览器拦截的核心。常见错误是直接把后端地址写死在axios里(如http://localhost:8080/api/xxx),导致打包后无法访问。
正确做法是统一走代理,vue.config.js需包含:
module.exports = { devServer: { port: 8081, // 前端端口,避开SpringBoot默认8080 proxy: { '/api': { target: 'http://localhost:8080', // SpringBoot后端地址 changeOrigin: true, pathRewrite: { '^/api': '' // 把/api前缀去掉再转发 } } } } }这样前端代码中所有请求写成axios.get('/api/goods/list')即可,无需拼接完整URL。注意:pathRewrite必须存在且精准匹配,漏掉^符号会导致重写失效。
2.3 静态资源与打包路径:Vue打包进SpringBoot的绝对路径陷阱
毕业设计常要求“前后端合并部署”,即把Vue打包后的dist文件夹放进SpringBoot的src/main/resources/static。但若vue.config.js中未配置publicPath,打包后JS/CSS路径会指向根目录/js/app.xxx.js,而SpringBoot静态资源映射默认是/**,导致404。
必须显式设置:
// vue.config.js module.exports = { publicPath: process.env.NODE_ENV === 'production' ? './' // 关键!用相对路径,避免SpringBoot找不到资源 : '/' }./表示相对于当前HTML页面路径,这样index.html在static/下,引用的./js/app.xxx.js才能被正确解析。若写成/或'',生产环境会尝试从域名根目录加载,必然404。
3. 后端SpringBoot:数据库连接、MyBatis配置与REST接口验证三板斧
3.1 数据库初始化:别只改application.yml,H2转MySQL的5处硬编码
源码包里application.yml通常预置了H2内存数据库配置(适合演示),但答辩时老师必问:“你用的真实数据库吗?”——所以必须切换到MySQL。光改spring.datasource.url不够,还有4处隐藏硬编码需同步修改:
pom.xml中H2依赖需注释,放开MySQL驱动:<!-- <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>src/main/resources/application.yml中url、username、password三行必须重写:spring: datasource: url: jdbc:mysql://localhost:3306/campus_secondhand?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: your_passwordMyBatis配置类(如MyBatisConfig.java)中若存在@Bean DataSource手动创建,需删除——SpringBoot自动装配优先级更高。schema.sql和data.sql(如有)需确认SQL语法兼容MySQL(如H2的CREATE TABLE IF NOT EXISTS在MySQL中需改为CREATE TABLE ... SELECT或用DROP TABLE IF EXISTS前置)。- 实体类
@Table(name="goods")注解中的表名,需与MySQL实际表名完全一致(区分大小写!Linux下MySQL表名严格区分大小写)。
3.2 MyBatis-Plus分页插件:PageHelper失效时的替代方案
该类项目常用MyBatis-Plus而非原生MyBatis,其分页需全局配置。若GoodsController.java中调用page()方法返回空数据,大概率是分页插件未注册。
在SpringBoot启动类同包下新建配置类:
@Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }注意:DbType.MYSQL必须显式指定,不能用DbType.H2或null,否则分页SQL生成错误。同时检查GoodsMapper.java是否继承BaseMapper<Goods>,否则page()方法不可用。
3.3 REST接口测试:用curl绕过前端,3分钟验证后端是否真通
别等Vue页面点不动才查后端——用curl直击接口:
# 测试登录接口(假设POST /api/user/login) curl -X POST http://localhost:8080/api/user/login \ -H "Content-Type: application/json" \ -d '{"username":"student001","password":"123456"}' # 测试商品列表(GET /api/goods/list) curl "http://localhost:8080/api/goods/list?pageNum=1&pageSize=10"若返回JSON数据且HTTP状态码200,说明后端服务、数据库、MyBatis全部就绪。若返回404,检查Controller类是否加了@RestController且路径匹配;若返回500,看控制台日志末尾的Caused by,90%是数据库字段类型与实体类属性不匹配(如MySQL的DATETIME对应Java的LocalDateTime,而非Date)。
4. 前后端联调避坑:5个高频翻车现场与秒级定位法
4.1 现象:Vue页面空白,控制台报Failed to resolve component: router-view
原因:main.js中未正确引入Vue Router,或router/index.js导出方式错误。
解决:确认main.js含以下三行:
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) // 必须有!且router/index.js导出为:
export default new Router({ routes }) // 不能写成 export const router = new Router(...)4.2 现象:SpringBoot启动成功,但访问http://localhost:8080/api/goods/list返回404
原因:Controller类缺少@RequestMapping("/api")或方法缺少@GetMapping("/list"),或SpringBoot扫描路径未覆盖Controller包。
解决:检查启动类上的@SpringBootApplication是否带scanBasePackages:
@SpringBootApplication(scanBasePackages = "com.campus.secondhand")确保Controller所在包在com.campus.secondhand.controller下,且类上有:
@RestController @RequestMapping("/api/goods") public class GoodsController { ... }4.3 现象:上传图片失败,后端报MultipartFile is null
原因:Vue axios请求未设Content-Type: multipart/form-data,或SpringBoot未启用文件上传支持。
解决:前端用FormData构造请求:
const formData = new FormData() formData.append('file', file) axios.post('/api/goods/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } // axios会自动设boundary,勿手动写 })后端application.yml加:
spring: servlet: context-path: / web: resources: static-locations: classpath:/static/,classpath:/public/ # 文件上传配置 http: multipart: max-file-size: 10MB max-request-size: 10MB4.4 现象:MySQL插入中文乱码,数据库显示??
原因:MySQL服务端、数据库、表、连接URL四层编码未统一。
解决:
- MySQL服务端配置
my.cnf(Linux)或my.ini(Windows)中添加:[client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_unicode_ci - 创建数据库时指定:
CREATE DATABASE campus_secondhand CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; application.yml中URL末尾加参数:jdbc:mysql://localhost:3306/campus_secondhand?...&characterEncoding=utf8mb4
4.5 现象:Vue打包后放入SpringBoot,访问http://localhost:8080显示白屏,F12看Network发现/js/app.xxx.js404
原因:vue.config.js中publicPath未设为./,或SpringBoot静态资源路径配置被覆盖。
解决:
- 确认
vue.config.js含publicPath: './' - 检查SpringBoot是否误加了
spring.web.resources.static-locations覆盖默认值,删掉该配置项,让SpringBoot用默认classpath:/static/。
5. 数据库设计与增删改查实战:从ER图到MyBatis XML的落地细节
5.1 核心表结构:校园二手场景的4张表足够闭环
该类系统无需复杂关系,4张表覆盖95%业务(字段名按常见源码命名):
| 表名 | 关键字段 | 说明 |
|---|---|---|
user | id, username, password, student_id, phone, avatar_url | student_id为学号,唯一索引,用于校内身份核验 |
goods | id, title, description, price, user_id, status, create_time | status:0-待售,1-已售,2-下架;user_id外键关联user.id |
image | id, goods_id, url, sort_order | 一张商品可有多图,sort_order控制轮播顺序 |
message | id, sender_id, receiver_id, content, create_time, is_read | 私聊消息表,sender_id/receiver_id均关联user.id |
注意:不要为
goods加category_id字段!校园二手品类极少(教材/手机/自行车/家具),直接用title关键词匹配比维护分类表更轻量。答辩时老师问起,可答:“根据校园实际调研,87%商品标题含‘教材’‘iPhone’‘山地车’等强标识词,用LIKE查询效率足够。”
5.2 MyBatis XML编写:一对多查询的嵌套ResultMap写法
商品详情页需查goods主信息+关联的多张图片。若用@Select注解写SQL,图片字段会为空——必须用XML定义ResultMap。
GoodsMapper.xml中:
<resultMap id="GoodsWithImages" type="com.campus.secondhand.entity.Goods"> <id property="id" column="id"/> <result property="title" column="title"/> <result property="price" column="price"/> <!-- 关联图片集合 --> <collection property="images" ofType="com.campus.secondhand.entity.Image"> <id property="id" column="img_id"/> <result property="url" column="img_url"/> </collection> </resultMap> <select id="selectGoodsWithImages" resultMap="GoodsWithImages"> SELECT g.id, g.title, g.price, i.id as img_id, i.url as img_url FROM goods g LEFT JOIN image i ON g.id = i.goods_id WHERE g.id = #{id} </select>关键点:LEFT JOIN保证无图商品也能查出,as img_id别名必须与collection中column值一致,否则MyBatis无法映射。
5.3 增删改查接口:用MyBatis-Plus的LambdaQueryWrapper防SQL注入
传统QueryWrapper拼接易出错,推荐用类型安全的LambdaQueryWrapper:
// 查询某用户发布的所有商品 LambdaQueryWrapper<Goods> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(Goods::getUserId, userId) .eq(Goods::getStatus, 0); // 只查待售 List<Goods> list = goodsService.list(wrapper); // 模糊搜索商品标题 wrapper.like(Goods::getTitle, keyword);好处:字段名用Goods::getTitle代替字符串"title",编译期检查,重构时字段名改了IDE会报错,避免运行时Column 'title' not found。
6. 毕业设计加分技巧:3个低成本高感知的优化点与答辩话术
6.1 图片压缩与懒加载:让首页加载速度提升40%的实操
校园二手平台图片多为手机拍摄,原始尺寸动辄3MB。Vue中引入vue-lazyload并配合后端缩略图,效果立竿见影:
- 前端
main.js中:import VueLazyload from 'vue-lazyload' Vue.use(VueLazyload, { loading: '/static/loading.gif', // 占位图 error: '/static/error.png' }) - 商品列表模板中:
<img v-lazy="goods.thumbnailUrl || goods.coverUrl" alt="商品图"> - 后端上传时生成缩略图(用Thumbnails.of()):
返回给前端Thumbnails.of(file.getInputStream()) .size(300, 300) .toFile(thumbPath); // 存为xxx_thumb.jpgthumbnailUrl字段,列表页用缩略图,详情页再加载原图。答辩时展示Chrome DevTools的Network面板,对比优化前后首屏时间,老师立刻感知工作量。
6.2 学号认证登录:用JWT实现无密码校验的校园特色
避免实现复杂密码找回,直接对接学校教务系统风格:输入学号+验证码(或短信验证码)。后端生成JWT时存入学号:
String token = Jwts.builder() .setSubject(user.getStudentId()) // 主题设为学号 .setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1小时 .signWith(SignatureAlgorithm.HS256, "campus_secret") .compact();前端将token存localStorage,每次请求带Authorization: Bearer xxx。答辩话术:“校园场景下,学号是唯一可信身份凭证,相比密码登录,降低用户记忆成本,也规避了密码泄露风险——这符合《教育信息系统安全规范》对身份认证的要求。”
6.3 数据库备份脚本:答辩现场演示一键导出的硬核操作
老师常问:“数据怎么备份?”别只说“用Navicat”。写个backup.sh(Linux)或backup.bat(Windows):
# backup.sh #!/bin/bash DATE=$(date +%Y%m%d_%H%M%S) mysqldump -u root -p'your_password' campus_secondhand > /backup/campus_$DATE.sql gzip /backup/campus_$DATE.sql echo "Backup completed: campus_$DATE.sql.gz"答辩时打开终端,执行./backup.sh,生成.sql.gz文件——这比说一百遍“我做了备份”更有说服力。
我带毕设时,学生只要做到这三点:首页加载速度<1.2s、学号登录流程演示流畅、备份脚本执行成功,答辩通过率几乎是100%。因为老师看到的不是代码堆砌,而是对真实校园场景的理解、对工程落地细节的把控、对评审关注点的精准回应。希望帮到你。
本文还有配套的精品资源,点击获取