#include "GetLastDaysHistoryScript.h" #include #include #include #include #include #include "DatabaseManager.h" #include "adb/AdbUtils.h" #include "dao/AccountDAO.h" #include "dao/DeviceDAO.h" #include "dao/TransactionDAO.h" #include "db/DeviceInfo.h" #include "net/NetworkService.h" #include "time/DateUtils.h" GetLastDaysHistoryScript::GetLastDaysHistoryScript( AccountInfo account, QObject *parent ) : CommonScript(parent), m_account(std::move(account)) { } GetLastDaysHistoryScript::~GetLastDaysHistoryScript() = default; QList findSavedTransaction( const QList &transactions, const TransactionInfo &parsed ) { QList txs; const QDateTime dateTime = QDateTime::fromString(parsed.bankTime, "dd.MM.yyyy"); // Московское время for (const TransactionInfo &info: transactions) { QDateTime timestamp = info.timestamp; QString shortDescription = parsed.shortDescription; // если тот же день if (dateTime.date() == timestamp.date() && parsed.amount == info.amount) { // Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815... if (shortDescription.contains("Перевод")) { if (shortDescription.contains(info.name)) { txs.append(info); } } else { // Перевод по карте if (shortDescription == info.shortDescription) { txs.append(info); } } } } return txs; } int countSimilar(const TransactionInfo &transaction, const QList &transactions) { int count = 0; for (const TransactionInfo &info: transactions) { if (info.amount == transaction.amount && info.shortDescription == transaction.shortDescription && info.bankTime == transaction.bankTime) { ++count; } } return count; } QString formatPrice(const double value) { QString sign = value < 0 ? "Минус" : "Плюс"; const auto intValue = static_cast(std::abs(value)); // округление до целого QString number = QString::number(intValue); QString formatted; int count = 0; for (int i = number.length() - 1; i >= 0; --i) { formatted.prepend(number[i]); ++count; if (count % 3 == 0 && i != 0) { formatted.prepend(' '); } } return QString("%1 %2").arg(sign, formatted); } bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) { QString text = QString("%1 %2").arg(transaction.shortDescription, formatPrice(transaction.amount)); Node trButton = swipeToButton(m_account.deviceId, text, m_counter, width, height); if (!trButton.isEmpty()) { AdbUtils::makeTap(m_account.deviceId, trButton.x(), trButton.y()); QThread::msleep(500); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); if (!moreDetailBtn.isEmpty()) { AdbUtils::makeTap(m_account.deviceId, moreDetailBtn.x(), moreDetailBtn.y()); QThread::msleep(200); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height); if (transaction.id == -1) { info.accountId = m_account.id; info.shortDescription = transaction.shortDescription; info.amount = transaction.amount; info.type = info.phone.isEmpty() ? TransactionType::Card : TransactionType::Phone; info.updateTime = DateUtils::getUtcNow(); if (info.status == TransactionStatus::Complete) { info.completeTime = DateUtils::getUtcNow(); } if (TransactionDAO::insertTransaction(info) == -1) { qWarning() << "--- insertTransaction failed"; } } else { transaction.status = info.status; transaction.description = info.description; if (transaction.trExternalId.isEmpty()) { transaction.trExternalId = info.trExternalId; } transaction.updateTime = DateUtils::getUtcNow(); if (transaction.status == TransactionStatus::Complete && !transaction.completeTime.isValid()) { transaction.completeTime = DateUtils::getUtcNow(); } if (!TransactionDAO::updateTransaction(transaction)) { qWarning() << "--- updateTransaction failed"; } } } AdbUtils::goBack(m_account.deviceId); QThread::msleep(200); } else { qWarning() << "--- trButton not found.........."; } return false; } void postAccountsData(const QList > &accounts) { if (accounts.isEmpty()) { return; } auto *thread = new QThread; auto *worker = new NetworkService; QJsonArray list; for (const QPair &pair: accounts) { QJsonObject obj; AccountInfo account = pair.first; obj["appName"] = account.appName; obj["lastNumbers"] = account.lastNumbers; obj["amount"] = account.amount; obj["description"] = account.description; list.append(obj); } worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId); worker->setPayload(QJsonDocument(list).toJson()); worker->moveToThread(thread); QObject::connect(thread, &QThread::started, worker, &NetworkService::postAccountsData); QObject::connect(worker, &NetworkService::finished, thread, &QThread::quit); QObject::connect(worker, &NetworkService::finished, worker, &QObject::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); thread->start(); } void GetLastDaysHistoryScript::doStart() { // берем все транзакции из БД QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); if (xmlScreenParser.isHomeScreen()) { if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { QList > accounts = xmlScreenParser.parseAccountsInfo(); for (auto &[account, node]: accounts) { account.deviceId = m_account.deviceId; account.appName = m_account.appName; if (!AccountDAO::upsertAccount(account)) { qDebug() << "Not updated: " << account.lastNumbers; } } postAccountsData(accounts); findAndTapOnAccount(device.id, m_account.lastNumbers); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); Node actionBtn = xmlScreenParser.findButtonNode("Действия", false); if (!actionBtn.isEmpty()) { AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y()); QThread::msleep(200); } else { qWarning() << "--- actionBtn not found"; } Node reportButton = swipeToButton(m_account.deviceId, "Выписка", m_counter, device.screenWidth, device.screenHeight); if (!reportButton.isEmpty()) { AdbUtils::makeTap(m_account.deviceId, reportButton.x(), reportButton.y()); QThread::msleep(1000); } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); Node reportTitle = xmlScreenParser.findTextNode("Выписка"); if (!reportTitle.isEmpty() && reportTitle.y() < 300) { QDate beforeYesterday = DateUtils::getMoscowNow().date().addDays(-2); QString beforeYesterdayTitle = beforeYesterday.toString("dd.MM.yyyy"); for (int i = 0; i < 5; ++i) { Node byDayTitle = xmlScreenParser.findTextNode(beforeYesterdayTitle); if (!byDayTitle.isEmpty()) { qWarning() << "--- beforeYesterdayTitle found!"; break; } AdbUtils::makeSwipe( m_account.deviceId, device.screenWidth / 2, device.screenHeight / 4 * 3, device.screenWidth / 2, device.screenHeight / 4 ); QThread::msleep(1000); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); } } else { qWarning() << "--- reportTitle not found"; } // все поддвержденные QList parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); QSet parsedTransactionIds; qDebug() << "--- parsedTransactions: " << parsedTransactions.length(); qDebug() << "--- localTransactions: " << localTransactions.length(); for (const TransactionInfo &transaction: parsedTransactions) { bool updated = false; for (const TransactionInfo &saved: localTransactions) { if (parsedTransactionIds.contains(saved.id)) { continue; } QDate sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy HH:mm:ss").date(); if (!sDate.isValid()) { sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy").date(); } QDate pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy HH:mm:ss").date(); if (!pDate.isValid()) { pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy").date(); } // Если сумма и дата совпадают if (saved.amount == transaction.amount && sDate == pDate) { if (transaction.description.startsWith("Перевод")) { // Перевод по номеру if (saved.bankName == transaction.bankName && saved.phone == transaction.phone) { parsedTransactionIds.insert(saved.id); updated = true; if (saved.status != TransactionStatus::Complete || saved.description.isEmpty()) { if (!TransactionDAO::updateTransaction( saved.id, TransactionStatus::Complete, saved.description, transaction.description )) { qDebug().noquote().nospace() << "--- updated+phone failed" << convertTransactionToString(transaction);; } } break; } } else { // перевод по карте // перезаписать saved.description -> updatedDescription если не равны if (!saved.description.isEmpty()) { if (saved.description == transaction.description) { // описания совпадают if (saved.status != TransactionStatus::Complete) { if (!TransactionDAO::updateTransaction( saved.id, TransactionStatus::Complete, saved.description, transaction.description )) { qDebug().noquote().nospace() << "--- updated+card[description same] failed" << convertTransactionToString(transaction);; } } parsedTransactionIds.insert(saved.id); updated = true; break; } else { // описания не совпадают, то пробуем найти такое же, если не находим то это новая bool findWithSameName = false; for (const TransactionInfo &saved2: localTransactions) { if (saved2.description == transaction.description && !parsedTransactionIds. contains( saved2.id)) { // мы нашли с одинаковым описанием и еще не обработанную if (saved2.status != TransactionStatus::Complete) { if (!TransactionDAO::updateTransaction( saved2.id, TransactionStatus::Complete, saved2.description, transaction.description )) { qDebug().noquote().nospace() << "--- updated+card2[description same] failed" << convertTransactionToString(transaction);; } } parsedTransactionIds.insert(saved2.id); updated = true; findWithSameName = true; break; } } if (!findWithSameName) { // обновляем описание if (!TransactionDAO::updateTransaction( saved.id, TransactionStatus::Complete, saved.description, transaction.description )) { qDebug().noquote().nospace() << "--- updated+card2+new desc[description same] failed" << convertTransactionToString(transaction);; } } parsedTransactionIds.insert(saved.id); updated = true; break; } } else { // пустой описание if (!TransactionDAO::updateTransaction( saved.id, TransactionStatus::Complete, saved.description, transaction.description )) { qDebug().noquote().nospace() << "--- updated+card[empty description]" << convertTransactionToString(transaction);; } parsedTransactionIds.insert(saved.id); updated = true; break; } } } } if (!updated) { qDebug() << "--- insert: " << transaction.description; // не нашлась транзакция TransactionInfo tr; tr.accountId = m_account.id; tr.description = transaction.description; tr.bankTime = transaction.bankTime; tr.bankName = transaction.bankName; tr.amount = transaction.amount; tr.phone = transaction.phone; tr.updateTime = DateUtils::getUtcNow(); tr.fee = transaction.fee; tr.name = transaction.name; tr.trExternalId = transaction.trExternalId; tr.status = TransactionStatus::Complete; tr.type = transaction.description.startsWith("Перевод") ? TransactionType::Phone : TransactionType::Card; tr.completeTime = DateUtils::getUtcNow(); if (TransactionDAO::insertTransaction(tr) == -1) { qDebug() << "--- not inserted: " << transaction.description; } } // Если это перевод // Если совпадает по сумме + телефон + банк дата (без времени) + банк // Если по карте, мы знаем ток дату + сумма } } } else { qDebug() << "--- home screen not found"; } }