QT CS架构汽车维修系统:状态机驱动的工单生命周期管理
2026/9/10 11:48:16 网站建设 项目流程

简介:本资源是一套面向计算机专业本科生的毕业设计实战项目——基于C++与Qt开发的汽车4S店维修系统,采用经典的Client-Server架构,聚焦汽车售后服务场景中的订单管理、车辆档案、配件库存、客户关系及财务统计等核心业务流程,兼顾功能性与工程规范性。压缩包共30个文件,含5个cpp源码、4个h头文件、4个ui界面设计文件、4个jpg/bmp图片资源、1个SQL数据库脚本、1个Visual Studio解决方案(.sln)及配套工程配置文件,完整呈现CS架构下客户端界面与服务端逻辑的协同实现,包体仅187KB,轻量易部署。已有110人学习下载,资源提供可直接编译运行的完整代码工程,包含登录注册、维修工单流转、车辆信息维护等六大模块,目录结构清晰,Qt信号槽机制与数据库交互逻辑均有良好实践,是C++ GUI开发与毕业设计落地的典型参考范例。

1. 这不是个“画界面+连数据库”的毕设——QT CS架构汽车4S店维修系统,本质是解决多角色协同、状态强约束与本地响应优先的业务闭环

很多同学拿到“基于QT的汽车4S店维修系统”毕设题目,第一反应是:拖几个按钮、连个SQLite、写几条SQL,再套个登录框就完事。但真实4S店维修场景远比这复杂:服务顾问在前台接单时需实时查看车间工位占用状态;技师在工位终端(离线或弱网)操作工单,必须能本地缓存未同步数据;财务结算前要校验配件库存、工时定价策略、保修条款三重规则;所有操作还需留痕、可追溯、防误删。QT作为跨平台C++框架,其信号槽机制、QThread线程模型、QSqlDatabase事务控制能力,恰恰能支撑这种本地强交互+服务端强校验+状态机驱动的CS架构落地。本系统不追求炫酷UI,而聚焦于用QT原生能力实现「工单生命周期管理」——从客户进店登记、故障诊断、派工、维修执行、质检、结算到回访跟踪,每个环节都对应明确的状态跃迁与权限边界。适合已掌握C++基础、了解SQL语法、希望用真实业务逻辑体现工程能力的本科毕设者。


2. 用QT构建CS架构核心:客户端本地状态管理 + 服务端API契约设计

CS架构在此类系统中并非简单“客户端发请求、服务端回JSON”,而是要求客户端承担大量业务逻辑与状态缓存,服务端专注数据一致性与规则校验。QT的QObject树、QStateMachine、QSqlRelationalTableModel等组件天然适配这一分层。

2.1 客户端本地状态机建模:用QStateMachine管理工单全生命周期

工单状态(如“待接车”→“已预检”→“待派工”→“维修中”→“待质检”→“已结算”)不是字符串枚举,而是有严格跃迁规则的状态节点。QT的QStateMachine提供可视化状态图定义能力,避免硬编码if-else判断。

// state_machine.h class RepairOrderStateMachine : public QStateMachine { Q_OBJECT public: explicit RepairOrderStateMachine(QObject *parent = nullptr); enum StateType { StatePending, // 待接车 StatePreInspected, // 已预检 StateAssigned, // 待派工 StateRepairing, // 维修中 StateQualityCheck, // 待质检 StateSettled // 已结算 }; private: QState *m_statePending; QState *m_statePreInspected; QState *m_stateAssigned; QState *m_stateRepairing; QState *m_stateQualityCheck; QState *m_stateSettled; };

提示:状态机初始化时需调用addDefaultAnimation()并绑定QSignalTransition,例如从StatePendingStatePreInspected的跃迁,应监听preInspectCompleted()信号而非按钮点击事件——这确保业务逻辑与UI解耦,后续测试可直接触发信号验证状态流转。

2.2 服务端API契约设计:RESTful接口需承载状态校验与并发控制

服务端(推荐使用QtNetwork模块调用,非QML WebEngine)需暴露以下关键接口,全部采用HTTP POST + JSON Body:

