617 lines
22 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mainwindow.h"
#include "ui_mainwindow.h"
/**
* @brief 切换到开卡页面
* 点击工具栏的“开卡”触发,切换到开卡页面。
* @param void
* @return void
* @author 柯劲帆
* @date 2024-07-28
*/
void MainWindow::on_NewCardAction_triggered()
{
ui->stackedWidget->setCurrentWidget(ui->newCardPage);
}
/**
* @brief 读卡器扫描卡片
* 点击开卡页面的“查询”触发。
* 如果读卡器未连接,显示警告信息并跳转到设置页面。
* 显示Inventory的查询结果最多显示10张卡。
* @param void
* @return void
* @author 柯劲帆
* @date 2024-07-29
*/
void MainWindow::on_inventoryButton_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->cardIdBox->clear();
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 (!softwareReady())
{
QMessageBox::warning(this, QString("提示"), QString("数据库未连接,请设置。"));
if (ui->stackedWidget->currentWidget() != ui->settingPage)
{
ui->stackedWidget->setCurrentWidget(ui->settingPage);
}
return;
}
if (ui->cardIdBox->currentIndex() == -1)
{
QMessageBox::warning(this, "提示", "请放置卡片并点击查询按钮。");
return;
}
if (!device.is_depositAllowed())
{
QMessageBox::warning(this, QString("提示"), QString("本设备不支持开卡。"));
return;
}
if (!newCardUserIdFilled)
{
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)
{
QMessageBox::warning(this, "提示", QString("数据库异常。\n重开卡失败,请重试。"));
return;
}
if (!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);
success = query.exec();
if (!success)
{
QMessageBox::warning(this, "提示", QString("数据库异常。开卡失败,请重试。"));
return;
}
success = reader.initCard(cardIdSelected); // 初始化卡
if (!success)
{
QMessageBox::warning(this, "提示", QString("读卡器设备异常。写卡失败,请重试。"));
return;
}
}
// 检查是否是新用户
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, "提示", "新用户开卡成功。");
success = reader.initCard(cardIdSelected); // 初始化卡
if (!success)
{
QMessageBox::warning(this, "提示", QString("读卡器设备异常。写卡失败,请重试。"));
return;
}
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) // 用户有挂失卡,需要移资
{
// 弹出验证用户界面,要求用户输入密码;在数据库中将挂失卡的信息和消费记录移到新卡
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;
}
success = reader.initCard(cardIdSelected); // 初始化卡
if (!success)
{
QMessageBox::warning(this, "提示", QString("读卡器设备异常。写卡失败,请重试。"));
return;
}
QMessageBox::information(this, "提示", "移资成功。");
return;
}
}
else // 用户无卡
{
QString info;
bool success = bindUserWithCard(userId, cardIdSelected, info); // 将新用户和卡绑定(卡在读取时已写入数据库)
if (!success)
{
QMessageBox::warning(this, "提示", info + QString("\n注册失败,请重新开卡。"));
return;
}
success = reader.initCard(cardIdSelected); // 初始化卡
if (!success)
{
QMessageBox::warning(this, "提示", QString("读卡器设备异常。写卡失败,请重试。"));
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;
}
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 id = :cardId;");
query.bindValue(":cardId", oldCardId);
isExecuted = query.exec();
if (!isExecuted)
{
info = QString("数据库异常。");
return false;
}
if (!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;
}
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;
}
return true;
}