python作为目前最火的编程语言,要让自己更快的掌握python这个语言的编程语法,还是要多加练习python编程技术,最近就用python的web框架 fastApi 开发了一个 二手交易平台 系统,主要做了二手交易平台管理端,用户发布购买的用户端。
后端我用的是fastAPI框架,管理端用的是vue2+ Element UI 。用户端用的是uni-app框架。
数据库:mysql8。
二手商品管理
代码:
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已删除")
订单管理:
代码:
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…