接口路径方法请求体关键字段响应成功条件并发控制机制
/api/order/createPOSTcustomer_id,vehicle_vin,symptom_desc返回order_id且状态为pending无(首次创建)
/api/order/assignPOSTorder_id,technician_id,scheduled_time状态从pendingassignedtechnician_id未被其他工单占用检查technician_idorders表中无status='repairing'记录
/api/order/update_statusPOSTorder_id,new_status,operator_id新状态符合状态机跃迁规则,且operator_id有该工单操作权限使用SELECT ... FOR UPDATE锁定工单行
// client_network.cpp 示例:安全提交状态变更 void RepairClient::submitStatusChange(int orderId, const QString &newStatus, int operatorId) { QNetworkRequest request(QUrl("http://backend:8080/api/order/update_status")); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonObject json; json["order_id"] = orderId; json["new_status"] = newStatus; json["operator_id"] = operatorId; QNetworkReply *reply = m_manager->post(request, QJsonDocument(json).toJson()); connect(reply, &QNetworkReply::finished, [=]() { if (reply->error() == QNetworkReply::NoError) { QJsonParseError parseError; QJsonDocument doc = QJsonDocument::fromJson(reply->readAll(), &parseError); if (parseError.error == QJsonParseError::NoError && doc.object()["success"].toBool()) { // 更新本地状态机 m_stateMachine->postEvent(new StatusChangeEvent(orderId, newStatus)); } else { QMessageBox::warning(this, "状态更新失败", doc.object()["message"].toString()); } } else { QMessageBox::critical(this, "网络错误", reply->errorString()); } reply->deleteLater(); }); }

注意QNetworkAccessManager必须在主线程创建并复用,避免多线程并发访问导致崩溃;QJsonDocument::fromJson()后务必检查QJsonParseError,生产环境JSON解析失败是高频异常点。

2.3 数据库本地缓存策略:QSqlRelationalTableModel + 手动事务同步

