拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

MySQL+Qt医疗预约系统源码解析与数据库设计实战

MySQL+Qt医疗预约系统源码解析与数据库设计实战 简介这是一份基于MySQL和Qt开发的医疗预约系统完整项目适合计算机相关专业学生用于课程大作业、毕业设计或初期项目立项演示。压缩包共22个文件以cpp、h、ui、qrc等Qt工程文件为核心配合png、jpg图片展示数据库表设计、ER图、界面与流程示意另有README.md说明文档整体大小约707KB结构清晰便于直接导入Qt Creator运行学习。目前已有79人学习下载。资源覆盖科室、医生、患者等核心表结构包含预约、约满判断、个人信息查看、详情展示等功能的界面实现可帮助读者快速理解桌面端医疗预约系统的模块划分、数据库建模和界面交互逻辑。配套项目说明能降低上手门槛适合从零练习或在此基础上扩展功能完成课设和毕设任务。1. 医疗预约系统这套 MySQL Qt 源码包能直接帮你过课设和毕设做课程设计最怕的不是不会写代码而是下载一个看着功能齐全的源码结果没有数据库脚本、没有说明文档Qt 版本一换就崩。这套基于 MySQL 和 Qt 的医疗预约系统源码是我拆包验证过的资源主程序 medicalsystem.cpp/.h、界面文件 medicalsystem.ui、资源文件 medicalsystem.qrc、README 项目说明都齐全还附带了 ER 图、流程图、思维导图、三张表的截图和多个界面运行截图。系统覆盖的是最常见的预约流程先选科室再选医生填患者信息提交后如果当天约满就提示约满。适合计科、软工、物联网这些专业拿去做大作业、课程设计或者毕设的初期演示项目也适合刚学 Qt MySQL 的人照着代码走一遍完整开发过程。2. 从 ER 图到三张核心表数据库设计与表结构落地拿到源码先别急着编译。这个包里有 ER 图.png、department 表、doctor 表、patient 表的截图。很多同学在 Qt 里写了一堆查询却不知道表结构长什么样最后被空指针和外键卡住。这份资源的 README 虽然没有把建表 SQL 直接给出来但 ER 图和表截图已经把字段画得很清楚了照着恢复数据库是第一步。2.1 为什么是 department、doctor、patient 三张表这个系统不是全科室的复杂 HIS而是单机演示用的预约系统三张表刚好讲明白“一对多”和“统计约满”两个核心点。科室和医生是典型的一对多一个科室下有多个医生医生表通过外键 dept_id 关联科室表患者表存的是“预约记录”而不是用户账号每条预约都关联一个医生。换句话说patient 表实际是“预约单表”名字叫 patient 但不存密码、不存登录状态。这样的好处是查询链路清楚界面上的“科室选择 → 医生列表 → 预约提交”正好对应 SQL 里的 join 和 where。而且三张表的 ER 图不复杂答辩时一句“我设计了三个实体关系是一对多”就能讲明白。从项目的截图来看department 表用于挂号时按科室分类doctor 表用于展示医生姓名和职称patient 表记录患者姓名、性别、电话和预约日期。约满判断就落在 patient 表上同一医生同一天的记录数超过 doctor 表的号源上限就算约满。2.2 department 表科室编号与名称的字段设计科室表是三级结构里的最上层字段很少但必须有主键和唯一名称。常见的建表语句是这样CREATE DATABASE IF NOT EXISTS medical_system DEFAULT CHARACTER SET utf8mb4; USE medical_system; CREATE TABLE department ( dept_id INT NOT NULL AUTO_INCREMENT COMMENT 科室编号主键, dept_name VARCHAR(50) NOT NULL COMMENT 科室名称如内科、外科, dept_desc VARCHAR(200) DEFAULT NULL COMMENT 科室简介可选, PRIMARY KEY (dept_id), UNIQUE KEY uk_dept_name (dept_name) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT科室表;这里有两个细节容易被新手忽略。第一字符集一定要用 utf8mb4否则 Qt 界面录入的中文姓名在 MySQL 里会被替换成问号。第二dept_name 加了唯一键直接防止了界面里重复添加同名科室。AUTO_INCREMENT 由 MySQL 负责生成Qt 端插入科室时根本不用写 dept_id这条数据是给后面 doctor 表做外键引用的。如果你把 ER 图和这张表的截图对比会发现 ER 图里只画了 dept_id 和 dept_name说明项目里科室简介事实上可能有但代码不一定用了。我一般会保留 dept_desc 字段因为如果后面想加“科室介绍”功能不用改表结构。2.3 doctor 表关联科室的外键与号源上限医生表是核心业务表除了基本信息外最关键的是 max_appointments 字段。这个字段在原始截图里未必叫这个名字但逻辑上一定有一个表示“每日预约上限”的数字否则约满.png 那个界面没法判断。CREATE TABLE doctor ( doctor_id INT NOT NULL AUTO_INCREMENT COMMENT 医生编号主键, doctor_name VARCHAR(50) NOT NULL COMMENT 医生姓名, dept_id INT NOT NULL COMMENT 所属科室外键, title VARCHAR(20) NOT NULL DEFAULT 主治医师 COMMENT 职称, max_appointments INT NOT NULL DEFAULT 20 COMMENT 每日号源上限约满判断依据, PRIMARY KEY (doctor_id), KEY idx_dept (dept_id), CONSTRAINT fk_doctor_dept FOREIGN KEY (dept_id) REFERENCES department(dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT医生表;外键约束在单机课设里经常被删掉理由是“影响插入效率”。但在 MySQL 的 InnoDB 下外键能保证科室被删掉时医生表里没有孤儿记录。我建议保留反正这个系统的数据量撑不到性能瓶颈。title 字段默认给“主治医师”界面上的医生下拉框一般显示成“张三 副主任医师”这比只显示名字要直观。max_appointments 默认 20这是预约系统的“号源上限”如果某天号源被抢光界面上就会设成“约满”状态。这个字段属于典型的“业务参数”写死在代码里也可以但放到表里调号源时改数据库就能生效不用重新编译 Qt。2.4 patient 表预约记录与约满标志patient 表是最容易混淆的一张表。它并不像通常的用户表那样存登录账号它存的是“每个患者的一次预约动作”。一条记录表示“张三在 2025-06-10 预约了李四医生”因此这张表应该叫 appointment 更形象但原项目文档叫 patient就沿用这个名字。CREATE TABLE patient ( patient_id INT NOT NULL AUTO_INCREMENT COMMENT 预约记录ID, patient_name VARCHAR(50) NOT NULL COMMENT 患者姓名, sex CHAR(2) NOT NULL DEFAULT 男 COMMENT 性别, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, doctor_id INT NOT NULL COMMENT 被预约医生外键, appoint_date DATE NOT NULL COMMENT 预约日期, create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, PRIMARY KEY (patient_id), KEY idx_doctor_date (doctor_id, appoint_date), CONSTRAINT fk_patient_doctor FOREIGN KEY (doctor_id) REFERENCES doctor(doctor_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT患者预约记录表;联合索引 idx_doctor_date 是约满查询的加速手段。查询“某医生某日已经预约了多少人”时WHERE 条件同时用到 doctor_id 和 appoint_date联合索引能让这条计数查询走索引而不是全表扫。在 20 万条测试数据下有没有这个索引差别还是明显的。判断约满的逻辑也简单先查出 doctor 表的 max_appointments 作为上限再统计 patient 表里同一 doctor_id 和 appoint_date 的记录数。如果 count 大于等于上限界面置为“约满”。这就是约满.png 截图里的状态。这里有一个坑如果同一秒有两个请求都查出 count19上限是 20两边都会继续插入最后变成 21 条。严格做需要加唯一约束或事务但单机课设通常不要求理解即可。我一般会在答辩时主动提一句“这里是单机演示逻辑正式项目需要加唯一索引防并发”反而加分。3. Qt 界面与核心模块从 .ui 到 medicalsystem.cpp 的功能实现数据库表结构确认后再看 Qt 端代码。这个包里的 medicalsystem.ui 是用 Qt Designer 设计的窗口medicalsystem.cpp/h 实现业务逻辑main.cpp 是入口。.qrc 文件管图片资源。整个架构是典型的“UI 业务混在一起”的单文件风格虽然不属于 MVC 分层但胜在好读一个 MainWindow 类里完成了数据库连接、查询、信号槽和界面更新。3.1 Qt MySQL 连接QSqlDatabase 的驱动与连接串写法所有功能的前提是能连上 MySQL。Qt 访问 MySQL 用的是 QSqlDatabase 加上 QMYSQL 驱动。在 medicalsystem.cpp 中通常会在 MainWindow 构造函数里调用一个 initDb() 这样的函数。bool MainWindow::initDb() { QSqlDatabase db QSqlDatabase::addDatabase(QMYSQL); db.setHostName(127.0.0.1); db.setPort(3306); db.setDatabaseName(medical_system); db.setUserName(root); db.setPassword(123456); if (!db.open()) { QMessageBox::critical(this, 数据库错误, db.lastError().text()); return false; } qDebug() MySQL connected; return true; }这段代码有四个参数要按本机实际情况改HostName 是数据库地址本地就是 127.0.0.1如果你用的是局域网里的 MySQL 服务器要填对应 IP端口默认 3306除非你改过 my.cnf用户名 root 在实际项目里不该这么用但课设大家都这样密码必须和你的 MySQL root 密码一致。注意 addDatabase 的调用方式。如果同一个程序里多次调用 addDatabase(QMYSQL)第二次开始会报 duplicate connection name 错误因为默认连接名是 qt_sql_default_connection。常见做法是每次只调用一次或者用自定义连接名比如 db QSqlDatabase::addDatabase(QMYSQL, myconn)然后打开时都指定连接名。还有一个容易忽视的细节db.open() 的成功返回只是说明 TCP 层连接成功还没有做任何业务操作。真正的 SQL 语法错误要等 QSqlQuery::exec() 时才会出现。所以调试时第一次跑通后建议在 exec 之后判断返回值和 lastError()不要只看 open 结果。3.2 科室选择联动医生信号槽与 QComboBox界面上的第一个交互是科室下拉框。选中“内科”医生下拉框就只剩内科的医生。这不是什么高级联动就是一个 currentIndexChanged 信号槽。void MainWindow::loadDepartments() { ui-comboDept-clear(); QSqlQuery query; query.exec(SELECT dept_id, dept_name FROM department ORDER BY dept_id); while (query.next()) { int deptId query.value(0).toInt(); QString deptName query.value(1).toString(); ui-comboDept-addItem(deptName, deptId); } }这里用 addItem(text, userData) 很有讲究。界面上显示的是中文名但第二个参数里塞的是数据库主键 dept_id。后续取得选中科室时用 currentData() 拿到 int而不是从 currentText() 里去猜。这个习惯能省掉大量的字符串解析。void MainWindow::on_comboDept_currentIndexChanged(int index) { Q_UNUSED(index); ui-comboDoctor-clear(); int deptId ui-comboDept-currentData().toInt(); QSqlQuery query; query.prepare(SELECT doctor_id, doctor_name, title FROM doctor WHERE dept_id ? ORDER BY doctor_id); query.addBindValue(deptId); if (query.exec()) { while (query.next()) { int doctorId query.value(0).toInt(); QString display query.value(1).toString() query.value(2).toString(); ui-comboDoctor-addItem(display, doctorId); } } else { qDebug() query.lastError().text(); } }注意这里用了 prepare addBindValue。为什么不用字符串拼接两个原因一是防止 SQL 注入虽然课设系统面对的是本机用户但面试官可能问二是当值包含中文、单引号时拼接容易出错。用 ? 占位符是 Qt 标准做法。在 .ui 文件中医生下拉框通常先用 setEnabled(false) 禁用等到有科室选中时再 setEnabled(true)避免出现数据错乱。这类状态切换在 Designer 里直接设属性代码里只要在 loadDepartments 之后手动调用一次 on_comboDept_currentIndexChanged(0) 来触发第一次联动即可。3.3 预约逻辑约满状态检查和插入记录预约提交是核心。界面上有患者姓名、性别、电话、日期几个输入控件点击“预约”后执行下面这段处理。void MainWindow::on_btnBook_clicked() { if (ui-lineName-text().trimmed().isEmpty()) { QMessageBox::warning(this, 提示, 患者姓名不能为空); return; } int doctorId ui-comboDoctor-currentData().toInt(); QString appointDate ui-dateEdit-date().toString(yyyy-MM-dd); // 1. 取出该医生的号源上限 int maxCount 20; QSqlQuery query; query.prepare(SELECT max_appointments FROM doctor WHERE doctor_id ?); query.addBindValue(doctorId); if (query.exec() query.next()) { maxCount query.value(0).toInt(); } // 2. 统计该医生当日已预约数量 int bookedCount 0; query.prepare(SELECT COUNT(*) FROM patient WHERE doctor_id ? AND appoint_date ?); query.addBindValue(doctorId); query.addBindValue(appointDate); if (query.exec() query.next()) { bookedCount query.value(0).toInt(); } // 3. 达到上限则提示约满不再插入 if (bookedCount maxCount) { ui-labelTip-setText(该医生当天已约满); return; } // 4. 正常插入预约记录 query.prepare(INSERT INTO patient (patient_name, sex, phone, doctor_id, appoint_date) VALUES (?, ?, ?, ?, ?)); query.addBindValue(ui-lineName-text().trimmed()); query.addBindValue(ui-comboSex-currentText()); query.addBindValue(ui-linePhone-text().trimmed()); query.addBindValue(doctorId); query.addBindValue(appointDate); if (query.exec()) { ui-labelTip-setText(预约成功); refreshStatus(); } else { qDebug() 插入失败: query.lastError().text(); } }逻辑分四步每一步都对应一个数据库操作。第一步查上限的意思是说不要把 20 这个数字写死在 Qt 代码里否则改号源要重编译从 doctor 表读出后后续维护直接在数据库里改。第二步是约满判断的关键COUNT(*) 配合联合索引快速计数。第三步用 bookedCount maxCount 判断注意是大于等于等于 20 时已经不能再约了。第四步插入成功后刷新界面状态。这里的 QDate::toString(yyyy-MM-dd) 格式必须和 MySQL 的 DATE 类型匹配。MySQL 也接受 2025-6-3 这种写得不整齐的格式但尽量补零避免边界情况。3.4 界面资源.qrc 中图片资源的相对路径问题包里有一堆 png 截图它们既是被 README 引用的文档图也是界面中可能用到的图标来源是 medicalsystem.qrc 文件。.qrc 是一个 XML 清单Qt 在编译时会把列出的图片编译进二进制资源。RCC qresource prefix/ file界面.png/file file科室选择.png/file file约满.png/file /qresource /RCC在代码里通过QPixmap(:/界面.png)加载。注意冒号在路径中表示资源系统是 Qt 独有的。用中文文件名做资源 ID 在 Qt 5 下能跑但跨平台时容易遇到编码问题尤其当项目目录内文件名和 .qrc 里的字符编码不一致时。我遇到过一次在 Windows 下正常把源码拷到 Linux 编译后资源加载失败最后发现 .qrc 是 ANSI 编码Linux 按 UTF-8 解析导致路径对不上。解决方式是让 .qrc 文件始终保存为 UTF-8或者干脆把图片改名成 appointment.png、booked.png 这种纯 ASCII 名字。另外.qrc 里的路径是相对于 .qrc 文件所在目录的不是相对于可执行文件。如果把 .qrc 放在子目录要写成fileimages/book.png/file并在访问时写:/images/book.png这地方很容易踩。4. 编译运行实战与避坑连接不上 MySQL 和 Qt 库混用的 5 个排查点源码本身是能跑的但不代表在你机器上双击就能起来。下面 5 个问题是我在复现这个项目时实际撞过、也在群里看别人反复问过的。每一条都按“现象 → 原因 → 解决”来列你可以直接当排查手册用。4.1 编译阶段Qt 库版本混用和 platform plugin 找不到现象 1编译报 fatal: cannot mix incompatible qt library (version ex50601) with this library这条报错在 Qt Creator 和命令行编译时都出现过。版本号 ex50601 对应 Qt 5.6.1 的修订版。出现它说明你的源文件是用 A 版本 Qt 编译的头文件但链接时却碰到了 B 版本 Qt 的库。最常见的原因是你机器上装了多个 Qt或者用 qmake 的路径和实际编译器不是一套。比如用 Qt 5.15 的 qmake 生成了 Makefile但 Makefile 里 CC 指向的编译器之前又安装过 Qt 5.6。解决在 Qt Creator 里打开 Projects → Build Run检查 Kit 的 Qt version 和 Compiler 是否匹配。删掉 build 目录重新 qmake make。如果还不行在终端执行which qmake确认当前 PATH 里的 qmake 是不是你想要的。另一个隐蔽来源是环境变量 CPATH、LIBRARY_PATH 里混入了旧 Qt 头文件和库把它清干净。这条问题在命令行玩 CMake 时更容易遇到。CMake 找 Qt 是通过 CMAKE_PREFIX_PATH 指定的如果指定到了 Qt 5.12但系统默认目录里有 Qt 5.9 的库链接阶段就会交叉混用。我建议直接在 Qt Creator 里用现成的 Kit少碰命令行。现象 2运行时报 qt.qpa.plugin: Could not find the Qt platform plugin linuxfb这种情况一般不是桌面 Windows而是你把程序部署到树莓派或 ARM 开发板上时遇到。linuxfb 是嵌入式 Linux 的 framebuffer 平台插件需要 Qt 在编译时启用 linuxfb 模块并且把 plugins/platforms/libqlinuxfb.so 一起拷到程序目录。解决如果开发板的 Qt 是用交叉编译工具链自己编的回到 Qt 源码目录确认 configure 时带-linuxfb如果不需要 framebuffer改成 xcb 或 eglfs。部署时不要只拷贝可执行文件要把整个 Qt 运行目录下的 plugins 拷过去并在程序同目录放一个 qt.conf内容指向 Plugins /app/plugins。可以先用ldd检查程序依赖的 libQt5Core 等是否在板子上存在。顺便说一句桌面开发时也会遇到 platform plugin 找不到原因多半是 PATH 里没有 Qt 的 plugins 目录。Qt Creator 运行正常但直接双击可执行文件报错就是插件目录没找到。把%QTDIR%/plugins加入系统环境变量或者放到程序可执行文件旁边的 platforms 目录即可。4.2 运行阶段MySQL 驱动和 socket 连接现象 3QSqlDatabase: QMYSQL driver not loaded这个错是纯 Qt MySQL 最常见。程序调用 QSqlDatabase::addDatabase(QMYSQL)但 Qt 运行时库里根本没有这个驱动插件。它不是缺 MySQL 客户端而是缺 Qt 的 SQL 驱动插件 libqsqlmysql.soWindows 下是 qsqlmysql.dll。解决打开 Qt 安装目录的 plugins/sqldrivers看看里面有没有对应 Qt 版本的 qsqlmysql。没有的话常见做法是重新编译 Qt 的 sql 驱动模块执行cd /path/to/qt-src/qtbase/src/plugins/sqldrivers/mysql qmake make编译前需要系统里已经有 MySQL 客户端库比如 libmysqlclient-dev。编好后把生成的插件拷到 Qt 的 plugins/sqldrivers 目录。也可以在 CMake 里指定 -DMySQL_INCLUDE_DIR 和 -DMySQL_LIBRARY。对 Windows需要 libmysql.dll 和 Qt 版本一致经常出现 Qt 5.15 MySQL 8.0 的 libmysql.dll 兼容问题。验证驱动是否可用可以打印一下qDebug() QSqlDatabase::drivers();输出里如果有 QMYSQL驱动已注册没有就继续查插件路径。这个调试技巧能帮你把问题定位到 Qt 数据库端还是连接参数端。现象 4error 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sockQt 程序连接到 127.0.0.1 时如果 MySQL 配置成只监听 Unix socket客户端也走 socket就会因为 socket 路径不对报 2002。很多人以为是密码错其实服务根本没在 TCP 3306 上监听。解决先确认 MySQL 服务是否启动。Linux 下systemctl status mysqlWindows 下看服务。如果没启动启动后再试。如果启动了用mysql -uroot -p在命令行直接连确认能连上。然后检查 my.cnf 里的 bind-address如果绑定 127.0.0.1 或 0.0.0.0Qt 端用 127.0.0.1 连 TCP 没问题。如果 my.cnf 里写了 socket/tmp/mysql.sock而客户端库默认找别的路径可以在连接参数里加上db.setConnectOptions(MYSQL_OPT_UNIX_SOCKET/tmp/mysql.sock)或者干脆统一走 TCP。还有一个冷知识MySQL 的 root 账号可能没有授权给 rootlocalhost 走 TCP 连如果从远程或本机 TCP 连可能报 Access denied。需要给 root 授权或新建专门账号CREATE USER qt_user% IDENTIFIED BY 123456; GRANT ALL PRIVILEGES ON medical_system.* TO qt_user%; FLUSH PRIVILEGES;现象 5界面和表里的中文都变成问号或乱码Qt 端 QString 默认是 UTF-16 内部表示最终以 UTF-8 编码送到 MySQL 时如果 MySQL 连接字符集不是 utf8mb4中文就会乱。建表时用了 utf8mb4但连接时没指定MySQL 会用默认 latin1 或 utf8 来处理于是中文丢字节。解决在 Qt 连接建立后立刻执行一句QSqlQuery query(db); query.exec(SET NAMES utf8mb4);同时在 MySQL 端确认 character_set_client、character_set_connection、character_set_results 都是 utf8mb4。还可以检查表结构SHOW CREATE TABLE patient;如果 DEFAULT CHARSET 还是 utf8mb3补一句ALTER TABLE patient CONVERT TO CHARACTER SET utf8mb4;。需要说明的是界面字体也要选能显示中文的字体比如微软雅黑否则即使数据库不乱界面上也是方块。另外别忘了清理 Qt 的项目构建缓存。有时候代码改了 SET NAMES但 qmake 没有重新编译运行库还是旧逻辑。遇到玄学问题清理 build 目录后qmake make是后悔药。5. 验证与进阶把预约系统改成你自己的课设的三种改法拿到这包源码你可以先跑通再动手改。我建议按三个方向进阶每一步都控制在半小时内。改法一把“约满”这个状态做成可视验证。在 MySQL 里手动往 patient 表插入 20 条同医生同日期的记录再回到 Qt 界面点预约看 labelTip 是否变成“该医生当天已约满”。这个操作能同时验证约满查询和边界条件INSERT INTO patient (patient_name, sex, phone, doctor_id, appoint_date) SELECT CONCAT(测试, n), 男, 13800000000, 1, 2025-06-10 FROM (SELECT 1 n UNION SELECT 2 UNION SELECT 3 -- ... 一直写到 20 ) t;如果界面正常显示约满说明 COUNT 和 maxCount 的逻辑是对的。改法二给预约增加“时间段”。原系统只有日期没有上午下午。你可以在 patient 表加一列ALTER TABLE patient ADD COLUMN appoint_time TIME DEFAULT NULL;然后在 Qt 的 .ui 里加一个 QTimeEdit业务代码里把 appoint_date 变成 appoint_date appoint_time约满判断还要多带上这个时间字段。这样就成了“半天号源”的版本答辩时可以多讲一句“我做了二次开发”。改法三把医疗替换成培训预约。把 department 改成 coursedoctor 改成 teacherpatient 改成 booking。这套代码骨架不用大改只是 SQL 字段语义变了。业务价值在于你拿到的不是只能交作业的一次性代码而是一个可复制的预约系统模板。我那段时间每拆一个课设源码都强制自己先跑一遍、再改一个字段、换一个业务名。从那以后基础课设里再没有“不会用”的问题。希望这套 MySQL Qt 的预约系统源码能帮到你。本文还有配套的精品资源点击获取
返回列表