1. 项目背景与技术选型
这个玩具商城交易平台项目采用了Python+Django作为后端,Vue3作为前端的技术架构。这种组合在当前的Web开发领域非常流行,尤其适合需要快速开发的中小型电商项目。
Python作为后端语言有几个显著优势:
- 开发效率高,Django框架提供了完善的ORM和Admin后台
- 丰富的第三方库支持,比如支付接口、图像处理等
- 易于与数据分析、爬虫等其他Python生态工具集成
Vue3作为前端框架的选择理由:
- Composition API提供了更好的代码组织和复用性
- 性能优化明显,特别是虚拟DOM的改进
- 更小的打包体积,对移动端更友好
- 更好的TypeScript支持
提示:在实际项目中,我们使用Python 3.8+版本和Vue3的最新稳定版,确保能使用所有新特性同时保持稳定性。
2. 系统架构设计
2.1 整体架构
系统采用前后端分离架构:
- 前端:Vue3 + Vite + Pinia + Element Plus
- 后端:Django + Django REST framework
- 数据库:MySQL/PostgreSQL
- 缓存:Redis
- 部署:Nginx + Docker
code复制前端 (Vue3) ← HTTP API → 后端 (Django)
↑
数据库/缓存
2.2 核心功能模块
-
用户模块:
- 注册/登录(JWT认证)
- 个人信息管理
- 收藏夹功能
-
商品模块:
- 商品分类与展示
- 搜索与筛选
- 商品详情(3D展示)
-
交易模块:
- 购物车
- 订单管理
- 支付接口集成
-
后台管理:
- 商品CRUD
- 订单处理
- 数据统计
3. 前端实现细节
3.1 Vue3项目搭建
使用Vite初始化项目:
bash复制npm create vite@latest handmall-frontend --template vue
cd handmall-frontend
npm install
核心依赖安装:
bash复制npm install pinia element-plus axios vue-router@4
3.2 路由配置示例
javascript复制// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@/views/HomeView.vue')
},
{
path: '/product/:id',
name: 'ProductDetail',
component: () => import('@/views/ProductDetail.vue'),
props: true
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
export default router
3.3 状态管理(Pinia)
创建store示例:
javascript复制// stores/cart.js
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
items: [],
total: 0
}),
actions: {
addItem(product) {
const existing = this.items.find(item => item.id === product.id)
if (existing) {
existing.quantity++
} else {
this.items.push({ ...product, quantity: 1 })
}
this.calculateTotal()
},
calculateTotal() {
this.total = this.items.reduce(
(sum, item) => sum + (item.price * item.quantity), 0
)
}
}
})
4. 后端实现细节
4.1 Django项目设置
创建项目和应用:
bash复制django-admin startproject handmall_backend
cd handmall_backend
python manage.py startapp api
安装必要依赖:
bash复制pip install djangorestframework django-cors-headers pillow
4.2 模型设计示例
python复制# api/models.py
from django.db import models
from django.contrib.auth.models import User
class Product(models.Model):
CATEGORY_CHOICES = [
('FIG', '手办'),
('MOD', '模型'),
('TOY', '玩具')
]
name = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(max_digits=10, decimal_places=2)
category = models.CharField(max_length=3, choices=CATEGORY_CHOICES)
stock = models.PositiveIntegerField()
image = models.ImageField(upload_to='products/')
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
products = models.ManyToManyField(Product, through='OrderItem')
total = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20, default='pending')
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
4.3 序列化与视图
python复制# api/serializers.py
from rest_framework import serializers
from .models import Product, Order, OrderItem
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class OrderItemSerializer(serializers.ModelSerializer):
class Meta:
model = OrderItem
fields = ['product', 'quantity', 'price']
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'user', 'items', 'total', 'status', 'created_at']
def create(self, validated_data):
items_data = validated_data.pop('items')
order = Order.objects.create(**validated_data)
for item_data in items_data:
OrderItem.objects.create(order=order, **item_data)
return order
5. 前后端交互实现
5.1 API接口设计
RESTful API设计规范:
- GET /api/products - 获取商品列表
- GET /api/products/:id - 获取商品详情
- POST /api/orders - 创建订单
- GET /api/orders - 获取用户订单列表
5.2 Axios封装示例
javascript复制// src/utils/api.js
import axios from 'axios'
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
})
// 请求拦截器
api.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
}, error => {
return Promise.reject(error)
})
// 响应拦截器
api.interceptors.response.use(response => {
return response.data
}, error => {
if (error.response.status === 401) {
// 处理未授权
}
return Promise.reject(error)
})
export default api
5.3 商品列表页实现
vue复制<template>
<div class="product-list">
<div v-for="product in products" :key="product.id" class="product-card">
<img :src="product.image" :alt="product.name">
<h3>{{ product.name }}</h3>
<p>¥{{ product.price }}</p>
<button @click="addToCart(product)">加入购物车</button>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import api from '@/utils/api'
import { useCartStore } from '@/stores/cart'
const products = ref([])
const cartStore = useCartStore()
const fetchProducts = async () => {
try {
const response = await api.get('/products')
products.value = response
} catch (error) {
console.error('获取商品列表失败:', error)
}
}
const addToCart = (product) => {
cartStore.addItem(product)
}
onMounted(() => {
fetchProducts()
})
</script>
6. 项目部署方案
6.1 前端部署
Vite项目构建:
bash复制npm run build
Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/dist;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
6.2 后端部署
使用Gunicorn运行Django:
bash复制gunicorn --bind 0.0.0.0:8000 handmall_backend.wsgi
Dockerfile示例:
dockerfile复制FROM python:3.8-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "handmall_backend.wsgi"]
6.3 数据库部署
使用Docker Compose编排服务:
yaml复制version: '3'
services:
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: yourpassword
POSTGRES_USER: youruser
POSTGRES_DB: handmall
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:6
ports:
- "6379:6379"
volumes:
- redis_data:/data
backend:
build: ./backend
ports:
- "8000:8000"
depends_on:
- db
- redis
environment:
DATABASE_URL: postgres://youruser:yourpassword@db:5432/handmall
REDIS_URL: redis://redis:6379/0
frontend:
build: ./frontend
ports:
- "80:80"
depends_on:
- backend
volumes:
postgres_data:
redis_data:
7. 开发中的常见问题与解决方案
7.1 跨域问题
开发环境下解决方案:
python复制# settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
...
'corsheaders.middleware.CorsMiddleware',
]
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173",
"http://127.0.0.1:5173"
]
7.2 图片上传与处理
Django处理图片上传:
python复制# serializers.py
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
extra_kwargs = {
'image': {'required': False}
}
# views.py
class ProductViewSet(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
parser_classes = [MultiPartParser, FormParser]
def perform_create(self, serializer):
serializer.save()
前端图片上传组件:
vue复制<template>
<div>
<input type="file" @change="handleFileChange">
<button @click="upload">上传</button>
</div>
</template>
<script setup>
const file = ref(null)
const handleFileChange = (e) => {
file.value = e.target.files[0]
}
const upload = async () => {
const formData = new FormData()
formData.append('image', file.value)
try {
await api.post('/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
} catch (error) {
console.error('上传失败:', error)
}
}
</script>
7.3 性能优化技巧
-
前端优化:
- 使用Vue的v-for时始终指定key
- 对大列表使用虚拟滚动
- 按需加载组件
-
后端优化:
- 使用select_related和prefetch_related优化查询
- 添加适当的数据库索引
- 使用缓存(Redis)存储频繁访问的数据
-
部署优化:
- 启用Gzip压缩
- 配置CDN加速静态资源
- 启用HTTP/2
8. 项目扩展方向
8.1 支付系统集成
支付宝支付示例:
python复制# 安装支付宝SDK
pip install python-alipay-sdk
# views.py
from alipay import AliPay
alipay = AliPay(
appid="your_app_id",
app_notify_url=None,
app_private_key_string=app_private_key_string,
alipay_public_key_string=alipay_public_key_string,
sign_type="RSA2",
debug=True
)
def create_payment(request, order_id):
order = Order.objects.get(id=order_id)
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order.id,
total_amount=str(order.total),
subject=f"订单支付-{order.id}",
return_url="https://yourdomain.com/payment/return/",
notify_url="https://yourdomain.com/payment/notify/"
)
payment_url = f"https://openapi.alipaydev.com/gateway.do?{order_string}"
return JsonResponse({'payment_url': payment_url})
8.2 用户行为分析
使用Django信号记录用户行为:
python复制# signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import ProductView
@receiver(post_save, sender=ProductView)
def update_product_popularity(sender, instance, created, **kwargs):
if created:
product = instance.product
product.view_count += 1
product.save()
8.3 推荐系统实现
基于用户行为的简单推荐:
python复制# utils/recommend.py
from collections import defaultdict
from .models import Order, Product
def get_recommendations(user):
# 获取相似用户的购买记录
user_orders = Order.objects.filter(user=user).prefetch_related('items')
user_products = set()
for order in user_orders:
for item in order.items.all():
user_products.add(item.product)
# 简单的基于物品的协同过滤
product_scores = defaultdict(int)
for product in user_products:
# 找出购买了该产品的其他用户
orders_with_product = Order.objects.filter(items__product=product)
for order in orders_with_product:
for item in order.items.all():
if item.product not in user_products:
product_scores[item.product] += 1
# 返回评分最高的5个产品
return sorted(product_scores.items(), key=lambda x: x[1], reverse=True)[:5]
9. 安全考虑
9.1 常见安全措施
-
XSS防护:
- Vue默认提供XSS防护
- 避免使用v-html渲染用户输入
-
CSRF防护:
- Django默认启用CSRF中间件
- 确保API请求携带CSRF token
-
SQL注入防护:
- 使用ORM而不是原生SQL
- 如果必须使用原生SQL,使用参数化查询
-
敏感数据保护:
- 密码使用bcrypt等强哈希算法
- 敏感信息加密存储
9.2 JWT安全实践
安全的JWT实现:
python复制# settings.py
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': True,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
'SIGNING_KEY': SECRET_KEY,
'AUTH_HEADER_TYPES': ('Bearer',),
}
前端JWT存储最佳实践:
javascript复制// 登录成功后
const login = async (credentials) => {
try {
const response = await api.post('/auth/login', credentials)
localStorage.setItem('token', response.access)
localStorage.setItem('refresh', response.refresh)
// 设置定时刷新token
setTimeout(refreshToken, 25 * 60 * 1000) // 25分钟后刷新
} catch (error) {
console.error('登录失败:', error)
}
}
// 刷新token
const refreshToken = async () => {
try {
const refresh = localStorage.getItem('refresh')
const response = await api.post('/auth/refresh', { refresh })
localStorage.setItem('token', response.access)
setTimeout(refreshToken, 25 * 60 * 1000)
} catch (error) {
// 刷新失败,要求重新登录
logout()
}
}
10. 测试策略
10.1 前端测试
使用Vitest进行组件测试:
javascript复制// tests/example.spec.js
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import ProductCard from '@/components/ProductCard.vue'
describe('ProductCard', () => {
it('显示正确的产品信息', () => {
const product = {
id: 1,
name: '测试产品',
price: 99.99,
image: 'test.jpg'
}
const wrapper = mount(ProductCard, {
props: { product }
})
expect(wrapper.text()).toContain('测试产品')
expect(wrapper.text()).toContain('99.99')
expect(wrapper.find('img').attributes('src')).toBe('test.jpg')
})
})
10.2 后端测试
Django测试示例:
python复制# tests/test_views.py
from django.urls import reverse
from rest_framework.test import APITestCase
from .models import Product
class ProductTests(APITestCase):
@classmethod
def setUpTestData(cls):
cls.product = Product.objects.create(
name="测试产品",
description="测试描述",
price=100.00,
category="FIG",
stock=10
)
def test_product_list(self):
url = reverse('product-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data), 1)
self.assertEqual(response.data[0]['name'], '测试产品')
def test_product_detail(self):
url = reverse('product-detail', args=[self.product.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['name'], '测试产品')
10.3 E2E测试
使用Cypress进行端到端测试:
javascript复制// cypress/e2e/products.cy.js
describe('产品相关测试', () => {
beforeEach(() => {
cy.visit('/')
})
it('应该显示产品列表', () => {
cy.get('.product-card').should('have.length.greaterThan', 0)
})
it('可以点击产品进入详情页', () => {
cy.get('.product-card:first-child').click()
cy.url().should('include', '/product/')
cy.contains('加入购物车').should('exist')
})
it('可以将产品加入购物车', () => {
cy.get('.product-card:first-child button').click()
cy.get('.cart-count').should('contain', '1')
})
})
11. 项目监控与维护
11.1 日志记录
Django日志配置:
python复制# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'debug.log',
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'DEBUG',
'propagate': True,
},
},
}
前端错误监控:
javascript复制// main.js
app.config.errorHandler = (err, vm, info) => {
console.error('Vue错误:', err, info)
// 可以发送错误到监控服务
api.post('/errors', {
error: err.toString(),
component: info,
stack: err.stack,
url: window.location.href
})
}
window.addEventListener('error', (event) => {
console.error('全局错误:', event.error)
api.post('/errors', {
error: event.error.toString(),
stack: event.error.stack,
url: window.location.href
})
})
11.2 性能监控
使用Django Debug Toolbar:
python复制# settings.py
if DEBUG:
INSTALLED_APPS += ['debug_toolbar']
MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware']
INTERNAL_IPS = ['127.0.0.1']
前端性能监控:
javascript复制// 使用web-vitals库
import { getCLS, getFID, getLCP } from 'web-vitals'
function sendToAnalytics(metric) {
const body = JSON.stringify(metric)
navigator.sendBeacon('/analytics', body)
}
getCLS(sendToAnalytics)
getFID(sendToAnalytics)
getLCP(sendToAnalytics)
12. 项目文档编写
12.1 API文档
使用Swagger/OpenAPI:
python复制# 安装drf-yasg
pip install drf-yasg
# urls.py
from drf_yasg.views import get_schema_view
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="HandMall API",
default_version='v1',
description="手办商城API文档",
),
public=True,
)
urlpatterns = [
...
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
]
12.2 项目README示例
markdown复制# HandMall - 手办模玩交易平台
基于Python+Django+Vue3构建的电商平台
## 功能特性
- 用户注册/登录
- 商品浏览与搜索
- 购物车与订单管理
- 支付系统集成
- 后台管理系统
## 技术栈
- 前端:Vue3 + Vite + Pinia + Element Plus
- 后端:Django + Django REST framework
- 数据库:PostgreSQL
- 缓存:Redis
## 开发环境搭建
1. 克隆仓库
2. 安装依赖
```bash
cd frontend && npm install
cd ../backend && pip install -r requirements.txt
- 配置环境变量
- 启动服务
bash复制# 前端 cd frontend && npm run dev # 后端 cd backend && python manage.py runserver
部署指南
使用Docker Compose:
bash复制docker-compose up -d --build
code复制
## 13. 团队协作与版本控制
### 13.1 Git工作流
推荐使用Git Flow:
1. `main`分支 - 生产环境代码
2. `develop`分支 - 开发集成分支
3. `feature/*`分支 - 功能开发分支
4. `release/*`分支 - 发布准备分支
5. `hotfix/*`分支 - 紧急修复分支
### 13.2 代码规范
前端ESLint配置示例:
```json
// .eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:vue/vue3-recommended"
],
"rules": {
"vue/multi-word-component-names": "off",
"indent": ["error", 2],
"quotes": ["error", "single"],
"semi": ["error", "never"]
}
}
后端Pylint配置:
ini复制# .pylintrc
[MASTER]
ignore=venv,migrations
[MESSAGES CONTROL]
disable=C0114,C0115,C0116
[FORMAT]
max-line-length=120
13.3 Commit规范
使用Conventional Commits:
code复制<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
常见type:
- feat: 新功能
- fix: bug修复
- docs: 文档变更
- style: 代码格式
- refactor: 代码重构
- test: 测试相关
- chore: 构建过程或辅助工具的变动
14. 项目优化与迭代
14.1 性能优化记录
-
数据库查询优化:
- 添加了产品列表页的缓存
- 使用select_related减少查询次数
- 添加了常用查询的数据库索引
-
前端加载优化:
- 实现了图片懒加载
- 使用Vite的代码分割功能
- 压缩了静态资源
-
API响应优化:
- 实现了分页
- 添加了ETag缓存
- 优化了序列化器
14.2 用户反馈处理
建立反馈处理流程:
- 收集反馈(表单、邮件、社交媒体)
- 分类整理(bug、建议、咨询)
- 优先级排序
- 分配处理
- 反馈结果
14.3 迭代计划
短期计划:
- 增加商品评价功能
- 实现优惠券系统
- 优化移动端体验
中期计划:
- 增加社区功能
- 实现直播带货
- 接入更多支付方式
长期计划:
- 开发移动应用
- 实现AR商品预览
- 构建推荐算法系统
