完成了开卡部分(除了硬件部分)。

This commit is contained in:
2024-07-30 02:13:05 +08:00
parent 26f9ff604e
commit ad40ab7ecd
7 changed files with 558 additions and 10 deletions

View File

@@ -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("未指定设备名");
}

View File

@@ -24,6 +24,7 @@ public:
void setDevice(QString name, Database *db);
QString getName();
QString getNameAndDepositAllowed();
bool is_verified();
bool is_depositAllowed();

View File

@@ -35,12 +35,14 @@ MainWindow::MainWindow(QWidget *parent)
databaseLabel = new QLabel("数据库无连接");
ui->statusBar->addWidget(databaseLabel);
deviceLabel = new QLabel(device.getName());
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;
}

View File

@@ -7,6 +7,7 @@
#include <QStackedWidget>
#include <QMessageBox>
#include <QSqlQuery>
#include <QInputDialog>
#include <readerAPI.h>
#include <databaseAPI.h>
@@ -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

View File

@@ -296,6 +296,9 @@
<property name="maximum">
<number>99999999</number>
</property>
<property name="value">
<number>21281280</number>
</property>
</widget>
</item>
<item row="0" column="1">
@@ -337,7 +340,7 @@
</item>
</layout>
</widget>
<widget class="QPushButton" name="pushButton">
<widget class="QPushButton" name="newCardButton">
<property name="geometry">
<rect>
<x>300</x>

View File

@@ -40,5 +40,520 @@ void MainWindow::on_inventoryButton_clicked()
{
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 (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;
}

View File

@@ -100,7 +100,7 @@ void MainWindow::on_connectDatabaseButton_clicked()
{
QMessageBox::warning(this, QString("设备名提示"), QString("该设备名无效,请重试。"));
}
deviceLabel->setText(device.getName());
deviceLabel->setText(device.getNameAndDepositAllowed());
}