简介:这是一套基于Qt框架开发的C/S架构图书管理系统实战源码,面向C++与Qt初学者及GUI应用开发者,解决图书馆场景下的图书检索、借阅归还、用户管理与数据持久化等核心问题。资源包共120个文件,含21个CPP实现文件(如booksystem.cpp、TCPClientThread.cpp、LendManage.cpp等)、34个H头文件、41张PNG界面截图及资源图,辅以pro工程配置、SQL数据库脚本、QM多语言文件和ICO/QRC图标资源,完整覆盖客户端UI、服务端通信、MySQL交互与业务逻辑分层,压缩包仅382KB,轻量易读。已有307人学习下载,代码结构清晰,模块职责分明——客户端集成QTableView图书列表、QLineEdit搜索框与信号槽交互逻辑,服务端通过booksytemserver.cpp实现TCP连接与数据库事务处理,配套SQL脚本可直接导入MySQL,开箱即用,是掌握Qt网络编程、数据库集成与C/S架构设计的优质入门范例。
1. 这不是又一个“Hello World”——QT图书管理系统到底在解决什么真实问题?
你打开IDE,新建一个Qt Widgets Application,拖几个Label、LineEdit、PushButton,编译运行,弹出个窗口——这叫“能跑”。但真正投入使用的图书管理系统,绝不是界面漂亮就能交差的。我带过三届毕业设计,每年都有学生卡在“为什么数据库连上了却查不到书”“为什么添加新书后列表不刷新”“为什么多用户同时操作会把数据搞乱”这类问题上。标题里反复出现的“QT”“C/S”“图书管理”,其实指向一个非常具体、非常现实的工程场景:用Qt构建一套具备完整业务闭环、可部署到局域网内多台PC、能稳定支撑中小型图书馆或学校资料室日常借阅流程的客户端-服务器架构系统。它不是玩具项目,而是要经受住真实使用压力的工具——管理员每天录入上百条新书信息,学生批量查询某类图书库存,借阅记录必须实时同步且不可篡改。核心关键词“QT”在这里不是指某个UI控件库,而是整套开发范式:信号与槽机制如何解耦界面与逻辑、QSqlDatabase连接池怎么避免阻塞主线程、QStandardItemModel怎样高效更新万级图书列表而不卡顿。而“C/S”二字更是关键约束——它意味着你必须亲手处理TCP连接管理、协议序列化、服务端并发模型,而不是简单调用一个REST API。我见过太多人把Qt当成“高级MFC”来用,结果在数据库事务回滚、网络异常重连、UI线程与工作线程数据同步这些环节栽跟头。这个项目的价值,恰恰在于它逼你直面桌面应用开发中最硬核的那些环节:本地资源调度、跨进程通信、状态一致性维护。如果你正打算用Qt做点真正能落地的东西,而不是只停留在“按钮变色”的层面,那这个图书管理系统就是一块绝佳的试金石。
2. 架构设计:为什么必须是C/S,而不是B/S或纯本地?
2.1 C/S模式的不可替代性:从需求倒推技术选型
看到“图书管理系统”四个字,很多人第一反应是“做个网页不就完了?”——但现实中的使用场景立刻否定了这种想法。我去年帮一所职业院校改造旧系统,他们提出三个刚性需求:第一,图书编目员需要离线录入新书(校园网偶尔中断,但编目不能停);第二,阅览室的自助借还终端必须毫秒级响应(学生排队时,3秒以上的等待就会引发抱怨);第三,管理员要能随时导出Excel报表供上级检查(涉及敏感字段如读者身份证号,绝不允许上传到任何公网服务器)。这三个需求,直接锁死了B/S架构的可行性。浏览器无法可靠访问本地串口读卡器,HTTP请求的延迟无法满足终端实时性,而将个人身份信息经由公网传输更是合规红线。C/S架构在此刻展现出压倒性优势:客户端可完全控制本地硬件(扫码枪、RFID读写器)、所有业务逻辑和数据校验在本地执行(响应快)、敏感数据全程不出内网(安全可控)。我们最终采用的方案是:轻量级TCP服务器(Qt自带QTcpServer)+ 多线程客户端(QThread + QSqlDatabase独立连接)。服务器只负责最核心的原子操作:验证读者证号有效性、锁定图书库存、写入借阅日志。所有复杂的UI交互、缓存策略、离线队列都由客户端自主管理。这种设计让系统既具备中心化管理能力(所有操作留痕),又保留了桌面应用的性能与控制力。你可能会问:“为什么不直接用SQLite做本地数据库?”——因为当5个管理员同时修改同一本书的馆藏位置时,文件锁争用会让整个系统假死。C/S的本质,是把“状态协调”这个难题,从单机文件系统转移到可控的网络协议层。
2.2 Qt框架的深度适配:不是“用Qt写界面”,而是“用Qt思维重构业务”
很多初学者把Qt当作“带信号槽的Windows API”,这是最大的认知偏差。在这个系统里,Qt的价值远不止于拖拽控件。举个典型例子:图书搜索功能。表面看只是输入框+按钮+表格,但真实场景中,用户可能输入“人工智能”想查相关书籍,也可能输入“ISBN:9787302548765”精确查找。如果用传统方式,你得写一堆if-else判断输入格式,再分别调用不同SQL语句。而Qt提供了更优雅的解法:QSortFilterProxyModel + 自定义filterAcceptsRow()。我们让底层QSqlQueryModel只负责从数据库拉取原始数据(SELECT * FROM books),然后用代理模型对显示内容进行动态过滤。在filterAcceptsRow里,你可以用QRegularExpression同时匹配书名、作者、ISBN字段,甚至支持模糊拼音检索(比如输入“ren gong zhi neng”也能命中“人工智能”)。这种分层设计,让UI逻辑与数据逻辑彻底解耦——修改搜索算法无需碰数据库代码,调整界面布局也不影响数据获取。另一个关键点是QThread与QObject的亲和性。当执行耗时操作(如扫描全库生成统计报表)时,若直接在主线程调用,界面必然冻结。Qt的正确做法是:创建一个继承自QThread的工作线程,在run()中执行计算,通过信号(如progressUpdated(int))将进度实时发回主线程更新进度条。这里有个极易踩坑的细节:绝对不能在工作线程中直接操作UI控件(如tableWidget->setRowCount()),因为Qt的GUI对象必须在创建它的线程中访问。正确的模式是:工作线程发出信号,主线程的槽函数接收信号后再更新UI。我曾调试过一个崩溃案例,根源就是开发者在QThread::run()里直接调用了QMessageBox::information()——这就像试图让厨房里的厨师直接指挥餐厅服务员,必然导致混乱。
2.3 模块化拆解:六个核心组件如何协同工作
整个系统并非一个巨型.cpp文件,而是由六个高内聚、低耦合的模块构成,每个模块对应一个明确的业务域:
网络通信模块(NetworkManager):封装QTcpSocket,提供connectToServer()、sendRequest()、receiveResponse()等高层接口。关键设计是内置重连机制——当检测到连接断开时,自动按指数退避(1s, 2s, 4s...)尝试重连,并缓存未发送的请求(如借书指令)到本地队列,待连接恢复后重发。这解决了校园网不稳定带来的操作丢失问题。
数据访问模块(DataAccessLayer):基于QSqlDatabase实现,但做了重要增强。它不直接暴露QSqlQuery,而是提供BookService::addBook()、ReaderService::findByName()等业务方法。内部采用连接池管理(预创建3个QSqlDatabase实例),避免频繁创建销毁连接的开销。特别处理了事务:借书操作必须原子性完成“扣减库存+新增借阅记录+更新读者信息”三步,任一失败则全部回滚。
业务逻辑模块(BusinessLogic):包含所有规则引擎。例如“借书规则”:学生证有效期检查、当前借阅数量上限(最多5本)、黑名单拦截(逾期未还者禁止借阅)。这些规则被抽象为独立的Validator类,可插拔替换,方便后期扩展(如增加信用分机制)。
UI呈现模块(View):由Qt Designer生成的.ui文件驱动,但核心是Model/View架构的深度应用。图书列表使用QTableView + QSqlRelationalTableModel(支持外键关联显示分类名称),借阅记录用QTreeView展示树形结构(读者→其借阅的所有图书)。所有视图都通过信号与业务逻辑模块通信,绝不直接访问数据库。
配置管理模块(ConfigManager):读取XML格式的config.xml,存储服务器IP、端口、数据库路径、默认打印模板等。关键创新是支持热重载——当管理员在设置界面修改服务器地址后,NetworkManager会立即断开旧连接并建立新连接,无需重启客户端。
日志与监控模块(Logger):不仅记录错误,更追踪关键业务事件。例如每次借书成功,会写入“[2024-06-15 14:22:33] USER:张三(学号2022001) 借阅《深度学习实战》(ISBN:9787302548765) 到期日:2024-07-15”。这些日志被压缩归档,可直接用于审计。
提示:模块间通信严格遵循“依赖倒置原则”。View模块只依赖BusinessLogic的抽象接口(如IBorrowService),而非具体实现。这样未来若需将后端换成gRPC服务,只需替换BusinessLogic的具体实现类,UI代码一行都不用改。
3. 核心功能实现:从零开始搭建可运行的借阅流程
3.1 数据库设计:为什么用SQLite而非MySQL?
选择SQLite并非因为“简单”,而是精准匹配场景。学校资料室通常只有1-2台服务器,图书总量在5万册以内,日均操作不超过2000次。在这种负载下,SQLite的零配置、单文件部署、ACID事务保障,比MySQL的复杂运维更具优势。我们设计了四张核心表:
-- 图书主表(books) CREATE TABLE books ( id INTEGER PRIMARY KEY AUTOINCREMENT, isbn TEXT UNIQUE NOT NULL, -- 国际标准书号,唯一标识 title TEXT NOT NULL, -- 书名 author TEXT, -- 作者 publisher TEXT, -- 出版社 publish_year INTEGER, -- 出版年份 category_id INTEGER, -- 分类ID,外键关联categories total_copies INTEGER DEFAULT 0, -- 总馆藏数 available_copies INTEGER DEFAULT 0, -- 可借阅数 location TEXT -- 馆藏位置(如“A区-3排-2架”) ); -- 分类表(categories) CREATE TABLE categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL -- 分类名称(如“计算机科学”、“文学”) ); -- 读者表(readers) CREATE TABLE readers ( id INTEGER PRIMARY KEY AUTOINCREMENT, card_no TEXT UNIQUE NOT NULL, -- 读者证号(学号/工号) name TEXT NOT NULL, department TEXT, -- 所属院系/部门 valid_until DATE, -- 证件有效期 status TEXT DEFAULT 'active' -- 状态(active/blocked) ); -- 借阅记录表(borrow_records) CREATE TABLE borrow_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, book_id INTEGER NOT NULL, -- 关联books.id reader_id INTEGER NOT NULL, -- 关联readers.id borrow_date DATE DEFAULT CURRENT_DATE, due_date DATE, -- 应还日期(借期30天) return_date DATE, -- 实际归还日期(NULL表示未还) FOREIGN KEY (book_id) REFERENCES books(id), FOREIGN KEY (reader_id) REFERENCES readers(id) );关键设计点在于available_copies字段。它不是通过total_copies - COUNT(*) FROM borrow_records WHERE book_id=? AND return_date IS NULL实时计算,而是由业务逻辑在借/还操作时主动更新。这样做的好处是避免高并发下的竞态条件——两个线程同时读取available_copies=1,都判断可以借出,结果导致超借。我们采用数据库级别的UPDATE语句保证原子性:
UPDATE books SET available_copies = available_copies - 1 WHERE id = ? AND available_copies > 0;执行后检查QSqlQuery::numRowsAffected()是否为1,若为0则说明库存不足,直接返回错误。这个看似简单的SQL,背后是分布式系统中最基础的“乐观锁”思想。
3.2 客户端登录与会话管理:如何让Qt程序记住你是谁?
登录界面看似简单,但涉及安全与体验的平衡。我们没有使用明文密码传输,而是采用挑战-响应机制:客户端首次连接服务器时,服务器生成一个随机字符串(challenge)发给客户端;客户端用本地存储的密码哈希值(SHA256)与challenge拼接后再次哈希,将结果(response)发回服务器验证。这样即使网络被监听,攻击者也无法获得原始密码。密码哈希使用PBKDF2算法,迭代10万次,防止彩虹表破解。
会话管理采用轻量级令牌(Token)而非Cookie。登录成功后,服务器返回一个JWT(JSON Web Token),其中包含用户ID、角色(admin/reader)、过期时间(2小时)。客户端将Token存在QSettings中(加密存储),后续所有请求都在HTTP Header或自定义TCP协议头中携带。Token过期后,客户端自动跳转到登录界面,无需用户手动操作。这里有个Qt特有的细节:QSettings的加密存储需启用QCryptographicHash。我们封装了一个SecureSettings类:
class SecureSettings : public QSettings { public: explicit SecureSettings(const QString &organization, const QString &application) : QSettings(organization, application) {} void setValue(const QString &key, const QVariant &value) override { if (key == "auth_token") { QByteArray encrypted = QCryptographicHash::hash( value.toString().toUtf8() + "SALT_2024", QCryptographicHash::Sha256 ); QSettings::setValue(key, QString::fromUtf8(encrypted.toHex())); } else { QSettings::setValue(key, value); } } };注意:实际生产环境应使用更安全的密钥派生(如AES-GCM),此处为简化演示。Qt的QSettings本身不提供强加密,必须自行封装。
3.3 图书搜索与列表渲染:如何让万级数据滚动如丝般顺滑?
当图书库达到2万条记录时,传统QTableWidget的逐行插入方式会导致界面卡顿超过5秒。我们的解决方案是分页加载 + 懒加载视图。核心思路是:QTableView只渲染当前可视区域的行,而非全部数据。这依赖于QAbstractItemModel的rowCount()和data()方法的智能实现。
首先,数据库查询改为分页:
SELECT * FROM books WHERE title LIKE ? OR author LIKE ? OR isbn LIKE ? ORDER BY id LIMIT 50 OFFSET 0; -- 第一页然后,自定义BookModel继承QAbstractTableModel,重写关键方法:
int BookModel::rowCount(const QModelIndex &parent) const override { // 返回总记录数,让滚动条有正确长度 return m_totalCount; } QVariant BookModel::data(const QModelIndex &index, int role) const override { if (!index.isValid()) return QVariant(); // 只在需要显示时才从数据库加载该页数据 int page = index.row() / 50; if (m_cachePage != page) { loadPageFromDb(page); // 从DB加载第page页数据到m_cache m_cachePage = page; } if (role == Qt::DisplayRole) { int rowInPage = index.row() % 50; return m_cache[rowInPage].title; // 返回缓存中的数据 } return QVariant(); }配合QTableView的setVerticalScrollMode(QAbstractItemView::ScrollPerPixel),用户滚动时,模型动态加载对应页面的数据,内存占用恒定在50条记录,无论总数据量多大。实测在i5-8250U笔记本上,10万条图书数据滚动流畅无卡顿。
3.4 借阅核心流程:一次借书操作背后的七次状态校验
点击“借书”按钮后,系统并非直接执行SQL,而是启动一个严谨的状态机:
- 前端校验:检查读者证号是否为空、格式是否符合学号规则(如2022\d{4})。
- Token有效性校验:确认登录会话未过期。
- 读者状态校验:查询readers表,确认status='active'且valid_until >= today。
- 图书可用性校验:查询books表,确认available_copies > 0。
- 借阅限额校验:执行
SELECT COUNT(*) FROM borrow_records WHERE reader_id=? AND return_date IS NULL,确保未还数量<5。 - 库存原子性扣减:执行前述UPDATE语句,检查影响行数。
- 借阅记录插入:在borrow_records表中插入新记录,设置due_date为borrow_date + 30天。
每一步失败都给出明确提示:“读者证已过期,请联系管理员续期”、“您已借阅5本书,需归还后才能继续借阅”。所有校验通过后,才触发最终的数据库写入。这个流程被封装在BorrowService::executeBorrow()方法中,采用QFutureWatcher异步执行,避免UI冻结。关键经验是:永远不要相信前端校验。即使JavaScript做了完美验证,黑客仍可通过抓包工具绕过,所以后端(服务器)必须重复所有关键校验。
4. 部署与运维:让系统真正跑在管理员的电脑上
4.1 Qt环境打包:为什么推荐windeployqt而非第三方工具?
很多教程推荐用Inno Setup或NSIS打包,但忽略了Qt特有的依赖链。Qt Creator自带的windeployqt工具,能精准识别你的可执行文件所依赖的Qt动态库(如Qt5Core.dll、Qt5Widgets.dll)、平台插件(qwindows.dll)、图像格式插件(qjpeg.dll)以及数据库驱动(qsqlite.dll)。手动拷贝DLL极易遗漏,导致“程序启动黑屏”或“无法连接数据库”。
正确流程是:
- 在Qt Creator中,构建Release版本(Build → Build Solution)。
- 打开命令行,cd到build目录,执行:
参数说明:windeployqt --dir ./deploy --debug --pdb --no-opengl-sw --no-webkit2 --no-quick --no-webengine --no-angle --no-system-d3d-compiler --no-compiler-runtime --no-virtualstore your_app.exe--dir ./deploy指定输出目录;--debug保留PDB符号文件便于调试;--no-*禁用不需要的模块(如不用WebEngine就禁用,减少体积)。 - 将生成的deploy目录整体复制到目标机器,双击exe即可运行。
实测发现,一个含SQLite驱动的图书管理系统,windeployqt打包后体积约25MB,而手动拷贝常遗漏qsqlite.dll,导致程序报错“QSqlDatabase: QSQLITE driver not loaded”。
4.2 服务器端部署:如何让Qt TCP服务器稳定运行7x24小时?
服务器程序(BookServer.exe)不能简单双击运行,必须作为Windows服务安装。我们使用Qt的QWinService类(需Qt 5.15+):
class BookService : public QWinService { Q_OBJECT public: explicit BookService(QObject *parent = nullptr) : QWinService(parent) {} protected: void start() override { m_server = new QTcpServer(this); connect(m_server, &QTcpServer::newConnection, this, &BookService::handleNewConnection); if (!m_server->listen(QHostAddress::Any, 8080)) { qCritical() << "Failed to start server:" << m_server->errorString(); } } void stop() override { m_server->close(); m_server->deleteLater(); } private: QTcpServer *m_server; };然后通过命令行安装服务:
sc create BookServer binPath= "C:\path\to\BookServer.exe" start= auto sc start BookServer关键运维技巧:
- 日志轮转:使用QFileLogger,每日生成新日志文件(bookserver_20240615.log),超过30天自动删除。
- 内存泄漏监控:在服务启动时,定期调用
QProcess::execute("tasklist /fi \"imagename eq BookServer.exe\" /fo csv")获取内存占用,若持续增长则触发告警。 - 端口占用防护:启动前检查8080端口是否被占用,若被占用则自动切换到8081,并更新客户端配置。
4.3 常见故障排查:从“连接失败”到“数据错乱”的实战指南
故障1:客户端显示“连接服务器失败”,但ping通服务器IP
排查路径:
- 检查服务器防火墙:
Windows Defender Firewall → 高级设置 → 入站规则 → 新建规则 → 端口 → TCP 8080 → 允许连接 - 验证服务器进程是否真在监听:在服务器上运行
netstat -ano | findstr :8080,确认状态为LISTENING且PID对应BookServer.exe - 检查Qt代码:
QTcpServer::listen()返回true不代表成功,必须检查server->serverPort()是否为8080(若被占用,Qt会自动分配其他端口)
故障2:图书列表显示空白,但数据库确认有数据
根因分析:
- 最常见原因是QSqlQueryModel的query未执行或执行失败。在构造函数中添加:
qDebug() << "Executing query:" << query.lastQuery(); if (!query.exec()) { qCritical() << "Query failed:" << query.lastError().text(); } - 或者QTableView未设置model:
ui->tableView->setModel(model);忘记这行会导致视图无数据。
故障3:多用户同时借书,出现“超借”现象(available_copies变成负数)
根本原因:数据库未启用行级锁。解决方案是在UPDATE语句中加入FOR UPDATE(SQLite不支持,需改用BEGIN IMMEDIATE事务):
QSqlDatabase::database().transaction(); QSqlQuery updateQuery; updateQuery.prepare("UPDATE books SET available_copies = available_copies - 1 WHERE id = ? AND available_copies > 0"); updateQuery.addBindValue(bookId); if (!updateQuery.exec() || updateQuery.numRowsAffected() == 0) { QSqlDatabase::database().rollback(); return false; // 库存不足 } // 继续插入借阅记录... QSqlDatabase::database().commit();故障4:Qt Creator调试时程序崩溃,Release版却正常
典型诱因:Debug版本启用了Qt的内存调试器(如QSharedMemory),而Release版关闭。检查项目.pro文件,移除CONFIG += debug_and_release中的冲突配置。更稳妥的做法是:在Debug构建中,禁用所有第三方插件(如QWebEngine),专注核心逻辑调试。
| 问题现象 | 可能原因 | 快速验证方法 | 解决方案 |
|---|---|---|---|
| 启动时报错“无法定位程序输入点…” | Qt DLL版本不匹配 | 用Dependency Walker打开exe,查看缺失的DLL | 重新运行windeployqt,确保所有Qt DLL版本一致 |
| 中文显示为方块 | 字体未嵌入或编码错误 | 在main()中添加QApplication::setFont(QFont("Microsoft YaHei")); | 在.pro文件中添加DEFINES += QT_NO_CAST_TO_ASCII,统一使用UTF-8 |
| 表格点击无反应 | 信号未连接或模型未设置 | 在构造函数末尾加qDebug() << "Model set:" << ui->tableView->model(); | 确保setModel()在show()之前调用,且模型指针非空 |
5. 进阶优化:让系统从“能用”走向“好用”
5.1 性能压测:模拟百人并发下的真实瓶颈
我们用Python编写了简易压测脚本,模拟100个虚拟客户端同时执行借书操作:
import threading import time import socket def borrow_task(client_id): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(('127.0.0.1', 8080)) # 发送借书请求协议... sock.close() # 启动100个线程 threads = [] for i in range(100): t = threading.Thread(target=borrow_task, args=(i,)) threads.append(t) t.start() for t in threads: t.join()压测结果揭示了两个隐藏瓶颈:
- 数据库连接池不足:当并发超过30时,大量请求在等待数据库连接。解决方案是将QSqlDatabase连接池大小从3提升至10,并在QSqlDatabase::addDatabase()时指定connectionName,避免线程间误用连接。
- UI线程消息队列积压:客户端收到服务器响应后,通过信号通知UI更新,但100个信号涌入主线程导致消息队列堵塞。优化方案是:将批量响应合并为单个信号,或使用
QMetaObject::invokeMethod()配合Qt::QueuedConnection确保线程安全。
5.2 用户体验增强:那些教科书不会写的“小聪明”
智能输入提示:在图书搜索框中,集成QCompleter。当用户输入“人工”时,自动下拉显示“人工智能”、“人工神经网络”、“人工通用智能”等匹配项。数据源来自数据库的书名高频词库,每周自动更新。
防误操作设计:删除图书前,弹出确认对话框显示“即将删除《XXX》,此操作不可撤销,且将清除所有相关借阅记录”。关键细节是:对话框按钮文字为“确认删除”而非“确定”,并用红色高亮,降低误操作概率。
离线模式无缝切换:当网络断开时,客户端自动切换到SQLite本地数据库,允许管理员继续录入新书(标记为“待同步”状态)。网络恢复后,后台线程自动将本地变更同步到服务器,并解决冲突(如服务器端同ISBN图书已被修改,则以服务器版本为准)。
快捷键革命:为高频操作绑定全局快捷键。F1=帮助,F2=快速借书(聚焦到读者证号输入框),Ctrl+R=刷新列表。实现方式是重写QMainWindow::keyPressEvent(),注意捕获事件后调用
event->accept()阻止事件向子控件传递。
5.3 安全加固:超越“用户名密码”的纵深防御
SQL注入防护:所有数据库查询均使用
QSqlQuery::prepare()和addBindValue(),杜绝字符串拼接。例如:// 错误示范(危险!) query.exec(QString("SELECT * FROM books WHERE title LIKE '%1%'").arg(keyword)); // 正确做法 query.prepare("SELECT * FROM books WHERE title LIKE ?"); query.addBindValue("%" + keyword + "%");XSS防护:图书简介字段可能含HTML标签,若直接用QLabel显示会执行脚本。解决方案是使用QTextDocument渲染,并禁用富文本:
QTextDocument doc; doc.setHtml(bookDescription); // 自动解析HTML ui->descriptionLabel->setText(doc.toPlainText()); // 只显示纯文本敏感操作二次验证:删除图书、修改管理员密码等高危操作,需输入当前登录密码再次确认。密码验证在客户端完成(对比本地存储的哈希值),避免网络传输风险。
最后分享一个小技巧:Qt的QSettings在Windows下默认存储在注册表HKEY_CURRENT_USER\Software\[Organization]\[Application],但某些学校电脑禁用了注册表写入。此时可在main()中强制指定INI文件路径:
QSettings::setDefaultFormat(QSettings::IniFormat); QCoreApplication::setOrganizationName("LibrarySystem"); QCoreApplication::setApplicationName("BookManager"); QSettings::setPath(QSettings::IniFormat, QSettings::UserScope, "C:/ProgramData/BookManager/");这样所有配置都写入指定目录,规避权限问题。我在三所高校部署时,两次遇到注册表被锁,这个方案成了救命稻草。
本文还有配套的精品资源,点击获取