分享一套锋哥原创的基于Python的微信小程序图书馆(图书借阅)管理系统(FastAPI+Vue3)
·
大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的微信小程序图书馆(图书借阅)管理系统(FastAPI+Vue3)

项目介绍
随着移动互联网与智慧校园建设的不断推进,传统图书馆借阅方式在效率、便捷性与数据统计能力方面已难以满足高校师生的日常需求。读者希望能够随时随地检索馆藏、办理借还与续借,管理人员则需要对图书、读者与借阅业务进行可视化监控与精细化管理。针对上述问题,本文设计并实现了一套基于微信小程序的图书馆(图书借阅)管理系统。系统采用前后端分离与多端协同架构:后端基于 Python 语言与 FastAPI 框架提供 RESTful API,使用 SQLAlchemy 完成对象关系映射,以 MySQL 数据库(库名 db_library)持久化业务数据;管理员端采用 Vue3、Vite、Element Plus 与 ECharts 构建 Web 管理后台,实现数据看板、图书与分类管理、读者管理、借阅管理、轮播图维护以及个人中心等功能;读者端基于微信小程序实现用户名密码登录与注册、图书浏览与检索、自助借阅、续借还书、个人资料与头像修改、密码修改等功能。系统在业务层实现了借阅上限校验、库存扣减与恢复、超期状态自动更新与罚款计算等规则。实践表明,该系统结构清晰、接口规范、交互便捷,能够有效提升图书借阅管理效率,对同类校园信息化应用具有一定参考价值。
源码下载
链接: https://pan.baidu.com/s/1kzu7OgwVgto-_5Etlhotvg?pwd=1234
提取码: 1234
系统展示






