大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的微信小程序自习室预约管理系统(FastAPI+Vue3)

项目介绍

随着高校在校生规模的持续扩大以及考研、考证等学习需求的不断增长,图书馆和教学楼自习室的座位资源日益紧张。传统的“先到先得、人工占座”模式普遍存在占座不到场、座位利用率低、管理人员难以实时掌握座位状态等问题,既浪费了公共学习资源,也降低了学生的学习体验。因此,设计并实现一套便捷、高效、可实时管理的自习室预约系统具有重要的现实意义。

本文设计并实现了一套基于微信小程序的自习室预约系统。系统采用前后端分离架构,用户端使用微信小程序,借助微信生态实现免注册的一键授权登录,方便学生随时随地完成自习室浏览、座位预约、扫码签到与签退、预约管理、公告查看与意见反馈等操作;后台管理端采用 Vue3 + Element Plus 构建,供管理员进行自习室管理、座位管理、预约审核、用户管理、公告发布与数据统计;服务端采用 Python 的 FastAPI 框架开发 RESTful 接口,结合 SQLAlchemy 进行数据持久化,使用 JWT 实现登录鉴权,并通过定时任务实现超时未签到预约的自动释放,从而提高座位的实际利用率。

论文首先分析了课题的研究背景与意义,介绍了微信小程序、FastAPI、Vue3、MySQL 等关键技术;随后从可行性、功能需求与非功能需求等方面进行了系统需求分析;接着完成了系统总体架构设计、功能结构设计以及包含 E-R 图和数据表的数据库设计;然后给出了微信登录、座位预约、扫码签到、后台管理等核心功能的具体实现与关键代码;最后对系统进行了功能测试。测试结果表明,系统运行稳定、功能完整、交互友好,能够较好地满足高校自习室预约管理的实际需求。

源码下载 

链接:https://pan.baidu.com/s/1s7LL2TiVaZRW4gDXYNh41Q?pwd=1234
提取码:1234

系统展示

核心代码

"""
数据库ORM模型
"""
from datetime import datetime, date

from sqlalchemy import Column, Integer, String, Text, DateTime, Date, ForeignKey
from sqlalchemy.orm import relationship

from app.database import Base


class Admin(Base):
    """管理员模型"""

    __tablename__ = "t_admin"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    username = Column(String(50), unique=True, nullable=False, comment="用户名")
    password = Column(String(100), nullable=False, comment="密码")
    real_name = Column(String(50), comment="真实姓名")
    status = Column(Integer, default=1, comment="状态: 1启用 0禁用")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")


class User(Base):
    """用户模型"""

    __tablename__ = "t_user"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    username = Column(String(50), unique=True, nullable=False, comment="用户名")
    password = Column(String(100), nullable=False, comment="密码")
    nickname = Column(String(50), comment="昵称")
    phone = Column(String(20), comment="手机号")
    student_no = Column(String(30), comment="学号")
    status = Column(Integer, default=1, comment="状态: 1启用 0禁用")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")

    # 关联预约记录
    reservations = relationship("Reservation", back_populates="user")


class Room(Base):
    """自习室模型"""

    __tablename__ = "t_room"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    name = Column(String(100), nullable=False, comment="自习室名称")
    location = Column(String(200), comment="位置")
    open_time = Column(String(10), default="08:00", comment="开放时间")
    close_time = Column(String(10), default="22:00", comment="关闭时间")
    seat_count = Column(Integer, default=0, comment="座位总数")
    status = Column(Integer, default=1, comment="状态: 1开放 0关闭")
    image = Column(String(500), comment="自习室图片")
    remark = Column(String(500), comment="备注")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")

    # 关联座位
    seats = relationship("Seat", back_populates="room")


class Seat(Base):
    """座位模型"""

    __tablename__ = "t_seat"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    room_id = Column(Integer, ForeignKey("t_room.id"), nullable=False, comment="所属自习室ID")
    seat_no = Column(String(20), nullable=False, comment="座位编号")
    status = Column(Integer, default=1, comment="状态: 1空闲 0维护")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")

    # 关联自习室
    room = relationship("Room", back_populates="seats")
    reservations = relationship("Reservation", back_populates="seat")


