电商系统实战教程 — 完整电商后台

适用人群:有后端基础的开发者
学习时长:约2-3周
技术栈:Vue 3 + Laravel + MySQL + Redis + 微信支付
功能:商品管理、订单系统、支付集成、数据统计

一、项目规划

1.1 功能模块

商品模块
✅ 商品分类管理
✅ 商品SPU/SKU管理
✅ 商品图片管理
✅ 库存管理
✅ 商品搜索

订单模块
✅ 购物车
✅ 订单创建
✅ 订单支付
✅ 订单发货
✅ 订单完成/取消

用户模块
✅ 用户注册/登录
✅ 收货地址管理
✅ 订单历史
✅ 个人中心

支付模块
✅ 微信支付
✅ 支付宝支付
✅ 支付回调处理

数据统计
✅ 销售统计
✅ 用户统计
✅ 商品排行

1.2 数据库设计

-- 商品SPU表
CREATE TABLE products (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    slug VARCHAR(200) UNIQUE NOT NULL,
    description TEXT,
    category_id BIGINT,
    brand_id BIGINT,
    cover_image VARCHAR(255),
    images JSON,
    status ENUM('on_sale', 'off_sale') DEFAULT 'on_sale',
    sort_order INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,
    FOREIGN KEY (category_id) REFERENCES categories(id),
    INDEX idx_category (category_id),
    INDEX idx_status (status)
);

-- 商品SKU表(规格)
CREATE TABLE product_skus (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    product_id BIGINT NOT NULL,
    sku_code VARCHAR(50) UNIQUE NOT NULL,
    specs JSON NOT NULL,  -- {"颜色": "红色", "尺寸": "XL"}
    price DECIMAL(10, 2) NOT NULL,
    original_price DECIMAL(10, 2),
    stock INT NOT NULL DEFAULT 0,
    sales INT DEFAULT 0,
    image VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
    INDEX idx_product (product_id),
    INDEX idx_sku_code (sku_code)
);

-- 购物车表
CREATE TABLE cart_items (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    sku_id BIGINT NOT NULL,
    quantity INT NOT NULL DEFAULT 1,
    checked BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE CASCADE,
    FOREIGN KEY (sku_id) REFERENCES product_skus(id) ON DELETE CASCADE,
    UNIQUE KEY uk_user_sku (user_id, sku_id)
);

-- 订单表
CREATE TABLE orders (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no VARCHAR(32) UNIQUE NOT NULL,
    user_id BIGINT NOT NULL,
    total_amount DECIMAL(10, 2) NOT NULL,
    discount_amount DECIMAL(10, 2) DEFAULT 0,
    payable_amount DECIMAL(10, 2) NOT NULL,
    status ENUM('pending', 'paid', 'shipped', 'completed', 'cancelled', 'refunded') DEFAULT 'pending',
    payment_method VARCHAR(20),
    payment_no VARCHAR(64),
    address_snapshot JSON NOT NULL,
    remark VARCHAR(500),
    paid_at TIMESTAMP NULL,
    shipped_at TIMESTAMP NULL,
    completed_at TIMESTAMP NULL,
    cancelled_at TIMESTAMP NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id),
    INDEX idx_order_no (order_no),
    INDEX idx_user_status (user_id, status),
    INDEX idx_created_at (created_at)
);

-- 订单商品表
CREATE TABLE order_items (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_id BIGINT NOT NULL,
    product_id BIGINT NOT NULL,
    sku_id BIGINT NOT NULL,
    product_name VARCHAR(200) NOT NULL,
    product_image VARCHAR(255),
    sku_specs JSON,
    price DECIMAL(10, 2) NOT NULL,
    quantity INT NOT NULL,
    subtotal DECIMAL(10, 2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id),
    INDEX idx_order (order_id)
);

