Vue3 + Spring Boot3 全栈实战:从零搭建商用级图书商城系统
本文详细介绍一个基于 Vue 3 + Spring Boot 3 的完整图书商城项目,包含用户端、商家端、管理后台三大模块,支持 Docker 一键部署,是一个真正可商用的电商模板级项目。
一、项目简介
1.1 项目背景
在电商领域,图书商城是一个经典的业务场景。本项目 BookMall 是一个功能完善的在线图书销售平台,采用当前主流的前后端分离架构,涵盖了电商系统的核心功能模块,非常适合作为学习全栈开发的实战项目,也可以直接用于商业项目的快速启动。
1.2 功能概览
用户端(前台)功能:
- 图书浏览:列表展示、搜索、排序、详情查看
- 店铺页:查看店铺信息与商品列表
- 购物车:添加、删除、修改数量、结算
- 订单管理:创建订单、查看订单列表/详情、发起支付、确认收货、取消订单
- 个人中心:基础信息、收货地址管理、发票申请
- 内容系统:文章/公告列表与详情、CMS 页面
- 活动页:促销活动、运营页面入口


商家端功能:
- 商家登录/注册
- 商品管理:上架/下架、编辑商品信息
- 订单管理:查看/处理订单
- 售后管理:售后列表与处理
- 发票管理:开票与记录查询
- 店铺设置:店铺资料维护

管理后台功能:
- 管理员登录与仪表盘
- 图书管理/审核、分类管理
- 订单管理、售后管理
- 用户管理、商家管理/审核
- 运营能力:优惠券、秒杀、消息推送、Banner、内容管理