class Reservation(Base):
    """预约记录模型"""

    __tablename__ = "t_reservation"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    user_id = Column(Integer, ForeignKey("t_user.id"), nullable=False, comment="用户ID")
    seat_id = Column(Integer, ForeignKey("t_seat.id"), nullable=False, comment="座位ID")
    room_id = Column(Integer, ForeignKey("t_room.id"), nullable=False, comment="自习室ID")
    reserve_date = Column(Date, nullable=False, comment="预约日期")
    start_time = Column(String(10), nullable=False, comment="开始时间")
    end_time = Column(String(10), nullable=False, comment="结束时间")
    status = Column(Integer, default=0, comment="状态: 0待使用 1已签到 2已完成 3已取消")
    checkin_time = Column(DateTime, comment="签到时间")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")

    # 关联
    user = relationship("User", back_populates="reservations")
    seat = relationship("Seat", back_populates="reservations")
    room = relationship("Room")


class Announcement(Base):
    """公告模型"""

    __tablename__ = "t_announcement"

    id = Column(Integer, primary_key=True, autoincrement=True, comment="主键ID")
    title = Column(String(200), nullable=False, comment="标题")
    content = Column(Text, comment="内容")
    status = Column(Integer, default=1, comment="状态: 1发布 0下架")
    create_time = Column(DateTime, default=datetime.now, comment="创建时间")
<template>
  <div class="page-container">
    <div class="search-bar">
      <el-input v-model="keyword" placeholder="搜索用户/自习室/座位" clearable style="width: 220px" @clear="loadData" />
      <el-select v-model="status" placeholder="状态" clearable style="width: 120px" @change="loadData">
        <el-option label="待使用" :value="0" />
        <el-option label="已签到" :value="1" />
        <el-option label="已完成" :value="2" />
        <el-option label="已取消" :value="3" />
      </el-select>
      <el-button type="primary" @click="loadData">搜索</el-button>
    </div>

    <div class="table-wrapper">
      <el-table :data="tableData" border stripe style="width: 100%">
        <el-table-column prop="id" label="ID" width="70" />
        <el-table-column prop="nickname" label="用户" min-width="100" />
        <el-table-column prop="room_name" label="自习室" min-width="130" />
        <el-table-column prop="seat_no" label="座位" width="90" />
        <el-table-column prop="reserve_date" label="预约日期" width="120" />
        <el-table-column label="时段" min-width="130">
          <template #default="{ row }">{{ row.start_time }} - {{ row.end_time }}</template>
        </el-table-column>
        <el-table-column label="状态" width="100">
          <template #default="{ row }">
            <el-tag :type="statusType(row.status)">{{ row.status_text }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="checkin_time" label="签到时间" min-width="170" />
        <el-table-column prop="create_time" label="创建时间" min-width="170" />
        <el-table-column label="操作" width="160" fixed="right">
          <template #default="{ row }">
            <el-button v-if="row.status <= 1" type="success" link @click="handleComplete(row)">完成</el-button>
            <el-button type="danger" link @click="handleDelete(row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>

    <div class="pagination-bar">
      <el-pagination
        v-model:current-page="page"
        v-model:page-size="pageSize"
        :total="total"
        :page-sizes="[10, 20, 50]"
        layout="total, sizes, prev, pager, next"
        @change="loadData"
      />
    </div>
  </div>
</template>

<script setup>
/**
 * 预约管理页面
 */
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getReservationList, completeReservation, deleteReservation } from '../api/reservations'

const keyword = ref('')
const status = ref(null)
const page = ref(1)
const pageSize = ref(10)
const total = ref(0)
const tableData = ref([])

/** 状态标签颜色 */
const statusType = (s) => {
  return { 0: 'info', 1: 'warning', 2: 'success', 3: 'danger' }[s] || 'info'
}

/** 加载列表 */
const loadData = async () => {
  const params = { page: page.value, page_size: pageSize.value, keyword: keyword.value }
  if (status.value !== null && status.value !== '') params.status = status.value
  const res = await getReservationList(params)
  tableData.value = res.data.list
  total.value = res.data.total
}

/** 标记完成 */
const handleComplete = (row) => {
  ElMessageBox.confirm('确定标记该预约为已完成吗?', '提示', { type: 'info' })
    .then(async () => {
      await completeReservation(row.id)
      ElMessage.success('操作成功')
      loadData()
    })
    .catch(() => {})
}

/** 删除 */
const handleDelete = (row) => {
  ElMessageBox.confirm('确定删除该预约记录吗?', '提示', { type: 'warning' })
    .then(async () => {
      await deleteReservation(row.id)
      ElMessage.success('删除成功')
      loadData()
    })
    .catch(() => {})
}

onMounted(() => loadData())
</script>

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