ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flask+MySQL学生宿舍管理系统:从数据库设计到并发床位分配与退宿流水

Flask+MySQL学生宿舍管理系统:从数据库设计到并发床位分配与退宿流水 简介这份资源是面向高校计算机相关专业学生的毕业设计、期末大作业与课程设计参考项目采用PythonFlaskMySQL技术栈实现学生宿舍管理系统适合具备一定Web开发基础、需要完整可运行项目练手或答辩展示的读者。压缩包共1955个文件约13.07MB以889个py源码与889个pyc编译文件为核心另含sql数据库脚本、txt说明、exe与wheel依赖组件、js与vue前端资源、png与svg界面素材及json、xml等配置项覆盖后端逻辑、前端页面与数据库结构。已有236人学习下载项目经本地编译验证可运行评审分达98分难度适中且经助教老师审定。读者可据此获得完整源码与数据库、清晰的目录分层结构、依赖安装与运行排错思路以及可直接借鉴的宿舍信息管理、用户权限与数据交互实现方案便于快速搭建环境并完成二次开发。1. 从一份“高分毕设”说起学生宿舍管理系统到底在管什么每年毕业季计算机相关专业的群里都会刷屏同一类关键词python、flask、mysql、学生宿舍管理系统、源码。很多同学拿到题目第一反应是“这不就是个增删改查吗”然后花两周拼出一个能跑但一碰就崩的 demo答辩时被老师追问“并发分配床位怎么保证不冲突”“退宿后历史记录怎么追溯”就哑火了。这个标题背后真正要交付的是一套能自圆其说的信息管理系统学生、宿舍楼、房间、床位、入住退宿记录这几类核心实体之间的关系要理清楚权限要分得开数据要留得下痕迹。它适合两类人一是需要一份结构完整、能讲清楚设计思路的毕设项目的学生二是想用一个小型但五脏俱全的 Web 项目练手 Flask 与 MySQL 配合的开发者。下面我按自己带过几届毕设的经验把从建库到部署的路径拆开讲重点放在那些答辩和上线时真正会被问到的地方。2. 技术选型与数据库设计为什么是 Flask MySQL 而不是别的2.1 Flask 在这个体量项目里的真实优势很多人一上来就问 flask 与 fastapi 比较该怎么选。放到宿舍管理系统这个场景里答案其实很明确这个项目的接口数量通常在 30 个以内没有高并发需求没有异步 IO 密集的调用核心诉求是“结构清晰、容易讲、方便二次修改”。Flask 的轻量和显式在这里是优点而不是缺点——路由、视图、模板、扩展各归各位答辩时你能指着代码说清楚每个请求从哪进、从哪出。FastAPI 的自动文档和类型校验确实漂亮但它带来的 async 心智负担对一个以 CRUD 为主的管理系统来说是过度设计。我一般会建议如果你的题目里没有“实时”“推送”“高并发”这类字眼Flask 就是更稳的选择。选型定了之后工程结构要提前规划不要所有代码堆在一个 app.py 里。常见做法是按功能分层dorm_system/ ├── app.py # 应用入口注册蓝图 ├── config.py # 数据库连接、密钥等配置 ├── models/ # 数据模型层 │ ├── student.py │ ├── dorm.py │ └── record.py ├── views/ # 蓝图视图层 │ ├── auth.py │ ├── student.py │ └── admin.py ├── templates/ # Jinja2 模板 ├── static/ # 静态资源 └── requirements.txt这个结构的好处是每个蓝图对应一类角色或一类操作后面加功能时不会牵一发动全身。requirements.txt 里至少要锁住 Flask、Flask-SQLAlchemy、PyMySQL、Flask-Login 这几个版本号写死避免换台机器就装不上。2.2 宿舍管理系统的表结构怎么设计才不留坑数据库设计是这类项目最容易翻车的地方。血泪经验是不要用“学生表里直接存宿舍号”这种偷懒做法否则换宿舍、退宿、查历史全是麻烦。正确的做法是把“当前状态”和“历史记录”分开。核心表至少这几张表名关键字段说明studentid, name, student_no, gender, class_name学生基本信息student_no 唯一buildingid, name, gender_type, floors宿舍楼gender_type 区分男寝女寝roomid, building_id, room_no, capacity, occupied房间occupied 是已住人数bedid, room_id, bed_no, status床位status 标记空闲/占用checkin_recordid, student_id, bed_id, checkin_time, checkout_time入住退宿流水这里的关键设计点是 bed 表独立出来。一个房间有 4 或 6 个床位把床位做成独立记录分配时就是“选一个 status空闲的 bed”退宿时把 bed 置空、往 checkin_record 写一条 checkout_time。这样任何时刻的住宿情况都能从 bed 表直接读出来历史则全在流水表里答辩时老师问“怎么查某个学生住过哪些宿舍”一条 SQL 就够。建表时字符集统一用 utf8mb4引擎 InnoDB外键该加就加。很多同学怕外键报错就全去掉结果数据一致性全靠代码兜底一旦有并发或异常就出现孤儿记录。MySQL 安装配置教程网上很多Windows 下装 8.0 版本即可注意安装时选 utf8mb4 排序规则不然后面中文乱码又要返工。2.3 用 SQLAlchemy 定义模型并初始化数据库下面这段是 models 层的核心写法用 Flask-SQLAlchemy 把上面的表映射成类from flask_sqlalchemy import SQLAlchemy db SQLAlchemy() class Bed(db.Model): __tablename__ bed id db.Column(db.Integer, primary_keyTrue) room_id db.Column(db.Integer, db.ForeignKey(room.id), nullableFalse) bed_no db.Column(db.Integer, nullableFalse) # 0 表示空闲1 表示已占用用整型方便索引和统计 status db.Column(db.Integer, default0, indexTrue) class CheckinRecord(db.Model): __tablename__ checkin_record id db.Column(db.Integer, primary_keyTrue) student_id db.Column(db.Integer, db.ForeignKey(student.id)) bed_id db.Column(db.Integer, db.ForeignKey(bed.id)) checkin_time db.Column(db.DateTime, server_defaultdb.func.now()) # 为空表示仍在住非空表示已退宿 checkout_time db.Column(db.DateTime, nullableTrue)逻辑说明status 字段加 index 是因为分配床位时要频繁按“空闲”筛选数据量大了没索引会明显变慢。checkout_time 用可空字段而不是单独的状态位好处是“在住”和“已退”用同一个字段就能表达查询当前在住学生就是checkout_time IS NULL。参数上server_default 让数据库负责时间戳避免应用服务器时区不一致的问题。初始化时在 app.py 里调用db.create_all()即可建表但生产环境更推荐用 Flask-Migrate 管理变更否则后期改字段只能手动 ALTER。这一步做完先用几条测试数据跑通增删改查再往下写业务逻辑。3. 核心功能落地床位分配、权限控制与退宿流水3.1 床位分配的事务处理与并发防重床位分配是这个系统里唯一有“竞争”味道的操作。两个管理员同时给两个学生分配同一个床位如果不做处理就会双写。常见做法是把“查空闲床位”和“更新床位状态”放进同一个数据库事务并利用行锁from flask import Blueprint, request, jsonify from models import db, Bed, CheckinRecord assign_bp Blueprint(assign, __name__) assign_bp.route(/assign_bed, methods[POST]) def assign_bed(): student_id request.json.get(student_id) room_id request.json.get(room_id) try: # with_for_update 对选中行加排他锁防止并发抢同一床位 bed Bed.query.filter_by(room_idroom_id, status0)\ .with_for_update().first() if not bed: return jsonify({code: 1, msg: 该房间已满}) bed.status 1 record CheckinRecord(student_idstudent_id, bed_idbed.id) db.session.add(record) db.session.commit() return jsonify({code: 0, bed_id: bed.id}) except Exception as e: db.session.rollback() return jsonify({code: 2, msg: str(e)})逻辑说明with_for_update()会生成SELECT ... FOR UPDATE在 InnoDB 下锁住被选中的那一行第二个请求会阻塞到第一个事务提交然后重新读到 status 已变自然拿不到同一床位。参数上 room_id 由前端传入实际项目里应该先校验该房间性别类型与学生性别是否匹配这一步别省否则会出现男生被分到女寝的尴尬。异常分支必须 rollback不然会话里的脏状态会影响后续请求。3.2 基于角色的权限控制怎么写才不混乱宿舍管理系统通常有三类角色学生、宿管员、管理员。学生只能看自己的信息和记录宿管员能操作本楼栋的分配和退宿管理员管全局。用 Flask-Login 加一个 role 字段就能覆盖from functools import wraps from flask_login import current_user def role_required(*roles): def decorator(f): wraps(f) def wrapper(*args, **kwargs): if current_user.role not in roles: return jsonify({code: 403, msg: 无权限}), 403 return f(*args, **kwargs) return wrapper return decorator assign_bp.route(/checkout/int:record_id, methods[POST]) role_required(admin, housekeeper) def checkout(record_id): record CheckinRecord.query.get_or_404(record_id) if record.checkout_time: return jsonify({code: 1, msg: 该记录已退宿}) record.checkout_time db.func.now() Bed.query.get(record.bed_id).status 0 db.session.commit() return jsonify({code: 0})逻辑说明装饰器把角色判断从业务代码里抽出来视图函数只关心自己的逻辑。退宿操作同时做两件事——写 checkout_time、把床位置空必须在一个事务里否则可能出现“记录显示已退但床位还占着”。参数上 record_id 用 get_or_404 避免空指针重复退宿直接拦截这就是前面说的“留痕”价值任何一次操作都有记录可查。3.3 退宿流水的查询与统计接口答辩时最容易被问的是“你怎么统计某栋楼当前入住率”。有了 bed 和 checkin_record 两张表这个查询很直接assign_bp.route(/stats/int:building_id) role_required(admin, housekeeper) def stats(building_id): total db.session.query(db.func.count(Bed.id))\ .join(Room).filter(Room.building_id building_id).scalar() used db.session.query(db.func.count(Bed.id))\ .join(Room).filter(Room.building_id building_id, Bed.status 1).scalar() rate round(used / total * 100, 2) if total else 0 return jsonify({total: total, used: used, rate: rate})逻辑说明两次 count 查询分别拿总床位和已用床位join Room 是为了按楼栋过滤。参数 rate 做了除零保护这是新手常忽略的细节。如果数据量大可以把这两次查询合并成一次条件聚合但在这个体量下没必要过度优化。统计接口建议加缓存或定时任务避免每次刷新页面都全表扫描。4. 避坑与排查那些答辩前夜才暴露的问题4.1 中文乱码从建库到连接串的三处检查现象学生姓名存进去变成问号或乱码。原因通常有三层数据库字符集不是 utf8mb4、连接串没指定 charset、表创建时继承了库的旧字符集。解决顺序是先SHOW VARIABLES LIKE character%确认库和服务器都是 utf8mb4再检查 SQLAlchemy 连接串写成mysqlpymysql://user:pwdhost/db?charsetutf8mb4最后确认建表语句里每张表都带DEFAULT CHARSETutf8mb4。三处都对了才不会复发。4.2 外键约束导致删数据失败现象想删一个测试学生报Cannot delete or update a parent row。原因是 checkin_record 里还有引用他的记录。这不是 bug 而是保护。解决方式是先删流水再删学生或者把外键设成ON DELETE CASCADE但要谨慎级联删除会连带清掉历史记录毕设里更推荐手动按顺序删逻辑更清楚。4.3 并发分配出现“一床两人”现象压测或演示时偶尔两个学生分到同一床位。原因是没有用事务和行锁两个请求都先读到 status0 再各自更新。解决办法就是 3.1 里的with_for_update()同时把隔离级别保持在默认的 REPEATABLE READ 即可。注意 SQLite 不支持行锁本地开发用 SQLite 测不出这个问题一定要用 MySQL 验证。4.4 部署后静态文件 404现象本地跑得好好的部署到服务器后 CSS、JS 全丢。原因多是 Flask 的 static 路径配置或反向代理没转发/static。解决方式是确认static_folder指向正确Nginx 配置里加一条location /static/ { alias /path/to/static/; }。另外生产环境不要用app.run()用 gunicorn 加 Nginxgunicorn -w 4 -b 127.0.0.1:8000 app:app是常见起法。4.5 时间字段时区错乱现象入住时间比实际早或晚 8 小时。原因是数据库服务器时区、应用服务器时区、Python datetime 三者不一致。解决方式是统一用 UTC 存储展示时再转本地时区或者在建连接时显式设置time_zone。毕设里最简单的做法是全部用数据库的NOW()生成时间应用层不自己拼时间字符串。5. 从能跑到能讲让这份源码在答辩和复用中站得住把功能跑通只是及格线真正拉开差距的是你能不能讲清楚“为什么这么设计”。我一般会做三件事来验证一套宿舍管理系统是否合格。第一写一个脚本模拟 50 个学生随机分配床位跑完检查 bed 表里 status1 的数量是否等于 checkin_record 里 checkout_time 为空的数量两个数对不上就说明事务有漏洞。第二故意在分配过程中杀掉进程重启后看有没有“床位占了但没记录”的脏数据这能验证事务边界。第三把 role_required 装饰器去掉一个用学生账号去调管理员接口确认返回 403 而不是 200权限测试不能只靠点页面。进阶一点的做法是把统计接口改成定时任务预计算用 APScheduler 每小时跑一次把结果写进一张 stats 缓存表页面直接读缓存。这样即使数据量涨到几万条列表页也不会卡。另一个值得加的技巧是给所有写操作加操作日志表记录谁在什么时间改了什么这在真实宿舍管理里是刚需答辩时也是加分项。最后说个我自己的习惯每次改完模型先flask db migrate生成迁移脚本看一眼生成的 SQL 再flask db upgrade绝不直接改数据库。这个习惯帮我省过好几次“本地能跑线上崩”的后悔药。这套东西不难难的是把每个边界都想一遍。希望帮到你。本文还有配套的精品资源点击获取
返回列表