尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

分享一套锋哥原创的基于Python的高校大学生迎新(新生报到)管理系统(FastAPI+Vue3)

分享一套锋哥原创的基于Python的高校大学生迎新(新生报到)管理系统(FastAPI+Vue3)
📅 发布时间:2026/7/19 18:23:04

大家好,我是Java1234_小锋老师,分享一套锋哥原创的基于Python的高校大学生迎新(新生报到)管理系统(FastAPI+Vue3)

项目介绍

随着高等教育规模持续扩大,高校每年迎新报到工作面临新生数量多、业务环节复杂、信息流转不畅等问题。传统以纸质表格和人工核验为主的迎新方式,容易出现信息重复录入、报到进度难以及时掌握、宿舍与缴费状态割裂等情况,已经难以满足数字化校园建设与精细化管理的现实需求。为此,本文设计并实现了一套基于Python的高校大学生迎新(新生报到)管理系统,以信息化手段贯通新生注册、信息维护、报到签到、宿舍分配、学费缴纳与通知公告等关键业务。

系统采用前后端分离架构:后端基于Python语言与FastAPI框架构建RESTful接口,利用SQLAlchemy完成对象关系映射,数据持久化采用MySQL数据库(库名db_orientation,表名统一以t_开头);前端基于Vue3与Element Plus实现管理端与学生端双入口,通过Axios完成接口调用,并结合ECharts实现首页数据统计可视化。系统围绕管理员与新生两类角色展开功能设计,管理员可进行组织架构维护、新生管理、报到审核、宿舍与费用管理及通知发布;新生可完成注册登录、查看报到进度、宿舍信息、缴费记录与校园公告。

本文依次完成需求分析、总体设计、数据库设计、系统实现与测试验证。实践表明,该系统界面清晰、流程完整、运行稳定,能够有效提升迎新工作效率与数据准确性,对同类高校信息化系统的建设具有一定参考价值。本系统已完成主要功能模块开发与联调,能够支撑迎新业务从信息准备到结果统计的完整闭环,具有较好的实用性与推广参考价值。

源码下载

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

系统展示

核心代码

