From 63fc2d966cbc5fce0efe5c281b2309167fb1da58 Mon Sep 17 00:00:00 2001 From: kejingfan Date: Wed, 31 Jul 2024 10:31:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E6=9F=A5=E8=AF=A2=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E7=9A=84=E2=80=9C=E4=BD=BF=E7=94=A8=E5=AD=A6/?= =?UTF-8?q?=E5=B7=A5=E5=8F=B7=E6=9F=A5=E8=AF=A2=E2=80=9D=E5=92=8C=E2=80=9C?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E5=8D=A1=E7=9A=84=E6=89=80=E6=9C=89=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E2=80=9D=E5=8A=9F=E8=83=BD=EF=BC=9B=E8=BF=98=E6=9C=AA?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=8D=A1=E5=86=85=E8=AE=B0=E5=BD=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CardManageSystem.pro | 1 + mainwindow.cpp | 30 ++++ mainwindow.h | 13 ++ mainwindow.ui | 247 +++++++++++++++++++++++++++- newCardPage.cpp | 4 +- queryPage.cpp | 377 +++++++++++++++++++++++++++++++++++++++++++ settingPage.cpp | 16 ++ 7 files changed, 681 insertions(+), 7 deletions(-) create mode 100644 queryPage.cpp diff --git a/CardManageSystem.pro b/CardManageSystem.pro index 04e0e7f..6f3e1aa 100644 --- a/CardManageSystem.pro +++ b/CardManageSystem.pro @@ -18,6 +18,7 @@ SOURCES += \ main.cpp \ mainwindow.cpp \ newCardPage.cpp \ + queryPage.cpp \ quitAppPage.cpp \ readerAPI.cpp \ reportLossPage.cpp \ diff --git a/mainwindow.cpp b/mainwindow.cpp index 22b9f93..496cb78 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -7,6 +7,7 @@ #include "reportLossPage.cpp" #include "depositPage.cpp" #include "consumePage.cpp" +#include "queryPage.cpp" MainWindow::MainWindow(QWidget *parent) @@ -53,6 +54,35 @@ MainWindow::MainWindow(QWidget *parent) connect(ui->depositUserIdBox, &QSpinBox::valueChanged, [this]{ depositUserIdFilled = true; }); + connect(ui->queryUserIdBox, &QSpinBox::valueChanged, [this]{ queryUserIdFilled = true; }); + + + // 查询表格设置 + ui->queryResultTable->setShowGrid(true); //设置显示格子线 + ui->queryResultTable->setSelectionBehavior(QAbstractItemView::SelectRows); //整行选中 + ui->queryResultTable->setEditTriggers(QAbstractItemView::NoEditTriggers); //禁止编辑 + ui->queryResultTable->horizontalHeader()->setStretchLastSection(true); //行头自适应表格 + ui->queryResultTable->horizontalHeader()->setHighlightSections(false); //点击表头时不对表头光亮 + ui->queryResultTable->setSortingEnabled(true); //启动排序 + // 设置表头字体加粗 + QFont queryResultTableFont = ui->queryResultTable->horizontalHeader()->font(); + queryResultTableFont.setBold(true); + ui->queryResultTable->horizontalHeader()->setFont(queryResultTableFont); + // 设置表头内容 + QStringList queryResultTableHeader; + queryResultTableHeader << "时间" << "类型" << "金额" << "余额" << "设备" << "交易号"; + ui->queryResultTable->setColumnCount(queryResultTableHeader.size()); //设置列数 + ui->queryResultTable->setHorizontalHeaderLabels(queryResultTableHeader); + ui->queryResultTable->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter); // 每列居中对齐 + + ui->queryResultTable->setColumnWidth(0, 130); + ui->queryResultTable->setColumnWidth(1, 40); + ui->queryResultTable->setColumnWidth(2, 60); + ui->queryResultTable->setColumnWidth(3, 80); + ui->queryResultTable->setColumnWidth(4, 100); + ui->queryResultTable->setColumnWidth(5, 240); + + // 设置启动页面 ui->stackedWidget->setCurrentWidget(ui->settingPage); diff --git a/mainwindow.h b/mainwindow.h index 96fc510..d288e99 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -11,6 +11,9 @@ #include #include +#include +#include + #include #include #include @@ -32,6 +35,7 @@ public: void updateStatusBarComNumber(); bool allReady(); bool softwareReady(); + bool databaseReady(); bool bindUserWithCard(int userId, QString cardId, QString &info); bool getNewUserInfo(QString &username, QString &password, QString &info); bool createUser(int userId, QString &info); @@ -41,6 +45,9 @@ public: bool topUpCard(QString cardId, double topUpValue, double &originalBalance, double &finalBalance, QString &recordId, QString &info); QString getRecordId(QDateTime currentTime, int userId, int recordType); bool deductCard(QString cardId, double deductValue, double &originalBalance, double &finalBalance, QString &recordId, QString &info); + void displayInTableWidget(std::vector transactionRecordList); + QStringList transactionRecord2QStringList(QDateTime time, int type, double value, double balance, QString device, QString id); + void queryPageInitContent(); private slots: void on_settingAction_triggered(); @@ -49,6 +56,7 @@ private slots: void on_reportLossAction_triggered(); void on_depositAction_triggered(); void on_ConsumptionAction_triggered(); + void on_QueryAction_triggered(); void on_connectReaderButton_clicked(); void on_confirmQuitButton_clicked(); @@ -61,6 +69,9 @@ private slots: void on_depositByUserIdButton_clicked(); void on_consumeInventoryButton_clicked(); void on_consumeButton_clicked(); + void on_queryInventoryButton_clicked(); + void on_userRecordQueryButton_clicked(); + void on_userIdRecordQueryButton_clicked(); private: Ui::MainWindow *ui; @@ -80,5 +91,7 @@ private: bool newCardUserIdFilled; ///< 开卡时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true bool reportLossUserIdFilled; ///< 挂失时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true bool depositUserIdFilled; ///< 充值时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true + bool queryUserIdFilled; ///< 查询时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true }; + #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 23c7d47..dad57ad 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 708 - 493 + 709 + 514 @@ -17,8 +17,8 @@ - -50 - -40 + -40 + 0 701 481 @@ -27,7 +27,7 @@ true - 2 + 3 @@ -416,6 +416,243 @@ + + + + + 60 + 160 + 591 + 311 + + + + + + + 420 + 130 + 91 + 23 + + + + 查询卡内记录 + + + + + + 520 + 130 + 91 + 23 + + + + 查询所有记录 + + + + + + 60 + 120 + 354 + 41 + + + + + + + 请选择卡: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + 读卡 + + + + + + + + + 60 + 80 + 350 + 41 + + + + + + + 请输入学/工号: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + 1000 + + + 99999999 + + + 21281280 + + + + + + + 查询学/工号记录 + + + + + + + + + 440 + 20 + 211 + 104 + + + + + + + 卡状态 + + + + + + + 余额 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 150 + 60 + + + + + 20 + true + + + + + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + 170 + 30 + 111 + 31 + + + + + 11 + + + + 查询交易记录 + + + Qt::AlignmentFlag::AlignCenter + + + diff --git a/newCardPage.cpp b/newCardPage.cpp index 4b96a8a..e43b7ae 100644 --- a/newCardPage.cpp +++ b/newCardPage.cpp @@ -433,7 +433,7 @@ bool MainWindow::verifyUser(int userId, QString prompt, QString &info) QString validPassword = query.value(0).toString(); if (validPassword != password) { - info = QString("密码不正确"); + info = QString("密码不正确。"); return false; } else @@ -443,7 +443,7 @@ bool MainWindow::verifyUser(int userId, QString prompt, QString &info) } else { - info = QString("学/工号不存在"); + info = QString("学/工号不存在。"); return false; } } diff --git a/queryPage.cpp b/queryPage.cpp new file mode 100644 index 0000000..008fcb2 --- /dev/null +++ b/queryPage.cpp @@ -0,0 +1,377 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + + +/** + * @brief 切换到查询页面 + * 点击工具栏的“查询”触发 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::on_QueryAction_triggered() +{ + queryPageInitContent(); + ui->stackedWidget->setCurrentWidget(ui->queryPage); +} + + +/** + * @brief 读卡器扫描卡片 + * 点击查询页面的“查询”触发。 + * 如果读卡器未连接,显示警告信息并跳转到设置页面。 + * 显示Inventory的查询结果,最多显示10张卡。 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::on_queryInventoryButton_clicked() +{ + if (!reader.is_connected()) + { + QMessageBox::warning(this, QString("提示"), QString("读卡器未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + QStringList cardIdList = reader.inventory(10); // 最多显示10张卡 + ui->queryCardIdBox->clear(); + if (cardIdList.empty()) + { + QMessageBox::warning(this, "提示", "未发现卡片,请将卡片放置于读卡器上方。"); + } + else + { + ui->queryCardIdBox->addItems(cardIdList); + } +} + + +/** + * @brief 用户选择卡片查询记录 + * 该函数在用户点击“查询”按钮时触发,用于根据选择的卡片ID查询该卡片的所有交易记录。 + * @param void + * @return void + * @details + * 函数首先检查数据库是否已连接。如果数据库未连接,函数将显示警告信息并返回。 + * 接着,函数检查是否选择了卡片。如果未选择卡片,函数将显示警告信息并返回。 + * 然后,函数会查询卡片的绑定用户信息,包括用户ID、卡片状态和余额。如果卡片未被启用或已挂失,函数将显示相应的警告信息并返回。 + * 在查询卡片信息成功后,函数会要求进行用户身份验证。如果验证失败,函数将显示警告信息并返回,并重置查询页面。 + * 如果验证成功,函数会查询该卡片的所有交易记录,并将这些记录转换为 `QStringList` 后显示在界面的表格中。 + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::on_userRecordQueryButton_clicked() +{ + if (!databaseReady()) + { + QMessageBox::warning(this, QString("提示"), QString("数据库未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + if (ui->queryCardIdBox->currentIndex() == -1) + { + QMessageBox::warning(this, "提示", "请放置卡片并点击查询按钮。"); + return; + } + QString cardId = ui->queryCardIdBox->currentText(); + int userId; + int cardStatus; + double balance; + + // 检查和获取绑定用户 + QSqlQuery query(db->getDatabase()); + query.prepare(QString("select userId, `status`, balance from card " + "where id = :cardId;")); + query.bindValue(":cardId", cardId); + bool success = false; + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, QString("提示"), QString("数据库异常。查询失败。")); + queryPageInitContent(); + return; + } + if (query.next()) // 卡已被注册,获取用户ID + { + cardStatus = query.value("status").toInt(); + if (cardStatus == 0) + { + QMessageBox::warning(this, QString("提示"), QString("此卡未被启用。")); + queryPageInitContent(); + return; + } + if (cardStatus == -1) + { + ui->queryCardStatusLabel->setText(QString("已被挂失")); + } + userId = query.value("userId").toInt(); + balance = query.value("balance").toDouble(); + ui->queryBalanceShowEdit->setText(QString::number(balance, 'f', 2)); + } + else // 卡没有注册 + { + query.finish(); + query.prepare(QString("insert into card " + "values (:cardId, 0, 0.0, null);")); + query.bindValue(":cardId", cardId); + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, QString("提示"), QString("数据库异常。查询失败。")); + queryPageInitContent(); + return; + } + + QMessageBox::warning(this, QString("提示"), QString("此卡未被启用。")); + queryPageInitContent(); + return; + } + + QString prompt = "查询用户所有交易记录需要验证用户身份。"; + QString info; + success = verifyUser(userId, prompt, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n用户验证不通过。查询失败。")); + queryPageInitContent(); + return; + } + + query.finish(); + query.prepare(QString("select time, type, value, balance, device, id from record_view " + "where cardId = :cardId;")); + query.bindValue(":cardId", cardId); + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, QString("提示"), QString("数据库异常。查询失败。")); + queryPageInitContent(); + return; + } + + std::vector transactionRecordList; + while (query.next()) + { + QStringList transactionRecord = transactionRecord2QStringList + ( + query.value("time").toDateTime(), + query.value("type").toInt(), + query.value("value").toDouble(), + query.value("balance").toDouble(), + query.value("device").toString(), + query.value("id").toString() + ); + transactionRecordList.push_back(transactionRecord); + } + + displayInTableWidget(transactionRecordList); +} + + +/** + * @brief 用户输入学/工号查询记录 + * 该函数在用户点击“查询”按钮时触发,用于根据输入的学/工号查询该用户的所有交易记录。 + * @param void + * @return void + * @details + * 函数首先检查软件是否已准备好并且数据库已连接。如果数据库未连接,函数将显示警告信息并返回。 + * 接着,函数检查是否输入了学/工号。如果未输入学/工号,函数将显示警告信息并返回。 + * 然后,函数会验证用户身份,如果验证失败,函数将显示警告信息并返回,并重置查询页面。 + * 如果验证成功,函数会查询该学/工号绑定的卡片信息,包括卡片ID、状态和余额,并将余额和状态显示在界面上。 + * 接着,函数会查询该卡片的所有交易记录,并将这些记录转换为 `QStringList` 后显示在界面的表格中。 + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::on_userIdRecordQueryButton_clicked() +{ + if (!softwareReady()) + { + QMessageBox::warning(this, QString("提示"), QString("数据库未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + if (!queryUserIdFilled) + { + QMessageBox::warning(this, "提示", "请填写学/工号。"); + return; + } + int userId = ui->queryUserIdBox->value(); + + bool success = false; + QString prompt = "查询用户所有交易记录需要验证用户身份。"; + QString info; + success = verifyUser(userId, prompt, info); // 内部会检查用户是否存在 + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n查询失败。")); + queryPageInitContent(); + return; + } + + QSqlQuery query(db->getDatabase()); + query.prepare(QString("select id, `status`, balance from card " + "where userId = :userId;")); + query.bindValue(":userId", userId); + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, "提示", "数据库异常。查询失败。"); + queryPageInitContent(); + return; + } + if (!query.next()) + { + QMessageBox::warning(this, "提示", "学/工号未绑定卡。查询失败。"); + queryPageInitContent(); + return; + } + QString cardId = query.value("id").toString(); + int cardStatus = query.value("status").toInt(); + double balance = query.value("balance").toDouble(); + ui->queryBalanceShowEdit->setText(QString::number(balance, 'f', 2)); + if (cardStatus == -1) + { + ui->queryCardStatusLabel->setText(QString("已被挂失")); + } + else if (cardStatus == 1) + { + ui->queryCardStatusLabel->setText(QString("启用中")); + } + + query.finish(); + query.prepare(QString("select time, type, value, balance, device, id from record_view " + "where cardId = :cardId;")); + query.bindValue(":cardId", cardId); + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, QString("提示"), QString("数据库异常。查询失败。")); + queryPageInitContent(); + return; + } + + std::vector transactionRecordList; + while (query.next()) + { + QStringList transactionRecord = transactionRecord2QStringList + ( + query.value("time").toDateTime(), + query.value("type").toInt(), + query.value("value").toDouble(), + query.value("balance").toDouble(), + query.value("device").toString(), + query.value("id").toString() + ); + transactionRecordList.push_back(transactionRecord); + } + + displayInTableWidget(transactionRecordList); +} + + +/** + * @brief 将数据库查询返回的数据转化为QStringList + * 该函数用于将数据库查询返回的交易记录数据转换为一个 `QStringList`,以便在界面上显示。 + * @param time 交易时间,类型为 QDateTime + * @param type 交易类型,非零表示充值,零表示消费 + * @param value 交易金额,类型为 double + * @param balance 交易后的余额,类型为 double + * @param device 交易设备名称,类型为 QString + * @param id 交易记录号,类型为 QString + * @return QStringList 转换后的交易记录列表 + * @details + * 函数将输入的交易时间、交易类型、交易金额、余额、设备名称和记录号转换为一个QStringList。 + * 交易类型用QString表示,充值为“充值”,消费为“消费”。交易金额根据交易类型显示为正值或负值,保留两位小数。 + * @author 柯劲帆 + * @date 2024-07-31 + */ +QStringList MainWindow::transactionRecord2QStringList(QDateTime time, int type, double value, double balance, QString device, QString id) +{ + QStringList stringList = { + time.toString("yyyy-MM-dd hh:mm:ss"), + type != 0 ? QString("充值") : QString("消费"), + QString::number((type != 0 ? value : -value), 'f', 2), + QString::number(balance, 'f', 2), + device, + id + }; + + return stringList; +} + + +/** + * @brief 初始化查询页面的各个组件内容 + * 该函数用于初始化查询页面上的各个组件,包括清空用户ID输入框、清空查询结果表、清空余额显示框和重置卡片状态标签。 + * @param void + * @return void + * @details + * 函数首先将 queryUserIdFilled 标志设置为false,表示尚未填入用户ID。 + * 然后,函数依次清空用户ID输入框、查询结果表和余额显示框,并将卡片状态标签设置为"-"。 + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::queryPageInitContent() +{ + queryUserIdFilled = false; + ui->queryUserIdBox->clear(); + ui->queryResultTable->clearContents(); + ui->queryBalanceShowEdit->clear(); + ui->queryCardStatusLabel->setText(QString("-")); +} + + +/** + * @brief 在列表组件中显示内容 + * 该函数用于在 `QTableWidget` 中显示交易记录列表。交易记录包括充值和消费信息,并且根据交易类型进行颜色标注。 + * @param transactionRecordList 包含交易记录的 `QStringList` 向量,每个 `QStringList` 表示一条记录 + * @return void + * @details + * 函数首先清除 `QTableWidget` 中的所有内容,然后设置行数为 `transactionRecordList` 的大小。 + * 接着,函数遍历每条交易记录,并将其内容逐行逐列插入 `QTableWidget` 中。对于充值记录,文本颜色设置为绿色; + * 对于消费记录,文本颜色设置为红色。所有单元格内容都右对齐。 + * @author 柯劲帆 + * @date 2024-07-31 + */ +void MainWindow::displayInTableWidget(std::vector transactionRecordList) +{ + ui->queryResultTable->clearContents(); + + int numRows = transactionRecordList.size(); + + ui->queryResultTable->setRowCount(numRows); + + for (int i = 0; i < numRows; i++) + { + QStringList transactionRecord = transactionRecordList[i]; + bool is_deposit = transactionRecord[1] == QString("充值"); + for (int j = 0; j < 6; j++) + { + QTableWidgetItem *item = new QTableWidgetItem(transactionRecord[j]); + item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); // 设置右对齐 + + // 渲染:充值为绿色,消费为红色(不知为何不工作) + if (is_deposit) + item->setForeground(QBrush(QColor(Qt::green))); + else + item->setForeground(QBrush(QColor(Qt::red))); + + ui->queryResultTable->setItem(i, j, item); // 设置内容 + } + } +} + diff --git a/settingPage.cpp b/settingPage.cpp index 7a83dad..48815ad 100644 --- a/settingPage.cpp +++ b/settingPage.cpp @@ -137,3 +137,19 @@ bool MainWindow::softwareReady() if (!device.is_verified()) return false; return true; } + + +/** + * @brief 检查数据库是否准备好 + * @param void + * @return 数据库状态 + * - true 已准备好 + * - false 未准备好 + * @author 柯劲帆 + * @date 2024-07-31 + */ +bool MainWindow::databaseReady() +{ + if (db == nullptr || !db->is_connected()) return false; + return true; +}