分享一套锋哥原创的基于Python的通讯录管理系统(FastAPI+Vue3)
·
大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的通讯录管理系统(FastAPI+Vue3)

项目介绍
随着移动互联网与数字化办公的快速发展,个人与企业在日常工作中需要对大量联系人信息进行统一管理。传统纸质通讯录或分散存储于手机、表格中的联系方式,存在检索不便、备份困难、多人协作能力弱、权限控制不足等问题。针对上述需求,本文设计并实现了一套基于Python的通讯录管理系统。系统采用前后端分离的B/S架构,后端使用Python语言与FastAPI框架构建高性能RESTful API,结合SQLAlchemy完成对象关系映射,数据持久化采用MySQL数据库;前端采用Vue3、Vite、Pinia、Vue Router与Element Plus构建管理端界面,并使用ECharts实现首页数据可视化统计。系统围绕用户认证、个人中心、联系人管理、分组管理、Excel导入导出、操作日志与后台用户管理等核心业务展开,通过JWT实现无状态身份认证,并通过角色区分管理员与普通用户,保证数据隔离与访问控制。论文完成了需求分析、总体设计、数据库E-R建模、详细表结构设计、关键模块实现与系统测试等工作。测试结果表明,系统功能完整、运行稳定,能够满足本科毕业设计对工程实践能力的训练要求,同时为后续扩展移动端接入、密码加密存储与更细粒度权限控制提供了良好基础。
源码下载
链接: https://pan.baidu.com/s/1INXE-TJiN7qTRTklTAPC-Q?pwd=1234
提取码: 1234
系统展示







