本文详细介绍一个基于 Vue 3 + Spring Boot 3 的完整图书商城项目,包含用户端、商家端、管理后台三大模块,支持 Docker 一键部署,是一个真正可商用的电商模板级项目。

一、项目简介

1.1 项目背景

在电商领域,图书商城是一个经典的业务场景。本项目 BookMall 是一个功能完善的在线图书销售平台,采用当前主流的前后端分离架构,涵盖了电商系统的核心功能模块,非常适合作为学习全栈开发的实战项目,也可以直接用于商业项目的快速启动。

1.2 功能概览

用户端(前台)功能:

  • 图书浏览:列表展示、搜索、排序、详情查看
  • 店铺页:查看店铺信息与商品列表
  • 购物车:添加、删除、修改数量、结算
  • 订单管理:创建订单、查看订单列表/详情、发起支付、确认收货、取消订单
  • 个人中心:基础信息、收货地址管理、发票申请
  • 内容系统:文章/公告列表与详情、CMS 页面
  • 活动页:促销活动、运营页面入口
    在这里插入图片描述
    在这里插入图片描述

商家端功能:

  • 商家登录/注册
  • 商品管理:上架/下架、编辑商品信息
  • 订单管理:查看/处理订单
  • 售后管理:售后列表与处理
  • 发票管理:开票与记录查询
  • 店铺设置:店铺资料维护
    在这里插入图片描述

管理后台功能:

  • 管理员登录与仪表盘
  • 图书管理/审核、分类管理
  • 订单管理、售后管理
  • 用户管理、商家管理/审核
  • 运营能力:优惠券、秒杀、消息推送、Banner、内容管理
    在这里插入图片描述

二、技术栈详解

2.1 前端技术栈

技术版本说明
Vue3.x渐进式 JavaScript 框架,使用组合式 API
Vite5.0新一代前端构建工具,开发体验极佳
Vue Router4.x官方路由管理器
Pinia2.x新一代状态管理库,替代 Vuex
Element Plus2.4.x基于 Vue 3 的 UI 组件库
Axios1.6.xHTTP 请求库
DOMPurify-XSS 攻击防护,对富文本做安全清洗
ECharts5.x数据可视化图表库

2.2 后端技术栈

技术版本说明
Java17LTS 长期支持版本
Spring Boot3.2.x主流 Java 开发框架
Spring Security6.x安全认证与授权框架
JWT-JSON Web Token 无状态认证
MyBatis-Plus3.5.5MyBatis 增强工具,简化 CRUD
MySQL8.0主业务关系型数据库
Redis7.0缓存、会话管理
RabbitMQ3.x消息队列,异步处理订单等
Elasticsearch8.11全文搜索引擎(可选)
MongoDB6.x用户行为日志存储(可选)
Knife4j4.3.0API 文档生成工具

2.3 部署运维

技术说明
Docker容器化部署
Docker Compose多容器编排
Nginx反向代理与静态资源托管
Let’s Encrypt + CertbotHTTPS 证书自动签发与续期

三、项目结构

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/loginPOST用户登录
/api/v1/auth/registerPOST用户注册
/api/v1/auth/logoutPOST退出登录
图书模块
接口方法说明
/api/v1/booksGET图书列表(分页)
/api/v1/books/{id}GET图书详情
/api/v1/books/searchGET搜索图书
/api/v1/books/hotGET热销图书
/api/v1/categoriesGET分类列表
/api/v1/categories/treeGET分类树
购物车模块
接口方法说明
/api/v1/cartGET获取购物车
/api/v1/cart/itemsPOST添加商品
/api/v1/cart/items/{id}PUT更新数量
/api/v1/cart/items/{id}DELETE删除商品
订单模块
接口方法说明
/api/v1/ordersPOST创建订单
/api/v1/ordersGET订单列表
/api/v1/orders/{id}GET订单详情
/api/v1/orders/{id}/payPOST支付订单
/api/v1/orders/{id}/cancelPOST取消订单
/api/v1/orders/{id}/confirmPOST确认收货

八、前端路由设计

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编程工具,助力开发者即刻编程。

更多推荐