fastApi框架开发一个宠物管理系统

0 阅读6分钟

今天给大家分享一个使用fastApi框架开发一个完整的web项目:宠物管理系统。

技术:

python:3.12

后端框架:fastApi

前端:vue2 + element-ui

数据库 :mysql8

这个系统只有管理后台,目前没有做用户前台,只是实现了后台的宠物相关的管理。

实现的菜单有:

首页、会员管理、会员充值、宠物类型、宠物档案、疫苗记录、医疗记录、宠物用品、订单管理、索赔记录、管理员管理

项目建议只是学习使用,如果商用,自行根据实际业务二开。

部分截图:

登录:

image.png

首页:

image.png

宠物档案:

image.png

项目截图:

image.png

from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from core.response import ResponseModel
from core.exceptions import CustomException
from core.deps import get_current_admin
from db.database import get_db
from models.admin import Admin
from models.member_recharge import MemberRecharge
from models.member import Member
from schemas.member_recharge import MemberRechargeCreate, MemberRechargeResponse, MemberRechargeListResponse, MemberRechargeCamelResponse, MemberRechargeListCamelResponse, MemberRechargePageQuery, MemberOption
import time
import random
import string

router = APIRouter()

def generate_order_no():
    """
    生成唯一的订单号
    格式:R + 时间戳(10位) + 随机4位数字
    """
    timestamp = str(int(time.time()))
    random_num = ''.join(random.choices(string.digits, k=4))
    return f"R{timestamp}{random_num}"