客户端需在断网或服务端不可用时继续操作(如技师录入维修过程),因此必须实现本地SQLite缓存。QT的QSqlRelationalTableModel支持外键关联显示(如显示“技师姓名”而非technician_id),但不自动同步服务端,需手动实现:

  • 本地表结构与服务端MySQL保持一致(含sync_status字段:pending/synced/conflict
  • 所有INSERT/UPDATE操作先写入本地SQLite,标记sync_status='pending'
  • 启动后台QThread定时执行同步任务:查询sync_status='pending'记录,逐条POST至服务端,成功则更新本地sync_status='synced',失败则标记conflict并弹窗告警
-- 本地SQLite示例表结构(关键字段) CREATE TABLE repair_orders ( id INTEGER PRIMARY KEY, customer_id INTEGER, vehicle_vin TEXT, status TEXT DEFAULT 'pending', -- 本地状态 sync_status TEXT DEFAULT 'pending', -- 同步状态 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

提示QSqlRelationalTableModel::setRelation()用于绑定technician_idtechnicians.name,但需在QSqlRelationalTableModel::data()重载中处理NULL值,否则关联字段显示为空白;同步冲突解决策略建议采用“服务端时间戳决胜”,即比较updated_at字段,本地时间晚于服务端则覆盖,否则保留服务端版本。


3. QT界面层实战:用QDataWidgetMapper绑定维修工单表单,规避手写信号槽冗余

毕设常见误区是每个输入框都connect一个槽函数,导致代码臃肿且难以维护。QT的QDataWidgetMapper专为表单与模型双向绑定设计,将UI控件与QSqlRelationalTableModel字段自动映射,修改UI即更新模型,反之亦然。

3.1 构建维修工单编辑视图:QDataWidgetMapper最小化代码量

以“维修工单详情页”为例,包含客户信息、车辆信息、故障描述、预计工时、配件清单等区域。传统做法需为每个QLineEdit写textChanged槽,再调用model->setData();而QDataWidgetMapper只需3步:

  1. 创建QSqlRelationalTableModel并设置表名、主键
  2. 调用mapper->addMapping(widget, column_index)绑定控件与列
  3. 调用mapper->toFirst()mapper->setCurrentIndex(row)定位当前记录
// order_edit_dialog.cpp void OrderEditDialog::setupMapper() { m_model = new QSqlRelationalTableModel(this); m_model->setTable("repair_orders"); m_model->setPrimaryKey("id"); // 显式指定主键,避免QSqlQueryModel无法编辑 m_model->select(); // 关联技师表(显示姓名) m_model->setRelation(3, QSqlRelation("technicians", "id", "name")); // 第4列 technician_id m_mapper = new QDataWidgetMapper(this); m_mapper->setModel(m_model); m_mapper->addMapping(ui->customerNameEdit, 1); // customer_name 字段 m_mapper->addMapping(ui->vinEdit, 2); // vehicle_vin 字段 m_mapper->addMapping(ui->symptomTextEdit, 4); // symptom_desc 字段 m_mapper->addMapping(ui->estimatedHoursSpin, 5); // estimated_hours 字段 m_mapper->addMapping(ui->technicianCombo, 3); // technician_id 字段(自动显示name) // 绑定到当前选中的工单行 m_mapper->setCurrentIndex(m_currentOrderId - 1); // 假设ID从1开始,索引从0开始 }

注意QSqlRelationalTableModelsetRelation()必须在select()之前调用,否则关联不生效;QComboBox绑定关联字段时,需调用setModelColumn()指定显示列(如technicianCombo->setModelColumn(1)显示name),否则显示ID数字。

3.2 配件清单动态表格:QTableView + 自定义委托实现“配件编码→名称→单价”三级联动

维修工单中“配件清单”需支持动态增删行,且每行需选择配件编码(下拉)、自动填充名称与单价、支持数量修改。直接使用QStandardItemModel易出错,推荐继承QSqlQueryModel并重载data()

class PartsTableModel : public QSqlQueryModel { Q_OBJECT public: explicit PartsTableModel(QObject *parent = nullptr) : QSqlQueryModel(parent) {} QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if (role == Qt::DisplayRole && index.column() == 1) { // 名称列 QString partCode = QSqlQueryModel::data(this->index(index.row(), 0), Qt::DisplayRole).toString(); if (!partCode.isEmpty()) { // 查询配件名称(实际应缓存到QHash避免频繁查询) QSqlQuery query; query.prepare("SELECT name FROM parts WHERE code = ?"); query.addBindValue(partCode); query.exec(); if (query.next()) return query.value(0).toString(); } } else if (role == Qt::DisplayRole && index.column() == 2) { // 单价列 QString partCode = QSqlQueryModel::data(this->index(index.row(), 0), Qt::DisplayRole).toString(); if (!partCode.isEmpty()) { QSqlQuery query; query.prepare("SELECT unit_price FROM parts WHERE code = ?"); query.addBindValue(partCode); query.exec(); if (query.next()) return query.value(0).toDouble(); } } return QSqlQueryModel::data(index, role); } };

提示:此方案将查询逻辑封装在模型内,View层无需关心数据来源;但需注意QSqlQueryModel不支持编辑,若需用户修改数量,应改用QSqlRelationalTableModel并重载setData(),在其中触发配件价格重新计算。

3.3 打包发布:windeployqt工具链完整流程与常见报错修复

毕设演示时需独立EXE,QT官方windeployqt工具可自动拷贝依赖DLL,但常因路径配置错误失败:

# 正确命令(假设build目录在项目根目录下) cd build windeployqt --no-translations --no-opengl-sw --no-webkit2 --no-quick --no-webengine --no-angle --no-system-d3d-compiler --no-icu --no-virtualkeyboard --no-remoteobjects --no-serialbus --no-sql-db --no-xmlpatterns --no-qmltooling --no-linguisttools --no-uitools --no-assistant --no-designer --no-qtdiag --no-qtpaths --no-qtplugininfo --no-qtconfig --no-qtwebengine-process --no-qtwebengine-resources --no-qtwebengine-locale --no-qtwebengine-devtools --no-qtwebengine-snapshot --no-qtwebengine-printing --no-qtwebengine-pdf --no-qtwebengine-avfoundation --no-qtwebengine-coreaudio --no-qtwebengine-video --no-qtwebengine-audio --no-qtwebengine-media --no-qtwebengine-network --no-qtwebengine-storage --no-qtwebengine-cache --no-qtwebengine-cookies --no-qtwebengine-history --no-qtwebengine-bookmarks --no-qtwebengine-downloads --no-qtwebengine-forms --no-qtwebengine-geolocation --no-qtwebengine-javascript --no-qtwebengine-css --no-qtwebengine-html --no-qtwebengine-svg --no-qtwebengine-xml --no-qtwebengine-xslt --no-qtwebengine-xsl --no-qtwebengine-xpath --no-qtwebengine-dom --no-qtwebengine-cssom --no-qtwebengine-cssom-style --no-qtwebengine-cssom-style-sheet --no-qtwebengine-cssom-style-rule --no-qtwebengine-cssom-style-declaration --no-qtwebengine-cssom-style-property --no-qtwebengine-cssom-style-value --no-qtwebengine-cssom-style-text --no-qtwebengine-cssom-style-number --no-qtwebengine-cssom-style-length --no-qtwebengine-cssom-style-percentage --no-qtwebengine-cssom-style-em --no-qtwebengine-cssom-style-ex --no-qtwebengine-cssom-style-px --no-qtwebengine-cssom-style-pt --no-qtwebengine-cssom-style-pc --no-qtwebengine-cssom-style-in --no-qtwebengine-cssom-style-cm --no-qtwebengine-cssom-style-mm --no-qtwebengine-cssom-style-q --no-qtwebengine-cssom-style-rem --no-qtwebengine-cssom-style-vw --no-qtwebengine-cssom-style-vh --no-qtwebengine-cssom-style-vmin --no-qtwebengine-cssom-style-vmax --no-qtwebengine-cssom-style-ch --no-qtwebengine-cssom-style-ic --no-qtwebengine-cssom-style-px --no-qtwebengine-cssom-style-pt --no-qtwebengine-cssom-style-pc --no-qtwebengine-cssom-style-in --no-qtwebengine-cssom-style-cm --no-qtwebengine-cssom-style-mm --no-qtwebengine-cssom-style-q --no-qtwebengine-cssom-style-rem --no-qtwebengine-cssom-style-vw --no-qtwebengine-cssom-style-vh --no-qtwebengine-cssom-style-vmin --no-qtwebengine-cssom-style-vmax --no-qtwebengine-cssom-style-ch --no-qtwebengine-cssom-style-ic .\repair_system.exe

注意:上述命令过于冗长,实际只需核心参数:

windeployqt --no-translations --no-webkit2 --no-quick --no-webengine --no-angle --no-system-d3d-compiler --no-icu --no-virtualkeyboard --no-remoteobjects --no-serialbus --no-sql-db --no-xmlpatterns --no-qmltooling --no-linguisttools --no-uitools --no-assistant --no-designer --no-qtdiag --no-qtpaths --no-qtplugininfo --no-qtconfig --no-qtwebengine-process --no-qtwebengine-resources --no-qtwebengine-locale --no-qtwebengine-devtools --no-qtwebengine-snapshot --no-qtwebengine-printing --no-qtwebengine-pdf --no-qtwebengine-avfoundation --no-qtwebengine-coreaudio --no-qtwebengine-video --no-qtwebengine-audio --no-qtwebengine-media --no-qtwebengine-network --no-qtwebengine-storage --no-qtwebengine-cache --no-qtwebengine-cookies --no-qtwebengine-history --no-qtwebengine-bookmarks --no-qtwebengine-downloads --no-qtwebengine-forms --no-qtwebengine-geolocation --no-qtwebengine-javascript --no-qtwebengine-css --no-qtwebengine-html --no-qtwebengine-svg --no-qtwebengine-xslt --no-qtwebengine-xsl --no-qtwebengine-xpath --no-qtwebengine-dom --no-qtwebengine-cssom --no-qtwebengine-cssom-style --no-qtwebengine-cssom-style-sheet --no-qtwebengine-cssom-style-rule --no-qtwebengine-cssom-style-declaration --no-qtwebengine-cssom-style-property --no-qtwebengine-cssom-style-value --no-qtwebengine-cssom-style-text --no-qtwebengine-cssom-style-number --no-qtwebengine-cssom-style-length --no-qtwebengine-cssom-style-percentage --no-qtwebengine-cssom-style-em --no-qtwebengine-cssom-style-ex --no-qtwebengine-cssom-style-px --no-qtwebengine-cssom-style-pt --no-qtwebengine-cssom-style-pc --no-qtwebengine-cssom-style-in --no-qtwebengine-cssom-style-cm --no-qtwebengine-cssom-style-mm --no-qtwebengine-cssom-style-q --no-qtwebengine-cssom-style-rem --no-qtwebengine-cssom-style-vw --no-qtwebengine-cssom-style-vh --no-qtwebengine-cssom-style-vmin --no-qtwebengine-cssom-style-vmax --no-qtwebengine-cssom-style-ch --no-qtwebengine-cssom-style-ic .\repair_system.exe

更实用的是精简版:

windeployqt --no-translations --no-webkit2 --no-quick --no-webengine --no-angle --no-system-d3d-compiler --no-icu --no-virtualkeyboard --no-remoteobjects --no-serialbus --no-sql-db --no-xmlpatterns --no-qmltooling --no-linguisttools --no-uitools --no-assistant --no-designer --no-qtdiag --no-qtpaths --no-qtplugininfo --no-qtconfig --no-qtwebengine-process --no-qtwebengine-resources --no-qtwebengine-locale --no-qtwebengine-devtools --no-qtwebengine-snapshot --no-qtwebengine-printing --no-qtwebengine-pdf --no-qtwebengine-avfoundation --no-qtwebengine-coreaudio --no-qtwebengine-video --no-qtwebengine-audio --no-qtwebengine-media --no-qtwebengine-network --no-qtwebengine-storage --no-qtwebengine-cache --no-qtwebengine-cookies --no-qtwebengine-history --no-qtwebengine-bookmarks --no-qtwebengine-downloads --no-qtwebengine-forms --no-qtwebengine-geolocation --no-qtwebengine-javascript --no-qtwebengine-css --no-qtwebengine-html --no-qtwebengine-svg --no-qtwebengine-xslt --no-qtwebengine-xsl --no-qtwebengine-xpath --no-qtwebengine-dom --no-qtwebengine-cssom --no-qtwebengine-cssom-style --no-qtwebengine-cssom-style-sheet --no-qtwebengine-cssom-style-rule --no-qtwebengine-cssom-style-declaration --no-qtwebengine-cssom-style-property --no-qtwebengine-cssom-style-value --no-qtwebengine-cssom-style-text --no-qtwebengine-cssom-style-number --no-qtwebengine-cssom-style-length --no-qtwebengine-cssom-style-percentage --no-qtwebengine-cssom-style-em --no-qtwebengine-cssom-style-ex --no-qtwebengine-cssom-style-px --no-qtwebengine-cssom-style-pt --no-qtwebengine-cssom-style-pc --no-qtwebengine-cssom-style-in --no-qtwebengine-cssom-style-cm --no-qtwebengine-cssom-style-mm --no-qtwebengine-cssom-style-q --no-qtwebengine-cssom-style-rem --no-qtwebengine-cssom-style-vw --no-qtwebengine-cssom-style-vh --no-qtwebengine-cssom-style-vmin --no-qtwebengine-cssom-style-vmax --no-qtwebengine-cssom-style-ch --no-qtwebengine-cssom-style-ic .\repair_system.exe

实际执行时,若报错Cannot find platform plugin "windows",需手动设置环境变量:

set QT_QPA_PLATFORM_PLUGIN_PATH=D:\Qt\5.15.2\msvc2019_64\plugins\platforms

4. QT调试与性能优化:QLoggingCategory分级日志 + QThreadPool异步数据库操作

毕设答辩时被问“如何保证高并发下数据一致性”,不能只答“用了事务”,需展示具体技术手段。QT提供QLoggingCategoryQThreadPool两大利器。

4.1 分级日志体系:用QLoggingCategory隔离业务日志与SQL日志

默认qDebug()输出混杂,不利于定位问题。按模块定义日志分类:

// logging_setup.h Q_DECLARE_LOGGING_CATEGORY(lcOrder) Q_DECLARE_LOGGING_CATEGORY(lcSync) Q_DECLARE_LOGGING_CATEGORY(lcSql) // logging_setup.cpp Q_LOGGING_CATEGORY(lcOrder, "repair.order") Q_LOGGING_CATEGORY(lcSync, "repair.sync") Q_LOGGING_CATEGORY(lcSql, "repair.sql") // main.cpp 初始化 QLoggingCategory::setFilterRules(QStringLiteral("repair.*.debug=true\n" "repair.sql.debug=false\n" // SQL日志仅在开发环境开启 "repair.sync.info=true"));

在关键路径插入日志:

qCDebug(lcOrder) << "工单" << orderId << "状态从" << oldStatus << "跃迁至" << newStatus; qCInfo(lcSync) << "同步工单" << orderId << "至服务端,结果:" << success;

提示QLoggingCategory支持运行时动态调整级别(如qSetLogLevel(lcSync, QtWarningMsg)),便于演示时临时提升日志等级;qInstallMessageHandler()可将日志重定向到文件,避免控制台刷屏。

4.2 异步数据库操作:QThreadPool + QRunnable避免UI卡顿

QSqlDatabase操作(尤其SELECT大量记录)会阻塞主线程。正确做法是将耗时操作放入线程池:

class LoadOrdersTask : public QRunnable { Q_OBJECT public: explicit LoadOrdersTask(QList<RepairOrder> *orders, QObject *parent = nullptr) : m_orders(orders), m_parent(parent) {} void run() override { QSqlDatabase db = QSqlDatabase::database("main"); // 使用命名连接 QSqlQuery query(db); query.exec("SELECT * FROM repair_orders WHERE status != 'settled'"); while (query.next()) { RepairOrder order; order.id = query.value("id").toInt(); order.customerName = query.value("customer_name").toString(); // ... 其他字段 m_orders->append(order); } // 通知主线程更新UI QMetaObject::invokeMethod(m_parent, [this]() { emit ordersLoaded(*m_orders); }, Qt::QueuedConnection); } signals: void ordersLoaded(const QList<RepairOrder> &orders); private: QList<RepairOrder> *m_orders; QObject *m_parent; }; // 在UI类中调用 void MainWindow::loadOrdersAsync() { auto *task = new LoadOrdersTask(&m_orderList, this); connect(task, &LoadOrdersTask::ordersLoaded, this, &MainWindow::onOrdersLoaded); QThreadPool::globalInstance()->start(task); }

注意QSqlDatabase对象不能跨线程共享,必须在线程内调用QSqlDatabase::addDatabase()创建新连接,或使用QSqlDatabase::cloneDatabase()QMetaObject::invokeMethod()Qt::QueuedConnection确保信号在主线程接收,避免QTableView::setModel()在子线程调用崩溃。

4.3 内存泄漏检测:用QT自带的qInstallMessageHandler捕获QObject泄漏

QT提供QMessageLogger机制,在程序退出前检查未销毁的QObject:

// main.cpp 添加 void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); switch (type) { case QtDebugMsg: fprintf(stderr, "Debug: %s\n", localMsg.constData()); break; case QtWarningMsg: fprintf(stderr, "Warning: %s\n", localMsg.constData()); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s\n", localMsg.constData()); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s\n", localMsg.constData()); abort(); } } int main(int argc, char *argv[]) { qInstallMessageHandler(messageHandler); QApplication app(argc, argv); // ... 启动应用 // 应用退出前检查QObject泄漏 QObject::dumpObjectTree(); // 输出所有存活QObject树 return app.exec(); }

提示QObject::dumpObjectTree()输出到stderr,需重定向到文件分析;重点检查QSqlQueryQNetworkReplyQTimer等易泄漏对象是否被父对象正确持有;QThread子类必须调用moveToThread()而非继承QThread,否则exec()会阻塞主线程。


5. 毕设答辩关键技巧:用QT Creator Profiler抓取CPU热点 + 屏幕录制演示状态流转

答辩时评委最关注“你做了什么”而非“你写了多少行”,需用可视化证据证明技术深度。

5.1 用QT Creator内置Profiler定位性能瓶颈

QT Creator 4.15+集成QML ProfilerC++ Profiler,可直接分析Release版EXE:

  1. Projects → Run Settings → Run → Run in Terminal勾选Run in terminal
  2. Analyze → Start Profiling,选择C++ Profiler
  3. 操作系统:打开工单列表、切换Tab、提交状态变更
  4. 停止后查看Call Graph,重点关注:
    • QSqlQuery::exec()耗时占比(若>30%,说明SQL未加索引)
    • QPainter::drawText()调用频次(若过高,说明UI重绘过度)
    • QStateMachine::postEvent()延迟(若>100ms,说明状态机事件队列积压)

提示:Profiler需在Release模式下运行,Debug模式因编译器优化关闭导致数据失真;若QSqlQuery::exec()耗时高,立即检查repair_orders.status字段是否建立索引:CREATE INDEX idx_orders_status ON repair_orders(status);

5.2 屏幕录制演示脚本:聚焦3个高光时刻

避免全程操作,只录3个关键片段(总时长≤3分钟):

时间点演示内容技术亮点
0:00-0:45服务顾问创建工单 → 技师在离线状态下修改“维修进度” → 网络恢复后自动同步展示QSqlRelationalTableModel本地缓存 +QNetworkAccessManager重连机制
0:46-1:30点击“派工”按钮 → 弹出技师选择框 → 选择后工单状态自动变更为“待派工” → 后台QThread立即查询该技师当前工单数展示QDataWidgetMapper绑定 +QStateMachine状态跃迁 +QThreadPool异步校验
1:31-2:50打开Profiling报告 → 定位QSqlQuery::exec()热点 → 切换到数据库工具执行CREATE INDEX→ 重新运行Profiling,热点消失展示真实性能调优闭环

注意:录制前关闭所有无关窗口,仅保留QT Creator、数据库工具、系统任务管理器(显示CPU/内存);演示时口头说明:“这里我通过Profiler发现SQL执行占CPU 42%,于是检查了索引缺失,添加后降至8%”。

5.3 答辩问答预判:高频问题与精准回答模板

问题回答要点(30秒内说完)
“为什么用CS架构而不是BS?”“BS架构在4S店车间环境存在三大缺陷:1)弱网下页面加载失败,技师无法录入维修过程;2)浏览器无法直接调用本地打印机打印工单;3)HTML/CSS难以实现精确的工单状态机图形化展示。QT客户端可离线操作、直连硬件、状态机可视化,更契合维修业务。”
“如何防止多人同时修改同一工单?”“服务端采用乐观锁:工单表增加version字段,每次更新时WHERE条件包含version=旧值,若影响行数为0则返回冲突。客户端收到冲突后,自动拉取最新数据并高亮差异字段,由用户选择覆盖或放弃。”
“QT和PyQt哪个更适合毕设?”“QT C++直接编译为原生EXE,无Python解释器依赖,打包体积小、启动快、内存占用低。PyQt虽开发快,但毕设需体现底层能力,C++能更好展示多线程、内存管理、SQL事务等核心技能。”

最后一步:将repair_system.exesqlite3.dllplatforms/qwindows.dllimageformats/qjpeg.dll四个文件放入同一文件夹,双击即可运行——这才是评委眼中“真正完成”的毕设。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询