- Extend `ScreenXmlParser` with methods for detecting and extracting account numbers from multiple screens. - Update database schema and DAO to support `account_number` field. - Modify card information scripts to collect, save, and utilize account numbers. - Enhance `GetCardInfoScript` logic to fetch account numbers via screen parsing and interaction.
384 lines
14 KiB
C++
384 lines
14 KiB
C++
#include <QSqlQuery>
|
|
#include <QSqlError>
|
|
#include <QRegularExpression>
|
|
#include "MaterialDAO.h"
|
|
|
|
#include "db/MaterialInfo.h"
|
|
#include "DatabaseManager.h"
|
|
#include "time/DateUtils.h"
|
|
|
|
bool MaterialDAO::upsertAccount(const MaterialInfo &info) {
|
|
// Валидация: lastNumbers должен содержать только цифры
|
|
static const QRegularExpression digitsOnly(R"(^\d+$)");
|
|
if (!info.lastNumbers.isEmpty() && !digitsOnly.match(info.lastNumbers).hasMatch()) {
|
|
qWarning() << "[MaterialDAO] upsertAccount rejected: lastNumbers is not numeric:"
|
|
<< info.lastNumbers << "appCode:" << info.appCode;
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
INSERT INTO materials (device_id, app_code, last_numbers, card_number, account_number, amount, update_time, status, description, currency, material_id)
|
|
VALUES (:device_id, :app_code, :last_numbers, :card_number, :account_number, :amount, :update_time, :status, :description, :currency, :material_id)
|
|
ON CONFLICT(device_id, app_code, last_numbers)
|
|
DO UPDATE SET
|
|
amount = excluded.amount,
|
|
update_time = excluded.update_time,
|
|
last_numbers = excluded.last_numbers,
|
|
card_number = CASE WHEN excluded.card_number != '' THEN excluded.card_number ELSE materials.card_number END,
|
|
account_number = CASE WHEN excluded.account_number != '' THEN excluded.account_number ELSE materials.account_number END,
|
|
status = excluded.status,
|
|
description = excluded.description,
|
|
currency = excluded.currency,
|
|
material_id = CASE WHEN excluded.material_id != '' THEN excluded.material_id ELSE materials.material_id END
|
|
)");
|
|
|
|
query.bindValue(":device_id", info.deviceId);
|
|
query.bindValue(":app_code", info.appCode);
|
|
query.bindValue(":last_numbers", info.lastNumbers);
|
|
query.bindValue(":card_number", info.cardNumber);
|
|
query.bindValue(":account_number", info.accountNumber);
|
|
query.bindValue(":currency", info.currency);
|
|
query.bindValue(":description", info.description);
|
|
query.bindValue(":amount", info.amount);
|
|
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
|
|
query.bindValue(":status", materialStatusToString(info.status));
|
|
query.bindValue(":material_id", info.materialId);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при вставке/обновлении account:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MaterialDAO::updateAccountById(
|
|
const int id,
|
|
const QString &status,
|
|
const QString &description,
|
|
const QString ¤cy
|
|
) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
UPDATE materials
|
|
SET status = :status,
|
|
description = :description,
|
|
currency = :currency
|
|
WHERE id = :id
|
|
)");
|
|
|
|
query.bindValue(":status", status);
|
|
query.bindValue(":description", description);
|
|
query.bindValue(":currency", currency);
|
|
query.bindValue(":id", id);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при обновлении account по id:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool MaterialDAO::updateStatus(const int id, const QString &status) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare("UPDATE materials SET status = :status WHERE id = :id");
|
|
query.bindValue(":status", status);
|
|
query.bindValue(":id", id);
|
|
if (!query.exec()) {
|
|
qWarning() << "[MaterialDAO] updateStatus failed:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MaterialDAO::deleteAccount(const int id) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare("DELETE FROM materials WHERE id = :id");
|
|
query.bindValue(":id", id);
|
|
return query.exec();
|
|
}
|
|
|
|
bool MaterialDAO::updateCardNumber(const int accountId, const QString &cardNumber) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare(R"(
|
|
UPDATE materials SET card_number = :card_number WHERE id = :id
|
|
)");
|
|
query.bindValue(":card_number", cardNumber);
|
|
query.bindValue(":id", accountId);
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при обновлении card_number:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool MaterialDAO::updateMaterialId(const int accountId, const QString &materialId) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare("UPDATE materials SET material_id = :material_id WHERE id = :id");
|
|
query.bindValue(":material_id", materialId);
|
|
query.bindValue(":id", accountId);
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при обновлении material_id:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
MaterialInfo parseAccount(const QSqlQuery &query) {
|
|
MaterialInfo acc;
|
|
acc.id = query.value("id").toInt();
|
|
acc.deviceId = query.value("device_id").toString();
|
|
acc.appCode = query.value("app_code").toString();
|
|
acc.lastNumbers = query.value("last_numbers").toString();
|
|
acc.cardNumber = query.value("card_number").toString();
|
|
acc.accountNumber = query.value("account_number").toString();
|
|
acc.materialId = query.value("material_id").toString();
|
|
acc.amount = query.value("amount").toDouble();
|
|
acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
|
|
acc.status = materialStatusFromString(query.value("status").toString());
|
|
acc.description = query.value("description").toString();
|
|
acc.currency = query.value("currency").toString();
|
|
return acc;
|
|
}
|
|
|
|
QList<MaterialInfo> MaterialDAO::activateExclusive(
|
|
const QString &deviceId, const QString &appCode, const int targetId) {
|
|
QList<MaterialInfo> deactivated;
|
|
QSqlDatabase db = DatabaseManager::instance().database();
|
|
|
|
db.transaction();
|
|
|
|
QSqlQuery sel(db);
|
|
sel.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE device_id = :device_id AND app_code = :app_code
|
|
AND status = 'active' AND id != :target_id
|
|
)");
|
|
sel.bindValue(":device_id", deviceId);
|
|
sel.bindValue(":app_code", appCode);
|
|
sel.bindValue(":target_id", targetId);
|
|
if (sel.exec()) {
|
|
while (sel.next()) deactivated.append(parseAccount(sel));
|
|
} else {
|
|
qWarning() << "[MaterialDAO] activateExclusive select failed:" << sel.lastError().text();
|
|
db.rollback();
|
|
return {};
|
|
}
|
|
|
|
QSqlQuery off(db);
|
|
off.prepare(R"(
|
|
UPDATE materials SET status = 'off'
|
|
WHERE device_id = :device_id AND app_code = :app_code AND id != :target_id
|
|
)");
|
|
off.bindValue(":device_id", deviceId);
|
|
off.bindValue(":app_code", appCode);
|
|
off.bindValue(":target_id", targetId);
|
|
if (!off.exec()) {
|
|
qWarning() << "[MaterialDAO] activateExclusive off-update failed:" << off.lastError().text();
|
|
db.rollback();
|
|
return {};
|
|
}
|
|
|
|
QSqlQuery on(db);
|
|
on.prepare("UPDATE materials SET status = 'active' WHERE id = :id");
|
|
on.bindValue(":id", targetId);
|
|
if (!on.exec()) {
|
|
qWarning() << "[MaterialDAO] activateExclusive on-update failed:" << on.lastError().text();
|
|
db.rollback();
|
|
return {};
|
|
}
|
|
|
|
db.commit();
|
|
return deactivated;
|
|
}
|
|
|
|
QList<MaterialInfo> MaterialDAO::getAccounts(const QString &deviceId, const QString &appName) {
|
|
QList<MaterialInfo> accounts;
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE device_id = :device_id AND app_code = :app_code
|
|
)");
|
|
|
|
query.bindValue(":device_id", deviceId);
|
|
query.bindValue(":app_code", appName);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при получении materials:" << query.lastError().text();
|
|
return accounts;
|
|
}
|
|
|
|
while (query.next()) {
|
|
accounts.append(parseAccount(query));
|
|
}
|
|
|
|
return accounts;
|
|
}
|
|
|
|
MaterialInfo MaterialDAO::getAccountById(const int accountId) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE id = :id
|
|
LIMIT 1
|
|
)");
|
|
|
|
query.bindValue(":id", accountId);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при получении account по ID:" << query.lastError().text();
|
|
return {};
|
|
}
|
|
|
|
if (query.next()) {
|
|
return parseAccount(query);
|
|
}
|
|
return {};
|
|
}
|
|
|
|
QList<MaterialInfo> MaterialDAO::getAccountsByAppCode(const QString &appCode) {
|
|
QList<MaterialInfo> accounts;
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE app_code = :app_code
|
|
)");
|
|
|
|
query.bindValue(":app_code", appCode);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при получении materials по app_code:" << query.lastError().text();
|
|
return accounts;
|
|
}
|
|
|
|
while (query.next()) {
|
|
accounts.append(parseAccount(query));
|
|
}
|
|
|
|
return accounts;
|
|
}
|
|
|
|
MaterialInfo MaterialDAO::findAppAccount(const QString &deviceId, const QString &appName, const QString &lastNumber) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE app_code = :appName AND last_numbers = :lastNumber AND device_id = :deviceId
|
|
LIMIT 1
|
|
)");
|
|
|
|
query.bindValue(":deviceId", deviceId);
|
|
query.bindValue(":appName", appName);
|
|
query.bindValue(":lastNumber", lastNumber);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при получении account по ID:" << query.lastError().text();
|
|
return {};
|
|
}
|
|
|
|
if (query.next()) {
|
|
return parseAccount(query);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
QStringList MaterialDAO::getActiveMaterialIds() {
|
|
QStringList ids;
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare(R"(
|
|
SELECT a.material_id
|
|
FROM materials a
|
|
JOIN bank_profiles app ON a.device_id = app.device_id AND a.app_code = app.code
|
|
JOIN devices d ON a.device_id = d.id
|
|
WHERE app.status = 'active'
|
|
AND a.material_id IS NOT NULL AND a.material_id != ''
|
|
AND d.status = 'device' AND d.image IS NOT NULL
|
|
)");
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при получении active material_ids:" << query.lastError().text();
|
|
return ids;
|
|
}
|
|
|
|
while (query.next()) {
|
|
ids << query.value(0).toString();
|
|
}
|
|
return ids;
|
|
}
|
|
|
|
QStringList MaterialDAO::getActiveMaterialIdsExcludingDevices(const QSet<QString> &excludeDevices) {
|
|
const QStringList all = getActiveMaterialIds();
|
|
if (excludeDevices.isEmpty()) return all;
|
|
|
|
QStringList filtered;
|
|
for (const QString &mid : all) {
|
|
const MaterialInfo acc = findByMaterialId(mid);
|
|
if (!excludeDevices.contains(acc.deviceId)) {
|
|
filtered << mid;
|
|
}
|
|
}
|
|
return filtered;
|
|
}
|
|
|
|
MaterialInfo MaterialDAO::findByMaterialId(const QString &materialId) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
|
|
query.prepare(R"(
|
|
SELECT id, device_id, app_code, last_numbers, card_number, account_number, material_id, amount, update_time, status, description, currency
|
|
FROM materials
|
|
WHERE material_id = :material_id
|
|
LIMIT 1
|
|
)");
|
|
|
|
query.bindValue(":material_id", materialId);
|
|
|
|
if (!query.exec()) {
|
|
qWarning() << "Ошибка при поиске account по material_id:" << query.lastError().text();
|
|
return {};
|
|
}
|
|
|
|
if (query.next()) {
|
|
return parseAccount(query);
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
bool MaterialDAO::migrateDeviceId(const QString &oldDeviceId, const QString &newDeviceId) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare("UPDATE materials SET device_id = :new_id WHERE device_id = :old_id");
|
|
query.bindValue(":new_id", newDeviceId);
|
|
query.bindValue(":old_id", oldDeviceId);
|
|
if (!query.exec()) {
|
|
qWarning() << "[MaterialDAO] migrateDeviceId failed:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
qDebug() << "[MaterialDAO] migrated" << query.numRowsAffected() << "materials from" << oldDeviceId << "to" << newDeviceId;
|
|
return true;
|
|
}
|
|
|
|
bool MaterialDAO::deleteAccountsByApp(const QString &deviceId, const QString &appCode) {
|
|
QSqlQuery query(DatabaseManager::instance().database());
|
|
query.prepare("DELETE FROM materials WHERE device_id = :device_id AND app_code = :app_code AND (material_id IS NULL OR material_id = '')");
|
|
query.bindValue(":device_id", deviceId);
|
|
query.bindValue(":app_code", appCode);
|
|
if (!query.exec()) {
|
|
qWarning() << "[MaterialDAO] deleteAccountsByApp failed:" << query.lastError().text();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|