今天给大家分享一个使用fastApi框架开发一个完整的web项目:宠物管理系统。
技术:
python:3.12
后端框架:fastApi
前端:vue2 + element-ui
数据库 :mysql8
这个系统只有管理后台,目前没有做用户前台,只是实现了后台的宠物相关的管理。
实现的菜单有:
首页、会员管理、会员充值、宠物类型、宠物档案、疫苗记录、医疗记录、宠物用品、订单管理、索赔记录、管理员管理
项目建议只是学习使用,如果商用,自行根据实际业务二开。
部分截图:
登录:
首页:
宠物档案:
项目截图:
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…