From 8968bba852e6f8a14dd7986a15de5ae8ac11aa36 Mon Sep 17 00:00:00 2001 From: kejingfan Date: Tue, 30 Jul 2024 22:32:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=85=85=E5=80=BC=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E7=9A=84=E6=89=80=E6=9C=89=E5=8A=9F=E8=83=BD=EF=BC=88?= =?UTF-8?q?=E4=B8=8D=E5=8C=85=E6=8B=AC=E5=86=99=E5=8D=A1=EF=BC=89=EF=BC=9B?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=E6=B6=88=E8=B4=B9=E9=A1=B5=E9=9D=A2=EF=BC=8C?= =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=B7=B3=E8=BD=AC=E5=92=8C=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=8D=A1=E5=87=BD=E6=95=B0=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CardManageSystem.pro | 2 + consumePage.cpp | 51 ++++++ depositPage.cpp | 329 ++++++++++++++++++++++++++++++++++++++ deviceAPI.cpp | 32 +++- deviceAPI.h | 2 + mainwindow.cpp | 6 +- mainwindow.h | 14 ++ mainwindow.ui | 365 ++++++++++++++++++++++++++++++++++++++++++- newCardPage.cpp | 2 +- 9 files changed, 792 insertions(+), 11 deletions(-) create mode 100644 consumePage.cpp create mode 100644 depositPage.cpp diff --git a/CardManageSystem.pro b/CardManageSystem.pro index ae4ea9c..04e0e7f 100644 --- a/CardManageSystem.pro +++ b/CardManageSystem.pro @@ -11,7 +11,9 @@ CONFIG += c++17 SOURCES += \ VCD.cpp \ VCDOurs.cpp \ + consumePage.cpp \ databaseAPI.cpp \ + depositPage.cpp \ deviceAPI.cpp \ main.cpp \ mainwindow.cpp \ diff --git a/consumePage.cpp b/consumePage.cpp new file mode 100644 index 0000000..61e0e00 --- /dev/null +++ b/consumePage.cpp @@ -0,0 +1,51 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + + +/** + * @brief 切换到消费页面 + * 点击工具栏的“消费”触发 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_ConsumptionAction_triggered() +{ + ui->stackedWidget->setCurrentWidget(ui->consumePage); +} + + +/** + * @brief 读卡器扫描卡片 + * 点击消费页面的“查询”触发。 + * 如果读卡器未连接,显示警告信息并跳转到设置页面。 + * 显示Inventory的查询结果,最多显示10张卡。 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_consumeInventoryButton_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->consumeCardIdBox->clear(); + if (cardIdList.empty()) + { + QMessageBox::warning(this, "提示", "未发现卡片,请将卡片放置于读卡器上方。"); + } + else + { + ui->consumeCardIdBox->addItems(cardIdList); + } +} diff --git a/depositPage.cpp b/depositPage.cpp new file mode 100644 index 0000000..785a0f8 --- /dev/null +++ b/depositPage.cpp @@ -0,0 +1,329 @@ +#include "mainwindow.h" +#include "ui_mainwindow.h" + + +/** + * @brief 切换到充值页面 + * 点击工具栏的“充值”触发 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_depositAction_triggered() +{ + ui->depositUserIdBox->clear(); + depositUserIdFilled = false; + ui->stackedWidget->setCurrentWidget(ui->depositPage); +} + + +/** + * @brief 读卡器扫描卡片 + * 点击充值页面的“查询”触发。 + * 如果读卡器未连接,显示警告信息并跳转到设置页面。 + * 显示Inventory的查询结果,最多显示10张卡。 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_depositInventoryButton_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->depositCardIdBox->clear(); + if (cardIdList.empty()) + { + QMessageBox::warning(this, "提示", "未发现卡片,请将卡片放置于读卡器上方。"); + } + else + { + ui->depositCardIdBox->addItems(cardIdList); + } +} + + +/** + * @brief 按卡号充值 + * 该函数在用户点击“按卡号充值”按钮时触发,用于对指定卡号进行充值操作。 + * @param void + * @return void + * @details + * 函数首先检查软件和设备的准备状态。如果数据库未连接或设备不支持充值,函数将显示警告信息并返回。 + * 接着,函数检查是否选择了卡号和充值金额。如果未选择卡号或充值金额无效,函数将显示警告信息并返回。 + * 然后,函数调用 `topUpCard` 函数进行充值操作,并在操作成功后显示充值结果。 + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_depositByCardIdButton_clicked() +{ + if (!softwareReady()) + { + QMessageBox::warning(this, QString("提示"), QString("数据库未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + if (!reader.is_connected()) + { + QMessageBox::warning(this, QString("提示"), QString("读卡器未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + if (!device.is_depositAllowed()) + { + QMessageBox::warning(this, QString("提示"), QString("本设备不支持充值。")); + return; + } + + if (ui->depositCardIdBox->currentIndex() == -1) + { + QMessageBox::warning(this, "提示", "请放置卡片并点击查询按钮。"); + return; + } + QString cardId = ui->depositCardIdBox->currentText(); + + double topUpValue = ui->depositValueBox->value(); + if (!(topUpValue > 0.00)) + { + QMessageBox::warning(this, "提示", "请输入大于0.00的充值金额。"); + return; + } + + bool success = false; + QString info; + double originalBalance, finalBalance; + QString recordId; + success = topUpCard(cardId, topUpValue, originalBalance, finalBalance, recordId, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n充值失败。")); + return; + } + + QString depositResultMessage = QString("充值成功:") + QString::number(topUpValue) + QString("元\n"); + depositResultMessage += QString("原余额:") + QString::number(originalBalance) + QString("元\n"); + depositResultMessage += QString("充值后余额:") + QString::number(finalBalance) + QString("元\n"); + depositResultMessage += QString("充值设备:") + device.getName() + QString("\n"); + depositResultMessage += QString("交易记录号:") + recordId + QString("\n"); + QMessageBox::information(this, "充值成功", depositResultMessage); + return; +} + + +/** + * @brief 按学/工号充值 + * 该函数在用户点击“按学/工号充值”按钮时触发,用于对指定学/工号的用户进行充值操作。 + * @param void + * @return void + * @details + * 函数首先检查软件和设备的准备状态。如果数据库未连接或设备不支持充值,函数将显示警告信息并返回。 + * 接着,函数检查是否填写了学/工号和充值金额。如果未填写或充值金额无效,函数将显示警告信息并返回。 + * 然后,函数在数据库中查询指定学/工号绑定的卡片。如果卡片不存在,函数将显示警告信息并返回。 + * 如果卡片存在,函数调用 `topUpCard` 函数进行充值操作,并在操作成功后显示充值结果。 + * @author 柯劲帆 + * @date 2024-07-30 + */ +void MainWindow::on_depositByUserIdButton_clicked() +{ + if (!softwareReady()) + { + QMessageBox::warning(this, QString("提示"), QString("数据库未连接,请设置。")); + if (ui->stackedWidget->currentWidget() != ui->settingPage) + { + ui->stackedWidget->setCurrentWidget(ui->settingPage); + } + return; + } + + if (!device.is_depositAllowed()) + { + QMessageBox::warning(this, QString("提示"), QString("本设备不支持充值。")); + return; + } + + if (!depositUserIdFilled) + { + QMessageBox::warning(this, "提示", "请填写学/工号。"); + return; + } + int userId = ui->depositUserIdBox->value(); + + double topUpValue = ui->depositValueBox->value(); + if (!(topUpValue > 0.00)) + { + QMessageBox::warning(this, "提示", "请输入大于0.00的充值金额。"); + return; + } + + QSqlQuery query(db->getDatabase()); + bool success = false; + query.prepare(QString("select id from card " + "where userId = :userId;")); + query.bindValue(":userId", userId); + success = query.exec(); + if (!success) + { + QMessageBox::warning(this, "提示", "数据库异常。充值失败。"); + return; + } + if (!query.next()) + { + QMessageBox::warning(this, "提示", "学/工号未绑定卡。充值失败。"); + return; + } + QString cardId = query.value("id").toString(); + + QString info; + double originalBalance = 0, finalBalance = 0; + QString recordId; + success = topUpCard(cardId, topUpValue, originalBalance, finalBalance, recordId, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n充值失败。")); + return; + } + + QString depositResultMessage = QString("充值成功:") + QString::number(topUpValue) + QString("元\n"); + depositResultMessage += QString("原余额:") + QString::number(originalBalance) + QString("元\n"); + depositResultMessage += QString("充值后余额:") + QString::number(finalBalance) + QString("元\n"); + depositResultMessage += QString("充值设备:") + device.getName() + QString("\n"); + depositResultMessage += QString("交易记录号:") + recordId + QString("\n"); + QMessageBox::information(this, "充值成功", depositResultMessage); + return; +} + + +/** + * @brief 充值卡 + * 该函数用于给指定的卡片充值。充值前会进行一系列检查,包括设备是否支持充值、卡片状态、充值金额是否超过限额等。 + * 如果所有检查通过,将调用存储过程 `sp_depositCard` 进行充值操作,并返回充值后的余额。 + * @param cardId 要充值的卡号 + * @param topUpValue 充值金额 + * @param originalBalance 充值前的余额,通过引用返回 + * @param finalBalance 充值后的余额,通过引用返回 + * @param recordId 交易编号,通过引用返回 + * @param info 如果出现异常,填入异常信息,通过引用返回 + * @return bool 是否充值成功 + * - true 成功 + * - false 失败 + * @details + * 函数首先检查设备是否支持充值。如果设备不支持,函数返回失败并设置错误信息。 + * 接着,函数查询数据库获取卡片的当前状态和余额。如果卡片不存在、已挂失或未启用,函数返回失败并设置相应的错误信息。 + * 然后,函数检查充值后余额是否超过限额(9999.99元)。如果超过限额,函数返回失败并设置错误信息。 + * 如果所有检查通过,函数调用存储过程 `sp_depositCard` 进行充值操作,并通过存储过程返回充值后的余额。 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::topUpCard(QString cardId, double topUpValue, double &originalBalance, double &finalBalance, QString &recordId, QString &info) +{ + if (!device.is_depositAllowed()) + { + info = "设备不支持充值。"; + return false; + } + + QSqlQuery query(db->getDatabase()); + bool success; + query.prepare(QString("select userId, `status`, balance from card " + "where id = :cardId;")); + query.bindValue(":cardId", cardId); + success = query.exec(); + if (!success) + { + info = "数据库异常。"; + return false; + } + if (!query.next()) + { + info = "卡号不存在。"; + return false; + } + int cardStatus = query.value("status").toInt(); + if (cardStatus == -1) + { + info = "此卡已被挂失。"; + return false; + } + if (cardStatus == 0) + { + info = "此卡未被启用。"; + return false; + } + originalBalance = query.value("balance").toDouble(); + int userId = query.value("userId").toInt(); + + if (originalBalance + topUpValue > (double)9999.99) + { + info = "充值金额超过限额。卡余额不得超过9999.99元。"; + return false; + } + + QDateTime currentTime = QDateTime::currentDateTime(); + recordId = getRecordId(currentTime, userId, 1); + + query.finish(); + query.prepare(QString("call sp_depositCard (:cardId, :value, :recordId, :device, :time, :type, @newBalance);")); + query.bindValue(":cardId", cardId); + query.bindValue(":value", topUpValue); + query.bindValue(":recordId", recordId); + query.bindValue(":device", device.getId()); + query.bindValue(":time", currentTime.toString("yyyy-MM-dd hh:mm:ss")); + query.bindValue(":type", 1); + success = query.exec(); + if (!success) + { + info = "数据库异常。"; + return false; + } + query.exec("select @newBalance;"); + query.next(); + finalBalance = query.value("@newBalance").toDouble(); + + /// @todo 写卡 + + return true; +} + + +/** + * @brief 生成唯一的记录号 + * 该函数用于生成一个唯一的记录号,记录号由当前时间、用户ID、记录类型和一个随机数拼接而成。 + * @param currentTime 当前时间 + * @param userId 用户的学/工号 + * @param recordType 记录类型 + * - 0 消费 + * - 1 充值 + * @return QString 生成的唯一记录号(长度为30个字符) + * @details + * 函数首先将当前时间转换为字符串格式,然后将用户ID转换为11位长度的字符串(右对齐,不足部分用0填充)。 + * 接着生成一个随机的4位十六进制数,最后将这些部分拼接起来形成一个唯一的记录号。 + * @author 柯劲帆 + * @date 2024-07-30 + */ +QString MainWindow::getRecordId(QDateTime currentTime, int userId, int recordType) +{ + QString timeStr = currentTime.toString("yyyyMMddhhmmss"); // 第0-13位:时间 + QString userIdStr = QString::number(userId).rightJustified(11, '0'); // 第14-24位:用户学/工号 + QString typeStr = QString::number(recordType); // 第25位:记录类型 + QString randomHex = QString::number(QRandomGenerator::global()->bounded(0x10000), 16).rightJustified(4, '0'); // 第26-29位:随机十六进制数 + QString recordId = timeStr + userIdStr + typeStr + randomHex; // 共30位 + return recordId; +} diff --git a/deviceAPI.cpp b/deviceAPI.cpp index 2c067b2..0a357cb 100644 --- a/deviceAPI.cpp +++ b/deviceAPI.cpp @@ -33,7 +33,7 @@ bool Device::is_depositAllowed() /** * @brief 设置并认证设备 - * 认证设备会更新设备名和充值权限。 + * 认证设备会更新设备名、充值权限和设备ID。 * @param name 设备名 * @param db 数据库 * @return void @@ -43,7 +43,7 @@ bool Device::is_depositAllowed() void Device::setDevice(QString name, Database* db) { QSqlQuery query(db->getDatabase()); - query.prepare(QString("select depositAllowed from device " + query.prepare(QString("select id, depositAllowed from device " "where name = :name;")); query.bindValue(":name", name); query.exec(); @@ -51,12 +51,14 @@ void Device::setDevice(QString name, Database* db) { verified = true; this->name = name; - depositAllowed = query.value(0).toBool(); + depositAllowed = query.value("depositAllowed").toBool(); + id = query.value("id").toInt(); } else { verified = false; depositAllowed = false; + id = -1; } } @@ -64,7 +66,7 @@ void Device::setDevice(QString name, Database* db) /** * @brief 获取设备名及其充值权限 * @param void - * @return 返回QString类name属性 + * @return QString 设备名及其充值权限 * - 若设备未认证返回"未指定设备名" * - 若设备已认证 * - 若设备可充值,返回设备名加"(可充值)" @@ -85,9 +87,9 @@ QString Device::getNameAndDepositAllowed() /** * @brief 获取设备名 * @param void - * @return 返回QString类name属性 - * - 若设备未认证返回"未指定设备名" - * - 若设备已认证返回设备名 + * @return QString 设备名(name属性) + * - 若设备未认证返回"未指定设备名" + * - 若设备已认证返回设备名 * @author 柯劲帆 * @date 2024-07-29 */ @@ -96,3 +98,19 @@ QString Device::getName() if (verified) return name; else return QString("未指定设备名"); } + + +/** + * @brief 获取设备ID + * @param void + * @return int 设备ID + * - 若设备未认证返回-1 + * - 若设备已认证返回设备ID + * @author 柯劲帆 + * @date 2024-07-30 + */ +int Device::getId() +{ + if (verified) return id; + else return -1; +} diff --git a/deviceAPI.h b/deviceAPI.h index c752db1..fac36c2 100644 --- a/deviceAPI.h +++ b/deviceAPI.h @@ -19,12 +19,14 @@ private: bool verified = false; bool depositAllowed = false; QString name = QString("未指定设备名"); + int id = -1; public: void setDevice(QString name, Database *db); QString getName(); QString getNameAndDepositAllowed(); + int getId(); bool is_verified(); bool is_depositAllowed(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 2f8461f..31b7e02 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -5,6 +5,8 @@ #include "quitAppPage.cpp" #include "newCardPage.cpp" #include "reportLossPage.cpp" +#include "depositPage.cpp" +#include "consumePage.cpp" MainWindow::MainWindow(QWidget *parent) @@ -44,10 +46,13 @@ MainWindow::MainWindow(QWidget *parent) ui->userIdBox->clear(); newCardUserIdFilled = false; connect(ui->userIdBox, &QSpinBox::valueChanged, [this]{ newCardUserIdFilled = true; }); + ui->reportLossUserIdBox->clear(); reportLossUserIdFilled = false; connect(ui->reportLossUserIdBox, &QSpinBox::valueChanged, [this]{ reportLossUserIdFilled = true; }); + connect(ui->depositUserIdBox, &QSpinBox::valueChanged, [this]{ depositUserIdFilled = true; }); + // 设置启动页面 ui->stackedWidget->setCurrentWidget(ui->settingPage); @@ -58,4 +63,3 @@ MainWindow::~MainWindow() { delete ui; } - diff --git a/mainwindow.h b/mainwindow.h index 05094c8..50a8408 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -36,12 +38,15 @@ public: bool verifyUser(int userId, QString prompt, QString &info); bool transferCard(int userId, QString newCardId, QString oldCardId, QString &info); bool reopenCard(QString cardId, QString &info); + bool topUpCard(QString cardId, double topUpValue, double &originalBalance, double &finalBalance, QString &recordId, QString &info); + QString getRecordId(QDateTime currentTime, int userId, int recordType); private slots: void on_settingAction_triggered(); void on_quitAppAction_triggered(); void on_NewCardAction_triggered(); void on_reportLossAction_triggered(); + void on_depositAction_triggered(); void on_connectReaderButton_clicked(); void on_confirmQuitButton_clicked(); @@ -49,6 +54,14 @@ private slots: void on_inventoryButton_clicked(); void on_newCardButton_clicked(); void on_reportLossButton_clicked(); + void on_depositInventoryButton_clicked(); + void on_depositByCardIdButton_clicked(); + + void on_depositByUserIdButton_clicked(); + + void on_consumeInventoryButton_clicked(); + + void on_ConsumptionAction_triggered(); private: Ui::MainWindow *ui; @@ -67,5 +80,6 @@ private: bool newCardUserIdFilled; ///< 开卡时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true bool reportLossUserIdFilled; ///< 挂失时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true + bool depositUserIdFilled; ///< 充值时:初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 7ef65f4..23c7d47 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -27,7 +27,7 @@ true - 3 + 2 @@ -233,7 +233,11 @@ - + + + QLineEdit::EchoMode::Password + + @@ -265,6 +269,153 @@ + + + + + 200 + 110 + 291 + 80 + + + + + + + + 11 + + + + 消费 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + 金额: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + 15 + true + + + + + + + + + + 0.000000000000000 + + + 300.000000000000000 + + + + + + + + + + + 190 + 220 + 354 + 81 + + + + + + + 请选择卡: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + 查询 + + + + + + + + + 310 + 330 + 80 + 23 + + + + 确定 + + + @@ -401,6 +552,216 @@ + + + + + 440 + 310 + 80 + 23 + + + + 按卡号充值 + + + + + + 170 + 310 + 91 + 23 + + + + 按学/工号充值 + + + + + + 330 + 220 + 354 + 81 + + + + + + + 请选择卡: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + + + + + 80 + 0 + + + + + 80 + 16777215 + + + + 查询 + + + + + + + + + 50 + 220 + 249 + 81 + + + + + + + 请输入学/工号: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + 1000 + + + 99999999 + + + 21281280 + + + + + + + + + 250 + 110 + 246 + 80 + + + + + + + + 11 + + + + 充值 + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + + 金额: + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + + 15 + true + + + + + + + + + + 9999.989999999999782 + + + + + + + + diff --git a/newCardPage.cpp b/newCardPage.cpp index c839036..4b96a8a 100644 --- a/newCardPage.cpp +++ b/newCardPage.cpp @@ -42,7 +42,7 @@ void MainWindow::on_inventoryButton_clicked() ui->cardIdBox->clear(); if (cardIdList.empty()) { - QMessageBox::warning(this, "查询卡结果", "未发现卡片,请将卡片放置于读卡器上方。"); + QMessageBox::warning(this, "提示", "未发现卡片,请将卡片放置于读卡器上方。"); } else {