ARTICLE DETAIL

资讯详情

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

Spring Boot+Vue前后端分离毕设:二手交易平台核心技术与实践解析

Spring Boot+Vue前后端分离毕设:二手交易平台核心技术与实践解析 简介基于Spring Boot的二手交易平台项目源码面向Java后端开发学习者及毕业设计人员覆盖商品发布、浏览、交易、订单管理等典型业务场景可作为课程设计与毕设改造基础。压缩包含812个文件约16.66MB其中Java源码实现后端业务逻辑Vue与JS构建管理端及前台页面HTML/CSS呈现静态样式SQL文件提供数据库初始化脚本另有XML映射、Maven配置等辅助文件整体目录结构清晰便于按模块查阅。目前已有152人学习下载。资源附带安装/运行批处理脚本、mvnw等工具配合JDK1.8、Tomcat7、MySQL5.7环境即可搭建运行能帮助读者省去环境配置与项目整合时间专注理解二手交易系统的前后端交互与Spring Boot工程实践。1. 毕业设计选型为什么是 springboot Vue 的前后端分离一个看似普通的二手交易平台题目真正拆开代码时才会发现仓库里藏着不少比业务逻辑更值得讲的东西IndexAsideStatic.vue、IndexHeader.vue 这类文件说明它是带管理后台的前后端分离项目而mvnw.cmd、1-install.bat、2-run.bat又暗示作者用 Maven Wrapper 和批处理脚本把环境依赖做了收敛。比较反直觉的是摘要里的 Tomcat7如果你用 Spring Boot 2.x 打成 war 包丢进 Tomcat7Servlet 版本和WebServlet扫描会对不上但如果只跑 jar 包Tomcat 版本跟运行完全无关。这篇博文会从实体关系、本地启动、交易核心链路和答辩安全自查四个角度把这个毕设真正值钱的细节拆出来。2. 实体关系、目录结构 与 Spring Boot 自动装配原理2.1 用户、商品与订单的最小实体集合绝大多数二手交易平台项目都不需要过度设计核心就是用户、商品、订单三类表再加上一个分类表和一个轮播图表就够了。用户表里除了id、username、password还要有avatar和phone因为前端管理后台的 IndexHeader.vue 组件里一般会显示用户头像和昵称后端接口如果没返回这些字段页面就会空一块。商品表是最容易埋坑的地方。二手商品的特殊性在于“可议价”和“状态多变”所以字段至少要有goods_name、goods_desc、price、original_price、cover_image、status、seller_id、buyer_id、end_time。这里的end_time是很多毕设忽略的字段学生答辩时被问“商品怎么下架”往往答不上来后面第 5 章我会专门讲定时下架的实现。订单表建议把快照字段放进去比如order_no、goods_id、seller_id、buyer_id、amount、status、create_time。因为二手商品的商品信息会被编辑订单如果只关联goods_id用户下单后再看订单详情看到的是修改后的商品描述这在演示时很容易被老师质疑。在orders表里冗余一份goods_name和amount快照逻辑上更合理。2.2 Spring Boot 自动装配在模块边界上的作用很多人在答辩时会背“Spring Boot 简化配置”这句话但被追问“为什么引入spring-boot-starter-web就会有 Tomcat 和 SpringMVC”就卡住了。要讲清楚得看SpringBootApplication里面的三个注解SpringBootConfiguration标记配置类、ComponentScan扫描当前包及其子包、EnableAutoConfiguration负责读取META-INF/spring.factories中的自动配置类列表。org.springframework.boot.autoconfigure.EnableAutoConfiguration\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration这段配置是 Spring Boot 2.x 里自动装配的入口文件内容截断示意。真正起过滤作用的是每个自动配置类上的ConditionalOnClass、ConditionalOnMissingBean这类条件注解。也就是说你 classpath 里有DataSource相关类且没有手动定义DataSourceSpring Boot 才会帮你创建数据库连接池。理解这一点后就知道为什么自定义拦截器或配置类要放在 Spring Boot 启动类的子包里否则ComponentScan扫不到自动配置也不会退避。这个项目里的事务管理就是典型的自动配置结果。spring-boot-starter-jdbc或spring-boot-starter-data-jpa被引入后DataSourceTransactionManagerAutoConfiguration会自动注册事务管理器代码里只需要在 Service 方法上加Transactional不需要写任何 XML 配置。这对毕业设计的好处是老师检查代码时不会看到一大堆 Spring 样板配置但这恰恰意味着你必须能解释自动配置的边界。2.3 前后端分离结构里的 .bak 文件与 Maven Wrapper项目文件里的index.html.bak、update-password.vue.bak、BreadCrumbs.vue.bak不是业务文件而是开发者在 IDE 里手动备份的旧版本。.bak后缀不会参与前端构建但放在源码仓库里会让人困惑我一般会建议删掉并改用 Git 分支管理。不过保留它们有一个好处能看出开发过程是先有登录和修改密码功能再扩展了后台布局组件。mvnw.cmd是 Maven Wrapper 的 Windows 执行脚本。它的作用是锁定 Maven 版本避免“本机能跑换台机器就报 Maven 版本不对”的问题。这个项目配套Maven 3.3.9和 Spring Boot 1.5.x 到 2.x 早期版本都能兼容。如果你把 JDK 换成 17 以上Maven 3.3.9 会因为无法识别 class 文件版本而失败所以跑之前先确认一下JAVA_HOME指向的是 JDK1.8。前端部分的IndexAsideStatic.vue、IndexHeader.vue、BreadCrumbs.vue是管理后台的布局组件分别对应侧边栏、顶部导航和面包屑。很多基于 Spring Boot 的 Java 毕设前端都直接从开源后台模板改造这类组件通常依赖 Vue2 和 ElementUI。如果前端npm install报错先看 Node 版本是否超过 16因为 Vue2 node-sass 在 Node 高版本下编译会出现 Python 和 C 构建报错这不是业务代码的问题。提示接手这种项目时优先查看前端包管理文件里的依赖版本不要盲目用npm update把所有依赖升级一遍否则组件 API 不兼容会牵出一堆无头异常。3. 从 JDK1.8 到 MySQL5.7本地环境与启动参数3.1 用 IDEA 创建 Spring Boot 项目时的版本匹配很多人在 IDEA 里新建 Spring Boot 项目时习惯选最新版本但这套二手交易平台用的技术栈比较老如果你在 2025 年新建项目并选择 Spring Boot 3.xJDK 必须是 17和摘要里的 JDK1.8 就完全错开了。所以更稳妥的做法是直接打开源码目录里的pom.xml看parent标签里锁定的version不要自行升级。Spring Boot 版本与外部容器的兼容性有个大致表格Spring Boot 版本默认内嵌 Tomcat可兼容的外部 TomcatJDK 要求1.5.x8.5.xTomcat 7/81.72.1.x9.0.xTomcat 8.51.82.7.x9.0.xTomcat 91.83.0.x 及以上10.1.xTomcat 1017这个项目如果坚持用 Tomcat7 部署 war 包Spring Boot 版本最好控制在 1.5.22。如果项目已经用了 Spring Boot 2.x那就走java -jar内嵌容器运行不要去改packaging为 war。很多毕业生踩的坑是学校服务器只有 Tomcat7于是把 Spring Boot 2.x 打成 war结果启动时NoSuchMethodError满屏飞最后答辩前夜才降版本。我一般会建议先在本地确认启动方式再动代码。3.2 数据库初始化与 application.yml 参数MySQL 5.7 建库时要注意字符集和排序规则。直接用CREATE DATABASE second_hand DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;比在 IDE 里右键新建要更可控因为utf8mb4能存 emoji 和部分生僻字。导入数据库脚本时Navicat11 与 MySQL 5.7 的连接要选“使用旧版密码认证”吗其实不需要MySQL5.7 默认的mysql_native_password协议 Navicat11 完全支持。application.yml是后端项目的核心配置文件建议至少包含下面这一段server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/second_hand?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true参数说明useUnicodetruecharacterEncodingutf8保证中文不会乱码serverTimezoneAsia/Shanghai是 JDBC 驱动版本升级后必须加的时区参数不加会报The server time zone value错误max-file-size控制上传图片大小二手交易的商品图一般不超过 5MB给到 10MB 是防止用户传过大的相机原图。map-underscore-to-camel-case开启后数据库字段seller_id能自动映射到 Java 属性sellerId省掉一长串 resultMap。3.3 一键安装与运行脚本的完整解读项目根目录下的1-install.bat和2-run.bat是给自己或答辩老师准备的快捷启动方式。两个脚本内容大致可以补全成下面这样echo off set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 set MAVEN_HOMED:\apache-maven-3.3.9 call mvnw.cmd clean install -DskipTests pause1-install.bat的逻辑是先设置 JDK 环境再用mvnw.cmd执行打包。clean会删除 target 目录里的旧产物install会把构建结果安装到本地 Maven 仓库-DskipTests跳过测试用例但不会跳过测试代码编译。如果你想跳过编译测试类速度更快可以改成-Dmaven.test.skiptrue。这个区别如果答得出来老师会觉得你确实手动构建过。echo off set JAVA_HOMEC:\Program Files\Java\jdk1.8.0_202 java -jar target\second-hand-0.0.1-SNAPSHOT.jar --server.port8080 pause2-run.bat直接用java -jar启动内嵌容器。--server.port8080是启动参数优先级高于application.yml里的配置。如果你本机 8080 被占用了可以把脚本里的端口改为 8081不需要改代码重新打包。启动后看到Started Application in x.xxx seconds说明后端正常这时候再去启动前端项目才能登录。前端如果也要一键启动一般会有package.json文件运行npm install安装依赖然后npm run dev启动 Vue 开发服务器。Vue CLI 默认端口是 8080和后端冲突所以前端开发服务器通常会配成 9528 或 8081并在vue.config.js里配置/api代理到后端 8080。见别人的项目时先找代理配置能找到说明作者真的联调过前后端。4. 交易核心链路登录、发布商品与订单状态机4.1 登录会话与拦截器实现二手交易平台有两种登录设计基于 Session 的传统方式和基于 JWT 的无状态方式。后端项目如果做了前后端分离JWT 更常见因为不需要考虑 Session 跨域共享问题。这里给出一个最小可用的拦截器实现Component public class JwtInterceptor implements HandlerInterceptor { private static final String SECRET your-256-bit-secret; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equalsIgnoreCase(request.getMethod())) { return true; } String token request.getHeader(Authorization); if (token ! null token.startsWith(Bearer )) { String jwt token.substring(7); try { Claims claims Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(jwt) .getBody(); request.setAttribute(userId, claims.get(userId)); return true; } catch (Exception e) { // 校验失败 } } response.setStatus(HttpStatus.UNAUTHORIZED.value()); return false; } }代码的逻辑是请求进入后先放行 OPTIONS 预检请求然后再从请求头取Authorization截掉Bearer这段前缀得到 JWT 本体用密钥解析。解析失败说明 token 过期或被人篡改直接返回 401。request.setAttribute是为了把当前登录用户的 id 透传给后续 Controller避免在业务代码里重复解析 token。这个拦截器要生效还需要注册到 WebMvc 配置里Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new JwtInterceptor()) .addPathPatterns(/api/**) .excludePathPatterns(/api/auth/login, /api/goods/list); } }addPathPatterns(/api/**)拦截所有以/api/开头的接口登录和商品列表是游客也能访问的所以要排除。注意excludePathPatterns里最好把前端轮播图、公告这类公开接口也放进去否则首页打开会黑屏。4.2 商品发布中的文件上传与图片回显商品发布页面通常需要一个富文本编辑器和一个图片上传按钮。后端的 Controller 接收图片后要把图片存到本地磁盘并在数据库里保存可访问的 URL。下面是一个常见的上传接口PostMapping(/api/goods/upload) public Result upload(RequestParam(file) MultipartFile file, HttpServletRequest request) { String originalFilename file.getOriginalFilename(); String ext originalFilename.substring(originalFilename.lastIndexOf(.)); String fileName UUID.randomUUID().toString().replace(-, ) ext; String savePath D:/upload/ fileName; File dest new File(savePath); if (!dest.getParentFile().exists()) { dest.getParentFile().mkdirs(); } file.transferTo(dest); String url /upload/ fileName; return Result.success(url); }逻辑说明先取出原始文件名并截取扩展名防止用户上传.jsp或.exe这类危险文件再用 UUID 拼出新文件名避免重名覆盖。transferTo是 Spring MVC 提供的快捷方法底层通过流拷贝完成上传。这里我把保存路径固定为D:/upload/实际项目中一般放到服务器专用目录。但图片上传后浏览器要能通过 URL 访问到磁盘文件必须配置静态资源映射Configuration public class ResourceConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file:D:/upload/); } }addResourceHandler定义 URL 前缀addResourceLocations指定磁盘目录。file:前缀是必须的表示读取本地文件而不是 classpath。两个方法保证/upload/xxx.jpg能映射到D:/upload/xxx.jpg。这里有个细节如果服务器是 Linux路径要改成/home/admin/upload/Windows 和 Linux 的目录结构差异会导致图片上传成功但回显失败。4.3 订单状态机的枚举与事务控制二手交易平台订单的状态不能简单用一个字符串存因为业务状态是有前置条件的。推荐用枚举加状态机先定义订单状态public enum OrderStatus { PENDING_PAYMENT(0, 待付款), PENDING_DELIVERY(1, 待发货), PENDING_RECEIPT(2, 待收货), COMPLETED(3, 已完成), CANCELED(4, 已取消); }然后在一个订单状态表中定义允许的转换路径当前状态允许转换到触发动作待付款待发货 / 已取消用户支付 / 超时取消待发货待收货 / 已取消卖家发货 / 买家申请退款待收货已完成买家确认收货已取消无终态已完成无终态为什么需要这张表因为如果代码里到处写if (1.equals(order.getStatus()))两个开发各自加状态时会覆盖对方的判断。用状态机枚举后可以这样校验public void confirmReceive(Integer orderId, Integer userId) { Order order orderMapper.selectById(orderId); if (order null || !order.getBuyerId().equals(userId)) { throw new BusinessException(订单不存在); } if (order.getStatus() ! OrderStatus.PENDING_RECEIPT.getCode()) { throw new BusinessException(当前状态不能确认收货); } order.setStatus(OrderStatus.COMPLETED.getCode()); orderMapper.updateById(order); }这段代码里先校验订单归属再校验状态是否等于待收货最后才更新为已完成。状态判断用还是equals取决于枚举的写法这里取的是枚举中的 int 值所以用!做比较没有问题。整个操作要加Transactional因为确认收货后一般还要更新卖家账户余额、生成或修改交易记录这些操作必须打包成一个事务。事务控制的常见误区是只在 Mapper 层加Transactional。正确做法是加在 Service 实现类上且要避免在一个类内部通过this调用带事务的方法否则 Spring AOP 代理不生效。这一点在答辩时经常被追问属于“看着简单但现场容易翻车”的知识点。5. 答辩高频追问heapdump 泄露、定时下架与本地验证5.1 Actuator heapdump 敏感信息泄露修复毕业设计如果引入了spring-boot-starter-actuator默认不会暴露heapdump但很多人为了看监控信息会写成exposure.include: *这一下就把heapdump和shutdown端点暴露了。攻击者访问http://localhost:8080/actuator/heapdump下载内存快照用 MAT 或 Eclipse Memory Analyzer 打开就能从堆里提取到数据库密码和 JWT 的 secret。这个漏洞在 springboot 面试题里也是高频考点。建议把配置收敛成management: endpoints: web: exposure: include: health,info endpoint: health: show-details: never这样生产环境只开放health和info。health探活info可以放项目版本、作者信息其他监控端点一律不暴露。如果确实需要查看内存信息可以研究 JDK 自带的jmap命令它在宕机或性能排查时比 heapdump 端点更安全因为不经过 HTTP 网络。5.2 商品到期自动下架的 Scheduled 实现二手交易和商城不一样商品有明确的展示结束时间。定时任务最常见的实现是 Spring 自带的Scheduled。启动类上加EnableScheduling开启任务调度然后在任务类中写一个方法Component public class GoodsExpireTask { Scheduled(cron 0 30 2 * * ?) public void autoOffShelf() { goodsService.autoOffShelf(); } }cron 表达式0 30 2 * * ?表示每天凌晨 2 点 30 分执行一次。为什么选这个时间因为二手交易平台白天的操作量大凌晨触发可以避开数据库高峰也符合“商品到期当天自动下架”的业务预期。对应的 SQL 是UPDATE goods SET status 0 WHERE status 1 AND end_time NOW();这里的status1表示上架中status0表示下架。直接一条 SQL 全表更新在小项目里没有问题但如果你想把下架记录写入日志表最好在 Java 里查出过期商品 id循环处理并记录日志否则排错时没有依据。定时任务的缺点是不可控如果服务器在凌晨 2 点关机任务永远不会执行所以偶尔也要看一眼日志确认执行时间。5.3 用一条命令验证整套交易流程答辩前最怕的不是功能缺失而是整套流程演示到一半崩了。建议提前用 curl 做一次接口级验证而不是只靠浏览器点击。启动后端后先请求登录接口curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456}这段命令会返回一个 JSON里面包含 JWT token。拿这个 token 去请求商品列表接口curl -H Authorization: Bearer token \ http://localhost:8080/api/goods?page1limit10如果返回商品 JSON 数组说明数据库连接、Mapper 映射、拦截器逻辑都正常。此时再打开浏览器进入管理后台基本不会出现白屏或列表加载失败。如果登录接口报 401优先检查excludePathPatterns是否把登录接口排除如果商品列表为空检查数据库goods表里有没有初始化数据。整个项目最值得优化的地方不是加更多功能而是把启动流程收敛成两条命令一条安装依赖一条启动服务。你把这套逻辑讲清楚老师看到的就不是“复制来的源码”而是一个能从环境到数据落地的完整工程。本文还有配套的精品资源点击获取
返回列表