From ad40ab7ecd42790f2118ccdd8b9f609155030560 Mon Sep 17 00:00:00 2001 From: kejingfan Date: Tue, 30 Jul 2024 02:13:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BA=86=E5=BC=80=E5=8D=A1?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=88=E9=99=A4=E4=BA=86=E7=A1=AC=E4=BB=B6?= =?UTF-8?q?=E9=83=A8=E5=88=86=EF=BC=89=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deviceAPI.cpp | 26 ++- deviceAPI.h | 1 + mainwindow.cpp | 7 +- mainwindow.h | 10 + mainwindow.ui | 5 +- newCardPage.cpp | 517 +++++++++++++++++++++++++++++++++++++++++++++++- settingPage.cpp | 2 +- 7 files changed, 558 insertions(+), 10 deletions(-) diff --git a/deviceAPI.cpp b/deviceAPI.cpp index 6d8d00c..2c067b2 100644 --- a/deviceAPI.cpp +++ b/deviceAPI.cpp @@ -43,13 +43,15 @@ bool Device::is_depositAllowed() void Device::setDevice(QString name, Database* db) { QSqlQuery query(db->getDatabase()); - QString sql = QString("select * from device where `name` = '%1';").arg(name); - query.exec(sql); + query.prepare(QString("select depositAllowed from device " + "where name = :name;")); + query.bindValue(":name", name); + query.exec(); if (query.next()) { verified = true; this->name = name; - depositAllowed = query.value(2).toBool(); + depositAllowed = query.value(0).toBool(); } else { @@ -70,7 +72,7 @@ void Device::setDevice(QString name, Database* db) * @author 柯劲帆 * @date 2024-07-28 */ -QString Device::getName() +QString Device::getNameAndDepositAllowed() { if (verified) { if (depositAllowed) return name + QString("(可充值)"); @@ -78,3 +80,19 @@ QString Device::getName() } else return QString("未指定设备名"); } + + +/** + * @brief 获取设备名 + * @param void + * @return 返回QString类name属性 + * - 若设备未认证返回"未指定设备名" + * - 若设备已认证返回设备名 + * @author 柯劲帆 + * @date 2024-07-29 + */ +QString Device::getName() +{ + if (verified) return name; + else return QString("未指定设备名"); +} diff --git a/deviceAPI.h b/deviceAPI.h index 6f70f17..c752db1 100644 --- a/deviceAPI.h +++ b/deviceAPI.h @@ -24,6 +24,7 @@ public: void setDevice(QString name, Database *db); QString getName(); + QString getNameAndDepositAllowed(); bool is_verified(); bool is_depositAllowed(); diff --git a/mainwindow.cpp b/mainwindow.cpp index 9e06ff0..c4aac12 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -35,12 +35,14 @@ MainWindow::MainWindow(QWidget *parent) databaseLabel = new QLabel("数据库无连接"); ui->statusBar->addWidget(databaseLabel); - deviceLabel = new QLabel(device.getName()); - ui->statusBar->addWidget(deviceLabel); + deviceLabel = new QLabel(device.getNameAndDepositAllowed()); + ui->statusBar->addWidget(deviceLabel); // 清空部分输入框 ui->userIdBox->clear(); + userIdFilled = false; + connect(ui->userIdBox, &QSpinBox::valueChanged, [this]{ userIdFilled = true; }); // 设置启动页面 @@ -52,4 +54,3 @@ MainWindow::~MainWindow() { delete ui; } - diff --git a/mainwindow.h b/mainwindow.h index 95d923e..48fa7b2 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,12 @@ public: void updateStatusBarComNumber(); bool ready(); + bool bindUserWithCard(int userId, QString cardId, QString &info); + bool getNewUserInfo(QString &username, QString &password, QString &info); + bool createUser(int userId, QString &info); + bool verifyUser(int userId, QString prompt, QString &info); + bool transferCard(int userId, QString newCardId, QString oldCardId, QString &info); + bool reopenCard(QString cardId, QString &info); private slots: void on_settingAction_triggered(); @@ -38,6 +45,7 @@ private slots: void on_confirmQuitButton_clicked(); void on_connectDatabaseButton_clicked(); void on_inventoryButton_clicked(); + void on_newCardButton_clicked(); private: Ui::MainWindow *ui; @@ -53,5 +61,7 @@ private: QCheckBox *databaseConnectStatusCheckBox; QLabel *databaseLabel; QLabel *deviceLabel; + + bool userIdFilled; ///< 初始时学/工号填写框被清空,该变量为false;当用户填写后该变量为true }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index 498ceb5..2954fe4 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -296,6 +296,9 @@ 99999999 + + 21281280 + @@ -337,7 +340,7 @@ - + 300 diff --git a/newCardPage.cpp b/newCardPage.cpp index 622042d..25ff740 100644 --- a/newCardPage.cpp +++ b/newCardPage.cpp @@ -40,5 +40,520 @@ void MainWindow::on_inventoryButton_clicked() { QStringList cardIdList = reader.inventory(10); // 最多显示10张卡 ui->cardIdBox->clear(); - ui->cardIdBox->addItems(cardIdList); + if (cardIdList.empty()) + { + QMessageBox::warning(this, "查询卡结果", "未发现卡片,请将卡片放置于读卡器上方。"); + } + else + { + ui->cardIdBox->addItems(cardIdList); + } + +} + + +/** + * @brief 开卡 + * 该函数在用户点击“确认”按钮时触发,用于处理新卡片的注册和绑定操作。 + * 函数首先检查是否选择了卡片和填写了用户ID,然后进行各种验证和数据库操作,确保卡片和用户的状态正确。 + * 如果所有操作成功,最终将新卡片和用户绑定。 + * @details + * 函数首先从用户界面中获取选择的卡片ID和填写的用户ID,并进行非空检查。 + * 如果未选择卡片或未填写用户ID,则弹出警告对话框并退出函数。 + * 然后,函数会查询数据库,验证卡片和用户的状态,并根据不同情况进行相应处理: + * - 如果卡片已被启用,提示用户开卡失败。 + * - 如果卡片已挂失,需要用户输入密码进行验证和重开卡。 + * - 如果卡片未在数据库中,则将其初始化为未启用状态。 + * 函数还会检查用户是否为新用户,如果是新用户,则插入新用户记录并绑定卡片。 + * 如果用户已有卡片,则需要根据卡片状态决定是否允许开新卡或进行移资操作。 + * @param void + * @return void + * @author 柯劲帆 + * @date 2024-07-29 + */ +void MainWindow::on_newCardButton_clicked() +{ + if (ui->cardIdBox->currentIndex() == -1) + { + QMessageBox::warning(this, "提示", "请放置卡片并点击查询按钮。"); + return; + } + + if (!userIdFilled) + { + QMessageBox::warning(this, "提示", "请填写学/工号。"); + return; + } + + QString cardIdSelected = ui->cardIdBox->currentText(); + int userId = ui->userIdBox->value(); + + // 查询卡片能否使用 + QSqlQuery query(db->getDatabase()); + query.finish(); + query.prepare(QString("select `status`, userId from card " + "where id = :cardId;")); + query.bindValue(":cardId", cardIdSelected); + bool success = query.exec(); + if (!success) + { + qDebug() << "select `status`, userId from card where id = :cardId;1"; + QMessageBox::warning(this, "提示", QString("数据库异常。开卡失败,请重试。")); + return; + } + int cardStatus; ///< 0未启用;1已启用;-1已挂失 + if (query.next()) + { + cardStatus = query.value("status").toInt(); + if (cardStatus == 1) // 已被启用,不能开卡 + { + QMessageBox::warning(this, "卡状态提示", "本卡已被启用,开卡失败。"); + return; + } + if (cardStatus == -1) // 已被挂失,需要密码重开 + { + // 弹出验证用户界面,要求用户输入密码(注意这里需要查询数据库得到挂失卡的用户) + int cardUserId = query.value("userId").toInt(); + query.finish(); + query.prepare("select `name` from `user` " + "where id = :userId"); + query.bindValue(":userId", cardUserId); + bool success = query.exec(); + if (!success || !query.next()) + { + QMessageBox::warning(this, "提示", QString("数据库异常。\n重开卡失败,请重试。")); + return; + } + QString cardUserName = query.value("name").toString(); + QString prompt = QString("本卡关联的学/工号为") + QString::number(cardUserId); + prompt += QString(",关联的姓名为") + cardUserName + QString("。\n"); + prompt += QString("如需重开本卡,请输入密码。"); + QString info; + success = verifyUser(cardUserId, prompt, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n验证用户失败,请重试。")); + return; + } + + success = reopenCard(cardIdSelected, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n重开卡失败,请重试。")); + return; + } + + return; + } + } + else // 该卡不在数据库中,将该卡在数据库中初始化,设置为未启用 + { + query.finish(); + query.prepare("insert into card " + "values (:cardId, 0, 0.00, null);"); + query.bindValue(":cardId", cardIdSelected); + query.exec(); + } + + // 检查是否是新用户 + query.finish(); + query.prepare("select 1 from `user` " + "where id = :userId;"); + query.bindValue(":userId", userId); + success = query.exec(); + if (!success) + { + qDebug() << "select 1 from `user` where id = :userId;"; + QMessageBox::warning(this, "提示", QString("数据库异常。开卡失败,请重试。")); + return; + } + if (!query.next()) // 库中无用户记录,将新用户插入数据库并开卡 + { + QString info; + bool success = createUser(userId, info); // 将新用户插入数据库 + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n注册失败,请重新开卡。")); + return; + } + success = bindUserWithCard(userId, cardIdSelected, info); // 将新用户和卡绑定(卡在读取时已写入数据库) + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n注册失败,请重新开卡。")); + return; + } + QMessageBox::information(this, "提示", "新用户开卡成功。"); + return; + } + else // 库中有用户记录 + { + // 检查用户是否有卡,不可开卡/有挂失卡,是否需要移资 + query.finish(); + query.prepare("select `status`, id from card " + "where userId = :userId;"); + query.bindValue(":userId", userId); + bool success = query.exec(); + if (!success) + { + qDebug() << "select `status`, id from card where userId = :userId;2"; + QMessageBox::warning(this, "提示", QString("数据库异常。开卡失败,请重试。")); + return; + } + if (query.next()) // 用户有卡 + { + int userCardStatus = query.value("status").toInt(); + if (userCardStatus == 1) // 用户已启用卡 + { + QMessageBox::warning(this, "提示", "该用户已有正在使用的卡,开卡失败。\n如需开新卡,请挂失正在使用的卡。"); + return; + } + else if (userCardStatus == -1) // 用户有挂失卡,需要移资 + { + /// @todo 弹出验证用户界面,要求用户输入密码;将挂失卡的信息和消费记录移到新卡 + QString info, prompt = QString("如需将挂失卡移资到本卡,请输入密码。"); + bool success = verifyUser(userId, prompt, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n验证用户失败。移资失败,请重试。")); + return; + } + + QString userCardId = query.value("id").toString(); + success = transferCard(userId, cardIdSelected, userCardId, info); + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n移资失败,请重试。")); + return; + } + + QMessageBox::information(this, "提示", "移资成功。"); + return; + } + } + else // 用户无卡 + { + QString info; + bool success = bindUserWithCard(userId, cardIdSelected, info); // 将新用户和卡绑定(卡在读取时已写入数据库) + if (!success) + { + QMessageBox::warning(this, "提示", info + QString("\n注册失败,请重新开卡。")); + return; + } + QMessageBox::information(this, "提示", "新用户开卡成功。"); + return; + } + } +} + + +/** + * @brief 绑定用户和卡号 + * 该函数用于在数据库中绑定用户和卡号。函数将更新卡片的状态、余额和用户ID。 + * 如果操作失败,函数将返回失败并设置错误信息。 + * @details + * 函数首先创建一个QSqlQuery对象,并准备一个更新卡片信息的SQL语句,将卡片的状态设置为启用(1),余额设置为0, + * 并将用户ID绑定到卡片ID。如果SQL执行失败,函数会将错误信息写入info并返回false。 + * @param userId 要绑定的用户的学/工号 + * @param cardId 要绑定的卡号 + * @param info 如果出现异常,填入异常信息 + * @return 是否绑定成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::bindUserWithCard(int userId, QString cardId, QString &info) +{ + QSqlQuery query(db->getDatabase()); + query.finish(); + query.prepare(QString("update card " + "set `status` = :status, balance = :balance, userId = :userId " + "where id = :cardId;")); + query.bindValue(":status", 1); + query.bindValue(":balance", (double)0.00); + query.bindValue(":userId", userId); + query.bindValue(":cardId", cardId); + bool updateCardIsExecuted = query.exec(); + if (!updateCardIsExecuted) + { + info = QString("数据库异常。"); + return false; + } + + /// @todo 写卡 + + return true; +} + + +/** + * @brief 在数据库中创建新用户 + * 该函数用于在数据库中创建新用户。首先,它调用 `getNewUserInfo` 函数获取新用户的姓名和密码。 + * 然后,将这些信息与用户ID一起插入到数据库中。如果任何步骤失败,函数将返回失败并设置错误信息。 + * @details + * 函数首先调用 `getNewUserInfo` 获取新用户的姓名和密码。如果获取信息失败,函数立即返回失败并设置错误信息。 + * 然后,函数准备并执行一个插入新用户信息的SQL语句,将用户ID、姓名和密码插入到数据库中的 `user` 表。 + * 如果SQL执行失败,函数返回失败并设置错误信息。 + * @param userId 要创建的用户的学/工号 + * @param info 如果出现异常,填入异常信息 + * @return bool 是否创建成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::createUser(int userId, QString &info) +{ + QString username, password; + if (!getNewUserInfo(username, password, info)) return false; + + QSqlQuery query(db->getDatabase()); + query.finish(); + query.prepare(QString("insert into `user` " + "values (:id, :name, :password);")); + query.bindValue(":id", userId); + query.bindValue(":name", username); + query.bindValue(":password", password); + bool insertUserIsExecuted = query.exec(); + if (!insertUserIsExecuted) + { + info = QString("数据库异常。"); + return false; + } + + return true; +} + + +/** + * @brief 弹出交互窗口获取新用户的信息 + * 该函数弹出多个输入对话框,依次获取新用户的姓名和密码,并进行简单验证。 + * 如果用户在任一步骤取消输入或输入无效,函数将返回失败并设置错误信息。 + * @details + * 函数依次弹出三个输入对话框,获取新用户的姓名、密码和确认密码。 + * - 如果用户取消输入或输入为空,函数返回失败并设置错误信息。 + * - 如果两次输入的密码不一致,函数返回失败并设置错误信息。 + * - 如果所有输入均有效,函数返回成功。 + * @param username 获取的新用户的姓名 + * @param password 获取的新用户的密码 + * @param info 如果出现异常,填入异常信息 + * @return bool 是否获取成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::getNewUserInfo(QString &username, QString &password, QString &info) +{ + bool ok = false; + username = QInputDialog::getText(this, tr("新用户注册"), tr("请输入姓名"), QLineEdit::Normal, 0, &ok); + if (!ok || username.isEmpty()) + { + info = QString("输入信息异常。"); + return false; + } + ok = false; + password = QInputDialog::getText(this, tr("新用户注册"), tr("请输入密码"), QLineEdit::Password, 0, &ok); + if (!ok || password.isEmpty()) + { + info = QString("输入信息异常。"); + return false; + } + ok = false; + QString confirmPassword = QInputDialog::getText(this, tr("新用户注册"), tr("请再次输入密码"), QLineEdit::Password, 0, &ok); + if (!ok || confirmPassword.isEmpty()) + { + info = QString("输入信息异常。"); + return false; + } + if (confirmPassword != password) + { + info = QString("两次输入的密码不一致。"); + return false; + } + + return true; +} + + +/** + * @brief 弹出交互窗口认证用户,在数据库中比对 + * 该函数用于弹出一个窗口要求用户输入密码,并在数据库中比对用户信息以验证身份。 + * 如果用户输入正确的密码,则认证成功,否则认证失败。 + * @details + * 函数首先弹出一个输入对话框,要求用户输入密码。如果用户取消输入或未输入密码,函数返回认证失败并设置错误信息。 + * 然后,函数在数据库中查询对应的用户ID和密码,并与用户输入的密码进行比对。如果密码正确,则返回认证成功。 + * 否则,函数返回认证失败并设置相应的错误信息。 + * @param userId 要认证的用户学/工号 + * @param prompt 在交互窗口的提示文字 + * @param info 如果出现异常,填入异常信息 + * @return bool 是否认证成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::verifyUser(int userId, QString prompt, QString &info) +{ + bool ok = false; + QString text; + if (prompt.isEmpty()) text = QString("请输入密码"); + else text = prompt + QString("\n请输入密码"); + QString password = QInputDialog::getText(this, tr("验证用户"), text, QLineEdit::Password, 0, &ok); + if (!ok || password.isEmpty()) + { + info = QString("输入信息异常。"); + return false; + } + + QSqlQuery query(db->getDatabase()); + query.finish(); + query.prepare(QString("select `password` from `user` " + "where id = :id;")); + query.bindValue(":id", userId); + if (!query.exec()) + { + info = QString("数据库异常。"); + return false; + } + if (query.next()) + { + QString validPassword = query.value(0).toString(); + if (validPassword != password) + { + info = QString("密码不正确"); + return false; + } + else + { + return true; + } + } + else + { + info = QString("学/工号不存在"); + return false; + } +} + + +/** + * @brief 移资,将旧卡信息和记录移到新卡,删除旧卡信息 + * 该函数用于将旧卡的余额和相关记录移到新卡,并删除旧卡的信息。函数执行以下步骤: + * - 查询旧卡的余额,并将其转移到新卡。 + * - 更新新卡的状态、余额和用户ID。 + * - 更新所有记录,将旧卡ID替换为新卡ID。 + * - 删除旧卡的信息 + * @details + * 函数首先查询旧卡的余额,并在执行过程中进行多次数据库操作,以确保所有相关信息和记录都正确地从旧卡移到新卡。 + * 如果任何操作失败,函数会将错误信息写入info并返回false。 + * @param userId 新旧卡所属的用户的学/工号 + * @param newCardId 新卡的ID + * @param oldCardId 旧卡的ID + * @param info 如果出现异常,填入异常信息 + * @return bool 是否移资成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::transferCard(int userId, QString newCardId, QString oldCardId, QString &info) +{ + QSqlQuery query(db->getDatabase()); + bool isExecuted = false; + + // 查询旧卡余额 + query.finish(); + query.prepare("select balance from card " + "where userId = :userId;"); + query.bindValue(":userId", oldCardId); + isExecuted = query.exec(); + if (!isExecuted || query.next()) + { + info = QString("数据库异常。"); + return false; + } + double balance = query.value("balance").toDouble(); + + // 更新新卡信息 + query.finish(); + query.prepare(QString("update card " + "set `status` = :status, balance = :balance, userId = :userId " + "where id = :cardId;")); + query.bindValue(":status", 1); + query.bindValue(":balance", balance); + query.bindValue(":userId", userId); + query.bindValue(":cardId", newCardId); + isExecuted = query.exec(); + if (!isExecuted) + { + info = QString("数据库异常。"); + return false; + } + + // 更新所有记录,将旧卡ID替换为新卡ID + query.finish(); + query.prepare(QString("update record " + "set cardId = :newCardId " + "where cardId = :oldCardId;")); + query.bindValue(":newCardId", newCardId); + query.bindValue(":oldCardId", oldCardId); + isExecuted = query.exec(); + if (!isExecuted) + { + info = QString("数据库异常。"); + return false; + } + + // 删除旧卡信息 + query.finish(); + query.prepare(QString("delete from card " + "where id = :cardId;")); + query.bindValue(":cardId", oldCardId); + isExecuted = query.exec(); + if (!isExecuted) + { + info = QString("数据库异常。"); + return false; + } + + /// @todo 将数据库上的记录写到新卡上 + + return true; +} + + +/** + * @brief 重开卡 + * 该函数用于重新启用一张已挂失的卡片。卡片信息和记录都保留在数据库中,只需将卡片的状态从-1(挂失)更改为1(启用)。 + * @details + * 函数首先创建一个QSqlQuery对象,并准备一个更新卡片状态的SQL语句,将指定卡片的状态更改为启用状态(1)。 + * 如果SQL执行失败,函数会将错误信息写入info并返回false。 + * @param userId 卡所属的用户的学/工号 + * @param info 如果出现异常,填入异常信息 + * @return bool 是否重开卡成功 + * - true 成功 + * - false 失败 + * @author 柯劲帆 + * @date 2024-07-30 + */ +bool MainWindow::reopenCard(QString cardId, QString &info) +{ + // 卡信息和记录都还在数据库中,只是status为-1,更改即可 + QSqlQuery query(db->getDatabase()); + query.finish(); + query.prepare("update card " + "set `status` = 1 " + "where id = :cardId"); + query.bindValue(":cardId", cardId); + bool success = query.exec(); + if (!success) + { + info = QString("数据库异常。"); + return false; + } + + /// @todo 看看是否有写卡需求 + + return true; } diff --git a/settingPage.cpp b/settingPage.cpp index 7be6325..97adce8 100644 --- a/settingPage.cpp +++ b/settingPage.cpp @@ -100,7 +100,7 @@ void MainWindow::on_connectDatabaseButton_clicked() { QMessageBox::warning(this, QString("设备名提示"), QString("该设备名无效,请重试。")); } - deviceLabel->setText(device.getName()); + deviceLabel->setText(device.getNameAndDepositAllowed()); }