本文详细介绍一个基于 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 技术亮点

  1. 前后端分离架构:Vue 3 + Spring Boot 3,技术栈主流,易于维护和扩展

  2. JWT 无状态认证:适合分布式部署,支持横向扩展

  3. 消息队列异步处理:RabbitMQ 解耦订单、库存、通知等业务

  4. 可插拔支付系统:策略模式设计,支持快速接入支付宝/微信支付

  5. 全文搜索能力:Elasticsearch 支持图书多维度搜索

  6. Docker 一键部署:包含完整的容器编排配置

  7. 生产级安全:JWT + BCrypt + HTTPS + 审计日志

10.2 业务亮点

  1. 三端合一:用户端、商家端、管理后台统一技术栈

  2. 完整电商闭环:浏览 → 购物车 → 下单 → 支付 → 发货 → 收货 → 评价

  3. 丰富的营销能力:优惠券、秒杀、积分、会员等级

  4. 内容管理系统:文章、公告、CMS 页面支持

  5. 售后服务:退款、退货完整流程

10.3 代码规范

  1. 分层架构清晰:Controller → Service → Mapper

  2. 统一异常处理:全局异常处理器 + 业务异常类

  3. RESTful API:规范的接口设计

  4. 前端状态管理:Pinia 统一管理应用状态

  5. 组件化开发:可复用的 Vue 组件


十一、扩展建议

如果您想在此项目基础上进行二次开发,以下是一些建议:

  1. 接入真实支付:替换 PaymentStrategy 实现,接入支付宝/微信支付 SDK

  2. 添加短信验证:接入阿里云/腾讯云短信服务

  3. 引入缓存机制:使用 Redis 缓存热点数据

  4. 日志收集:接入 ELK(Elasticsearch + Logstash + Kibana)

  5. 监控告警:接入 Prometheus + Grafana

  6. 微服务拆分:将订单、商品、用户拆分为独立服务


总结

本项目是一个功能完善、架构清晰的图书商城系统,涵盖了电商系统的核心功能模块。无论是作为学习全栈开发的实战项目,还是作为商业项目的快速启动模板,都具有很高的参考价值。

项目采用当前主流的技术栈(Vue 3 + Spring Boot 3),代码规范,文档完善,支持 Docker 一键部署,非常适合中小型电商项目的快速落地。

如有问题或建议,欢迎在评论区交流讨论!

如需源码,请进入Gitee获取 https://gitee.com/he-jun0000/SpringBootbook

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