二、技术栈详解
2.1 前端技术栈
| 技术 | 版本 | 说明 |
|---|---|---|
| Vue | 3.x | 渐进式 JavaScript 框架,使用组合式 API |
| Vite | 5.0 | 新一代前端构建工具,开发体验极佳 |
| Vue Router | 4.x | 官方路由管理器 |
| Pinia | 2.x | 新一代状态管理库,替代 Vuex |
| Element Plus | 2.4.x | 基于 Vue 3 的 UI 组件库 |
| Axios | 1.6.x | HTTP 请求库 |
| DOMPurify | - | XSS 攻击防护,对富文本做安全清洗 |
| ECharts | 5.x | 数据可视化图表库 |
2.2 后端技术栈
| 技术 | 版本 | 说明 |
|---|---|---|
| Java | 17 | LTS 长期支持版本 |
| Spring Boot | 3.2.x | 主流 Java 开发框架 |
| Spring Security | 6.x | 安全认证与授权框架 |
| JWT | - | JSON Web Token 无状态认证 |
| MyBatis-Plus | 3.5.5 | MyBatis 增强工具,简化 CRUD |
| MySQL | 8.0 | 主业务关系型数据库 |
| Redis | 7.0 | 缓存、会话管理 |
| RabbitMQ | 3.x | 消息队列,异步处理订单等 |
| Elasticsearch | 8.11 | 全文搜索引擎(可选) |
| MongoDB | 6.x | 用户行为日志存储(可选) |
| Knife4j | 4.3.0 | API 文档生成工具 |
2.3 部署运维
| 技术 | 说明 |
|---|---|
| Docker | 容器化部署 |
| Docker Compose | 多容器编排 |
| Nginx | 反向代理与静态资源托管 |
| Let’s Encrypt + Certbot | HTTPS 证书自动签发与续期 |
三、项目结构
BookMall/
├── bookmall-backend/ # 后端(Spring Boot)
│ ├── src/main/java/com/bookmall/
│ │ ├── controller/ # 控制器层(用户端/管理端/商家端)
│ │ ├── service/ # 业务逻辑层
│ │ ├── mapper/ # 数据访问层
│ │ ├── entity/ # 实体类
│ │ ├── dto/ # 数据传输对象
│ │ ├── vo/ # 视图对象
│ │ ├── config/ # 配置类
│ │ ├── security/ # 安全相关
│ │ ├── exception/ # 异常处理
│ │ ├── mq/ # 消息队列
│ │ └── utils/ # 工具类
│ └── src/main/resources/
│ ├── application.yml # 主配置文件
│ ├── application-prod.yml # 生产环境配置
│ └── schema.sql # 数据库初始化脚本
│
├── bookmall-frontend/ # 前端(Vue 3 + Vite)
│ ├── src/
│ │ ├── api/ # API 请求模块
│ │ ├── router/ # 路由配置
│ │ ├── store/ # Pinia 状态管理
│ │ ├── views/ # 页面组件
│ │ │ ├── admin/ # 管理后台页面
│ │ │ ├── seller/ # 商家端页面
│ │ │ └── user/ # 用户端页面
│ │ ├── utils/ # 工具函数
│ │ └── assets/ # 静态资源
│ └── vite.config.js # Vite 配置
│
├── nginx/ # Nginx 配置
├── docker-compose.yml # Docker 编排配置
├── docker-compose.prod.yml # 生产环境配置
├── deploy.sh # Linux 部署脚本
└── deploy.ps1 # Windows 部署脚本
四、系统架构设计
4.1 整体架构图
┌─────────────────────────────────────────────────────────────────┐
│ 用户层 │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 用户端 │ │ 商家端 │ │ 管理后台 │ │
│ │ (Vue 3) │ │ (Vue 3) │ │ (Vue 3) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Nginx 网关层 │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ 反向代理 │ 负载均衡 │ 静态资源 │ SSL/HTTPS │ 路由分发 │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 应用服务层(Spring Boot) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Controller │─▶│ Service │─▶│ Mapper │ │
│ │ 控制器 │ │ 业务层 │ │ 数据访问 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Security │ │ MQ │ │
│ │ JWT认证 │ │ 消息队列 │ │
│ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 数据存储层 │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ MySQL │ │ Redis │ │ ES │ │ MongoDB │ │
│ │ 业务数据 │ │ 缓存 │ │ 全文搜索 │ │ 行为日志 │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
4.2 认证授权流程
本项目采用 JWT(JSON Web Token) 实现无状态认证:
┌──────────┐ 1. 登录请求 ┌──────────┐
│ │ ──────────────────────▶ │ │
│ 前端 │ │ 后端 │
│ Vue 3 │ ◀────────────────────── │ Spring │
│ │ 2. 返回 JWT Token │ Boot │
└──────────┘ └──────────┘
│
│ 3. 存储 Token 到 localStorage
▼
┌──────────┐
│ 后续请求 │ ────▶ Authorization: Bearer {token}
└──────────┘
│
▼
┌──────────────────────────────────────────────────┐
│ JwtAuthenticationFilter │
│ 1. 提取 Token │
│ 2. 验证签名和过期时间 │
│ 3. 解析用户信息 │
│ 4. 设置 SecurityContext │
└──────────────────────────────────────────────────┘
4.3 消息队列架构
使用 RabbitMQ 实现订单异步处理:
┌─────────────────────────────────────────────────────────────────┐
│ RabbitMQ 消息流转 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 订单交换机 (order.exchange) │
│ ├── order.create.queue ──▶ 订单创建事件处理 │
│ ├── order.pay.queue ──▶ 支付完成,更新状态 │
│ ├── order.cancel.queue ──▶ 订单取消,库存恢复 │
│ └── order.delay.queue ──▶ 超时未支付自动取消(死信队列) │
│ │
│ 库存交换机 (stock.exchange) │
│ ├── stock.deduct.queue ──▶ 库存扣减 │
│ └── stock.restore.queue ──▶ 库存恢复 │
│ │
│ 通知交换机 (notification.exchange) │
│ └── notification.queue ──▶ 站内消息、邮件通知 │
│ │
└─────────────────────────────────────────────────────────────────┘
五、核心模块实现详解
5.1 用户认证模块
5.1.1 登录接口实现
@RestController
@RequestMapping("/api/v1/auth")
public class AuthController {
@Autowired
private AuthService authService;
@PostMapping("/login")
public Result<LoginResponse> login(@RequestBody @Valid LoginRequest request) {
// 1. 验证用户名密码
User user = authService.authenticate(request.getUsername(), request.getPassword());
// 2. 生成 JWT Token
String token = jwtTokenProvider.generateToken(user);
// 3. 返回登录结果
return Result.success(new LoginResponse(token, user));
}
@PostMapping("/register")
public Result<Void> register(@RequestBody @Valid RegisterRequest request) {
authService.register(request);
return Result.success();
}
}
5.1.2 JWT 工具类
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String jwtSecret;
@Value("${jwt.expiration}")
private long jwtExpiration;
public String generateToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpiration);
return Jwts.builder()
.setSubject(String.valueOf(user.getId()))
.claim("username", user.getUsername())
.claim("role", user.getRole())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS256, jwtSecret)
.compact();
}
public Long getUserIdFromToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return Long.parseLong(claims.getSubject());
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
5.1.3 安全配置
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
// 公开接口
.requestMatchers("/api/v1/auth/**").permitAll()
.requestMatchers("/api/v1/books/**").permitAll()
.requestMatchers("/api/v1/categories/**").permitAll()
// 需要认证的接口
.requestMatchers("/api/v1/cart/**").authenticated()
.requestMatchers("/api/v1/orders/**").authenticated()
// 管理员接口
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
// 商家接口
.requestMatchers("/api/v1/seller/**").hasRole("SELLER")
.anyRequest().authenticated()
)
.addFilterBefore(jwtAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
5.2 订单模块
5.2.1 订单创建流程
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Override
public OrderVO createOrder(Long userId, CreateOrderRequest request) {
// 1. 获取购物车商品
List<Cart> cartItems = cartMapper.selectByUserId(userId);
if (cartItems.isEmpty()) {
throw new BusinessException("购物车为空");
}
// 2. 验证库存
for (Cart item : cartItems) {
Book book = bookMapper.selectById(item.getBookId());
if (book.getStock() < item.getQuantity()) {
throw new BusinessException("商品库存不足:" + book.getTitle());
}
}
// 3. 计算订单金额
BigDecimal totalAmount = calculateTotalAmount(cartItems);
BigDecimal discountAmount = calculateDiscount(request.getCouponId(), totalAmount);
BigDecimal payAmount = totalAmount.subtract(discountAmount);
// 4. 创建订单
Order order = new Order();
order.setOrderNo(IdUtil.getSnowflakeNextIdStr());
order.setUserId(userId);
order.setTotalAmount(totalAmount);
order.setPayAmount(payAmount);
order.setStatus(OrderStatus.PENDING_PAYMENT);
order.setCreateTime(LocalDateTime.now());
orderMapper.insert(order);
// 5. 创建订单明细
for (Cart item : cartItems) {
OrderItem orderItem = new OrderItem();
orderItem.setOrderId(order.getId());
orderItem.setBookId(item.getBookId());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(item.getPrice());
orderItemMapper.insert(orderItem);
}
// 6. 清空购物车
cartMapper.deleteByUserId(userId);
// 7. 发送订单创建消息(异步处理)
messageProducer.sendOrderCreated(order.getId());
return convertToVO(order);
}
}
5.2.2 订单状态流转
┌─────────────┐ 支付成功 ┌─────────────┐ 商家发货 ┌─────────────┐
│ 待支付 │ ────────────▶ │ 已支付 │ ────────────▶ │ 已发货 │
│ PENDING │ │ PAID │ │ SHIPPED │
└─────────────┘ └─────────────┘ └─────────────┘
│ │
│ 取消/超时 用户确认收货
▼ ▼
┌─────────────┐ ┌─────────────┐
│ 已取消 │ │ 已完成 │
│ CANCELLED │ │ COMPLETED │
└─────────────┘ └─────────────┘
│
申请售后
▼
┌─────────────┐
│ 售后中 │
│ AFTER_SALE │
└─────────────┘
5.3 支付模块(策略模式)
5.3.1 支付策略接口
public interface PaymentStrategy {
/**
* 发起支付
*/
PaymentResult pay(Order order, BigDecimal amount);
/**
* 验证支付回调
*/
boolean verifyCallback(PaymentCallback callback);
/**
* 退款
*/
RefundResult refund(String paymentId, BigDecimal amount);
}
5.3.2 策略实现
// 支付宝支付(预留接口)
@Component("alipay")
public class AliPayStrategy implements PaymentStrategy {
@Override
public PaymentResult pay(Order order, BigDecimal amount) {
// 调用支付宝 SDK
// 返回支付链接或二维码
return null;
}
}
// 微信支付(预留接口)
@Component("wechatpay")
public class WechatPayStrategy implements PaymentStrategy {
@Override
public PaymentResult pay(Order order, BigDecimal amount) {
// 调用微信支付 SDK
// 返回 JSAPI 支付参数
return null;
}
}
// 余额支付
@Component("balance")
public class BalancePayStrategy implements PaymentStrategy {
@Override
public PaymentResult pay(Order order, BigDecimal amount) {
// 检查用户余额
// 扣减余额
// 记录交易流水
return PaymentResult.success();
}
}
5.3.3 支付服务
@Service
public class PaymentServiceImpl implements PaymentService {
@Autowired
private Map<String, PaymentStrategy> strategyMap;
@Override
public PaymentResult pay(Long orderId, String paymentMethod) {
Order order = orderMapper.selectById(orderId);
// 选择支付策略
PaymentStrategy strategy = strategyMap.get(paymentMethod);
if (strategy == null) {
throw new BusinessException("不支持的支付方式");
}
// 执行支付
PaymentResult result = strategy.pay(order, order.getPayAmount());
if (result.isSuccess()) {
// 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPayTime(LocalDateTime.now());
orderMapper.updateById(order);
// 发送消息
messageProducer.sendOrderPaid(orderId);
}
return result;
}
}
5.4 购物车模块
5.4.1 前端状态管理(Pinia)
// store/cart.js
import { defineStore } from 'pinia'
import { getCart, addToCart, updateCartItem, removeFromCart } from '@/api/cart'
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
loading: false
}),
getters: {
// 已选商品
selectedItems: (state) => state.items.filter(item => item.selected),
// 总数量
totalCount: (state) => state.items.reduce((sum, item) => sum + item.quantity, 0),
// 总金额
totalPrice: (state) => {
return state.items
.filter(item => item.selected)
.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
},
actions: {
// 获取购物车
async fetchCart() {
this.loading = true
try {
const res = await getCart()
this.items = res.data
} finally {
this.loading = false
}
},
// 添加商品
async addItem(bookId, quantity = 1) {
await addToCart({ bookId, quantity })
await this.fetchCart()
},
// 更新数量
async updateQuantity(itemId, quantity) {
await updateCartItem(itemId, { quantity })
const item = this.items.find(i => i.id === itemId)
if (item) item.quantity = quantity
},
// 删除商品
async removeItem(itemId) {
await removeFromCart(itemId)
this.items = this.items.filter(i => i.id !== itemId)
},
// 切换选中状态
toggleSelect(itemId) {
const item = this.items.find(i => i.id === itemId)
if (item) item.selected = !item.selected
},
// 全选/取消全选
selectAll(selected) {
this.items.forEach(item => item.selected = selected)
}
}
})
5.4.2 购物车页面
<!-- views/Cart.vue -->
<template>
<div class="cart-page">
<el-table :data="cartStore.items" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="商品信息" min-width="300">
<template #default="{ row }">
<div class="book-info">
<img :src="row.coverImage" class="cover" />
<div class="details">
<h4>{{ row.title }}</h4>
<p class="author">{{ row.author }}</p>
</div>
</div>
</template>
</el-table-column>
<el-table-column label="单价" width="120">
<template #default="{ row }">
<span class="price">¥{{ row.price.toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="数量" width="150">
<template #default="{ row }">
<el-input-number
v-model="row.quantity"
:min="1"
:max="row.stock"
@change="(val) => handleQuantityChange(row.id, val)"
/>
</template>
</el-table-column>
<el-table-column label="小计" width="120">
<template #default="{ row }">
<span class="subtotal">¥{{ (row.price * row.quantity).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template #default="{ row }">
<el-button type="danger" text @click="handleRemove(row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 结算栏 -->
<div class="checkout-bar">
<div class="total">
已选 {{ cartStore.selectedItems.length }} 件,合计:
<span class="amount">¥{{ cartStore.totalPrice.toFixed(2) }}</span>
</div>
<el-button type="primary" size="large" @click="handleCheckout">
去结算
</el-button>
</div>
</div>
</template>
<script setup>
import { useCartStore } from '@/store/cart'
import { useRouter } from 'vue-router'
const cartStore = useCartStore()
const router = useRouter()
// 初始化获取购物车
cartStore.fetchCart()
const handleQuantityChange = (id, quantity) => {
cartStore.updateQuantity(id, quantity)
}
const handleRemove = (id) => {
cartStore.removeItem(id)
}
const handleCheckout = () => {
if (cartStore.selectedItems.length === 0) {
ElMessage.warning('请选择要结算的商品')
return
}
router.push('/checkout')
}
</script>
六、数据库设计
6.1 核心表结构
用户表(user)
CREATE TABLE `user` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`username` VARCHAR(50) NOT NULL UNIQUE COMMENT '用户名',
`password` VARCHAR(255) NOT NULL COMMENT '密码(BCrypt加密)',
`nickname` VARCHAR(50) COMMENT '昵称',
`email` VARCHAR(100) COMMENT '邮箱',
`phone` VARCHAR(20) COMMENT '手机号',
`avatar` VARCHAR(255) COMMENT '头像URL',
`role` TINYINT DEFAULT 0 COMMENT '角色:0-普通用户,1-管理员',
`status` TINYINT DEFAULT 1 COMMENT '状态:0-禁用,1-正常',
`points` INT DEFAULT 0 COMMENT '积分',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_username` (`username`),
INDEX `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
图书表(book)
CREATE TABLE `book` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`isbn` VARCHAR(20) UNIQUE COMMENT 'ISBN号',
`title` VARCHAR(200) NOT NULL COMMENT '书名',
`author` VARCHAR(100) COMMENT '作者',
`publisher` VARCHAR(100) COMMENT '出版社',
`publish_date` DATE COMMENT '出版日期',
`category_id` BIGINT COMMENT '分类ID',
`seller_id` BIGINT COMMENT '商家ID',
`price` DECIMAL(10,2) NOT NULL COMMENT '原价',
`discount_price` DECIMAL(10,2) COMMENT '促销价',
`stock` INT DEFAULT 0 COMMENT '库存',
`sales` INT DEFAULT 0 COMMENT '销量',
`cover_image` VARCHAR(255) COMMENT '封面图',
`images` TEXT COMMENT '详情图(JSON数组)',
`description` TEXT COMMENT '简介',
`status` TINYINT DEFAULT 0 COMMENT '状态:0-下架,1-上架',
`audit_status` TINYINT DEFAULT 0 COMMENT '审核:0-待审,1-通过,2-拒绝',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_category` (`category_id`),
INDEX `idx_seller` (`seller_id`),
INDEX `idx_title` (`title`),
FULLTEXT INDEX `ft_title_author` (`title`, `author`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='图书表';
订单表(orders)
CREATE TABLE `orders` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`order_no` VARCHAR(32) NOT NULL UNIQUE COMMENT '订单编号',
`user_id` BIGINT NOT NULL COMMENT '用户ID',
`total_amount` DECIMAL(10,2) NOT NULL COMMENT '商品总价',
`freight` DECIMAL(10,2) DEFAULT 0 COMMENT '运费',
`coupon_id` BIGINT COMMENT '优惠券ID',
`coupon_amount` DECIMAL(10,2) DEFAULT 0 COMMENT '优惠金额',
`points_used` INT DEFAULT 0 COMMENT '使用积分',
`points_amount` DECIMAL(10,2) DEFAULT 0 COMMENT '积分抵扣',
`pay_amount` DECIMAL(10,2) NOT NULL COMMENT '实付金额',
`status` TINYINT DEFAULT 0 COMMENT '状态:0-待支付,1-已支付,2-已发货,3-已完成,4-已取消',
`receiver_name` VARCHAR(50) COMMENT '收货人',
`receiver_phone` VARCHAR(20) COMMENT '收货电话',
`receiver_address` VARCHAR(255) COMMENT '收货地址',
`pay_type` TINYINT COMMENT '支付方式:1-支付宝,2-微信,3-余额',
`pay_time` DATETIME COMMENT '支付时间',
`ship_time` DATETIME COMMENT '发货时间',
`receive_time` DATETIME COMMENT '收货时间',
`express_company` VARCHAR(50) COMMENT '快递公司',
`tracking_no` VARCHAR(50) COMMENT '快递单号',
`remark` VARCHAR(500) COMMENT '订单备注',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_user` (`user_id`),
INDEX `idx_status` (`status`),
INDEX `idx_create_time` (`create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
6.2 E-R 关系图
┌──────────┐ 1:N ┌──────────┐ N:1 ┌──────────┐
│ User │─────────────▶│ Order │◀─────────────│ Book │
└──────────┘ └──────────┘ └──────────┘
│ │ │
│ 1:N │ 1:N │ N:1
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Address │ │OrderItem │ │ Category │
└──────────┘ └──────────┘ └──────────┘
│
│ 1:N
▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Cart │ │ Coupon │──────────────│UserCoupon│
└──────────┘ └──────────┘ 1:N └──────────┘
七、API 接口设计
7.1 RESTful API 规范
本项目遵循 RESTful API 设计规范:
| HTTP 方法 | 操作 | 示例 |
|---|---|---|
| GET | 查询资源 | GET /api/v1/books |
| POST | 创建资源 | POST /api/v1/orders |
| PUT | 更新资源 | PUT /api/v1/books/{id} |
| DELETE | 删除资源 | DELETE /api/v1/cart/items/{id} |
7.2 统一响应格式
public class Result<T> {
private int code; // 状态码:200成功,其他失败
private String message; // 提示信息
private T data; // 响应数据
public static <T> Result<T> success(T data) {
return new Result<>(200, "success", data);
}
public static <T> Result<T> error(int code, String message) {
return new Result<>(code, message, null);
}
}
7.3 核心接口列表
认证模块
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/v1/auth/login | POST | 用户登录 |
| /api/v1/auth/register | POST | 用户注册 |
| /api/v1/auth/logout | POST | 退出登录 |
图书模块
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/v1/books | GET | 图书列表(分页) |
| /api/v1/books/{id} | GET | 图书详情 |
| /api/v1/books/search | GET | 搜索图书 |
| /api/v1/books/hot | GET | 热销图书 |
| /api/v1/categories | GET | 分类列表 |
| /api/v1/categories/tree | GET | 分类树 |
购物车模块
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/v1/cart | GET | 获取购物车 |
| /api/v1/cart/items | POST | 添加商品 |
| /api/v1/cart/items/{id} | PUT | 更新数量 |
| /api/v1/cart/items/{id} | DELETE | 删除商品 |
订单模块
| 接口 | 方法 | 说明 |
|---|---|---|
| /api/v1/orders | POST | 创建订单 |
| /api/v1/orders | GET | 订单列表 |
| /api/v1/orders/{id} | GET | 订单详情 |
| /api/v1/orders/{id}/pay | POST | 支付订单 |
| /api/v1/orders/{id}/cancel | POST | 取消订单 |
| /api/v1/orders/{id}/confirm | POST | 确认收货 |
八、前端路由设计
8.1 路由配置
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/store/user'
const routes = [
// 公开路由
{
path: '/',
component: () => import('@/views/Layout.vue'),
children: [
{ path: '', name: 'Home', component: () => import('@/views/Home.vue') },
{ path: 'books', name: 'BookList', component: () => import('@/views/BookList.vue') },
{ path: 'book/:id', name: 'BookDetail', component: () => import('@/views/BookDetail.vue') },
{ path: 'shop/:id', name: 'ShopDetail', component: () => import('@/views/ShopDetail.vue') },
]
},
// 认证路由
{ path: '/login', name: 'Login', component: () => import('@/views/Login.vue') },
{ path: '/register', name: 'Register', component: () => import('@/views/Register.vue') },
// 需要登录的路由
{
path: '/cart',
name: 'Cart',
component: () => import('@/views/Cart.vue'),
meta: { requiresAuth: true }
},
{
path: '/checkout',
name: 'Checkout',
component: () => import('@/views/Checkout.vue'),
meta: { requiresAuth: true }
},
{
path: '/orders',
name: 'OrderList',
component: () => import('@/views/OrderList.vue'),
meta: { requiresAuth: true }
},
// 管理后台路由
{
path: '/admin',
component: () => import('@/views/admin/Layout.vue'),
meta: { requiresAuth: true, requiresAdmin: true },
children: [
{ path: '', redirect: '/admin/dashboard' },
{ path: 'dashboard', name: 'AdminDashboard', component: () => import('@/views/admin/Dashboard.vue') },
{ path: 'books', name: 'AdminBooks', component: () => import('@/views/admin/BookManage.vue') },
{ path: 'orders', name: 'AdminOrders', component: () => import('@/views/admin/OrderManage.vue') },
{ path: 'users', name: 'AdminUsers', component: () => import('@/views/admin/UserManage.vue') },
]
},
// 商家端路由
{
path: '/seller',
component: () => import('@/views/seller/Layout.vue'),
meta: { requiresAuth: true, requiresSeller: true },
children: [
{ path: '', redirect: '/seller/dashboard' },
{ path: 'dashboard', name: 'SellerDashboard', component: () => import('@/views/seller/Dashboard.vue') },
{ path: 'products', name: 'SellerProducts', component: () => import('@/views/seller/ProductManage.vue') },
{ path: 'orders', name: 'SellerOrders', component: () => import('@/views/seller/OrderManage.vue') },
]
},
// 404
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('@/views/NotFound.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes
})
// 全局路由守卫
router.beforeEach((to, from, next) => {
const userStore = useUserStore()
// 需要登录
if (to.meta.requiresAuth && !userStore.isLoggedIn) {
next({ name: 'Login', query: { redirect: to.fullPath } })
return
}
// 需要管理员权限
if (to.meta.requiresAdmin && !userStore.isAdmin) {
next({ name: 'Home' })
return
}
// 需要商家权限
if (to.meta.requiresSeller && !userStore.isSeller) {
next({ name: 'Home' })
return
}
next()
})
export default router
九、部署指南
9.1 本地开发环境
后端启动
# 1. 创建数据库
mysql -u root -p
CREATE DATABASE bookmall DEFAULT CHARACTER SET utf8mb4;
# 2. 执行初始化脚本
mysql -u root -p bookmall < bookmall-backend/src/main/resources/schema.sql
# 3. 启动后端
cd bookmall-backend
mvn spring-boot:run
前端启动
cd bookmall-frontend
npm install
npm run dev
访问地址:
- 前台:http://localhost:3000
- 管理后台:http://localhost:3000/admin/login
- 商家端:http://localhost:3000/seller/login
默认账号:
- 管理员:admin / 123456
- 测试用户:test / 123456
- 商家:seller1 / 123456
9.2 Docker 一键部署
Linux/macOS
./deploy.sh --domain your-domain.com --email your-email@example.com
Windows PowerShell
powershell -ExecutionPolicy Bypass -File .\deploy.ps1 -Domain your-domain.com -Email your-email@example.com
手动部署
# 仅 HTTP
docker compose up -d --build
# 启用 HTTPS
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build
9.3 生产环境配置
环境变量
# .env.prod
MYSQL_ROOT_PASSWORD=your_strong_password
JWT_SECRET=your_jwt_secret_key
CORS_ALLOWED_ORIGINS=https://your-domain.com
PAYMENT_MOCK_ENABLED=false
Nginx 配置
server {
listen 80;
server_name your-domain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
# 前端
location / {
proxy_pass http://frontend:80;
}
# 后端 API
location /api {
proxy_pass http://backend:8080;
}
# 上传文件
location /uploads {
alias /var/www/uploads;
expires 30d;
}
}
十、项目亮点总结
10.1 技术亮点
-
前后端分离架构:Vue 3 + Spring Boot 3,技术栈主流,易于维护和扩展
-
JWT 无状态认证:适合分布式部署,支持横向扩展
-
消息队列异步处理:RabbitMQ 解耦订单、库存、通知等业务
-
可插拔支付系统:策略模式设计,支持快速接入支付宝/微信支付
-
全文搜索能力:Elasticsearch 支持图书多维度搜索
-
Docker 一键部署:包含完整的容器编排配置
-
生产级安全:JWT + BCrypt + HTTPS + 审计日志
10.2 业务亮点
-
三端合一:用户端、商家端、管理后台统一技术栈
-
完整电商闭环:浏览 → 购物车 → 下单 → 支付 → 发货 → 收货 → 评价
-
丰富的营销能力:优惠券、秒杀、积分、会员等级
-
内容管理系统:文章、公告、CMS 页面支持
-
售后服务:退款、退货完整流程
10.3 代码规范
-
分层架构清晰:Controller → Service → Mapper
-
统一异常处理:全局异常处理器 + 业务异常类
-
RESTful API:规范的接口设计
-
前端状态管理:Pinia 统一管理应用状态
-
组件化开发:可复用的 Vue 组件
十一、扩展建议
如果您想在此项目基础上进行二次开发,以下是一些建议:
-
接入真实支付:替换 PaymentStrategy 实现,接入支付宝/微信支付 SDK
-
添加短信验证:接入阿里云/腾讯云短信服务
-
引入缓存机制:使用 Redis 缓存热点数据
-
日志收集:接入 ELK(Elasticsearch + Logstash + Kibana)
-
监控告警:接入 Prometheus + Grafana
-
微服务拆分:将订单、商品、用户拆分为独立服务
总结
本项目是一个功能完善、架构清晰的图书商城系统,涵盖了电商系统的核心功能模块。无论是作为学习全栈开发的实战项目,还是作为商业项目的快速启动模板,都具有很高的参考价值。
项目采用当前主流的技术栈(Vue 3 + Spring Boot 3),代码规范,文档完善,支持 Docker 一键部署,非常适合中小型电商项目的快速落地。
如有问题或建议,欢迎在评论区交流讨论!
如需源码,请进入Gitee获取 https://gitee.com/he-jun0000/SpringBootbook
更多推荐




所有评论(0)