核心代码
"""
用户服务
"""
from sqlalchemy import or_
from sqlalchemy.orm import Session
from app.common.deps import CurrentUser
from app.common.exceptions import BusinessException
from app.common.jwt_util import JwtUtil
from app.common.utils import build_page_result
from app.models.user import User
from app.schemas.serializers import user_to_dict
from app.schemas.user import LoginDTO, ProfileUpdateDTO, RegisterDTO, UserSaveDTO
class UserService:
"""用户服务类"""
@staticmethod
def login(db: Session, dto: LoginDTO) -> dict:
"""用户登录"""
user = db.query(User).filter(User.username == dto.username).first()
if not user:
raise BusinessException("用户名不存在")
if user.password != dto.password:
raise BusinessException("密码错误")
if user.status != 1:
raise BusinessException("账号已被禁用")
token = JwtUtil.generate_token(user.id, user.username, user.role)
return {"token": token, "user": user_to_dict(user)}
@staticmethod
def register(db: Session, dto: RegisterDTO) -> None:
"""用户注册"""
if dto.confirmPassword is not None and dto.password != dto.confirmPassword:
raise BusinessException("两次输入的密码不一致")
exists = db.query(User).filter(User.username == dto.username).count()
if exists > 0:
raise BusinessException("用户名已存在")
user = User(
username=dto.username,
password=dto.password,
nickname=dto.nickname or dto.username,
phone=dto.phone,
email=dto.email,
gender=0,
role=1,
status=1,
)
db.add(user)
db.commit()
@staticmethod
def get_by_id(db: Session, user_id: int) -> User | None:
"""根据ID获取用户"""
return db.query(User).filter(User.id == user_id).first()
@staticmethod
def page_users(db: Session, page_num: int, page_size: int, keyword: str | None) -> dict:
"""分页查询用户"""
query = db.query(User)
if keyword:
like = f"%{keyword}%"
query = query.filter(
or_(User.username.like(like), User.nickname.like(like), User.phone.like(like))
)
total = query.count()
records = (
query.order_by(User.create_time.desc())
.offset((page_num - 1) * page_size)
.limit(page_size)
.all()
)
return build_page_result(
[user_to_dict(item) for item in records], total, page_num, page_size
)
@staticmethod
def save_user(db: Session, dto: UserSaveDTO) -> None:
"""管理员新增用户"""
exists = db.query(User).filter(User.username == dto.username).count()
if exists > 0:
raise BusinessException("用户名已存在")
user = User(
username=dto.username,
password=dto.password or "123456",
nickname=dto.nickname,
phone=dto.phone,
email=dto.email,
gender=dto.gender or 0,
role=dto.role if dto.role is not None else 1,
status=dto.status if dto.status is not None else 1,
avatar=dto.avatar,
)
db.add(user)
db.commit()
@staticmethod
def update_user(db: Session, dto: UserSaveDTO) -> None:
"""管理员修改用户"""
user = db.query(User).filter(User.id == dto.id).first()
if not user:
raise BusinessException("用户不存在")
user.nickname = dto.nickname
user.phone = dto.phone
user.email = dto.email
user.gender = dto.gender if dto.gender is not None else user.gender
user.role = dto.role if dto.role is not None else user.role
user.status = dto.status if dto.status is not None else user.status
if dto.password:
user.password = dto.password
db.commit()
@staticmethod
def delete_user(db: Session, user_id: int) -> None:
"""管理员删除用户"""
if user_id == 1:
raise BusinessException("不能删除超级管理员")
user = db.query(User).filter(User.id == user_id).first()
if user:
db.delete(user)
db.commit()
@staticmethod
def update_profile(db: Session, user_id: int, dto: ProfileUpdateDTO) -> None:
"""更新个人资料"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise BusinessException("用户不存在")
if dto.nickname is not None:
user.nickname = dto.nickname
if dto.phone is not None:
user.phone = dto.phone
if dto.email is not None:
user.email = dto.email
if dto.gender is not None:
user.gender = dto.gender
if dto.avatar:
user.avatar = dto.avatar
db.commit()
@staticmethod
def change_password(db: Session, user_id: int, old_password: str, new_password: str) -> None:
"""修改密码"""
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise BusinessException("用户不存在")
if user.password != old_password:
raise BusinessException("旧密码错误")
user.password = new_password
db.commit()
@staticmethod
def count_all(db: Session) -> int:
"""统计用户总数"""
return db.query(User).count()
<template>
<div class="page-container">
<div class="search-bar">
<el-form :inline="true">
<el-form-item label="分组名称">
<el-input v-model="query.keyword" placeholder="搜索分组名称" clearable @keyup.enter="loadData" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadData"><el-icon><Search /></el-icon> 搜索</el-button>
<el-button @click="query.keyword='';query.pageNum=1;loadData()"><el-icon><Refresh /></el-icon> 重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="table-card">
<div style="margin-bottom:16px">
<el-button type="primary" @click="openDialog()"><el-icon><Plus /></el-icon> 新增分组</el-button>
</div>
<el-table :data="tableData" stripe border style="width:100%">
<el-table-column prop="groupName" label="分组名称" min-width="160" />
<el-table-column prop="contactCount" label="联系人数量" min-width="120" align="center" />
<el-table-column prop="sort" label="排序" min-width="80" align="center" />
<el-table-column v-if="userStore.isAdmin" prop="ownerName" label="所属用户" min-width="120" />
<el-table-column label="创建时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.createTime) }}</template>
</el-table-column>
<el-table-column label="操作" width="160" fixed="right" align="center">
<template #default="{ row }">
<el-button type="primary" link @click="openDialog(row)">编辑</el-button>
<el-button type="danger" link @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-wrap">
<el-pagination
v-model:current-page="query.pageNum"
v-model:page-size="query.pageSize"
:total="total"
:page-sizes="[10, 20, 50]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="loadData"
@current-change="loadData"
/>
</div>
</div>
<el-dialog v-model="dialogVisible" :title="form.id ? '编辑分组' : '新增分组'" width="480px" destroy-on-close>
<el-form ref="formRef" :model="form" :rules="formRules" label-width="80px">
<el-form-item label="分组名称" prop="groupName">
<el-input v-model="form.groupName" placeholder="请输入分组名称" />
</el-form-item>
<el-form-item label="排序">
<el-input-number v-model="form.sort" :min="0" :max="999" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import dayjs from 'dayjs'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useUserStore } from '@/store/user'
import { getGroupPage, addGroup, updateGroup, deleteGroup } from '@/api'
const userStore = useUserStore()
const tableData = ref([])
const total = ref(0)
const dialogVisible = ref(false)
const formRef = ref()
const query = reactive({ pageNum: 1, pageSize: 10, keyword: '' })
const form = reactive({ id: null, groupName: '', sort: 0 })
const formRules = { groupName: [{ required: true, message: '请输入分组名称', trigger: 'blur' }] }
function formatDateTime(d) { return d ? dayjs(d).format('YYYY-MM-DD HH:mm:ss') : '' }
async function loadData() {
const res = await getGroupPage(query)
tableData.value = res.data.records
total.value = res.data.total
}
function openDialog(row) {
if (row) {
Object.assign(form, { id: row.id, groupName: row.groupName, sort: row.sort })
} else {
Object.assign(form, { id: null, groupName: '', sort: 0 })
}
dialogVisible.value = true
}
async function handleSubmit() {
await formRef.value.validate()
if (form.id) {
await updateGroup(form)
ElMessage.success('修改成功')
} else {
await addGroup(form)
ElMessage.success('新增成功')
}
dialogVisible.value = false
loadData()
}
async function handleDelete(row) {
await ElMessageBox.confirm(`确定删除分组「${row.groupName}」吗?`, '提示', { type: 'warning' })
await deleteGroup(row.id)
ElMessage.success('删除成功')
loadData()
}
onMounted(loadData)
</script>
更多推荐




所有评论(0)