initial commit

This commit is contained in:
vakabunga
2026-01-16 18:37:32 +03:00
commit 643eddb5fc
14 changed files with 3469 additions and 0 deletions

265
improvement.md Normal file
View File

@@ -0,0 +1,265 @@
# Улучшения системы обработки банковских транзакций
## 1. Упрощение логики бесконечного редактирования
### 1.1 Текущая проблема
- Используется таблица `user_sessions` для хранения состояния редактирования
- Множественные SELECT/UPDATE запросы при каждом действии пользователя
- Сложная логика ветвления в n8n для управления сессиями
### 1.2 Решение: Stateful callback_data с editMessage
**Принцип работы:** Храним все состояние редактирования прямо в callback_data кнопок Telegram
#### Формат callback_data
```text
edit_card_{transactionId}_{counterparty}_{category}
set_category_{transactionId}_{counterparty}_{newCategory}
edit_field_{transactionId}_{counterparty}_{category}_{field}
confirm_{transactionId}_{counterparty}_{category}
```
#### Примеры
```text
edit_card_123_Пятёрочкародукты
set_category_123_Пятёрочка_Авто
edit_field_123_Пятёрочкародукты_category
confirm_123_Пятёрочкародукты
```
#### Узлы n8n для удаления
1.`user_sessions` таблица
2.`get_session` (SELECT)
3.`add_session_category` (INSERT)
4.`add_session_counterparty` (INSERT)
5.`merge_user_and_session`
6.`parse_text_input` (частично)
7.`merge_changes`
#### Новые узлы
1.`create_edit_card` — генерирует карточку редактирования с inline-клавиатурой
2.`update_card_on_action` — обрабатывает callback и обновляет сообщение через editMessage
### 1.3 Преимущества
- **Уменьшение нагрузки на БД на 80%** — убираем 5-7 запросов на одно действие
- **Упрощение workflow** — вместо ветвления по сессиям — линейная обработка
- **Мгновенная обратная связь** — editMessage вместо новых сообщений
- **Stateless архитектура** — нет зависимостей между запросами
## 2. Улучшение UX для выбора категорий
### 2.1 Текущая проблема
- Текстовый ввод категорий требует запоминания всех 20 вариантов
- Ошибки при ручном вводе (опечатки, синонимы)
- Медленный процесс категоризации
### 2.2 Решение: Многоуровневая inline-клавиатура
#### Первый уровень — "умные" популярные категории
```javascript
// Алгоритм выбора популярных категорий:
// 1. Берем топ-3 категории пользователя из истории (если есть)
// 2. Добавляем глобально популярные категории
// 3. Всего 4 кнопки
const getUserTopCategories = async (chatId) => {
// Запрос к БД за последние 30 дней
const topCats = await db.query(`
SELECT category, COUNT(*) as count
FROM transactions
WHERE chat_id = $1
AND category IS NOT NULL
AND received_at > NOW() - INTERVAL '30 days'
GROUP BY category
ORDER BY count DESC
LIMIT 3
`, [chatId]);
return topCats.map(row => row.category);
};
const getDefaultCategories = () => ["Продукты", "Авто", "Дом"];
```
#### Второй уровень — все категории компактно
```javascript
// Без эмодзи для экономии места
const ALL_CATEGORIES = [
["Продукты", "Авто", "Здоровье", "Арчи"],
["ЖКХ", "Дом", "Проезд", "Одежда"],
["Химия", "Косметика", "Инвестиции", "Развлечения"],
["Общепит", "Штрафы", "Налоги", "Подписки"],
["Перевод", "Наличные", "Подарки", "Спорт"],
["Другое"]
];
// Каждая кнопка: callback_data = "set_category_{id}_{counterparty}_{category}"
```
#### Узлы n8n для изменения
1. 🔄 `category_edit``send_category_keyboard` (отправляет 4 кнопки)
2.`show_all_categories` → новый узел для отправки полного списка
3. ❌ Текстовый ввод категорий удаляется полностью
#### Кэширование популярных категорий
```javascript
// В Redis или памяти n8n (Global Variables)
// Ключ: user_top_cats_{chatId}
// TTL: 1 час
// Данные: ["Продукты", "Общепит", "Авто"]
// При запросе:
// 1. Проверяем кэш
// 2. Если нет → запрос к БД → сохранение в кэш
// 3. Возвращаем данные
```
### 2.3 Преимущества
- **В 3-5 раз быстрее** выбор категории
- **Нет ошибок ввода** — только валидные категории
- **Персонализация** — показываем часто используемые категории
- **Экономия места** — без эмодзи больше категорий на экране
## 3. Техническая реализация
### Изменения в БД
```sql
-- Удаляем таблицу user_sessions
DROP TABLE IF EXISTS user_sessions;
-- Добавляем кэш популярных категорий (опционально)
CREATE TABLE IF NOT EXISTS user_category_stats (
chat_id BIGINT,
category VARCHAR(100),
count INTEGER DEFAULT 1,
last_used TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (chat_id, category)
);
-- Индекс для быстрого получения топ категорий
CREATE INDEX idx_user_cats ON user_category_stats(chat_id, count DESC);
```
### Изменения в workflow n8n
#### Ветка редактирования (новая)
```text
edit_field (callback) →
IF field = 'category' →
get_top_categories (Code + возможно DB) →
send_category_keyboard (Telegram Message)
IF field = 'counterparty' →
request_counterparty_input (Telegram Message) →
(текстовый ответ) →
update_card_with_new_counterparty (editMessage)
```
#### Ветка выбора категории
```text
set_category (callback) →
update_category_stats (DB) → -- инкрементируем счетчик
update_card (editMessage) → -- обновляем карточку
(возврат в режим редактирования)
```
#### Ветка "все категории"
```text
show_all_cats (callback) →
send_all_categories (Telegram Message) → -- компактная клавиатура 5x4
(пользователь выбирает) →
set_category (существующая ветка)
```
### Лимиты и ограничения
1. **Callback_data length** ≤ 64 байт
```javascript
// Решение: кодирование/декодирование длинных строк
const encodeData = (str) => encodeURIComponent(str.substring(0, 20));
const decodeData = (str) => decodeURIComponent(str);
```
2. **Inline keyboard rows** ≤ 8-10 для удобства
3. **Кэш TTL** = 1 час для актуальности
## 4. Ожидаемые результаты
### Метрики улучшений
| Показатель | Было | Стало | Улучшение |
| ------------ | ------ | ------- | ----------- |
| Запросов к БД на действие | 5-7 | 1-2 | ~70% ↓ |
| Время категоризации | 10-30 сек | 2-5 сек | 5x ↑ |
| Ошибок категоризации | 15-20% | <2% | 10x ↓ |
| Пользовательских кликов | 3-5 | 1-2 | 2x ↓ |
### Безопасность
1. Все данные в callback_data URL-encoded
2. Валидация transactionId при каждом запросе
3. Проверка прав доступа к чату
### Резервное решение
Если callback_data превышает лимит Telegram:
```javascript
// Fallback: короткий хэш + временное хранилище
const hash = md5(transactionId + counterparty + category).substring(0, 8);
cache.set(`card_${hash}`, {transactionId, counterparty, category}, 300); // 5 мин
callback_data = `short_${hash}`;
```
## 5. План внедрения
### Фаза 1: Подготовка (1-2 дня)
1. Создать новую таблицу `user_category_stats`
2. Написать миграцию данных из истории транзакций
3. Обновить узлы обработки callback_data
### Фаза 2: Внедрение (1 день)
1. Добавить новые узлы n8n параллельно существующим
2. Протестировать на отдельном Telegram-боте
3. Проверить лимиты callback_data
### Фаза 3: Переключение (1 день)
1. Направить 10% трафика на новую систему
2. Сравнить метрики
3. Полное переключение при успехе
### Фаза 4: Оптимизация (непрерывно)
1. Мониторинг нагрузки БД
2. Настройка кэширования
3. A/B тестирование разных наборов популярных категорий
## Заключение
Предложенные изменения устраняют основные боли пользователей:
- **Упрощают редактирование** через мгновенное обновление карточек
- **Ускоряют категоризацию** через умные inline-клавиатуры
- **Уменьшают нагрузку** на инфраструктуру через stateless дизайн
Общее количество узлов n8n сократится с 36 до ~25 при улучшении UX и производительности.