fastApi框架开发一个二手交易平台,提高自己的python技术~

28 阅读17分钟

python作为目前最火的编程语言,要让自己更快的掌握python这个语言的编程语法,还是要多加练习python编程技术,最近就用python的web框架 fastApi 开发了一个 二手交易平台 系统,主要做了二手交易平台管理端,用户发布购买的用户端。

image.png

后端我用的是fastAPI框架,管理端用的是vue2+ Element UI 。用户端用的是uni-app框架
数据库:mysql8

二手商品管理

image.png

代码:

from datetime import datetime
from typing import Optional, List
import json
import uuid
from fastapi import APIRouter, Depends, Query, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import and_, or_
from core.response import ResponseModel
from core.exceptions import CustomException
from core.deps import get_current_admin, get_current_user
from db.database import get_db
from models.product import Product
from models.product_category import ProductCategory
from models.user import User
from models.admin import Admin
from schemas.product import (
    ProductApprove, 
    ProductResponse, 
    ProductListResponse,
    ProductCreate
)

router = APIRouter()

@router.post("")
async def create_product(
    product_data: ProductCreate,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    创建商品接口
    用户发布商品,自动生成商品编号,设置默认状态
    """
    try:
        # 验证商品分类是否存在
        category = db.query(ProductCategory).filter(
            and_(
                ProductCategory.id == product_data.category_id,
                ProductCategory.is_deleted == 0,
                ProductCategory.category_status == 1  # 分类状态正常
            )
        ).first()
        
        if not category:
            raise CustomException(msg="商品分类不存在或已禁用")
        
        # 生成商品编号(使用时间戳+随机数)
        product_code = f"SP{datetime.now().strftime('%Y%m%d%H%M%S')}{str(uuid.uuid4())[:6].upper()}"
        
        # 处理图片地址 - 将数组转换为JSON字符串
        main_image_url = None
        if hasattr(product_data, 'main_image_url') and product_data.main_image_url:
            if isinstance(product_data.main_image_url, list):
                main_image_url = json.dumps(product_data.main_image_url)
            else:
                main_image_url = product_data.main_image_url
        
        sub_images_urls = None
        if hasattr(product_data, 'sub_images_urls') and product_data.sub_images_urls:
            if isinstance(product_data.sub_images_urls, list):
                sub_images_urls = product_data.sub_images_urls
            else:
                # 如果是字符串,尝试解析为列表
                try:
                    sub_images_urls = json.loads(product_data.sub_images_urls)
                except:
                    sub_images_urls = [product_data.sub_images_urls]
        
        # 创建商品对象
        new_product = Product(
            product_code=product_code,  # 自动生成的商品编号
            product_name=product_data.product_name,  # 商品名称
            category_id=product_data.category_id,  # 商品分类表ID
            user_id=current_user.id,  # 用户表ID(发布人) - 从token获取当前登录用户
            product_brand=product_data.product_brand,  # 商品品牌
            newness_levels=product_data.newness_levels,  # 几成新(1-10)
            product_description=product_data.product_description,  # 商品描述
            remarks=product_data.remarks,  # 备注
            approval_status=0,  # 审批状态:默认0未审批
            main_image_url=main_image_url,  # 商品主图地址
            sub_images_urls=sub_images_urls,  # 商品子图地址集合
            original_price=product_data.original_price,  # 商品原价
            selling_price=product_data.selling_price,  # 商品卖价
            product_status=0,  # 商品状态:默认0未上架
            contact_person=product_data.contact_person,  # 商品联系人
            contact_phone=product_data.contact_phone,  # 联系电话
            created_time=datetime.now(),  # 创建时间
            created_id=current_user.id,  # 创建用户ID
            updated_time=datetime.now(),  # 更新时间
            updated_id=current_user.id,  # 更新用户ID
            is_deleted=0  # 是否删除:0未删除
        )
        
        # 保存到数据库
        db.add(new_product)
        db.commit()
        db.refresh(new_product)
        
        # 返回创建的商品信息
        return ResponseModel.success(
            data={
                "id": new_product.id,
                "product_code": new_product.product_code,
                "product_name": new_product.product_name,
                "category_id": new_product.category_id,
                "category_name": category.category_name,
                "user_id": new_product.user_id,
                "product_brand": new_product.product_brand,
                "newness_levels": new_product.newness_levels,
                "product_description": new_product.product_description,
                "remarks": new_product.remarks,
                "approval_status": new_product.approval_status,
                "main_image_url": new_product.main_image_url,
                "sub_images_urls": new_product.sub_images_urls,
                "original_price": float(new_product.original_price) if new_product.original_price else None,
                "selling_price": float(new_product.selling_price) if new_product.selling_price else None,
                "product_status": new_product.product_status,
                "contact_person": new_product.contact_person,
                "contact_phone": new_product.contact_phone,
                "created_time": new_product.created_time.strftime("%Y-%m-%d %H:%M:%S") if new_product.created_time else None,
                "updated_time": new_product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if new_product.updated_time else None
            },
            msg="商品发布成功"
        )
        
    except CustomException:
        # 重新抛出自定义异常
        raise
    except Exception as e:
        # 回滚事务
        db.rollback()
        raise CustomException(msg=f"商品发布失败: {str(e)}")

@router.get("/test-public")
async def test_public():
    """
    测试公开接口 - 最简单的实现
    """
    return ResponseModel.success(data={"message": "公开接口测试成功"}, msg="测试成功")

@router.get("/my/published")
async def get_user_published_product_list(
    page: int = Query(1, ge=1, description="页码"),
    pageSize: int = Query(10, ge=1, le=100, description="每页数量"),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    获取用户发布的商品列表(分页)
    返回当前用户发布的所有商品,包括各种状态的商品
    """
    try:
        # 构建查询条件 - 查询当前用户发布的所有商品
        query = db.query(
            Product,
            ProductCategory.category_name
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).filter(
            and_(
                Product.user_id == current_user.id,  # 只查询当前用户发布的商品
                Product.is_deleted == 0
            )
        )
        
        # 按创建时间倒序排序
        query = query.order_by(Product.created_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        product_list = query.offset((page - 1) * pageSize).limit(pageSize).all()
        
        # 格式化数据
        formatted_product_list = []
        for product, category_name in product_list:
    
            
            product_dict = {
                "id": product.id,
                "product_code": product.product_code,  # 商品编号
                "product_name": product.product_name,  # 商品名称
                "category_id": product.category_id,
                "category_name": category_name,
                "product_brand": product.product_brand,  # 商品品牌
                "newness_levels": product.newness_levels,  # 几成新(1-10)
                "approval_status": product.approval_status,  # 审批状态:0未审批 1审批通过 2审批拒绝
                "main_image_url": product.main_image_url,  # 商品主图地址
                "selling_price": float(product.selling_price) if product.selling_price else None,  # 商品卖价
                "product_status": product.product_status,  # 商品状态:0未上架 1已上架 2已锁单 3已卖出
                "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
                "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None
            }
            formatted_product_list.append(product_dict)
        
        return ResponseModel.success(
            data={
                "list": formatted_product_list,
                "pagination": {
                    "total": total,
                    "page": page,
                    "pageSize": pageSize
                }
            },
            msg="获取成功"
        )
    except Exception as e:
        raise CustomException(msg=f"获取用户发布商品列表失败: {str(e)}")

@router.get("/public")
async def get_public_product_list(
    page: int = Query(1, ge=1, description="页码"),
    pageSize: int = Query(10, ge=1, le=100, description="每页数量"),
    productName: Optional[str] = Query(None, description="商品名称关键词"),
    categoryId: Optional[int] = Query(None, description="商品分类ID"),
    db: Session = Depends(get_db)
):
    """
    获取公开商品列表(分页)- 不需要登录
    只返回已审批通过且已上架的商品
    """
    try:
        # 构建查询条件 - 只查询已审批通过且已上架的商品
        query = db.query(
            Product,
            ProductCategory.category_name,
            User.nickname.label('publisher_name')
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).join(
            User, Product.user_id == User.id
        ).filter(
            and_(
                Product.is_deleted == 0,
                Product.approval_status == 1,  # 审批通过
                Product.product_status == 1    # 已上架
            )
        )
        
        # 商品名称搜索条件
        if productName and productName.strip():
            query = query.filter(Product.product_name.like(f"%{productName}%"))
        
        # 分类筛选条件
        if categoryId:
            query = query.filter(Product.category_id == categoryId)
        
        # 按创建时间倒序排序
        query = query.order_by(Product.created_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        product_list = query.offset((page - 1) * pageSize).limit(pageSize).all()
        
        # 格式化数据
        formatted_product_list = []
        for product, category_name, publisher_name in product_list:
    
            
            product_dict = {
                "id": product.id,
                "product_code": product.product_code,
                "product_name": product.product_name,
                "category_id": product.category_id,
                "category_name": category_name,
                "user_id": product.user_id,
                "publisher_name": publisher_name,
                "product_brand": product.product_brand,
                "newness_levels": product.newness_levels,
                "product_description": product.product_description,
                "main_image_url": product.main_image_url,  # 返回处理后的图片ID
                "sub_images_urls": product.sub_images_urls,
                "original_price": float(product.original_price) if product.original_price else None,
                "selling_price": float(product.selling_price) if product.selling_price else None,
                "product_status": product.product_status,
                "contact_person": product.contact_person,
                "contact_phone": product.contact_phone,
                "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
                "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None
            }
            formatted_product_list.append(product_dict)
        
        return ResponseModel.success(
            data={
                "list": formatted_product_list,
                "pagination": {
                    "total": total,
                    "page": page,
                    "pageSize": pageSize
                }
            },
            msg="获取成功"
        )
    except Exception as e:
        raise CustomException(msg=f"获取商品列表失败: {str(e)}")

@router.get("/my/{product_id}")
async def get_my_product_detail(
    product_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    获取用户发布的单个商品详情
    只能查看自己发布的商品详情
    """
    try:
        # 查询商品详情(包含关联数据)- 只查询当前用户发布的商品
        product_query = db.query(
            Product,
            ProductCategory.category_name
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).filter(
            and_(
                Product.id == product_id,
                Product.user_id == current_user.id,  # 只能查看自己发布的商品
                Product.is_deleted == 0
            )
        ).first()
        
        if not product_query:
            raise CustomException(msg="商品不存在或无权限查看")
        
        product, category_name = product_query
        

        # 格式化数据
        product_dict = {
            "id": product.id,
            "product_code": product.product_code,  # 商品编号
            "product_name": product.product_name,  # 商品名称
            "category_id": product.category_id,
            "category_name": category_name,  # 商品分类名称
            "product_brand": product.product_brand,  # 商品品牌
            "newness_levels": product.newness_levels,  # 几成新(1-10)
            "product_description": product.product_description,  # 商品描述
            "remarks": product.remarks,  # 备注
            "approval_status": product.approval_status,  # 审批状态:0未审批 1审批通过 2审批拒绝
            "approval_time": product.approval_time.strftime("%Y-%m-%d %H:%M:%S") if product.approval_time else None,  # 审批时间
            "main_image_url": product.main_image_url,  # 商品主图地址
            "sub_images_urls": product.sub_images_urls,  # 商品子图地址集合
            "original_price": float(product.original_price) if product.original_price else None,  # 商品原价
            "selling_price": float(product.selling_price) if product.selling_price else None,  # 商品卖价
            "product_status": product.product_status,  # 商品状态:0未上架 1已上架 2已锁单 3已卖出
            "contact_person": product.contact_person,  # 商品联系人
            "contact_phone": product.contact_phone,  # 联系电话
            "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
            "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None
        }
        
        return ResponseModel.success(
            data=product_dict,
            msg="获取成功"
        )
    except CustomException:
        raise
    except Exception as e:
        raise CustomException(msg=f"获取商品详情失败: {str(e)}")

@router.put("/my/{product_id}/sold")
async def mark_product_as_sold(
    product_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    标记商品为已卖出
    将商品状态更新为3(已卖出)
    """
    try:
        # 查询商品
        product = db.query(Product).filter(
            and_(
                Product.id == product_id,
                Product.user_id == current_user.id,  # 只能操作自己发布的商品
                Product.is_deleted == 0
            )
        ).first()
        
        if not product:
            raise CustomException(msg="商品不存在或无权限操作")
        
        # 检查商品状态是否可以标记为卖出
        if product.product_status != 1:
            raise CustomException(msg="只有已上架的商品才能标记为卖出")
        
        # 更新商品状态为已卖出
        product.product_status = 3  # 已卖出
        product.updated_id = current_user.id
        product.updated_time = datetime.now()
        
        # 保存到数据库
        db.commit()
        db.refresh(product)
        
        return ResponseModel.success(
            data={"product_status": product.product_status},
            msg="标记卖出成功"
        )
    except CustomException:
        raise
    except Exception as e:
        db.rollback()
        raise CustomException(msg=f"标记卖出失败: {str(e)}")

@router.delete("/my/{product_id}")
async def delete_my_product(
    product_id: int,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    删除用户发布的商品(软删除)
    只能删除自己发布的商品
    """
    try:
        # 查询商品
        product = db.query(Product).filter(
            and_(
                Product.id == product_id,
                Product.user_id == current_user.id,  # 只能操作自己发布的商品
                Product.is_deleted == 0
            )
        ).first()
        
        if not product:
            raise CustomException(msg="商品不存在或无权限操作")
        
        # 检查商品状态是否可以删除
        if product.product_status == 3:
            raise CustomException(msg="已卖出的商品无法删除")
        
        # 软删除商品
        product.is_deleted = 1
        product.updated_id = current_user.id
        product.updated_time = datetime.now()
        
        # 保存到数据库
        db.commit()
        
        return ResponseModel.success(
            data=None,
            msg="删除成功"
        )
    except CustomException:
        raise
    except Exception as e:
        db.rollback()
        raise CustomException(msg=f"删除商品失败: {str(e)}")

@router.get("/public/{product_id}")
async def get_public_product_detail(
    product_id: int,
    db: Session = Depends(get_db)
):
    """
    获取公开商品详情 - 不需要登录
    只返回已审批通过且已上架的商品详情
    """
    try:
        # 查询商品详情(包含关联数据)- 只查询已审批通过且已上架的商品
        product_query = db.query(
            Product,
            ProductCategory.category_name,
            User.nickname.label('publisher_name')
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).join(
            User, Product.user_id == User.id
        ).filter(
            and_(
                Product.id == product_id,
                Product.is_deleted == 0,
                Product.approval_status == 1,  # 审批通过
                Product.product_status == 1    # 已上架
            )
        ).first()
        
        if not product_query:
            raise CustomException(msg="商品不存在或未上架")
        
        product, category_name, publisher_name = product_query
        
 
        
        # 格式化数据
        product_dict = {
            "id": product.id,
            "product_code": product.product_code,
            "product_name": product.product_name,
            "category_id": product.category_id,
            "category_name": category_name,
            "user_id": product.user_id,
            "publisher_name": publisher_name,
            "product_brand": product.product_brand,
            "newness_levels": product.newness_levels,
            "product_description": product.product_description,
            "main_image_url": product.main_image_url,  # 返回处理后的图片ID
            "sub_images_urls": product.sub_images_urls,
            "original_price": float(product.original_price) if product.original_price else None,
            "selling_price": float(product.selling_price) if product.selling_price else None,
            "product_status": product.product_status,
            "contact_person": product.contact_person,
            "contact_phone": product.contact_phone,
            "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
            "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None
        }
        
        return ResponseModel.success(
            data=product_dict,
            msg="获取成功"
        )
    except CustomException:
        raise
    except Exception as e:
        raise CustomException(msg=f"获取商品详情失败: {str(e)}")

@router.get("")
async def get_product_list(
    page: int = Query(1, ge=1, description="页码"),
    pageSize: int = Query(10, ge=1, le=100, description="每页数量"),
    productName: Optional[str] = Query(None, description="商品名称关键词"),
    categoryId: Optional[str] = Query(None, description="商品分类ID"),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取商品列表(分页)
    """
    try:
        # 构建查询条件
        query = db.query(
            Product,
            ProductCategory.category_name,
            User.nickname.label('publisher_name')
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).join(
            User, Product.user_id == User.id
        ).filter(Product.is_deleted == 0)
        
        # 商品名称搜索条件
        if productName and productName.strip():
            query = query.filter(Product.product_name.like(f"%{productName}%"))
        
        # 分类筛选条件
        if categoryId and categoryId.strip():
            try:
                category_id_int = int(categoryId)
                query = query.filter(Product.category_id == category_id_int)
            except ValueError:
                # 如果categoryId不能转换为整数,忽略此筛选条件
                pass
        
        # 按创建时间倒序排序
        query = query.order_by(Product.created_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        product_list = query.offset((page - 1) * pageSize).limit(pageSize).all()
        
        # 格式化数据
        formatted_product_list = []
        for product, category_name, publisher_name in product_list:
            product_dict = {
                "id": product.id,
                "product_code": product.product_code,
                "product_name": product.product_name,
                "category_id": product.category_id,
                "category_name": category_name,
                "user_id": product.user_id,
                "publisher_name": publisher_name,
                "product_brand": product.product_brand,
                "newness_levels": product.newness_levels,
                "product_description": product.product_description,
                "remarks": product.remarks,
                "approval_status": product.approval_status,
                "approver_id": product.approver_id,
                "approval_time": product.approval_time.strftime("%Y-%m-%d %H:%M:%S") if product.approval_time else None,
                "main_image_url": product.main_image_url,
                "sub_images_urls": product.sub_images_urls,
                "original_price": product.original_price,
                "selling_price": product.selling_price,
                "product_status": product.product_status,
                "contact_person": product.contact_person,
                "contact_phone": product.contact_phone,
                "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
                "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None,
                "created_id": product.created_id,
                "updated_id": product.updated_id,
                "is_deleted": product.is_deleted
            }
            formatted_product_list.append(product_dict)
        
        return ResponseModel.success(
            data={
                "list": formatted_product_list,
                "pagination": {
                    "total": total,
                    "page": page,
                    "pageSize": pageSize
                }
            },
            msg="获取成功"
        )
    except Exception as e:
        raise CustomException(msg=f"获取商品列表失败: {str(e)}")

@router.get("/{product_id}")
async def get_product_detail(
    product_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取商品详情
    """
    try:
        # 查询商品详情(包含关联数据)
        product_query = db.query(
            Product,
            ProductCategory.category_name,
            User.nickname.label('publisher_name'),
            Admin.username.label('approver_name')
        ).join(
            ProductCategory, Product.category_id == ProductCategory.id
        ).join(
            User, Product.user_id == User.id
        ).outerjoin(
            Admin, Product.approver_id == Admin.id
        ).filter(
            and_(Product.id == product_id, Product.is_deleted == 0)
        ).first()
        
        if not product_query:
            raise CustomException(msg="商品不存在")
        
        product, category_name, publisher_name, approver_name = product_query
        
        # 格式化数据
        product_dict = {
            "id": product.id,
            "product_code": product.product_code,
            "product_name": product.product_name,
            "category_id": product.category_id,
            "category_name": category_name,
            "user_id": product.user_id,
            "publisher_name": publisher_name,
            "product_brand": product.product_brand,
            "newness_levels": product.newness_levels,
            "product_description": product.product_description,
            "remarks": product.remarks,
            "approval_status": product.approval_status,
            "approver_id": product.approver_id,
            "approver_name": approver_name,
            "approval_time": product.approval_time.strftime("%Y-%m-%d %H:%M:%S") if product.approval_time else None,
            "main_image_url": product.main_image_url,
            "main_image_display_url": product.main_image_url,  # 前端需要的显示URL字段
            "sub_images_urls": product.sub_images_urls,
            "sub_images_display_urls": product.sub_images_urls,  # 前端需要的显示URL字段
            "original_price": product.original_price,
            "selling_price": product.selling_price,
            "product_status": product.product_status,
            "contact_person": product.contact_person,
            "contact_phone": product.contact_phone,
            "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
            "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None,
            "created_id": product.created_id,
            "updated_id": product.updated_id,
            "is_deleted": product.is_deleted
        }
        
        return ResponseModel.success(
            data=product_dict,
            msg="获取成功"
        )
    except CustomException:
        raise
    except Exception as e:
        raise CustomException(msg=f"获取商品详情失败: {str(e)}")

@router.put("/{product_id}/approve")
async def approve_product(
    product_id: int,
    approve_data: ProductApprove,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    审批商品
    """
    try:
        # 查询商品
        product = db.query(Product).filter(
            and_(Product.id == product_id, Product.is_deleted == 0)
        ).first()
        
        if not product:
            raise CustomException(msg="商品不存在")
        
        # 检查商品是否已审批
        if product.approval_status != 0:
            raise CustomException(msg="商品已审批,无法重复审批")
        
        # 验证审批状态
        if approve_data.approval_status not in [1, 2]:
            raise CustomException(msg="审批状态无效")
        
        # 更新审批信息
        product.approval_status = approve_data.approval_status
        product.remarks = approve_data.remarks
        product.approver_id = current_admin.id
        product.approval_time = datetime.now()
        product.updated_id = current_admin.id
        product.updated_time = datetime.now()
        
        # 如果审批通过,自动上架商品
        if approve_data.approval_status == 1:
            product.product_status = 1  # 已上架
        
        # 保存到数据库
        db.commit()
        db.refresh(product)
        
        # 格式化时间字段
        product_dict = {
            "id": product.id,
            "product_code": product.product_code,
            "product_name": product.product_name,
            "category_id": product.category_id,
            "user_id": product.user_id,
            "product_brand": product.product_brand,
            "newness_levels": product.newness_levels,
            "product_description": product.product_description,
            "remarks": product.remarks,
            "approval_status": product.approval_status,
            "approver_id": product.approver_id,
            "approval_time": product.approval_time.strftime("%Y-%m-%d %H:%M:%S") if product.approval_time else None,
            "main_image_url": product.main_image_url,
            "sub_images_urls": product.sub_images_urls,
            "original_price": product.original_price,
            "selling_price": product.selling_price,
            "product_status": product.product_status,
            "contact_person": product.contact_person,
            "contact_phone": product.contact_phone,
            "created_time": product.created_time.strftime("%Y-%m-%d %H:%M:%S") if product.created_time else None,
            "updated_time": product.updated_time.strftime("%Y-%m-%d %H:%M:%S") if product.updated_time else None,
            "created_id": product.created_id,
            "updated_id": product.updated_id,
            "is_deleted": product.is_deleted
        }
        
        return ResponseModel.success(
            data=product_dict,
            msg="审批成功"
        )
    except CustomException:
        raise
    except Exception as e:
        db.rollback()
        raise CustomException(msg=f"审批商品失败: {str(e)}")
from sqlalchemy import Column, Integer, String, DateTime, Text, SmallInteger, DECIMAL, JSON
from sqlalchemy.ext.declarative import declarative_base
from db.database import Base

class Product(Base):
    """
    商品表模型
    """
    __tablename__ = "products"
    
    id = Column(Integer, primary_key=True, index=True, comment="主键ID")
    product_code = Column(String(50), nullable=False, index=True, comment="商品编号")
    product_name = Column(String(200), nullable=False, comment="商品名称")
    category_id = Column(Integer, nullable=False, index=True, comment="商品分类表ID")
    user_id = Column(Integer, nullable=False, index=True, comment="用户表ID(发布人)")
    product_brand = Column(String(100), nullable=True, comment="商品品牌")
    newness_levels = Column(SmallInteger, nullable=True, comment="几成新(1-10)")
    product_description = Column(Text, nullable=True, comment="商品描述")
    remarks = Column(Text, nullable=True, comment="备注")
    approval_status = Column(SmallInteger, nullable=True, default=0, index=True, comment="审批状态:0未审批 1审批通过 2审批拒绝")
    approver_id = Column(Integer, nullable=True, comment="审批人ID(管理员ID)")
    approval_time = Column(DateTime, nullable=True, comment="审批时间")
    main_image_url = Column(String(500), nullable=True, comment="商品主图地址")
    sub_images_urls = Column(JSON, nullable=True, comment="商品子图地址集合")
    original_price = Column(DECIMAL(10, 2), nullable=True, comment="商品原价")
    selling_price = Column(DECIMAL(10, 2), nullable=True, comment="商品卖价")
    product_status = Column(SmallInteger, nullable=True, default=0, index=True, comment="商品状态:0未上架 1已上架 2已锁单 3已卖出")
    contact_person = Column(String(50), nullable=True, comment="商品联系人")
    contact_phone = Column(String(20), nullable=True, comment="联系电话")
    created_time = Column(DateTime, nullable=True, comment="创建时间")
    created_id = Column(Integer, nullable=True, default=0, comment="创建用户ID")
    updated_time = Column(DateTime, nullable=True, comment="更新时间")
    updated_id = Column(Integer, nullable=True, default=0, comment="更新用户ID")
    is_deleted = Column(SmallInteger, nullable=True, default=0, comment="是否删除:0未删除 1已删除")

订单管理:

image.png

代码:

from datetime import datetime
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session, aliased
from sqlalchemy import and_
from core.response import ResponseModel
from core.exceptions import CustomException
from core.deps import get_current_admin, get_current_user
from db.database import get_db
from models.order import Order
from models.product import Product
from models.user import User
from models.admin import Admin
from schemas.order import (
    OrderResponse,
    OrderListResponse,
    OrderDetailResponse,
    BuyProductRequest
)
import uuid
import time

router = APIRouter()

@router.get("")
async def get_order_list(
    page: int = Query(1, ge=1, description="页码"),
    pageSize: int = Query(10, ge=1, le=100, description="每页数量"),
    orderCode: Optional[str] = Query(None, description="订单编号搜索"),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取订单列表(分页)
    支持按订单编号搜索
    """
    try:
        # 构建多表查询
        BuyerUser = aliased(User)
        SellerUser = aliased(User)
        
        query = db.query(
            Order,
            Product.product_name,
            BuyerUser.nickname.label('buyer_name'),
            SellerUser.nickname.label('seller_name')
        ).join(
            Product, Order.product_id == Product.id
        ).join(
            BuyerUser, Order.buyer_id == BuyerUser.id, isouter=True
        ).join(
            SellerUser, Order.seller_id == SellerUser.id, isouter=True
        ).filter(Order.is_deleted == 0)
        
        # 订单编号搜索条件
        if orderCode and orderCode.strip():
            query = query.filter(Order.order_code.like(f"%{orderCode}%"))
        
        # 按创建时间倒序排序
        query = query.order_by(Order.created_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        order_list = query.offset((page - 1) * pageSize).limit(pageSize).all()
        
        # 格式化数据
        formatted_order_list = []
        for order, product_name, buyer_name, seller_name in order_list:
            order_dict = {
                "id": order.id,
                "order_code": order.order_code,
                "product_id": order.product_id,
                "product_name": product_name,
                "buyer_id": order.buyer_id,
                "buyer_name": buyer_name if buyer_name else "未知用户",
                "seller_id": order.seller_id,
                "seller_name": seller_name if seller_name else "未知用户",
                "order_amount": float(order.order_amount) if order.order_amount else 0.0,
                "deal_amount": float(order.deal_amount) if order.deal_amount else 0.0,
                "remarks": order.remarks,
                "trade_methods": order.trade_methods,
                "buyer_trade_evidence": order.buyer_trade_evidence,
                "seller_trade_evidence": order.seller_trade_evidence,
                "created_time": order.created_time.strftime("%Y-%m-%d %H:%M:%S") if order.created_time else None,
                "updated_time": order.updated_time.strftime("%Y-%m-%d %H:%M:%S") if order.updated_time else None,
                "created_id": order.created_id,
                "updated_id": order.updated_id,
                "is_deleted": order.is_deleted
            }
            formatted_order_list.append(order_dict)
        
        # 构建响应数据
        response_data = {
            "list": formatted_order_list,
            "total": total,
            "page": page,
            "pageSize": pageSize
        }
        
        return ResponseModel.success(data=response_data, msg="获取订单列表成功")
        
    except Exception as e:
        raise CustomException(msg=f"获取订单列表失败: {str(e)}")

@router.get("/{order_id}")
async def get_order_detail(
    order_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取订单详情
    """
    try:
        # 查询订单信息
        order = db.query(Order).filter(
            and_(Order.id == order_id, Order.is_deleted == 0)
        ).first()
        
        if not order:
            raise CustomException(msg="订单不存在")
        
        # 查询关联的商品信息
        product = db.query(Product).filter(Product.id == order.product_id).first()
        
        # 查询买方信息
        buyer = db.query(User).filter(User.id == order.buyer_id).first()
        
        # 查询卖方信息
        seller = db.query(User).filter(User.id == order.seller_id).first()
        
        # 构建详情数据
        order_detail = {
            "id": order.id,
            "order_code": order.order_code,
            "product_id": order.product_id,
            "product_name": product.product_name if product else "未知商品",
            "product_code": product.product_code if product else "未知编号",  # 添加商品编号
            "main_image_url": product.main_image_url if product else None,  # 添加商品主图地址
            "buyer_id": order.buyer_id,
            "buyer_name": buyer.nickname if buyer else "未知用户",
            "seller_id": order.seller_id,
            "seller_name": seller.nickname if seller else "未知用户",
            "seller_phone": seller.phone if seller else "未知电话",  # 添加卖方联系电话
            "order_amount": float(order.order_amount) if order.order_amount else 0.0,
            "deal_amount": float(order.deal_amount) if order.deal_amount else 0.0,
            "remarks": order.remarks,
            "trade_methods": order.trade_methods,
            "buyer_trade_evidence": order.buyer_trade_evidence,
            "seller_trade_evidence": order.seller_trade_evidence,
            "created_time": order.created_time.strftime("%Y-%m-%d %H:%M:%S") if order.created_time else None,
            "updated_time": order.updated_time.strftime("%Y-%m-%d %H:%M:%S") if order.updated_time else None,
            "created_id": order.created_id,
            "updated_id": order.updated_id,
            "is_deleted": order.is_deleted
        }
        
        return ResponseModel.success(data=order_detail, msg="获取订单详情成功")
        
    except CustomException:
        raise
    except Exception as e:
        raise CustomException(msg=f"获取订单详情失败: {str(e)}")

@router.get("/my/orders")
async def get_my_orders(
    page: int = Query(1, ge=1, description="页码"),
    pageSize: int = Query(10, ge=1, le=100, description="每页数量"),
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    获取当前用户的订单列表(分页)
    用户端接口,只返回当前用户作为买方的订单
    """
    try:
        # 构建多表查询,查询当前用户作为买方的订单
        query = db.query(
            Order,
            Product.product_name,
            Product.main_image_url
        ).join(
            Product, Order.product_id == Product.id
        ).filter(
            and_(
                Order.buyer_id == current_user.id,  # 只查询当前用户作为买方的订单
                Order.is_deleted == 0
            )
        )
        
        # 按创建时间倒序排序
        query = query.order_by(Order.created_time.desc())
        
        # 获取总数
        total = query.count()
        
        # 分页查询
        order_list = query.offset((page - 1) * pageSize).limit(pageSize).all()
        
        # 格式化数据,按照前端需要的字段返回
        formatted_order_list = []
        for order, product_name, main_image_url in order_list:
            order_dict = {
                "id": order.id,
                "order_code": order.order_code,
                "product_id": order.product_id,
                "product_name": product_name,
                "main_image_url": main_image_url,  # 商品主图,前端用于显示商品图片
                "order_amount": float(order.order_amount) if order.order_amount else 0.0,
                "deal_amount": float(order.deal_amount) if order.deal_amount else 0.0,
                "remarks": order.remarks,
                "trade_methods": order.trade_methods,
                "buyer_trade_evidence": order.buyer_trade_evidence,  # 买方交易凭证,前端用于判断交易状态
                "created_time": order.created_time.strftime("%Y-%m-%d %H:%M:%S") if order.created_time else None,
                "updated_time": order.updated_time.strftime("%Y-%m-%d %H:%M:%S") if order.updated_time else None
            }
            formatted_order_list.append(order_dict)
        
        # 构建响应数据,按照前端期望的格式返回
        response_data = {
            "list": formatted_order_list,
            "total": total,
            "page": page,
            "pageSize": pageSize
        }
        
        return ResponseModel.success(data=response_data, msg="获取我的订单列表成功")
        
    except Exception as e:
        raise CustomException(msg=f"获取我的订单列表失败: {str(e)}")

@router.post("/buy")
async def buy_product(
    request: BuyProductRequest,
    db: Session = Depends(get_db),
    current_user: User = Depends(get_current_user)
):
    """
    购买商品(创建订单)
    """
    try:
        # 查询商品信息
        product = db.query(Product).filter(
            Product.id == request.product_id,
            Product.is_deleted == 0
        ).first()
        
        if not product:
            raise CustomException(msg="商品不存在")
        
        # 检查商品状态是否为已上架
        if product.product_status != 1:
            raise CustomException(msg="商品状态不允许购买")
        
        # 检查是否为自己发布的商品
        if product.user_id == current_user.id:
            raise CustomException(msg="不能购买自己发布的商品")
        
        # 生成订单编号
        order_code = f"ORD{int(time.time())}{str(uuid.uuid4())[:8].upper()}"
        
        # 创建订单
        new_order = Order(
            order_code=order_code,
            product_id=request.product_id,
            buyer_id=current_user.id,
            seller_id=product.user_id,
            order_amount=product.selling_price,
            deal_amount=product.selling_price,
            remarks=request.remarks,
            trade_methods=2,  # 写死为2 线下交易
            buyer_trade_evidence=request.buyer_trade_evidence,
            created_time=datetime.now(),
            created_id=current_user.id,
            updated_time=datetime.now(),
            updated_id=current_user.id,
            is_deleted=0
        )
        
        # 保存订单到数据库
        db.add(new_order)
        db.flush()  # 获取订单ID
        
        # 更新商品状态为已卖出
        product.product_status = 3
        product.updated_time = datetime.now()
        product.updated_id = current_user.id
        
        # 提交事务
        db.commit()
        
        # 返回订单信息
        return ResponseModel.success(
            data={
                "id": new_order.id,
                "order_code": new_order.order_code,
                "product_id": new_order.product_id,
                "buyer_id": new_order.buyer_id,
                "seller_id": new_order.seller_id,
                "order_amount": float(new_order.order_amount),
                "deal_amount": float(new_order.deal_amount),
                "remarks": new_order.remarks,
                "trade_methods": new_order.trade_methods,
                "buyer_trade_evidence": new_order.buyer_trade_evidence,
                "created_time": new_order.created_time.strftime("%Y-%m-%d %H:%M:%S")
            },
            msg="订单创建成功"
        )
        
    except CustomException as e:
        db.rollback()
        raise e
    except Exception as e:
        db.rollback()
        raise CustomException(msg=f"创建订单失败: {str(e)}")
from datetime import datetime
from sqlalchemy import Column, Integer, String, DateTime, DECIMAL, Text, ForeignKey
from sqlalchemy.orm import relationship
from db.database import Base

class Order(Base):
    """
    订单数据模型
    """
    __tablename__ = "orders"

    id = Column(Integer, primary_key=True, index=True, autoincrement=True, comment='主键ID')
    order_code = Column(String(50), nullable=False, index=True, comment='订单编号')
    product_id = Column(Integer, ForeignKey('products.id'), nullable=False, index=True, comment='商品ID')
    buyer_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment='买方人ID(用户表ID)')
    seller_id = Column(Integer, ForeignKey('users.id'), nullable=False, index=True, comment='卖方人ID(用户表ID)')
    order_amount = Column(DECIMAL(10, 2), nullable=False, comment='订单金额')
    deal_amount = Column(DECIMAL(10, 2), nullable=True, comment='成交金额')
    remarks = Column(Text, nullable=True, comment='备注')
    trade_methods = Column(Integer, nullable=True, default=2, comment='交易方式:1线上交易 2线下交易')
    buyer_trade_evidence = Column(String(500), nullable=True, comment='买方人交易凭证(拍照上传)')
    seller_trade_evidence = Column(String(500), nullable=True, comment='卖方人交易凭证(拍照上传)')
    created_time = Column(DateTime, default=datetime.now, comment='创建时间')
    created_id = Column(Integer, default=0, comment='创建用户ID')
    updated_time = Column(DateTime, default=datetime.now, onupdate=datetime.now, comment='更新时间')
    updated_id = Column(Integer, default=0, comment='更新用户ID')
    is_deleted = Column(Integer, default=0, comment='是否删除:0未删除 1已删除')

    def __repr__(self):
        return f"<Order(id={self.id}, order_code='{self.order_code}', product_id={self.product_id})>"

这个项目的代码量还是很多的,只能分享一部分,如果看到文章的你,也在学习python,希望这篇文章对你有所帮助,也可以自己尝试写一个类似的项目。我也搭建了一个预览地址,方便大家模仿。 test.wwwoop.com/?s=/er-shou…