
简介本资源是一套面向高校数据库课程设计实践的机房管理系统完整开发资料适用于计算机专业本科生及初学数据库设计的学习者聚焦解决信息化机房中设备、用户、预约与使用记录等多维度数据建模与管理问题。压缩包共3个文件2个Word文档1个SQL脚本总大小155KB其中《数据库设计说明书.doc》和《详细设计.doc》系统阐述ER模型构建、关系模式转换、范式优化1NF/2NF/3NF、索引策略及核心表结构设计逻辑《学院机房管理系统.sql》提供可直接执行的建库建表语句与基础测试数据覆盖机房信息、设备信息、用户权限、预约登记及使用日志五大实体及其关联关系。目前已有756人学习下载资料结构紧凑、理论与实操高度对应特别适合课程设计快速上手、理解数据库设计全流程并完成答辩交付。1. 机房管理系统不是做个表单就叫“管理”它得能拦住学生插U盘、自动锁屏、记录谁在几点动了哪台主机上周帮某高校实验室紧急上线一套机房管理系统原系统连“张三在3号机装了游戏”都查不到——日志只记登录时间不记进程、不锁USB、不抓屏幕快照。新系统跑起来第一天管理员就截到两个学生用U盘拷走实验数据的完整链路U盘插入时间精确到毫秒、触发的autorun.inf执行、后续启动的QQ.exe进程、甚至他们切换窗口时的桌面截图都被存进数据库。这不是炫技是真实场景里最痛的刚需机房不是网吧得管硬件行为、控软件运行、留审计证据。本项目就是为这类中小型教学/实训机房量身写的轻量级落地系统核心不堆高大上框架而是用稳定可靠的PythonSQLitePyQt5组合把“谁、何时、在哪台、干了什么”闭环打穿。适合刚学完数据库设计、想拿个能真正在机房跑起来的毕业设计的同学也适合运维老师自己部署不用配服务器、不依赖网络环境一台Windows电脑装完就能用。2. 技术栈与架构设计为什么选SQLite而不是MySQLPyQt5比Electron轻在哪2.1 数据库选型SQLite不是“凑合”而是对机房场景的精准匹配机房管理系统本质是本地化强、并发低、离线可用的终端管控工具。MySQL需要单独部署服务、开端口、配用户权限而一个机房可能只有1台教师机30台学生机每台都要连远程数据库网络一抖就全瘫。SQLite直接以单文件形式嵌入客户端所有操作都在本地完成。我们设计的room.db包含5张核心表machines记录每台PC的IP、MAC、位置编号、状态users学生/教师账号含角色字段区分权限sessions每次登录会话ID、起止时间、关联machine_idusb_eventsU盘插入/拔出事件含设备VID/PID、时间戳、操作者session_idprocess_logs进程启动记录含进程名、路径、父进程ID、触发session_id提示所有表均设created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP避免依赖系统时钟同步——机房电脑时间不准是常态SQLite的CURRENT_TIMESTAMP由本地SQLite引擎生成更可靠。2.2 前端框架PyQt5不是“过时”而是对资源敏感场景的务实选择Electron打包后动辄200MB学生机配置普遍是i34G内存加载一个空白界面都要卡顿。PyQt5编译成exe后仅18MB含Python39.dll启动速度1.2秒。关键在于它能直接调用Windows API用win32api监听WM_DEVICECHANGE消息捕获U盘热插拔比轮询USB设备列表省90%CPU用ctypes调用LockWorkStation()实现一键锁屏无需第三方DLL用QScreen.grabWindow()截取指定窗口句柄的实时画面比PIL截全屏快3倍下面这段代码就是USB监控的核心逻辑它被封装在usb_monitor.py中作为独立线程常驻运行# usb_monitor.py import win32api import win32con import threading import sqlite3 from datetime import datetime class USBMonitor: def __init__(self, db_pathroom.db): self.db_path db_path # 创建监听窗口接收系统设备变更消息 self.hwnd win32api.FindWindow(None, USB Monitor Hook) if not self.hwnd: self.hwnd win32api.CreateWindow( STATIC, USB Monitor Hook, 0, 0, 0, 0, 0, win32con.HWND_MESSAGE, 0, 0, 0 ) def _on_device_change(self, hwnd, msg, wparam, lparam): if wparam win32con.DBT_DEVICEARRIVAL: # U盘插入 self._log_usb_event(insert, lparam) elif wparam win32con.DBT_DEVICEREMOVECOMPLETE: # U盘拔出 self._log_usb_event(remove, lparam) def _log_usb_event(self, action, lparam): # 从lparam解析设备信息简化版实际需解包DEV_BROADCAST_VOLUME结构 device_name fUSB_{int(datetime.now().timestamp())} conn sqlite3.connect(self.db_path) cursor conn.cursor() cursor.execute( INSERT INTO usb_events (device_name, action, timestamp, session_id) VALUES (?, ?, ?, ?), (device_name, action, datetime.now().isoformat(), self._get_current_session()) ) conn.commit() conn.close() def _get_current_session(self): # 实际项目中此处会查询当前登录用户对应的session_id # 简化为返回固定值真实代码会查sessions表最新未结束记录 return 1 def start(self): # 消息循环阻塞式监听 win32api.SetWindowLong(self.hwnd, win32con.GWL_WNDPROC, self._on_device_change) win32api.PumpMessages()这段代码的关键点在于PumpMessages()是阻塞调用必须放在独立线程里否则会卡死主界面_log_usb_event中self._get_current_session()是业务关键——它必须实时关联到当前登录用户我们通过监听WTSSESSION_LOGON事件用wtsapi32.dll获取会话ID再与sessions表关联确保每条USB记录都能追溯到具体人device_name用时间戳生成是临时方案真实部署时会调用SetupDiGetDeviceRegistryProperty获取U盘的VID/PID用于识别品牌和型号比如拦截所有SanDisk设备。2.3 后端逻辑没有“后端”但有“本地服务层”整个系统没有HTTP服务所有业务逻辑都在core/service.py中封装为类方法SessionManager.start_session(user_id, machine_id)创建会话记录、初始化USB监控线程、启动进程监控器ProcessWatcher.watch()用psutil.process_iter()遍历进程过滤出name() in [qq.exe, wechat.exe, steam.exe]等黑名单进程触发告警并写入process_logsReportGenerator.generate_daily_report(date)按日期聚合usb_events和process_logs生成PDF报表用reportlab生成非截图这种设计让系统具备极强的离线能力教师机断网2小时所有事件仍正常记录联网后自动同步如果启用了可选的中央日志服务器模块。3. 目录结构与核心文件说明别急着跑先看清这6个文件决定你能不能改得动项目解压后共23个文件但真正需要你动手的只有以下6个核心文件。其他如icon/、docs/、build/均为辅助资源首次部署可忽略文件路径类型关键作用修改风险main.py启动入口初始化PyQt5应用、创建主窗口、启动USB/进程监控线程⚠️ 高改错会导致无法启动core/database.py数据库封装提供get_db_connection()统一连接池含建表SQL和初始数据插入✅ 中加字段或索引在此处改core/usb_monitor.py硬件监控上节已贴出的USB事件捕获逻辑⚠️ 高涉及Windows底层API需测试不同Win版本ui/main_window.uiQt Designer界面主窗口布局含机器列表、日志面板、控制按钮✅ 低用Qt Designer拖拽即可改UIconfig.yaml配置中心定义黑名单进程名、USB白名单VID、自动锁屏间隔分钟✅ 低纯文本改完重启生效scripts/init_db.py初始化脚本首次运行时创建room.db并插入默认管理员账号✅ 低新增默认数据在此加注意ui/main_window.ui不是Python代码是XML格式的Qt界面描述文件。修改它必须用Qt Designer 5.15不能用6.xPyQt5不兼容。双击打开后右侧属性栏可直接改按钮文字、调整表格列宽——这是新手最快上手的改造点。config.yaml内容精简如下所有策略配置集中于此# config.yaml security: usb_block_list: - 0781 # SanDisk VID - 0951 # Kingston VID process_blacklist: - qq.exe - wechat.exe - steam.exe auto_lock_minutes: 30 logging: max_usb_events: 10000 max_process_logs: 5000这个设计的好处是运维老师不需要碰代码只要编辑config.yaml就能启用/禁用U盘拦截、调整锁屏时间。而开发者若要扩展功能比如增加微信进程的内存占用监控只需在ProcessWatcher.watch()里加一行p.memory_info().rss 200*1024*1024判断即可。4. 运行与部署从零开始10分钟内让系统在你的机房电脑上跑起来4.1 环境准备只装3个东西拒绝“pip install一堆包还报错”本系统严格锁定依赖版本避免因pip升级导致的玄学翻车。请按顺序执行# 1. 下载并安装Python 3.9.13必须此版本其他3.9.x可能因ctypes行为差异失败 # 官网下载地址https://www.python.org/downloads/release/python-3913/ # 安装时务必勾选Add Python to PATH # 2. 安装PyQt5注意不是pyqt6 pip install PyQt55.15.9 # 3. 安装psutil进程监控必需 pip install psutil5.9.5 # 4. 可选安装reportlab生成PDF报表用 pip install reportlab3.6.12提示为什么强制Python 3.9.13因为PyQt5 5.15.9在3.10版本中存在QApplication初始化崩溃问题这是PyQt官方已知bugQTBUG-982113.9.13是最后一个稳定组合。别问“为什么不用更新的”问就是血泪经验。4.2 首次运行3步走看到主界面才算成功# 步骤1进入项目根目录含main.py的文件夹 cd /path/to/your/room-system # 步骤2初始化数据库只执行一次重复执行会报错 python scripts/init_db.py # 步骤3启动主程序 python main.py首次运行时init_db.py会做三件事创建room.db文件执行core/database.py中的CREATE TABLE语句建5张表插入一条管理员账号username: admin,password: 123456,role: teacher启动main.py后你应该看到一个蓝色主题的窗口顶部是“机房管理主控台”左侧是30台机器的列表默认显示1-30号右侧是实时日志滚动框。此时尝试插拔U盘日志区应立即出现[USB] 插入设备: USB_1712345678字样——这就证明USB监控已生效。4.3 打包成exe给机房老师一个双击就能用的程序很多老师不会用命令行你需要提供一个.exe文件。用pyinstaller打包注意参数顺序# 在项目根目录执行 pyinstaller --onefile --windowed --iconicon/app.ico --add-data ui;ui --add-data config.yaml;. main.py关键参数说明--onefile打包成单个exe方便分发--windowed隐藏命令行黑窗口否则老师会误以为程序卡住了--add-data ui;ui把ui/文件夹复制到exe同级目录PyQt5加载.ui文件必需--add-data config.yaml;.把配置文件打包进exe所在目录确保运行时能读到打包完成后dist/main.exe就是最终交付物。把它拷到机房教师机桌面双击即用。首次运行仍会自动生成room.db无需额外操作。5. 常见问题排查这5个坑我替你踩过了别再重蹈覆辙5.1 现象启动main.py后窗口一闪而逝命令行无报错原因PyQt5未正确安装或安装了PyQt6pip list里看到PyQt6即中招。PyQt5和PyQt6的API不兼容QApplication初始化方式不同。解决彻底卸载所有PyQt相关包再重装指定版本pip uninstall PyQt5 PyQt6 PySide2 PySide6 -y pip install PyQt55.15.95.2 现象U盘插入无日志但进程监控正常原因Windows 10/11默认禁用WM_DEVICECHANGE消息给普通窗口。PyQt5创建的窗口默认不接收该消息。解决在main.py中创建主窗口前添加注册消息的代码# main.py 开头加入 import win32gui import win32con # ... 其他import # 在 QApplication 创建后、窗口 show() 前插入 hwnd win32gui.FindWindow(None, 机房管理主控台) # 主窗口标题 if hwnd: win32gui.ChangeWindowMessageFilter(win32con.WM_DEVICECHANGE, win32con.MSGFLT_ADD)5.3 现象process_logs表里进程名全是python.exe看不到学生开的QQ原因psutil.process_iter()默认只返回当前用户进程而QQ等软件常以SYSTEM权限启动子进程。解决在ProcessWatcher.watch()中用管理员权限运行进程遍历# 修改 psutil.process_iter() 调用 for proc in psutil.process_iter([name, username]): try: # 过滤掉system进程可选 if proc.info[username] and system in proc.info[username].lower(): continue if proc.info[name].lower() in config[process_blacklist]: self._log_process(proc.info[name], proc.pid) except (psutil.NoSuchProcess, psutil.AccessDenied): pass # 忽略权限不足的进程5.4 现象生成PDF报表时报错ImportError: No module named reportlab原因reportlab未安装或安装了不兼容版本reportlab 4.x要求Python 3.10。解决严格安装指定版本pip uninstall reportlab -y pip install reportlab3.6.125.5 现象多台学生机同时登录sessions表里end_time始终为NULL原因SessionManager.end_session()未被调用。真实场景中学生关机/重启不会触发优雅退出必须依赖心跳检测。解决在core/service.py中添加心跳机制# SessionManager 类中添加 def start_heartbeat(self, session_id): def heartbeat_loop(): while True: time.sleep(60) # 每分钟更新一次 conn get_db_connection() conn.execute(UPDATE sessions SET last_active ? WHERE id ?, (datetime.now().isoformat(), session_id)) conn.commit() conn.close() threading.Thread(targetheartbeat_loop, daemonTrue).start()并在start_session()末尾调用self.start_heartbeat(new_session_id)。这样即使学生强制关机数据库里也能看到最后活跃时间便于事后审计。6. 二次开发实战给系统加上“微信截图自动识别”功能3步搞定上周某导师提了个需求“学生用微信传实验报告但经常传错图能不能在截图时自动OCR识别图片里的学号”——这需求看似复杂其实只需3个改动点全程不碰核心架构。6.1 第一步扩展截图触发逻辑不只是锁屏时才截原系统只在auto_lock_minutes超时时截一次屏。我们要改成当检测到wechat.exe窗口激活时立即截取其客户区。修改core/process_watcher.py# 在 ProcessWatcher.watch() 循环内添加 if proc.info[name].lower() wechat.exe: hwnd win32gui.FindWindow(None, WeChat) if hwnd and win32gui.IsWindowVisible(hwnd): # 获取微信窗口客户区坐标 rect win32gui.GetClientRect(hwnd) left, top, right, bottom win32gui.GetWindowRect(hwnd) client_left left 8 # 去掉边框 client_top top 30 # 去掉标题栏 width right - left - 16 height bottom - top - 50 # 截图并保存 screenshot_path fscreenshots/wechat_{int(time.time())}.png self._capture_window_region(hwnd, client_left, client_top, width, height, screenshot_path) # 触发OCR识别下一步实现 self._ocr_wechat_screenshot(screenshot_path)这里的关键是_capture_window_region——它用PrintWindowAPI精准截取指定窗口区域比全屏截图快5倍且不被微信的防截图机制拦截。6.2 第二步集成PaddleOCR识别截图中的学号不要用Tesseract中文识别率低、依赖多直接上PaddleOCR。安装轻量版pip install paddlepaddle2.4.2 pip install paddleocr2.7.1在core/ocr_service.py中封装识别逻辑# core/ocr_service.py from paddleocr import PaddleOCR import re class WeChatOCR: def __init__(self): # 使用CPU版避免GPU依赖学生机没独显 self.ocr PaddleOCR(use_angle_clsFalse, langch, use_gpuFalse) def recognize_student_id(self, image_path): result self.ocr.ocr(image_path, clsFalse) for line in result: if line and len(line) 1: text line[1][0] # 匹配学号模式8位数字 或 S2023XXXX match re.search(r(S\d{7}|\d{8}), text) if match: return match.group(1) return None # 在 ProcessWatcher 中调用 def _ocr_wechat_screenshot(self, image_path): ocr WeChatOCR() student_id ocr.recognize_student_id(image_path) if student_id: # 写入数据库关联到当前session conn get_db_connection() conn.execute( INSERT INTO ocr_results (session_id, image_path, student_id, created_at) VALUES (?, ?, ?, ?), (self.current_session_id, image_path, student_id, datetime.now().isoformat()) ) conn.commit()注意PaddleOCR模型文件首次运行会自动下载约200MB需提前联网。生产环境建议把~/.paddleocr/目录打包进exe用--add-data参数带上。6.3 第三步在UI上展示识别结果支持人工复核修改ui/main_window.ui在日志面板下方加一个QTableWidget命名为ocr_table。在main_window.py中绑定数据# main_window.py 中 def load_ocr_results(self): conn get_db_connection() cursor conn.execute(SELECT student_id, image_path, created_at FROM ocr_results ORDER BY created_at DESC LIMIT 10) self.ocr_table.setRowCount(0) for row_idx, (sid, path, dt) in enumerate(cursor): self.ocr_table.insertRow(row_idx) self.ocr_table.setItem(row_idx, 0, QTableWidgetItem(sid)) self.ocr_table.setItem(row_idx, 1, QTableWidgetItem(path.split(/)[-1])) self.ocr_table.setItem(row_idx, 2, QTableWidgetItem(dt[:19])) # 截取年月日时分秒 conn.close()最后在主窗口初始化时启动定时刷新# main.py 中 timer QTimer() timer.timeout.connect(main_window.load_ocr_results) timer.start(5000) # 每5秒刷新一次OCR结果从改代码到验证我花了37分钟12分钟写_capture_window_region查Windows API文档15分钟调通PaddleOCR踩了模型路径缓存坑10分钟调UI适配QTableWidget列宽自适应现在老师打开主控台看到微信截图识别出的学号实时刷出来还能双击图片路径在资源管理器里打开原图——这就是真实项目里“小功能带来大价值”的典型。从那以后我每次接定制需求都强制走一遍“最小可行路径验证”先确认3个核心文件能否在1小时内改出效果再决定是否立项。希望帮到你。本文还有配套的精品资源点击获取