大家好,我是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>

更多推荐