""" 复杂查询服务 包含 JOIN 分页查询与首页统计数据 """ from typing import Any, Optional from sqlalchemy import func, select, text from sqlalchemy.orm import Session from models.checkin import Checkin from models.class_info import ClassInfo from models.department import Department from models.dorm_allocation import DormAllocation from models.dorm_building import DormBuilding from models.dorm_room import DormRoom from models.fee import Fee from models.major import Major from models.student import Student from schemas.base import dump_model from schemas.entity import ( CheckinVO, ClassInfoVO, DormAllocationVO, DormRoomVO, FeeVO, MajorVO, StudentVO, ) from utils.datetime_util import normalize_value from utils.response import page_result def _paginate_rows(rows: list, total: int, page_num: int, page_size: int) -> dict: """构造分页结果""" return page_result(rows, total, page_num, page_size) def get_student_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None, dept_id: Optional[int] = None, checkin_status: Optional[int] = None, ) -> dict: """ 分页查询学生(含院系、专业、班级名称) """ query = ( select( Student, Department.dept_name.label("dept_name"), Major.major_name.label("major_name"), ClassInfo.class_name.label("class_name"), ) .outerjoin(Department, Student.dept_id == Department.id) .outerjoin(Major, Student.major_id == Major.id) .outerjoin(ClassInfo, Student.class_id == ClassInfo.id) ) count_query = select(func.count()).select_from(Student) if keyword: like = f"%{keyword}%" cond = ( Student.name.like(like) | Student.student_no.like(like) | Student.username.like(like) ) query = query.where(cond) count_query = count_query.where(cond) if dept_id is not None: query = query.where(Student.dept_id == dept_id) count_query = count_query.where(Student.dept_id == dept_id) if checkin_status is not None: query = query.where(Student.checkin_status == checkin_status) count_query = count_query.where(Student.checkin_status == checkin_status) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(Student.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for student, dept_name, major_name, class_name in rows: vo = StudentVO.model_validate(student) data = dump_model(vo) data["deptName"] = dept_name data["majorName"] = major_name data["className"] = class_name data.pop("password", None) records.append(data) return _paginate_rows(records, total, page_num, page_size) def get_student_detail(db: Session, student_id: int) -> Optional[dict]: """ 查询学生详情(含院系、专业、班级名称) """ row = db.execute( select( Student, Department.dept_name.label("dept_name"), Major.major_name.label("major_name"), ClassInfo.class_name.label("class_name"), ) .outerjoin(Department, Student.dept_id == Department.id) .outerjoin(Major, Student.major_id == Major.id) .outerjoin(ClassInfo, Student.class_id == ClassInfo.id) .where(Student.id == student_id) ).first() if not row: return None student, dept_name, major_name, class_name = row data = dump_model(StudentVO.model_validate(student)) data["deptName"] = dept_name data["majorName"] = major_name data["className"] = class_name data.pop("password", None) return data def get_major_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None ) -> dict: """分页查询专业(含院系名称)""" query = ( select(Major, Department.dept_name.label("dept_name")) .outerjoin(Department, Major.dept_id == Department.id) ) count_query = select(func.count()).select_from(Major) if keyword: like = f"%{keyword}%" cond = Major.major_name.like(like) | Major.major_code.like(like) query = query.where(cond) count_query = count_query.where(cond) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(Major.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for major, dept_name in rows: data = dump_model(MajorVO.model_validate(major)) data["deptName"] = dept_name records.append(data) return _paginate_rows(records, total, page_num, page_size) def get_class_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None ) -> dict: """分页查询班级(含专业、院系名称)""" query = ( select( ClassInfo, Major.major_name.label("major_name"), Department.dept_name.label("dept_name"), ) .outerjoin(Major, ClassInfo.major_id == Major.id) .outerjoin(Department, Major.dept_id == Department.id) ) count_query = select(func.count()).select_from(ClassInfo) if keyword: like = f"%{keyword}%" cond = ClassInfo.class_name.like(like) | ClassInfo.class_code.like(like) query = query.where(cond) count_query = count_query.where(cond) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(ClassInfo.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for cls, major_name, dept_name in rows: data = dump_model(ClassInfoVO.model_validate(cls)) data["majorName"] = major_name data["deptName"] = dept_name records.append(data) return _paginate_rows(records, total, page_num, page_size) def get_fee_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None, pay_status: Optional[int] = None, student_id: Optional[int] = None, ) -> dict: """分页查询缴费记录(含学生信息)""" from models.student import Student as Stu query = ( select(Fee, Stu.name.label("student_name"), Stu.student_no.label("student_no")) .outerjoin(Stu, Fee.student_id == Stu.id) ) count_query = select(func.count()).select_from(Fee) if keyword: like = f"%{keyword}%" query = query.where( (Stu.name.like(like)) | (Stu.student_no.like(like)) | (Fee.fee_item.like(like)) ) count_query = count_query.join(Stu, Fee.student_id == Stu.id).where( (Stu.name.like(like)) | (Stu.student_no.like(like)) | (Fee.fee_item.like(like)) ) if pay_status is not None: query = query.where(Fee.pay_status == pay_status) count_query = count_query.where(Fee.pay_status == pay_status) if student_id is not None: query = query.where(Fee.student_id == student_id) count_query = count_query.where(Fee.student_id == student_id) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(Fee.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for fee, student_name, student_no in rows: data = dump_model(FeeVO.model_validate(fee)) data["studentName"] = student_name data["studentNo"] = student_no records.append(data) return _paginate_rows(records, total, page_num, page_size) def get_checkin_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None ) -> dict: """分页查询报到记录(含学生信息)""" from models.student import Student as Stu query = ( select(Checkin, Stu.name.label("student_name"), Stu.student_no.label("student_no")) .outerjoin(Stu, Checkin.student_id == Stu.id) ) count_query = select(func.count()).select_from(Checkin) if keyword: like = f"%{keyword}%" query = query.where((Stu.name.like(like)) | (Stu.student_no.like(like))) count_query = count_query.join(Stu, Checkin.student_id == Stu.id).where( (Stu.name.like(like)) | (Stu.student_no.like(like)) ) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(Checkin.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for checkin, student_name, student_no in rows: data = dump_model(CheckinVO.model_validate(checkin)) data["studentName"] = student_name data["studentNo"] = student_no records.append(data) return _paginate_rows(records, total, page_num, page_size) def get_dorm_room_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None, building_id: Optional[int] = None, ) -> dict: """分页查询宿舍房间(含楼栋名称)""" query = ( select(DormRoom, DormBuilding.building_name.label("building_name")) .outerjoin(DormBuilding, DormRoom.building_id == DormBuilding.id) ) count_query = select(func.count()).select_from(DormRoom) if keyword: query = query.where(DormRoom.room_no.like(f"%{keyword}%")) count_query = count_query.where(DormRoom.room_no.like(f"%{keyword}%")) if building_id is not None: query = query.where(DormRoom.building_id == building_id) count_query = count_query.where(DormRoom.building_id == building_id) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(DormRoom.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [] for room, building_name in rows: data = dump_model(DormRoomVO.model_validate(room)) data["buildingName"] = building_name records.append(data) return _paginate_rows(records, total, page_num, page_size) def _allocation_row_to_dict(row) -> dict: """将宿舍分配 JOIN 查询结果转为字典""" allocation, student_name, student_no, building_id, building_name, room_no = row data = dump_model(DormAllocationVO.model_validate(allocation)) data["studentName"] = student_name data["studentNo"] = student_no data["buildingId"] = building_id data["buildingName"] = building_name data["roomNo"] = room_no return data def get_dorm_allocation_page( db: Session, page_num: int, page_size: int, keyword: Optional[str] = None ) -> dict: """分页查询宿舍分配(含学生、房间、楼栋信息)""" from models.student import Student as Stu query = ( select( DormAllocation, Stu.name.label("student_name"), Stu.student_no.label("student_no"), DormRoom.building_id.label("building_id"), DormBuilding.building_name.label("building_name"), DormRoom.room_no.label("room_no"), ) .outerjoin(Stu, DormAllocation.student_id == Stu.id) .outerjoin(DormRoom, DormAllocation.room_id == DormRoom.id) .outerjoin(DormBuilding, DormRoom.building_id == DormBuilding.id) ) count_query = select(func.count()).select_from(DormAllocation) if keyword: like = f"%{keyword}%" query = query.where((Stu.name.like(like)) | (Stu.student_no.like(like))) count_query = count_query.join(Stu, DormAllocation.student_id == Stu.id).where( (Stu.name.like(like)) | (Stu.student_no.like(like)) ) total = db.scalar(count_query) or 0 rows = ( db.execute( query.order_by(DormAllocation.id.desc()) .offset((page_num - 1) * page_size) .limit(page_size) ) .all() ) records = [_allocation_row_to_dict(row) for row in rows] return _paginate_rows(records, total, page_num, page_size) def get_dorm_allocation_by_student(db: Session, student_id: int) -> Optional[dict]: """查询指定学生的宿舍分配""" from models.student import Student as Stu row = db.execute( select( DormAllocation, Stu.name.label("student_name"), Stu.student_no.label("student_no"), DormRoom.building_id.label("building_id"), DormBuilding.building_name.label("building_name"), DormRoom.room_no.label("room_no"), ) .outerjoin(Stu, DormAllocation.student_id == Stu.id) .outerjoin(DormRoom, DormAllocation.room_id == DormRoom.id) .outerjoin(DormBuilding, DormRoom.building_id == DormBuilding.id) .where(DormAllocation.student_id == student_id) .limit(1) ).first() if not row: return None return _allocation_row_to_dict(row) def get_dashboard_stats(db: Session) -> dict[str, Any]: """ 获取首页统计数据 """ checkin_stats = db.execute( text( """ SELECT COUNT(*) AS total, SUM(CASE WHEN checkin_status = 1 THEN 1 ELSE 0 END) AS checkedIn, SUM(CASE WHEN checkin_status = 0 THEN 1 ELSE 0 END) AS notCheckedIn FROM t_student """ ) ).mappings().first() dept_stats = db.execute( text( """ SELECT d.dept_name AS name, COUNT(s.id) AS value FROM t_department d LEFT JOIN t_student s ON d.id = s.dept_id GROUP BY d.id, d.dept_name ORDER BY value DESC """ ) ).mappings().all() fee_stats = db.execute( text( """ SELECT COUNT(*) AS total, SUM(CASE WHEN pay_status = 1 THEN 1 ELSE 0 END) AS paid, SUM(CASE WHEN pay_status = 0 THEN 1 ELSE 0 END) AS unpaid FROM t_fee """ ) ).mappings().first() checkin_trend = db.execute( text( """ SELECT DATE_FORMAT(checkin_time, '%Y-%m-%d') AS date, COUNT(*) AS count FROM t_checkin WHERE checkin_time IS NOT NULL GROUP BY DATE_FORMAT(checkin_time, '%Y-%m-%d') ORDER BY date ASC """ ) ).mappings().all() return normalize_value( { "checkinStats": dict(checkin_stats) if checkin_stats else {}, "deptStats": [dict(row) for row in dept_stats], "feeStats": dict(fee_stats) if fee_stats else {}, "checkinTrend": [dict(row) for row in checkin_trend], } )
<template> <div class="page-container"> <!-- 统计卡片 --> <el-row :gutter="20" class="stat-row"> <el-col :span="6"> <div class="stat-card"> <div class="stat-label">新生总数</div> <div class="stat-value">{{ stats.checkinStats?.total || 0 }}</div> </div> </el-col> <el-col :span="6"> <div class="stat-card green"> <div class="stat-label">已报到</div> <div class="stat-value">{{ stats.checkinStats?.checkedIn || 0 }}</div> </div> </el-col> <el-col :span="6"> <div class="stat-card orange"> <div class="stat-label">未报到</div> <div class="stat-value">{{ stats.checkinStats?.notCheckedIn || 0 }}</div> </div> </el-col> <el-col :span="6"> <div class="stat-card blue"> <div class="stat-label">缴费完成率</div> <div class="stat-value">{{ payRate }}%</div> </div> </el-col> </el-row> <!-- 图表区域 --> <el-row :gutter="20" style="margin-top: 20px"> <el-col :span="12"> <div class="chart-card"> <h3>各院系新生人数</h3> <div ref="deptChartRef" class="chart-box"></div> </div> </el-col> <el-col :span="12"> <div class="chart-card"> <h3>报到情况统计</h3> <div ref="checkinChartRef" class="chart-box"></div> </div> </el-col> </el-row> <el-row :gutter="20" style="margin-top: 20px"> <el-col :span="12"> <div class="chart-card"> <h3>缴费情况统计</h3> <div ref="feeChartRef" class="chart-box"></div> </div> </el-col> <el-col :span="12"> <div class="chart-card"> <h3>每日报到趋势</h3> <div ref="trendChartRef" class="chart-box"></div> </div> </el-col> </el-row> </div> </template> <script setup> import { ref, computed, onMounted, onUnmounted } from 'vue' import * as echarts from 'echarts' import { getDashboardStats } from '@/api/index' const stats = ref({}) const deptChartRef = ref() const checkinChartRef = ref() const feeChartRef = ref() const trendChartRef = ref() let charts = [] /** 缴费完成率 */ const payRate = computed(() => { const f = stats.value.feeStats if (!f || !f.total || f.total == 0) return 0 return Math.round((f.paid / f.total) * 100) }) /** 初始化图表 */ const initCharts = () => { // 院系柱状图 const deptChart = echarts.init(deptChartRef.value) const deptData = stats.value.deptStats || [] deptChart.setOption({ tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: deptData.map(d => d.name), axisLabel: { rotate: 15 } }, yAxis: { type: 'value', minInterval: 1 }, series: [{ type: 'bar', data: deptData.map(d => d.value), itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: '#667eea' }, { offset: 1, color: '#764ba2' } ])}, barWidth: '40%', borderRadius: [6, 6, 0, 0] }] }) charts.push(deptChart) // 报到饼图 const checkinChart = echarts.init(checkinChartRef.value) const cs = stats.value.checkinStats || {} checkinChart.setOption({ tooltip: { trigger: 'item' }, legend: { bottom: 0 }, series: [{ type: 'pie', radius: ['40%', '70%'], data: [ { name: '已报到', value: cs.checkedIn || 0, itemStyle: { color: '#67c23a' } }, { name: '未报到', value: cs.notCheckedIn || 0, itemStyle: { color: '#f56c6c' } } ], label: { formatter: '{b}: {c}人 ({d}%)' } }] }) charts.push(checkinChart) // 缴费饼图 const feeChart = echarts.init(feeChartRef.value) const fs = stats.value.feeStats || {} feeChart.setOption({ tooltip: { trigger: 'item' }, legend: { bottom: 0 }, series: [{ type: 'pie', radius: ['40%', '70%'], data: [ { name: '已缴费', value: fs.paid || 0, itemStyle: { color: '#409eff' } }, { name: '未缴费', value: fs.unpaid || 0, itemStyle: { color: '#e6a23c' } } ], label: { formatter: '{b}: {c}项 ({d}%)' } }] }) charts.push(feeChart) // 报到趋势折线图 const trendChart = echarts.init(trendChartRef.value) const trend = stats.value.checkinTrend || [] trendChart.setOption({ tooltip: { trigger: 'axis' }, xAxis: { type: 'category', data: trend.map(t => t.date) }, yAxis: { type: 'value', minInterval: 1 }, series: [{ type: 'line', data: trend.map(t => t.count), smooth: true, areaStyle: { color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ { offset: 0, color: 'rgba(64,158,255,0.3)' }, { offset: 1, color: 'rgba(64,158,255,0.05)' } ])}, itemStyle: { color: '#409eff' }, lineStyle: { width: 3 } }] }) charts.push(trendChart) } onMounted(async () => { const res = await getDashboardStats() stats.value = res.data initCharts() window.addEventListener('resize', () => charts.forEach(c => c.resize())) }) onUnmounted(() => { charts.forEach(c => c.dispose()) charts = [] }) </script> <style scoped> .stat-row { margin-bottom: 0; } .chart-card { background: #fff; border-radius: 12px; padding: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.06); } .chart-card h3 { margin-bottom: 12px; color: #303133; font-size: 16px; border-left: 4px solid #409eff; padding-left: 10px; } .chart-box { height: 320px; } </style>

相关新闻

  • 2026亲测有效教程:图片大小必须小于300K怎么弄 - 效率工具研究所
  • 《数字人文技术及运用》课程有感:且教且学且珍惜
  • ChatLaw终极指南:如何快速部署中文法律AI助手并掌握核心功能

最新新闻

  • 萧邦售后地址及维修保养服务点查询权威公示(2026年7月最新) - 萧邦中国官方服务中心
  • 2026年7月最新格拉苏蒂沈阳银泰百货维修保养服务电话 - 亨得利钟表维修中心
  • 厦门欧米茄回收价格查询与靠谱平台实测排行(2026年7月最新) - 嘉价奢侈品回收平台
  • 2026年7月最新美度金华金义宝龙广场维修保养服务电话 - 亨得利钟表维修中心
  • 2026云南美国留学机构哪家好?昆明本科硕士申请十家机构谁更靠谱 - 环球新视野
  • 2026年7月最新南通萧邦官方售后客服电话及服务网点地址查询 - 萧邦中国官方服务中心

日新闻

  • 百达翡丽官方服务项目及价格查询|维修地址与电话权威信息通告(2026年7月最新) - 百达翡丽服务中心
  • 2026年药食同源冲泡饮品哪家好:衡身堂三伏天内调外养 - 晚香时候
  • 芝柏官方更换原装表带价格查询|详细地址与24小时客服电话权威信息公告(2026年7月最新) - 亨得利官方服务中心

周新闻

  • SaaS软件行业GEO实践:AI搜索时代的品牌可见性与获客新路径
  • 什么是PCTFE?医药高端包装的“防潮王牌“材料
  • 【JVM调优实战】16-可视化利器-JConsole-VisualVM-JMC

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号