文章目录
-
- [📖 前言](#📖 前言)
- [🎯 项目介绍](#🎯 项目介绍)
- [🛠 技术栈](#🛠 技术栈)
- [🏗 系统架构](#🏗 系统架构)
- [💾 数据库设计](#💾 数据库设计)
-
- [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,从后端接口到部署上线,完整走了一遍前后端分离的开发流程。这篇文章记录一下整个项目的技术选型、架构设计和踩坑经验,希望能给做类似项目的同学一些参考。
项目地址:
- 🌐 GitHub:https://github.com/dxiangwiki/campus-trade
- 🚀 在线演示:https://dxiangwiki.github.io/campus-trade/
🎯 项目介绍
一个面向校园场景的二手物品交易平台,支持用户注册登录、商品发布、在线下单、订单管理等功能。
核心功能
- 👤 用户模块:注册、登录、个人中心
- 📦 商品模块:发布商品、商品列表、关键词搜索、分页展示
- 🛒 订单模块:在线下单、订单列表、状态管理
- 🔒 权限控制: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 自动:
- 检出代码
- 安装依赖
npm run build- 上传 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 自动化部署
学到的
- 前后端分离:RESTful API 设计,Axios 统一封装
- Vue 组件化:props 传递 + $emit 事件
- JPA 方法命名:优雅地避免写 SQL
- GitHub Actions:CI/CD 自动化部署
- Monorepo:前后端一个仓库,管理方便
后续计划
- JWT 登录态(替代 localStorage 明文)
- 图片上传(本地磁盘 / 阿里云 OSS)
- 订单状态流转(付款 / 收货 / 完成)
- WebSocket 实时消息通知
- 卖家视角(我的发布 / 卖出订单)
- ECharts 数据看板
🔗 项目地址
- GitHub:https://github.com/dxiangwiki/campus-trade
- 在线演示:https://dxiangwiki.github.io/campus-trade/
- 技术栈:Vue2 + ElementUI + Axios + SpringBoot 4.x + JPA + MySQL
如果这个项目对你有帮助,欢迎给个 ⭐ Star!也欢迎在评论区交流。
作者 :dxiangwiki
日期:2026-10