# FastAPI 在电商系统开发中的最佳实践 ## 引言 在当今快速发展的电商行业中,后端 API 的性能、可维护性和开发效率直接影响着用户体验和业务增长。FastAPI 作为一个现代、快速(高性能)的 Python Web 框架,凭借其出色的性能、直观的类型提示和自动 API 文档生成等特性,已成为构建电商后端服务的理想选择。 本文将深入探讨如何在实际电商项目中应用 FastAPI 的最佳实践,涵盖项目结构、依赖注入、异步处理、数据验证、安全认证等关键方面。无论您是正在构建一个新的电商平台,还是优化现有系统,这些实践都将帮助您创建更健壮、可扩展和高效的 API 服务。 ## 核心内容 ### 1. 项目结构设计 良好的项目结构是维护大型电商应用的基础。以下是一个推荐的电商项目结构: ``` ecommerce-api/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 应用入口 │ ├── core/ # 核心配置和工具 │ │ ├── __init__.py │ │ ├── config.py # 配置管理 │ │ ├── security.py # 安全相关工具 │ │ └── dependencies.py # 依赖注入 │ ├── api/ # API 路由 │ │ ├── __init__.py │ │ ├── v1/ # API 版本管理 │ │ │ ├── __init__.py │ │ │ ├── endpoints/ │ │ │ │ ├── auth.py │ │ │ │ ├── products.py │ │ │ │ ├── orders.py │ │ │ │ └── users.py │ │ │ └── api.py # API 路由注册 │ ├── models/ # 数据模型 │ │ ├── __init__.py │ │ ├── user.py │ │ ├── product.py │ │ └── order.py │ ├── schemas/ # Pydantic 模式 │ │ ├── __init__.py │ │ ├── user.py │ │ ├── product.py │ │ └── order.py │ ├── services/ # 业务逻辑层 │ │ ├── __init__.py │ │ ├── user_service.py │ │ ├── product_service.py │ │ └── order_service.py │ └── db/ # 数据库相关 │ ├── __init__.py │ ├── session.py # 数据库会话管理 │ └── repositories/ # 数据访问层 │ ├── __init__.py │ ├── user_repo.py │ └── product_repo.py ├── tests/ # 测试文件 ├── alembic/ # 数据库迁移 ├── requirements.txt └── .env.example ``` ### 2. 配置管理 电商系统通常需要多种环境配置(开发、测试、生产)。使用 Pydantic 的 `BaseSettings` 可以优雅地管理配置: ```python # app/core/config.py from pydantic import BaseSettings, PostgresDsn, validator from typing import Optional, List class Settings(BaseSettings): # 基础配置 API_V1_STR: str = "/api/v1" PROJECT_NAME: str = "E-Commerce API" # 安全配置 SECRET_KEY: str ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 30 # 数据库配置 POSTGRES_SERVER: str POSTGRES_USER: str POSTGRES_PASSWORD: str POSTGRES_DB: str DATABASE_URI: Optional[PostgresDsn] = None # CORS 配置 BACKEND_CORS_ORIGINS: List[str] = [] @validator("DATABASE_URI", pre=True) def assemble_db_connection(cls, v: Optional[str], values: dict) -> str: if isinstance(v, str): return v return PostgresDsn.build( scheme="postgresql", user=values.get("POSTGRES_USER"), password=values.get("POSTGRES_PASSWORD"), host=values.get("POSTGRES_SERVER"), path=f"/{values.get('POSTGRES_DB') or ''}", ) @validator("BACKEND_CORS_ORIGINS", pre=True) def assemble_cors_origins(cls, v: str | List[str]) -> List[str]: if isinstance(v, str) and not v.startswith("["): return [i.strip() for i in v.split(",")] elif isinstance(v, (list, str)): return v raise ValueError(v) class Config: env_file = ".env" case_sensitive = True settings = Settings() ``` ### 3. 依赖注入系统 FastAPI 的依赖注入系统是其强大功能之一,特别适合电商系统中的复杂业务逻辑: ```python # app/core/dependencies.py from fastapi import Depends, HTTPException, status from fastapi.security import OAuth2PasswordBearer from jose import JWTError, jwt from sqlalchemy.orm import Session from app.core.config import settings from app.db.session import get_db from app.models.user import User from app.schemas.token import TokenPayload oauth2_scheme = OAuth2PasswordBearer( tokenUrl=f"{settings.API_V1_STR}/auth/login" ) async def get_current_user( db: Session = Depends(get_db), token: str = Depends(oauth2_scheme) ) -> User: """获取当前认证用户""" credentials_exception = HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="无法验证凭证", headers={"WWW-Authenticate": "Bearer"}, ) try: payload = jwt.decode( token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM] ) user_id: str = payload.get("sub") if user_id is None: raise credentials_exception token_data = TokenPayload(sub=user_id) except JWTError: raise credentials_exception user = db.query(User).filter(User.id == token_data.sub).first() if user is None: raise credentials_exception if not user.is_active: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="用户已被禁用" ) return user def get_current_active_superuser( current_user: User = Depends(get_current_user), ) -> User: """检查当前用户是否为超级管理员""" if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="权限不足" ) return current_user ``` ### 4. 异步数据库操作 电商系统需要处理高并发请求,异步操作至关重要: ```python # app/db/session.py from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine from sqlalchemy.orm import sessionmaker from app.core.config import settings # 创建异步引擎 engine = create_async_engine( settings.DATABASE_URI.replace("postgresql", "postgresql+asyncpg"), echo=True, # 生产环境设为 False future=True ) # 创建异步会话工厂 AsyncSessionLocal = sessionmaker( engine, class_=AsyncSession, expire_on_commit=False ) async def get_db() -> AsyncSession: """获取数据库会话依赖""" async with AsyncSessionLocal() as session: try: yield session await session.commit() except Exception: await session.rollback() raise finally: await session.close() ``` ### 5. 数据验证与序列化 使用 Pydantic 模型进行数据验证和序列化: ```python # app/schemas/product.py from pydantic import BaseModel, Field, validator from typing import Optional, List from datetime import datetime from decimal import Decimal class ProductBase(BaseModel): name: str = Field(..., min_length=1, max_length=200) description: Optional[str] = None price: Decimal = Field(..., gt=0) stock_quantity: int = Field(..., ge=0) category_id: int sku: str = Field(..., min_length=1, max_length=50) @validator("price") def validate_price(cls, v): """验证价格格式""" if v.as_tuple().exponent < -2: raise ValueError("价格最多支持两位小数") return v class ProductCreate(ProductBase): pass class ProductUpdate(BaseModel): name: Optional[str] = Field(None, min_length=1, max_length=200) description: Optional[str] = None price: Optional[Decimal] = Field(None, gt=0) stock_quantity: Optional[int] = Field(None, ge=0) category_id: Optional[int] = None class ProductInDB(ProductBase): id: int created_at: datetime updated_at: datetime is_active: bool class Config: orm_mode = True class ProductResponse(ProductInDB): pass class ProductListResponse(BaseModel): items: List[ProductResponse] total: int page: int size: int pages: int ``` ## 实践应用 ### 1. 商品管理 API 实现 ```python # app/api/v1/endpoints/products.py from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func from app.core.dependencies import get_current_user, get_current_active_superuser from app.db.session import get_db from app.models.product import Product from app.models.user import User from app.schemas.product import ( ProductCreate, ProductUpdate, ProductResponse, ProductListResponse ) from app.services.product_service import ProductService router = APIRouter() @router.get("/", response_model=ProductListResponse) async def list_products( db: AsyncSession = Depends(get_db), skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), category_id: Optional[int] = None, min_price: Optional[float] = None, max_price: Optional[float] = None, search: Optional[str] = None, in_stock_only: bool = False ): """ 获取商品列表 - 支持分页 - 支持按分类筛选 - 支持价格区间筛选 - 支持关键词搜索 - 支持仅显示有库存商品 """ # 构建查询条件 conditions = [] if category_id: conditions.append(Product.category_id == category_id) if min_price is not None: conditions.append(Product.price >= min_price) if max_price is not None: conditions.append(Product.price <= max_price) if in_stock_only: conditions.append(Product.stock_quantity > 0) if search: conditions.append( Product.name.ilike(f"%{search}%") | Product.description.ilike(f"%{search}%") ) # 执行查询 query = select(Product).where(*conditions) total_query = select(func.count()).select_from(query.subquery()) # 分页 query = query.offset(skip).limit(limit) # 异步执行查询 result = await db.execute(query) products = result.scalars().all() total_result = await db.execute(total_query) total = total_result.scalar() return ProductListResponse( items=products, total=total, page=skip // limit + 1 if limit > 0 else 1, size=limit, pages=(total + limit - 1) // limit if limit > 0 else 1 ) @router.post("/", response_model=ProductResponse, status_code=status.HTTP_201_CREATED) async def create_product( *, db: AsyncSession = Depends(get_db), product_in: ProductCreate, current_user: User = Depends(get_current_active_superuser) ): """ 创建新商品(仅管理员) """ # 检查 SKU 是否已存在 existing_product = await ProductService.get_by_sku(db, product_in.sku) if existing_product: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="SKU 已存在" ) product = await ProductService.create(db, product_in) return product @router.put("/{product_id}", response_model=ProductResponse) async def update_product( *, db: AsyncSession = Depends(get_db), product_id: int, product_in: ProductUpdate, current_user: User = Depends(get_current_active_superuser) ): """ 更新商品信息(仅管理员) """ product = await ProductService.get(db, product_id) if not product: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="商品不存在" ) # 如果更新 SKU,检查是否重复 if product_in.sku and product_in.sku != product.sku: existing = await ProductService.get_by_sku(db, product_in.sku) if existing: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="SKU 已存在" ) product = await ProductService.update(db, product, product_in) return product @router.get("/{product_id}", response_model=ProductResponse) async def get_product( *, db: AsyncSession = Depends(get_db), product_id: int ): """ 获取商品详情 """ product = await ProductService.get(db, product_id) if not product: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="商品不存在" ) return product ``` ### 2. 订单处理服务 ```python # app/services/order_service.py from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select from typing import List, Optional from decimal import Decimal from app.models.order import Order, OrderStatus, OrderItem from app.models.product import Product from app.schemas.order import OrderCreate, OrderUpdate from app.core.exceptions import BusinessException class OrderService: """订单服务类""" @staticmethod async def create_order( db: AsyncSession, order_in: OrderCreate, user_id: int ) -> Order: """ 创建订单 - 验证商品库存 - 计算总金额 - 创建订单记录 """ total_amount = Decimal('0') order_items = [] # 验证每个订单项 for item in order_in.items: # 获取商品信息 result = await db.execute( select(Product).where( Product.id == item.product_id, Product.is_active == True ) ) product = result.scalar_one_or_none() if not product: raise BusinessException(f"商品 {item.product_id} 不存在或已下架") if product.stock_quantity < item.quantity: raise BusinessException( f"商品 {product.name} 库存不足," f"剩余 {product.stock_quantity} 件" ) # 计算金额 item_price = product.price * item.quantity total_amount += item_price # 创建订单项 order_item = OrderItem( product_id=item.product_id, quantity=item.quantity, unit_price=product.price, total_price=item_price ) order_items.append(order_item) # 减少库存(悲观锁) product.stock_quantity -= item.quantity # 创建订单 order = Order( user_id=user_id, total_amount=total_amount, shipping_address=order_in.shipping_address, status=OrderStatus.PENDING, items=order_items ) db.add(order) await db.commit() await db.refresh(order) return order @staticmethod async def cancel_order(db: AsyncSession, order_id: int, user_id: int) -> Order: """ 取消订单 - 只有待处理订单可以取消 - 恢复商品库存 """ result = await db.execute( select(Order).where( Order.id == order_id, Order.user_id == user_id ) ) order = result.scalar_one_or_none() if not order: raise BusinessException("订单不存在") if order.status != OrderStatus.PENDING: raise BusinessException("只有待处理订单可以取消") # 恢复库存 for item in order.items: result = await db.execute( select(Product).where(Product.id == item.product_id) ) product = result.scalar_one() product.stock_quantity += item.quantity # 更新订单状态 order.status = OrderStatus.CANCELLED await db.commit() await db.refresh(order) return order ``` ### 3. 缓存优化策略 ```python # app/core/cache.py from typing import Any, Optional import

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