核心代码
"""
读者业务服务
"""
from datetime import datetime
from sqlalchemy import or_
from sqlalchemy.orm import Session
from app.common.exceptions import BusinessException
from app.common.result import Result, fail, page_result, success, success_msg
from app.models.reader import Reader
from app.schemas.reader import ReaderRequest
from app.utils.datetime_util import format_datetime
class ReaderService:
"""读者业务服务类"""
def __init__(self, db: Session):
"""
构造读者服务
:param db: 数据库会话
"""
self.db = db
def list_readers(self, page: int, page_size: int, keyword: str) -> Result:
"""
分页查询读者列表
:param page: 页码
:param page_size: 每页条数
:param keyword: 关键字
:return: 分页结果
"""
query = self.db.query(Reader)
if keyword:
query = query.filter(or_(
Reader.real_name.like(f"%{keyword}%"),
Reader.username.like(f"%{keyword}%"),
Reader.card_no.like(f"%{keyword}%"),
))
total = query.count()
items = query.order_by(Reader.id.desc()).offset((page - 1) * page_size).limit(page_size).all()
list_data = [self._to_map(r) for r in items]
return page_result(list_data, total, page, page_size)
def all_readers(self) -> Result:
"""
全部正常读者(下拉用)
:return: 读者简要列表
"""
items = self.db.query(Reader).filter(Reader.status == 1).order_by(Reader.id.asc()).all()
list_data = [{
"id": r.id,
"real_name": r.real_name,
"card_no": r.card_no,
"max_borrow": r.max_borrow,
} for r in items]
return success(list_data)
def add(self, req: ReaderRequest) -> Result:
"""
新增读者
:param req: 请求体
:return: 统一响应
"""
if self.db.query(Reader).filter(Reader.username == req.username).count() > 0:
return fail("用户名已存在")
if self.db.query(Reader).filter(Reader.card_no == req.card_no).count() > 0:
return fail("借书证号已存在")
reader = Reader(
username=req.username,
password=req.password if req.password else "123456",
card_no=req.card_no,
real_name=req.real_name,
gender=req.gender if req.gender is not None else 1,
phone=req.phone,
email=req.email,
max_borrow=req.max_borrow if req.max_borrow is not None else 5,
status=req.status if req.status is not None else 1,
create_time=datetime.now(),
)
self.db.add(reader)
self.db.commit()
return success_msg("添加成功")
def update(self, reader_id: int, req: ReaderRequest) -> Result:
"""
修改读者
:param reader_id: 读者ID
:param req: 请求体
:return: 统一响应
"""
reader = self.db.query(Reader).filter(Reader.id == reader_id).first()
if reader is None:
raise BusinessException("读者不存在")
if req.card_no is not None:
reader.card_no = req.card_no
if req.real_name is not None:
reader.real_name = req.real_name
if req.gender is not None:
reader.gender = req.gender
if req.phone is not None:
reader.phone = req.phone
if req.email is not None:
reader.email = req.email
if req.max_borrow is not None:
reader.max_borrow = req.max_borrow
if req.status is not None:
reader.status = req.status
if req.password is not None:
reader.password = req.password
self.db.commit()
return success_msg("修改成功")
def delete(self, reader_id: int) -> Result:
"""
删除读者
:param reader_id: 读者ID
:return: 统一响应
"""
reader = self.db.query(Reader).filter(Reader.id == reader_id).first()
if reader is None:
raise BusinessException("读者不存在")
self.db.delete(reader)
self.db.commit()
return success_msg("删除成功")
def _to_map(self, reader: Reader) -> dict:
"""
读者实体转字典
:param reader: 读者实体
:return: 字典
"""
return {
"id": reader.id,
"username": reader.username,
"card_no": reader.card_no,
"real_name": reader.real_name,
"gender": reader.gender,
"phone": reader.phone,
"email": reader.email,
"max_borrow": reader.max_borrow,
"status": reader.status,
"create_time": format_datetime(reader.create_time),
}
<template>
<div class="page-container">
<div class="search-bar">
<el-select v-model="form.reader_id" placeholder="选择读者" filterable style="width:220px">
<el-option v-for="r in readers" :key="r.id" :label="`${r.real_name} (${r.card_no})`" :value="r.id" />
</el-select>
<el-select v-model="form.book_id" placeholder="选择图书" filterable style="width:280px">
<el-option v-for="b in books" :key="b.id" :label="`${b.title} (库存:${b.stock_count})`" :value="b.id" :disabled="b.stock_count <= 0" />
</el-select>
<el-button type="primary" @click="handleLend"><el-icon><Switch /></el-icon>办理借书</el-button>
</div>
<div class="table-card">
<h3 style="margin-bottom:16px;color:#303133">当前在借图书</h3>
<el-table :data="borrowingList" stripe border style="width:100%">
<el-table-column prop="reader_name" label="读者" min-width="100" />
<el-table-column prop="card_no" label="借书证号" min-width="130" />
<el-table-column prop="book_title" label="图书" min-width="180" show-overflow-tooltip />
<el-table-column prop="borrow_time" label="借阅时间" min-width="170" />
<el-table-column prop="due_time" label="应还时间" min-width="170" />
<el-table-column prop="renew_count" label="续借次数" min-width="90" />
<el-table-column prop="status_text" label="状态" min-width="80">
<template #default="{ row }">
<el-tag :type="row.status === 3 ? 'danger' : 'warning'" size="small">{{ row.status_text }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="fine" label="罚款(元)" min-width="90" />
<el-table-column label="操作" min-width="180" fixed="right">
<template #default="{ row }">
<el-button v-if="row.status !== 2" type="success" link @click="handleReturn(row)">还书</el-button>
<el-button v-if="row.status === 1" type="primary" link @click="handleRenew(row)">续借</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination-wrap">
<el-pagination v-model:current-page="page" v-model:page-size="pageSize" :total="total"
layout="total, sizes, prev, pager, next" @change="loadBorrowing" />
</div>
</div>
</div>
</template>
<script setup>
/** 借阅管理页面 - 管理员办理借书/还书/续借 */
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getAllReaders, getBookList, getBorrowList, lendBook, returnBook, renewBook } from '@/api'
const readers = ref([])
const books = ref([])
const borrowingList = ref([])
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const form = reactive({ reader_id: null, book_id: null })
async function loadReaders() {
const res = await getAllReaders()
readers.value = res.data
}
async function loadBooks() {
const res = await getBookList({ page: 1, page_size: 100 })
books.value = res.data.list
}
async function loadBorrowing() {
const res = await getBorrowList({ page: page.value, page_size: pageSize.value, active_only: 1 })
borrowingList.value = res.data.list
total.value = res.data.total
}
async function handleLend() {
if (!form.reader_id || !form.book_id) {
ElMessage.warning('请选择读者和图书')
return
}
await lendBook(form)
ElMessage.success('借书成功')
form.book_id = null
loadBooks()
loadBorrowing()
}
async function handleReturn(row) {
await ElMessageBox.confirm(`确定为「${row.reader_name}」办理还书吗?`, '还书确认', { type: 'info' })
const res = await returnBook(row.id)
ElMessage.success(res.msg)
loadBooks()
loadBorrowing()
}
async function handleRenew(row) {
await ElMessageBox.confirm(`确定为「${row.book_title}」办理续借吗?`, '续借确认', { type: 'info' })
await renewBook(row.id)
ElMessage.success('续借成功')
loadBorrowing()
}
onMounted(() => { loadReaders(); loadBooks(); loadBorrowing() })
</script>
更多推荐





所有评论(0)