1
0
forked from BRT/arc
arc/docs/events.md
slava 73e23f437d Add Ozon pay_to_new_number and pay_to_old_number XML assets
Included XML files for the "pay to new number" and "pay to old number" features in the Ozon platform.
2026-03-01 12:02:37 +07:00

238 lines
7.4 KiB
Markdown
Raw 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.

# События и задачи
[← Главная](index.md)
Описание полного цикла задачи: от получения из API до завершения и отчёта.
---
## Ключевые файлы
| Файл | Роль |
|------|------|
| `services/EventHandler.h/cpp` | Оркестратор: поллинг, сохранение в БД, запуск потоков |
| `services/net/NetworkService.h/cpp` | HTTP-клиент: `getTasks()`, `postTaskResult()` |
| `database/dao/EventDAO.h/cpp` | CRUD для таблицы `events` |
| `models/db/EventInfo.h` | Модель задачи |
---
## Жизненный цикл задачи
```
Wait → InProgress → Complete
↘ Error
```
---
## Схема работы
### 1. Получение задач (каждые 5 секунд)
```
QTimer (5s)
└─→ NetworkService::getTasks()
└─→ GET /api/v1/task/get_tasks (Bearer token)
└─→ signal tasksReceived(QJsonArray)
└─→ EventHandler::onTasksReceived()
```
Формат ответа API:
```json
{
"success": true,
"data": [
{ "bank_name": "ozon", "material_id": "c340cd2e-cefd-48d6-a813-c4be097ae493", "type": "FETCH_PROFILE", "body": {} },
{ "bank_name": "birbank", "material_id": "a1b2c3d4-0000-0000-0000-000000000001", "type": "FETCH_TRANSACTIONS", "body": {} },
{ "bank_name": "ozon", "material_id": "a1b2c3d4-0000-0000-0000-000000000002", "type": "CREATE_TRANSACTION", "body": { "amount": 1000, "currency": 643 } }
]
}
```
> `material_id` — UUID строка. Поддерживается также числовой формат для совместимости (парсится через `task["material_id"].isString() ? toString() : QString::number(toInt())`).
### 2. Обработка задачи (`onTasksReceived`)
```
Для каждой задачи из API:
├─ existsActiveByExternalId(material_id)?
│ YES → пропустить (дедупликация)
│ NO → продолжить
├─ AccountDAO::findByMaterialId(material_id)
│ прямой SELECT WHERE material_id = ?
НЕ НАЙДЕН → sendTaskResult(status=error) и пропустить
└─ EventDAO::insertEvent(status=Wait)
```
Маппинг типов задач:
| Тип в API | `EventType` в коде | Скрипт |
|-----------|--------------------|--------|
| `FETCH_PROFILE` | `FetchProfile` | `Ozon::LoginAndCheckAccountsScript` |
| `FETCH_TRANSACTIONS` | `FetchTransactions` | `GetLastDaysHistoryBirbankScript` |
| `CREATE_TRANSACTION` | `CreateTransaction` | *(не реализован)* |
### 3. Запуск задач (тот же `onTasksReceived`)
```
Для каждого онлайн устройства:
├─ m_threads[deviceId] запущен? → пропустить
│ (одно устройство = один поток одновременно)
├─ EventDAO::getFirstWaitEventByDeviceId(deviceId)
│ самая старая Wait-задача (ORDER BY timestamp ASC)
├─ ApplicationDAO::getApplication() → статус == "active"?
├─ EventDAO::updateEvent(id, InProgress)
└─ startThreadForTask(account, event)
```
### 4. Выполнение скрипта (QThread)
```
QThread
└─→ Script::start()
├─ ADB команды (adb shell, screenshot, xml dump)
├─ ScreenXmlParser (парсинг UI)
├─ Tesseract OCR (при необходимости)
└─ emit finishedWithResult(error или "")
└─→ EventHandler::updateEventThread()
```
> **Watchdog:** если скрипт не завершился за **5 минут** — поток принудительно завершается.
### 5. Завершение задачи (`updateEventThread`)
```
result == "" → EventDAO::updateEvent(Complete)
result != "" → EventDAO::updateEvent(Error, message)
└─→ sendEventStatus() (новый QThread)
└─→ POST /api/v1/event/status ⚠️ устаревший эндпоинт, требует замены на task_result
```
---
## Отправка результата задачи
Используется `POST /api/v1/task/task_result` через `NetworkService::postTaskResult()`.
### При ненайденном аккаунте (material_id не в БД)
```json
{
"type": "FETCH_PROFILE",
"material_id": "c340cd2e-cefd-48d6-a813-c4be097ae493",
"bank_name": "ozon",
"data": {
"status": "error",
"description": "Account not found for material_id: c340cd2e-cefd-48d6-a813-c4be097ae493"
}
}
```
### При успешном выполнении — FETCH_PROFILE
```json
{
"type": "FETCH_PROFILE",
"material_id": "c340cd2e-cefd-48d6-a813-c4be097ae493",
"bank_name": "ozon",
"data": {
"bank_account_id": "string",
"currency": 643,
"amount": 15000,
"extra": {}
}
}
```
### При успешном выполнении — FETCH_TRANSACTIONS
```json
{
"type": "FETCH_TRANSACTIONS",
"material_id": "a1b2c3d4-0000-0000-0000-000000000001",
"bank_name": "birbank",
"data": [
{
"bank_transaction_id": "string",
"amount": 500,
"currency": 944,
"transaction_type": "bank",
"bank_participator_id": "string",
"status": "completed",
"extra": {},
"created_at": "2024-01-01T12:00:00"
}
]
}
```
### При ошибке выполнения скрипта
```json
{
"type": "FETCH_PROFILE",
"material_id": "c340cd2e-cefd-48d6-a813-c4be097ae493",
"bank_name": "ozon",
"data": {
"status": "error",
"description": "Cant open the home screen: emulator-5554 ozon"
}
}
```
---
## Модель `EventInfo`
```cpp
struct EventInfo {
int id; // локальный ID в SQLite
QString externalId; // material_id из API (UUID строка)
int accountId; // FK → accounts
QString deviceId; // ADB device id
double amount;
QString phone;
QString bankName;
QString comment;
QByteArray screenshot; // последний скриншот экрана
EventStatus status; // Wait / InProgress / Complete / Error
EventType type; // FetchProfile / FetchTransactions / CreateTransaction
QDateTime timestamp;
QDateTime updateTime;
QDateTime completeTime;
};
```
---
## Дедупликация
`EventDAO::existsActiveByExternalId(const QString &externalId)` — проверяет наличие записи
с `status IN ('wait', 'inprogress')` для данного `external_id`.
Гарантирует что одна и та же задача не попадёт в очередь дважды
пока не завершится предыдущее выполнение.
---
## Ограничения
- **1 поток на устройство** — пока устройство занято, остальные задачи ждут следующего поллинга
- **Очерёдность** — внутри устройства задачи выполняются по `timestamp ASC`
- **Интервал поллинга** — 5 секунд (`m_pollTimer->setInterval`)
- **Пути API** — настраиваются в `config.ini`:
- `net/pathGetTasks` (дефолт `/api/v1/task/get_tasks`)
- `net/pathTaskResult` (дефолт `/api/v1/task/task_result`)