@router.get("/pageList")
async def get_member_recharge_page_list(
    query_params: MemberRechargePageQuery = Depends(),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取会员充值分页列表(关联查询会员信息)
    """
    # 构建查询,关联member表
    db_query = db.query(
        MemberRecharge.id,
        MemberRecharge.order_no,
        MemberRecharge.recharge_amount,
        MemberRecharge.member_id,
        MemberRecharge.actual_amount,
        MemberRecharge.remark,
        MemberRecharge.created_time,
        MemberRecharge.updated_time,
        Member.member_no,
        Member.name.label('member_name'),
        Member.phone.label('member_phone')
    ).join(
        Member, MemberRecharge.member_id == Member.id
    ).filter(
        MemberRecharge.is_deleted == 0,
        Member.is_deleted == 0
    )
    
    # 如果有搜索条件
    if query_params.search:
        search_pattern = f"%{query_params.search}%"
        db_query = db_query.filter(
            (Member.name.like(search_pattern)) |
            (Member.phone.like(search_pattern)) |
            (Member.member_no.like(search_pattern)) |
            (MemberRecharge.order_no.like(search_pattern))
        )
    
    # 计算总数
    total = db_query.count()
    
    # 分页查询
    offset = (query_params.pageNum - 1) * query_params.pageSize
    items = db_query.order_by(MemberRecharge.id.desc()).offset(offset).limit(query_params.pageSize).all()
    
    # 转换为小驼峰格式
    camel_items = []
    for item in items:
        camel_item = MemberRechargeCamelResponse(
            id=item.id,
            orderNo=item.order_no,
            rechargeAmount=float(item.recharge_amount),
            memberId=item.member_id,
            memberNo=item.member_no,
            memberName=item.member_name,
            memberPhone=item.member_phone,
            actualAmount=float(item.actual_amount),
            remark=item.remark,
            createdTime=item.created_time.strftime("%Y-%m-%d %H:%M:%S") if item.created_time else None,
            updatedTime=item.updated_time.strftime("%Y-%m-%d %H:%M:%S") if item.updated_time else None
        )
        camel_items.append(camel_item)
    
    # 构建响应数据
    response = MemberRechargeListCamelResponse(total=total, records=camel_items)
    
    return ResponseModel.success(
        data=response.model_dump(),
        msg="获取成功"
    )

@router.get("/detail/{recharge_id}")
async def get_member_recharge_detail(
    recharge_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取会员充值详情(关联查询会员信息)
    """
    # 查询充值记录,关联member表
    result = db.query(
        MemberRecharge.id,
        MemberRecharge.order_no,
        MemberRecharge.recharge_amount,
        MemberRecharge.member_id,
        MemberRecharge.actual_amount,
        MemberRecharge.remark,
        MemberRecharge.created_time,
        MemberRecharge.updated_time,
        Member.member_no,
        Member.name.label('member_name'),
        Member.phone.label('member_phone')
    ).join(
        Member, MemberRecharge.member_id == Member.id
    ).filter(
        MemberRecharge.id == recharge_id,
        MemberRecharge.is_deleted == 0,
        Member.is_deleted == 0
    ).first()
    
    if not result:
        raise CustomException(msg="充值记录不存在")
    
    # 转换为小驼峰格式
    camel_response = MemberRechargeCamelResponse(
        id=result.id,
        orderNo=result.order_no,
        rechargeAmount=float(result.recharge_amount),
        memberId=result.member_id,
        memberNo=result.member_no,
        memberName=result.member_name,
        memberPhone=result.member_phone,
        actualAmount=float(result.actual_amount),
        remark=result.remark,
        createdTime=result.created_time.strftime("%Y-%m-%d %H:%M:%S") if result.created_time else None,
        updatedTime=result.updated_time.strftime("%Y-%m-%d %H:%M:%S") if result.updated_time else None
    )
    
    return ResponseModel.success(
        data=camel_response.model_dump(),
        msg="获取成功"
    )

@router.get("/memberOptions")
async def get_member_options(
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取会员选项列表(用于下拉框)
    """
    members = db.query(Member).filter(Member.is_deleted == 0).all()
    
    options = []
    for member in members:
        option = MemberOption(
            id=member.id,
            memberNo=member.member_no,
            name=member.name,
            phone=member.phone,
            balance=float(member.balance) if member.balance else 0.00
        )
        options.append(option)
    
    return ResponseModel.success(
        data=[option.model_dump() for option in options],
        msg="获取成功"
    )

@router.post("/add")
async def add_member_recharge(
    recharge_in: MemberRechargeCreate,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    添加会员充值记录并更新会员余额
    """
    # 检查会员是否存在
    member = db.query(Member).filter(Member.id == recharge_in.member_id, Member.is_deleted == 0).first()
    if not member:
        raise CustomException(msg="会员不存在")
    
    # 生成唯一的订单号
    order_no = generate_order_no()
    while db.query(MemberRecharge).filter(MemberRecharge.order_no == order_no).first():
        order_no = generate_order_no()
    
    # 创建充值记录
    recharge = MemberRecharge(
        order_no=order_no,
        recharge_amount=recharge_in.recharge_amount,
        member_id=recharge_in.member_id,
        actual_amount=recharge_in.actual_amount,
        remark=recharge_in.remark,
        created_id=current_admin.id
    )
    
    db.add(recharge)
    db.commit()
    db.refresh(recharge)
    
    # 更新会员余额
    current_balance = float(member.balance) if member.balance else 0.00
    new_balance = current_balance + float(recharge_in.recharge_amount)
    member.balance = new_balance
    member.updated_id = current_admin.id
    
    db.commit()
    db.refresh(member)
    
    # 查询完整的充值记录(包含会员信息)
    result = db.query(
        MemberRecharge.id,
        MemberRecharge.order_no,
        MemberRecharge.recharge_amount,
        MemberRecharge.member_id,
        MemberRecharge.actual_amount,
        MemberRecharge.remark,
        MemberRecharge.created_time,
        MemberRecharge.updated_time,
        Member.member_no,
        Member.name.label('member_name'),
        Member.phone.label('member_phone')
    ).join(
        Member, MemberRecharge.member_id == Member.id
    ).filter(
        MemberRecharge.id == recharge.id
    ).first()
    
    # 转换为小驼峰格式
    camel_response = MemberRechargeCamelResponse(
        id=result.id,
        orderNo=result.order_no,
        rechargeAmount=float(result.recharge_amount),
        memberId=result.member_id,
        memberNo=result.member_no,
        memberName=result.member_name,
        memberPhone=result.member_phone,
        actualAmount=float(result.actual_amount),
        remark=result.remark,
        createdTime=result.created_time.strftime("%Y-%m-%d %H:%M:%S") if result.created_time else None,
        updatedTime=result.updated_time.strftime("%Y-%m-%d %H:%M:%S") if result.updated_time else None
    )
    
    return ResponseModel.success(
        data=camel_response.model_dump(),
        msg="充值成功"
    )

from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from datetime import datetime
from core.response import ResponseModel
from core.exceptions import CustomException
from core.deps import get_current_admin
from db.database import get_db
from models.admin import Admin
from models.pet_profile import PetProfile
from models.pet_type import PetType
from schemas.pet_profile import PetProfileCreate, PetProfileResponse, PetProfileListResponse, PetProfileCamelResponse, PetProfileListCamelResponse, PetProfilePageQuery, PetProfileUpdateInfo

router = APIRouter()

@router.get("/pageList")
async def get_pet_profile_page_list(
    query_params: PetProfilePageQuery = Depends(),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取宠物档案分页列表(关联查询宠物类型信息)
    """
    # 构建查询,关联pet_type表
    db_query = db.query(
        PetProfile.id,
        PetProfile.pet_name,
        PetProfile.pet_breed,
        PetProfile.pet_type_id,
        PetProfile.birth_date,
        PetProfile.gender,
        PetProfile.color,
        PetProfile.weight,
        PetProfile.is_sterilized,
        PetProfile.allergy_history,
        PetProfile.health_status,
        PetProfile.remark,
        PetProfile.contact_person,
        PetProfile.contact_phone,
        PetProfile.created_time,
        PetProfile.updated_time,
        PetType.type_name
    ).join(
        PetType, PetProfile.pet_type_id == PetType.id
    ).filter(
        PetProfile.is_deleted == 0,
        PetType.is_deleted == 0
    )
    
    # 如果有搜索条件
    if query_params.search:
        search_pattern = f"%{query_params.search}%"
        db_query = db_query.filter(
            (PetProfile.pet_name.like(search_pattern)) |
            (PetProfile.pet_breed.like(search_pattern)) |
            (PetProfile.contact_person.like(search_pattern)) |
            (PetProfile.contact_phone.like(search_pattern))
        )
    
    # 如果有宠物类型筛选
    if query_params.petTypeId:
        db_query = db_query.filter(PetProfile.pet_type_id == query_params.petTypeId)
    
    # 计算总数
    total = db_query.count()
    
    # 分页查询
    offset = (query_params.pageNum - 1) * query_params.pageSize
    items = db_query.order_by(PetProfile.id.desc()).offset(offset).limit(query_params.pageSize).all()
    
    # 转换为小驼峰格式
    camel_items = []
    for item in items:
        camel_item = PetProfileCamelResponse(
            id=item.id,
            petName=item.pet_name,
            petBreed=item.pet_breed,
            petTypeId=item.pet_type_id,
            petTypeName=item.type_name,
            birthDate=item.birth_date.strftime("%Y-%m-%d") if item.birth_date else None,
            gender=item.gender,
            color=item.color,
            weight=float(item.weight) if item.weight else None,
            isSterilized=item.is_sterilized,
            allergyHistory=item.allergy_history,
            healthStatus=item.health_status,
            remark=item.remark,
            contactPerson=item.contact_person,
            contactPhone=item.contact_phone,
            createdTime=item.created_time.strftime("%Y-%m-%d %H:%M:%S") if item.created_time else None,
            updatedTime=item.updated_time.strftime("%Y-%m-%d %H:%M:%S") if item.updated_time else None
        )
        camel_items.append(camel_item)
    
    # 构建响应数据
    response = PetProfileListCamelResponse(total=total, records=camel_items)
    
    return ResponseModel.success(
        data=response.model_dump(),
        msg="获取成功"
    )

@router.get("/detail/{pet_profile_id}")
async def get_pet_profile_detail(
    pet_profile_id: int,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取宠物档案详情(关联查询宠物类型信息)
    """
    # 查询宠物档案,关联pet_type表
    result = db.query(
        PetProfile.id,
        PetProfile.pet_name,
        PetProfile.pet_breed,
        PetProfile.pet_type_id,
        PetProfile.birth_date,
        PetProfile.gender,
        PetProfile.color,
        PetProfile.weight,
        PetProfile.is_sterilized,
        PetProfile.allergy_history,
        PetProfile.health_status,
        PetProfile.remark,
        PetProfile.contact_person,
        PetProfile.contact_phone,
        PetProfile.created_time,
        PetProfile.updated_time,
        PetType.type_name
    ).join(
        PetType, PetProfile.pet_type_id == PetType.id
    ).filter(
        PetProfile.id == pet_profile_id,
        PetProfile.is_deleted == 0,
        PetType.is_deleted == 0
    ).first()
    
    if not result:
        raise CustomException(msg="宠物档案不存在")
    
    # 转换为小驼峰格式
    camel_response = PetProfileCamelResponse(
        id=result.id,
        petName=result.pet_name,
        petBreed=result.pet_breed,
        petTypeId=result.pet_type_id,
        petTypeName=result.type_name,
        birthDate=result.birth_date.strftime("%Y-%m-%d") if result.birth_date else None,
        gender=result.gender,
        color=result.color,
        weight=float(result.weight) if result.weight else None,
        isSterilized=result.is_sterilized,
        allergyHistory=result.allergy_history,
        healthStatus=result.health_status,
        remark=result.remark,
        contactPerson=result.contact_person,
        contactPhone=result.contact_phone,
        createdTime=result.created_time.strftime("%Y-%m-%d %H:%M:%S") if result.created_time else None,
        updatedTime=result.updated_time.strftime("%Y-%m-%d %H:%M:%S") if result.updated_time else None
    )
    
    return ResponseModel.success(
        data=camel_response.model_dump(),
        msg="获取成功"
    )

@router.get("/petTypeOptions")
async def get_pet_type_options(
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    获取宠物类型选项列表(用于下拉框)
    """
    pet_types = db.query(PetType).filter(PetType.is_deleted == 0, PetType.status == 1).all()
    
    options = []
    for pet_type in pet_types:
        option = {
            "id": pet_type.id,
            "typeName": pet_type.type_name,
            "typeOrder": pet_type.type_order
        }
        options.append(option)
    
    return ResponseModel.success(
        data=options,
        msg="获取成功"
    )

@router.post("/add")
async def add_pet_profile(
    pet_profile_in: PetProfileCreate,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    添加宠物档案
    """
    # 检查宠物类型是否存在
    pet_type = db.query(PetType).filter(PetType.id == pet_profile_in.pet_type_id, PetType.is_deleted == 0).first()
    if not pet_type:
        raise CustomException(msg="宠物类型不存在")
    
    # 处理出生日期
    birth_date_value = None
    if pet_profile_in.birth_date:
        birth_date_value = datetime.strptime(pet_profile_in.birth_date, "%Y-%m-%d")
    
    # 创建宠物档案
    pet_profile = PetProfile(
        pet_name=pet_profile_in.pet_name,
        pet_breed=pet_profile_in.pet_breed,
        pet_type_id=pet_profile_in.pet_type_id,
        birth_date=birth_date_value,
        gender=pet_profile_in.gender,
        color=pet_profile_in.color,
        weight=pet_profile_in.weight,
        is_sterilized=pet_profile_in.is_sterilized,
        allergy_history=pet_profile_in.allergy_history,
        health_status=pet_profile_in.health_status,
        remark=pet_profile_in.remark,
        contact_person=pet_profile_in.contact_person,
        contact_phone=pet_profile_in.contact_phone,
        created_id=current_admin.id
    )
    
    db.add(pet_profile)
    db.commit()
    db.refresh(pet_profile)
    
    # 查询完整的宠物档案(包含宠物类型信息)
    result = db.query(
        PetProfile.id,
        PetProfile.pet_name,
        PetProfile.pet_breed,
        PetProfile.pet_type_id,
        PetProfile.birth_date,
        PetProfile.gender,
        PetProfile.color,
        PetProfile.weight,
        PetProfile.is_sterilized,
        PetProfile.allergy_history,
        PetProfile.health_status,
        PetProfile.remark,
        PetProfile.contact_person,
        PetProfile.contact_phone,
        PetProfile.created_time,
        PetProfile.updated_time,
        PetType.type_name
    ).join(
        PetType, PetProfile.pet_type_id == PetType.id
    ).filter(
        PetProfile.id == pet_profile.id
    ).first()
    
    # 转换为小驼峰格式
    camel_response = PetProfileCamelResponse(
        id=result.id,
        petName=result.pet_name,
        petBreed=result.pet_breed,
        petTypeId=result.pet_type_id,
        petTypeName=result.type_name,
        birthDate=result.birth_date.strftime("%Y-%m-%d") if result.birth_date else None,
        gender=result.gender,
        color=result.color,
        weight=float(result.weight) if result.weight else None,
        isSterilized=result.is_sterilized,
        allergyHistory=result.allergy_history,
        healthStatus=result.health_status,
        remark=result.remark,
        contactPerson=result.contact_person,
        contactPhone=result.contact_phone,
        createdTime=result.created_time.strftime("%Y-%m-%d %H:%M:%S") if result.created_time else None,
        updatedTime=result.updated_time.strftime("%Y-%m-%d %H:%M:%S") if result.updated_time else None
    )
    
    return ResponseModel.success(
        data=camel_response.model_dump(),
        msg="添加成功"
    )

@router.put("/updatePetProfile")
async def update_pet_profile_info(
    pet_profile_in: PetProfileUpdateInfo,
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    更新宠物档案信息
    """
    pet_profile = db.query(PetProfile).filter(PetProfile.id == pet_profile_in.id, PetProfile.is_deleted == 0).first()
    if not pet_profile:
        raise CustomException(msg="宠物档案不存在")
    
    # 如果更新宠物类型,检查宠物类型是否存在
    if pet_profile_in.petTypeId is not None:
        pet_type = db.query(PetType).filter(PetType.id == pet_profile_in.petTypeId, PetType.is_deleted == 0).first()
        if not pet_type:
            raise CustomException(msg="宠物类型不存在")
        pet_profile.pet_type_id = pet_profile_in.petTypeId
    
    # 更新宠物档案信息
    if pet_profile_in.petName is not None:
        pet_profile.pet_name = pet_profile_in.petName
    
    if pet_profile_in.petBreed is not None:
        pet_profile.pet_breed = pet_profile_in.petBreed
    
    if pet_profile_in.birthDate is not None:
        birth_date_value = datetime.strptime(pet_profile_in.birthDate, "%Y-%m-%d")
        pet_profile.birth_date = birth_date_value
    
    if pet_profile_in.gender is not None:
        pet_profile.gender = pet_profile_in.gender
    
    if pet_profile_in.color is not None:
        pet_profile.color = pet_profile_in.color
    
    if pet_profile_in.weight is not None:
        pet_profile.weight = pet_profile_in.weight
    
    if pet_profile_in.isSterilized is not None:
        pet_profile.is_sterilized = pet_profile_in.isSterilized
    
    if pet_profile_in.allergyHistory is not None:
        pet_profile.allergy_history = pet_profile_in.allergyHistory
    
    if pet_profile_in.healthStatus is not None:
        pet_profile.health_status = pet_profile_in.healthStatus
    
    if pet_profile_in.remark is not None:
        pet_profile.remark = pet_profile_in.remark
    
    if pet_profile_in.contactPerson is not None:
        pet_profile.contact_person = pet_profile_in.contactPerson
    
    if pet_profile_in.contactPhone is not None:
        pet_profile.contact_phone = pet_profile_in.contactPhone
    
    pet_profile.updated_id = current_admin.id
    
    db.commit()
    db.refresh(pet_profile)
    
    # 查询完整的宠物档案(包含宠物类型信息)
    result = db.query(
        PetProfile.id,
        PetProfile.pet_name,
        PetProfile.pet_breed,
        PetProfile.pet_type_id,
        PetProfile.birth_date,
        PetProfile.gender,
        PetProfile.color,
        PetProfile.weight,
        PetProfile.is_sterilized,
        PetProfile.allergy_history,
        PetProfile.health_status,
        PetProfile.remark,
        PetProfile.contact_person,
        PetProfile.contact_phone,
        PetProfile.created_time,
        PetProfile.updated_time,
        PetType.type_name
    ).join(
        PetType, PetProfile.pet_type_id == PetType.id
    ).filter(
        PetProfile.id == pet_profile.id
    ).first()
    
    # 转换为小驼峰格式
    camel_response = PetProfileCamelResponse(
        id=result.id,
        petName=result.pet_name,
        petBreed=result.pet_breed,
        petTypeId=result.pet_type_id,
        petTypeName=result.type_name,
        birthDate=result.birth_date.strftime("%Y-%m-%d") if result.birth_date else None,
        gender=result.gender,
        color=result.color,
        weight=float(result.weight) if result.weight else None,
        isSterilized=result.is_sterilized,
        allergyHistory=result.allergy_history,
        healthStatus=result.health_status,
        remark=result.remark,
        contactPerson=result.contact_person,
        contactPhone=result.contact_phone,
        createdTime=result.created_time.strftime("%Y-%m-%d %H:%M:%S") if result.created_time else None,
        updatedTime=result.updated_time.strftime("%Y-%m-%d %H:%M:%S") if result.updated_time else None
    )
    
    return ResponseModel.success(
        data=camel_response.model_dump(),
        msg="更新成功"
    )

@router.delete("/deletePetProfile")
async def delete_pet_profile_info(
    id: int = Query(..., description="宠物档案ID"),
    db: Session = Depends(get_db),
    current_admin: Admin = Depends(get_current_admin)
):
    """
    删除宠物档案(软删除)
    """
    pet_profile = db.query(PetProfile).filter(PetProfile.id == id, PetProfile.is_deleted == 0).first()
    if not pet_profile:
        raise CustomException(msg="宠物档案不存在")
    
    # 软删除
    pet_profile.is_deleted = 1
    pet_profile.updated_id = current_admin.id
    
    db.commit()
    
    return ResponseModel.success(
        msg="删除成功"
    )

项目预览地址:test.wwwoop.com/?s=/chong-w…