从零搭建校园二手交易系统:Vue2 + SpringBoot 4.x 全栈实战

文章目录

    • [📖 前言](#📖 前言)
    • [🎯 项目介绍](#🎯 项目介绍)
    • [🛠 技术栈](#🛠 技术栈)
    • [🏗 系统架构](#🏗 系统架构)
    • [💾 数据库设计](#💾 数据库设计)
      • [user 用户表](#user 用户表)
      • [product 商品表](#product 商品表)
      • [orders 订单表](#orders 订单表)
    • [🔧 关键技术点](#🔧 关键技术点)
      • [1. 组件化开发](#1. 组件化开发)
      • [2. Vue Router 路由守卫](#2. Vue Router 路由守卫)
      • [3. Axios 二次封装](#3. Axios 二次封装)
      • [4. Spring Data JPA 方法名自动生成 SQL](#4. Spring Data JPA 方法名自动生成 SQL)
      • [5. 统一响应结果](#5. 统一响应结果)
      • [6. 全局异常处理](#6. 全局异常处理)
    • [🐛 踩坑记录](#🐛 踩坑记录)
      • [坑 1:SpringBoot 4.x 不支持 MyBatis-Plus](#坑 1:SpringBoot 4.x 不支持 MyBatis-Plus)
      • [坑 2:MySQL 保留字 order](#坑 2:MySQL 保留字 order)
      • [坑 3:Git 子模块问题](#坑 3:Git 子模块问题)
      • [坑 4:Vue Router 部署到 Pages 后刷新 404](#坑 4:Vue Router 部署到 Pages 后刷新 404)
      • [坑 5:JPA 分页页码从 0 开始](#坑 5:JPA 分页页码从 0 开始)
    • [🚀 部署方案](#🚀 部署方案)
    • [📊 项目总结](#📊 项目总结)
    • [🔗 项目地址](#🔗 项目地址)

📖 前言

最近花了一周时间做了一个校园二手交易系统,从数据库设计到前端 UI,从后端接口到部署上线,完整走了一遍前后端分离的开发流程。这篇文章记录一下整个项目的技术选型、架构设计和踩坑经验,希望能给做类似项目的同学一些参考。

项目地址:


🎯 项目介绍

一个面向校园场景的二手物品交易平台,支持用户注册登录、商品发布、在线下单、订单管理等功能。

核心功能

  • 👤 用户模块:注册、登录、个人中心
  • 📦 商品模块:发布商品、商品列表、关键词搜索、分页展示
  • 🛒 订单模块:在线下单、订单列表、状态管理
  • 🔒 权限控制:Vue Router 路由守卫,未登录自动跳转

界面预览


🛠 技术栈

分类 技术
前端框架 Vue 2.6 + Vue Router 3
UI 组件库 ElementUI 2.15
HTTP 客户端 Axios 1.x
后端框架 SpringBoot 4.1 + Spring Data JPA
数据库 MySQL 9.4
工具库 Lombok
部署 GitHub Pages + GitHub Actions

🏗 系统架构

复制代码
┌─────────────┐         ┌─────────────┐         ┌─────────────┐
│   浏览器    │  HTTP   │  SpringBoot │   JPA   │    MySQL    │
│  (Vue2 SPA) │ ──────► │   后端服务  │ ──────► │   数据库    │
└─────────────┘         └─────────────┘         └─────────────┘
      │                        │
      │  ElementUI             │  Controller
      │  Vue Router            │  Service
      │  Axios                 │  Repository

项目结构

复制代码
campus-trade/
├── campus-trade-backend/          # 后端
│   └── src/main/java/com/campus/trade/
│       ├── common/                # 通用(Result、异常处理)
│       ├── config/                # 配置(跨域)
│       ├── controller/            # 接口层
│       ├── dto/                   # 数据传输对象
│       ├── entity/                # 实体类
│       ├── repository/            # 数据访问层
│       └── service/               # 业务逻辑层
└── campus-trade-frontend/         # 前端
    └── src/
        ├── api/                   # 接口封装
        ├── components/            # 复用组件
        ├── router/                # 路由配置
        ├── utils/                 # 工具(Axios 封装)
        └── views/                 # 页面

💾 数据库设计

user 用户表

字段 类型 说明
id BIGINT 主键
username VARCHAR(50) 用户名(唯一)
password VARCHAR(100) 密码
nickname VARCHAR(50) 昵称
phone VARCHAR(20) 手机号
create_time DATETIME 注册时间

product 商品表

字段 类型 说明
id BIGINT 主键
title VARCHAR(100) 商品标题
description VARCHAR(500) 描述
price DECIMAL(10,2) 价格
category VARCHAR(50) 分类
status TINYINT 0在售 1已售
seller_id BIGINT 卖家 ID
create_time DATETIME 发布时间

orders 订单表

字段 类型 说明
id BIGINT 主键
order_no VARCHAR(50) 订单号(唯一)
product_id BIGINT 商品 ID
buyer_id BIGINT 买家 ID
seller_id BIGINT 卖家 ID
amount DECIMAL(10,2) 金额
status TINYINT 0待付款 1已付款 2已完成
create_time DATETIME 下单时间

💡 踩坑 :订单表名用 orders 而不是 order,因为 order 是 MySQL 保留字,用 JPA 会报错。


🔧 关键技术点

1. 组件化开发

前端拆分为 7 个页面 + 1 个复用组件:

复制代码
views/                    # 页面级组件
├── Layout.vue            # 主布局(顶栏 + 内容区)
├── Login.vue             # 登录页
├── Register.vue          # 注册页
├── ProductList.vue       # 商品列表
├── ProductPublish.vue    # 发布商品
├── OrderManage.vue       # 订单管理
└── UserCenter.vue        # 个人中心

components/               # 复用组件
└── ProductCard.vue       # 商品卡片

ProductCard.vue 通过 props 接收商品数据,通过 $emit 触发购买事件,在多处复用。

2. Vue Router 路由守卫

javascript 复制代码
router.beforeEach((to, from, next) => {
  const user = localStorage.getItem('user')
  const publicPaths = ['/login', '/register']
  if (!publicPaths.includes(to.path) && !user) {
    next('/login')       // 未登录跳登录
  } else {
    next()
  }
})

3. Axios 二次封装

统一处理请求拦截、响应错误、Loading 状态:

javascript 复制代码
// 请求拦截:开启 Loading + 带用户 ID
service.interceptors.request.use(config => {
  loadingInstance = Loading.service({ fullscreen: true })
  const user = JSON.parse(localStorage.getItem('user') || 'null')
  if (user) config.headers['X-User-Id'] = user.id
  return config
})

// 响应拦截:统一处理错误 + 解包 data
service.interceptors.response.use(res => {
  loadingInstance && loadingInstance.close()
  const data = res.data
  if (data.code !== 200) {
    Message.error(data.msg)
    return Promise.reject(new Error(data.msg))
  }
  return data.data
})

4. Spring Data JPA 方法名自动生成 SQL

Repository 只需声明方法名,JPA 自动生成 SQL:

java 复制代码
public interface ProductRepository extends JpaRepository<Product, Long> {
    // 自动生成:SELECT * FROM product WHERE status=? ORDER BY create_time DESC LIMIT ...
    Page<Product> findByStatusOrderByCreateTimeDesc(Integer status, Pageable pageable);
    
    // 自动生成:SELECT * FROM product WHERE status=? AND title LIKE %?% ORDER BY create_time DESC
    Page<Product> findByStatusAndTitleContainingOrderByCreateTimeDesc(
            Integer status, String title, Pageable pageable);
}

一行 SQL 都不用写,方法名就是 SQL 语义。

5. 统一响应结果

java 复制代码
@Data
public class Result<T> {
    private Integer code;
    private String msg;
    private T data;
    
    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.code = 200; r.msg = "success"; r.data = data;
        return r;
    }
    
    public static <T> Result<T> error(String msg) {
        Result<T> r = new Result<>();
        r.code = 500; r.msg = msg;
        return r;
    }
}

6. 全局异常处理

java 复制代码
@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(RuntimeException.class)
    public Result<?> handleRuntime(RuntimeException e) {
        return Result.error(e.getMessage());
    }
}

Service 层直接 throw new RuntimeException("商品不存在"),前端收到的就是 {code: 500, msg: "商品不存在"}。


🐛 踩坑记录

坑 1:SpringBoot 4.x 不支持 MyBatis-Plus

现象:用 Spring Initializr 生成项目时,最低只能选 SpringBoot 4.x,但 MyBatis-Plus 3.5.x 只支持到 SpringBoot 3.x。

解决 :改用 Spring Data JPA(SpringBoot 官方亲儿子),比 MyBatis-Plus 更贴合 4.x。

坑 2:MySQL 保留字 order

现象 :实体类 @Table(name = "order") 启动报错。

解决 :表名改成 orders,实体类用 @Table(name = "orders")。

坑 3:Git 子模块问题

现象 :在 monorepo 里推送后,GitHub Actions 报 No url found for submodule path。

原因 :子目录里残留了 .git 文件夹,被 Git 误判为子模块。

解决 :删除子目录的 .git,并 git rm --cached 后重新 git add。

坑 4:Vue Router 部署到 Pages 后刷新 404

解决 :路由模式改成 hash(URL 带 #),Pages 静态托管就能正确处理。

坑 5:JPA 分页页码从 0 开始

现象 :前端传 pageNo=1,JPA 显示的还是第一页(其实是第 0 页)。

解决 :Service 层做 pageNo - 1,前端始终从 1 开始,用户无感。


🚀 部署方案

后端

SpringBoot 打包成 jar,java -jar xxx.jar 运行,或部署到云服务器。

前端

GitHub Pages + GitHub Actions 自动化部署:

每次 git push 到 main 分支,GitHub Actions 自动:

  1. 检出代码
  2. 安装依赖
  3. npm run build
  4. 上传 dist 到 Pages

工作流配置(.github/workflows/deploy-pages.yml):

yaml 复制代码
name: Deploy to GitHub Pages
on:
  push:
    branches: [main]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: '18' }
      - run: npm install
        working-directory: ./campus-trade-frontend
      - run: npm run build
        working-directory: ./campus-trade-frontend
      - uses: actions/upload-pages-artifact@v3
        with:
          path: ./campus-trade-frontend/dist
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
    steps:
      - uses: actions/deploy-pages@v4

📊 项目总结

完成度

  • ✅ 用户注册 / 登录
  • ✅ 商品发布 / 列表 / 搜索 / 分页
  • ✅ 在线下单 / 订单管理
  • ✅ 统一异常处理
  • ✅ 跨域配置
  • ✅ GitHub Pages 自动化部署

学到的

  1. 前后端分离:RESTful API 设计,Axios 统一封装
  2. Vue 组件化:props 传递 + $emit 事件
  3. JPA 方法命名:优雅地避免写 SQL
  4. GitHub Actions:CI/CD 自动化部署
  5. Monorepo:前后端一个仓库,管理方便

后续计划

  • JWT 登录态(替代 localStorage 明文)
  • 图片上传(本地磁盘 / 阿里云 OSS)
  • 订单状态流转(付款 / 收货 / 完成)
  • WebSocket 实时消息通知
  • 卖家视角(我的发布 / 卖出订单)
  • ECharts 数据看板

🔗 项目地址

如果这个项目对你有帮助,欢迎给个 ⭐ Star!也欢迎在评论区交流。


作者 :dxiangwiki

日期:2026-10

相关推荐
省钱兄--zs1 小时前
碰碰好评卡系统开发实战指南 核心功能实现与部署落地全流程拆解
数据仓库·经验分享·spring boot·小程序·架构
lexiangqicheng1 小时前
1,自定义表单通俗理解-PC设计APP渲染
java·前端·javascript
南极企鹅1 小时前
生产环境gc文件分析
java·jvm·算法
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 前后端分离的工程实践
java·spring cloud·架构
Lost of 程序猿1 小时前
责任链模式实战:审批流里“层层把关“的艺术
后端·设计模式·asp.net·责任链模式
溪语流沙2 小时前
面试加分项!Manacher:O(n)求最长回文子串的对称之美
java·python·leetcode·面试·职场和发展
知守观2 小时前
Maven SNAPSHOT 更新排查:-U 无效、clean 无用,问题在 _remote.repositories(附完整链路)
java·后端·maven
牛战士coding2 小时前
从一句\“取消订单\“到退款通知:AI 写操作完整业务闭环
java·人工智能