-- 收货地址表
CREATE TABLE addresses (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    user_id BIGINT NOT NULL,
    name VARCHAR(50) NOT NULL,
    phone VARCHAR(20) NOT NULL,
    province VARCHAR(50) NOT NULL,
    city VARCHAR(50) NOT NULL,
    district VARCHAR(50) NOT NULL,
    detail VARCHAR(200) NOT NULL,
    is_default BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    INDEX idx_user (user_id)
);


二、后端实现

2.1 购物车服务

// app/Services/CartService.php
class CartService
{
    // 添加到购物车
    public function add($userId, $productId, $skuId, $quantity = 1)
    {
        $sku = ProductSku::findOrFail($skuId);
        
        if ($sku->stock < $quantity) {
            throw new \Exception('库存不足');
        }
        
        $cartItem = CartItem::where('user_id', $userId)
            ->where('sku_id', $skuId)
            ->first();
        
        if ($cartItem) {
            $cartItem->increment('quantity', $quantity);
        } else {
            CartItem::create([
                'user_id' => $userId,
                'product_id' => $productId,
                'sku_id' => $skuId,
                'quantity' => $quantity,
            ]);
        }
        
        return $this->getCart($userId);
    }
    
    // 获取购物车
    public function getCart($userId)
    {
        return CartItem::where('user_id', $userId)
            ->with(['product', 'sku'])
            ->get();
    }
    
    // 更新数量
    public function updateQuantity($userId, $cartItemId, $quantity)
    {
        $cartItem = CartItem::where('user_id', $userId)
            ->findOrFail($cartItemId);
        
        if ($cartItem->sku->stock < $quantity) {
            throw new \Exception('库存不足');
        }
        
        $cartItem->update(['quantity' => $quantity]);
        return $this->getCart($userId);
    }
    
    // 删除
    public function remove($userId, $cartItemId)
    {
        CartItem::where('user_id', $userId)
            ->where('id', $cartItemId)
            ->delete();
        
        return $this->getCart($userId);
    }
}

2.2 订单服务

