适用人群:有后端基础的开发者
学习时长:约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)
✅ 日志配置
✅ 监控告警
✅ 数据备份
学习建议
- 先完成商品模块,这是基础
- 再做购物车和订单,这是核心
- 集成支付功能,这是难点
- 添加数据统计,这是增值
- 测试完整流程,确保没有bug
下一步扩展
- 添加优惠券系统
- 实现秒杀功能
- 添加物流查询
- 实现推荐系统
- 添加客服系统