337 lines
12 KiB
C++
337 lines
12 KiB
C++
#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;
|
||
}
|
||
|
||
success = reader.insertRecord(recordId, cardId);
|
||
if (!success)
|
||
{
|
||
QMessageBox::warning(this, "提示", "充值成功。写卡失败。本交易不记录在卡上。");
|
||
}
|
||
|
||
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);
|
||
ui->depositValueBox->setValue(0.00);
|
||
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);
|
||
ui->depositValueBox->setValue(0.00);
|
||
return;
|
||
}
|
||
|
||
|
||
/**
|
||
* @brief 充值卡
|
||
* 该函数用于给指定的卡片充值。充值前会进行一系列检查,包括设备是否支持充值、卡片状态、充值金额是否超过限额等。
|
||
* 如果所有检查通过,将调用存储过程 `sp_depositCard` 进行充值操作,并返回充值后的余额。
|
||
* @param cardId 要充值的卡号
|
||
* @param topUpValue 充值金额
|
||
* @param originalBalance 充值前的余额,通过引用返回
|
||
* @param finalBalance 充值后的余额,通过引用返回
|
||
* @param recordId 交易编号,通过引用返回
|
||
* @param info 如果出现异常,填入异常信息,通过引用返回
|
||
* @return int 是否充值成功
|
||
* - 0 失败
|
||
* - 1 成功
|
||
* - 2 充值成功但写卡失败
|
||
* @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();
|
||
|
||
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.toUpper();
|
||
}
|