// app/Services/OrderService.php
class OrderService
{
    // 创建订单
    public function create($userId, $addressId, $cartItemIds, $remark = '')
    {
        DB::beginTransaction();
        
        try {
            // 1. 获取购物车商品
            $cartItems = CartItem::whereIn('id', $cartItemIds)
                ->where('user_id', $userId)
                ->with(['product', 'sku'])
                ->get();
            
            if ($cartItems->isEmpty()) {
                throw new \Exception('购物车为空');
            }
            
            // 2. 检查库存
            foreach ($cartItems as $item) {
                if ($item->sku->stock < $item->quantity) {
                    throw new \Exception("{$item->product->name} 库存不足");
                }
            }
            
            // 3. 获取收货地址
            $address = Address::where('user_id', $userId)
                ->findOrFail($addressId);
            
            // 4. 计算金额
            $totalAmount = $cartItems->sum(fn($item) => $item->sku->price * $item->quantity);
            
            // 5. 创建订单
            $order = Order::create([
                'order_no' => $this->generateOrderNo(),
                'user_id' => $userId,
                'total_amount' => $totalAmount,
                'payable_amount' => $totalAmount,
                'address_snapshot' => $address->toArray(),
                'remark' => $remark,
            ]);
            
            // 6. 创建订单商品
            foreach ($cartItems as $item) {
                OrderItem::create([
                    'order_id' => $order->id,
                    'product_id' => $item->product_id,
                    'sku_id' => $item->sku_id,
                    'product_name' => $item->product->name,
                    'product_image' => $item->product->cover_image,
                    'sku_specs' => $item->sku->specs,
                    'price' => $item->sku->price,
                    'quantity' => $item->quantity,
                    'subtotal' => $item->sku->price * $item->quantity,
                ]);
                
                // 7. 扣减库存
                $item->sku->decrement('stock', $item->quantity);
                $item->sku->increment('sales', $item->quantity);
            }
            
            // 8. 清空购物车
            CartItem::whereIn('id', $cartItemIds)->delete();
            
            DB::commit();
            
            return $order;
            
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
    
    // 生成订单号
    private function generateOrderNo()
    {
        return date('YmdHis') . str_pad(random_int(1, 999999), 6, '0', STR_PAD_LEFT);
    }
    
    // 支付
    public function pay($orderId, $userId, $paymentMethod)
    {
        $order = Order::where('user_id', $userId)
            ->where('id', $orderId)
            ->where('status', 'pending')
            ->firstOrFail();
        
        // 调用支付接口
        $paymentNo = $this->createPayment($order, $paymentMethod);
        
        $order->update([
            'payment_method' => $paymentMethod,
            'payment_no' => $paymentNo,
        ]);
        
        return $paymentNo;
    }
}


三、前端实现

3.1 购物车页面

<!-- src/views/Cart.vue -->
<template>
  <div class="cart">
    <h1>购物车</h1>
    
    <div v-if="cartItems.length === 0" class="empty">
      <p>购物车为空</p>
      <router-link to="/">去购物</router-link>
    </div>
    
    <div v-else>
      <div v-for="item in cartItems" :key="item.id" class="cart-item">
        <input 
          type="checkbox" 
          :checked="item.checked"
          @change="toggleCheck(item)"
        />
        <img :src="item.product.cover_image" class="product-image" />
        <div class="product-info">
          <h3>{{ item.product.name }}</h3>
          <p class="specs">{{ formatSpecs(item.sku.specs) }}</p>
          <p class="price">¥{{ item.sku.price }}</p>
        </div>
        <div class="quantity">
          <button @click="updateQuantity(item, item.quantity - 1)">-</button>
          <span>{{ item.quantity }}</span>
          <button @click="updateQuantity(item, item.quantity + 1)">+</button>
        </div>
        <button class="delete" @click="removeItem(item.id)">删除</button>
      </div>
      
      <div class="cart-footer">
        <div class="total">
          共 {{ checkedItems.length }} 件,合计:
          <span class="amount">¥{{ totalAmount }}</span>
        </div>
        <button class="checkout-btn" @click="checkout">去结算</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { getCart, updateCartQuantity, removeCartItem } from '@/api/cart'

const router = useRouter()
const cartItems = ref([])

const checkedItems = computed(() => cartItems.value.filter(item => item.checked))
const totalAmount = computed(() => 
  checkedItems.value.reduce((sum, item) => sum + item.sku.price * item.quantity, 0).toFixed(2)
)

const fetchCart = async () => {
  const res = await getCart()
  cartItems.value = res.data
}

const updateQuantity = async (item, quantity) => {
  if (quantity < 1) return
  await updateCartQuantity(item.id, quantity)
  item.quantity = quantity
}

const removeItem = async (id) => {
  await removeCartItem(id)
  cartItems.value = cartItems.value.filter(item => item.id !== id)
}

const checkout = () => {
  router.push({
    path: '/checkout',
    query: { items: checkedItems.value.map(item => item.id).join(',') }
  })
}

const formatSpecs = (specs) => {
  if (!specs) return ''
  return Object.entries(specs).map(([k, v]) => `${k}: ${v}`).join(', ')
}

onMounted(fetchCart)
</script>


四、部署清单

✅ 后端API部署(Laravel + Nginx)
✅ 前端部署(Vue 3 + Nginx)
✅ 数据库初始化(MySQL)
✅ Redis缓存配置
✅ SSL证书配置
✅ 微信支付配置
✅ 文件存储配置(OSS)
✅ 日志配置
✅ 监控告警
✅ 数据备份


学习建议

  1. 先完成商品模块,这是基础
  2. 再做购物车和订单,这是核心
  3. 集成支付功能,这是难点
  4. 添加数据统计,这是增值
  5. 测试完整流程,确保没有bug

下一步扩展

  • 添加优惠券系统
  • 实现秒杀功能
  • 添加物流查询
  • 实现推荐系统
  • 添加客服系统
返回首页