feat: Интеграция n8n + Redis Pub/Sub + SSE для real-time обработки заявок
🎯 Основные изменения: Backend: - ✅ Добавлен SSE endpoint для real-time событий (/api/v1/events/{task_id}) - ✅ Redis Pub/Sub для публикации/подписки на события OCR/Vision - ✅ Удален aioboto3 из requirements.txt (конфликт зависимостей) - ✅ Добавлен OCR worker (deprecated, логика перенесена в n8n) Frontend (React): - ✅ Автогенерация claim_id и session_id - ✅ Клиентская конвертация файлов в PDF (JPG/PNG/HEIC/WEBP) - ✅ Сжатие изображений до 2MB перед конвертацией - ✅ SSE подписка на события OCR/Vision в Step1Policy - ✅ Валидация документов (полис vs неподходящий контент) - ✅ Real-time прогресс загрузки и обработки файлов - ✅ Интеграция с n8n webhooks для проверки полиса и загрузки файлов n8n Workflows: - ✅ Проверка полиса в MySQL + запись в PostgreSQL - ✅ Загрузка файлов в S3 + OCR + Vision AI - ✅ Публикация событий в Redis через backend API - ✅ Валидация документов (распознавание полисов ERV) Документация: - 📝 N8N_INTEGRATION.md - интеграция с n8n - 📝 N8N_SQL_QUERIES.md - SQL запросы для workflows - 📝 N8N_PDF_COMPRESS.md - сжатие PDF - 📝 N8N_STIRLING_COMPRESS.md - интеграция Stirling-PDF Утилиты: - 🔧 monitor_redis.py/sh - мониторинг Redis Pub/Sub - 🔧 test_redis_events.sh - тестирование событий - 🔧 pdfConverter.ts - клиентская конвертация в PDF Архитектура: React → n8n webhooks (sync) → MySQL/PostgreSQL/S3 → n8n workflows (async) → OCR/Vision → Redis Pub/Sub → SSE → React
This commit is contained in:
256
N8N_PDF_COMPRESS.md
Normal file
256
N8N_PDF_COMPRESS.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# 🗜️ PDF Compression в n8n
|
||||
|
||||
## 📋 Проблема
|
||||
Пользователь загружает PDF 5-10 MB → долгая обработка OCR
|
||||
|
||||
## ✅ Решение: 2-уровневая система
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Уровень 1: Frontend (React)
|
||||
|
||||
**Что делаем:**
|
||||
- JPG/PNG → сжатие до 2MB → конвертация в PDF
|
||||
- PDF < 5MB → пропускаем
|
||||
- PDF > 10MB → **отклоняем** с сообщением
|
||||
|
||||
**Код:** `frontend/src/utils/pdfConverter.ts` ✅ УЖЕ ГОТОВО
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Уровень 2: Backend (n8n)
|
||||
|
||||
### Workflow для сжатия PDF > 5MB
|
||||
|
||||
```
|
||||
Webhook (file upload)
|
||||
↓
|
||||
IF Node: file_size > 5 MB?
|
||||
├─ FALSE → S3 Upload (оригинал)
|
||||
└─ TRUE → Python Code Node (compress)
|
||||
↓
|
||||
S3 Upload (compressed)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐍 Python Code Node - PDF Compression
|
||||
|
||||
### Установка библиотеки в n8n
|
||||
|
||||
```bash
|
||||
# В контейнере n8n
|
||||
docker exec -it <n8n_container_name> sh
|
||||
apk add --no-cache python3 py3-pip
|
||||
pip3 install pypdf
|
||||
```
|
||||
|
||||
### Code Node конфигурация
|
||||
|
||||
**Language:** Python
|
||||
**Mode:** Run Once for All Items
|
||||
|
||||
**Code:**
|
||||
```python
|
||||
import io
|
||||
from pypdf import PdfReader, PdfWriter
|
||||
|
||||
# Получаем binary data из предыдущей ноды
|
||||
input_data = items[0].binary['data']
|
||||
pdf_bytes = input_data
|
||||
|
||||
# Читаем PDF
|
||||
reader = PdfReader(io.BytesIO(pdf_bytes))
|
||||
writer = PdfWriter()
|
||||
|
||||
# Копируем страницы с оптимизацией
|
||||
for page in reader.pages:
|
||||
# Удаляем неиспользуемые объекты
|
||||
page.compress_content_streams()
|
||||
writer.add_page(page)
|
||||
|
||||
# Применяем сжатие
|
||||
writer.compress_identical_objects()
|
||||
writer.remove_duplication()
|
||||
|
||||
# Сжимаем изображения (если есть)
|
||||
for page in writer.pages:
|
||||
for img in page.images:
|
||||
img.replace(img.image, quality=70)
|
||||
|
||||
# Выводим в bytes
|
||||
output = io.BytesIO()
|
||||
writer.write(output)
|
||||
compressed_bytes = output.getvalue()
|
||||
|
||||
# Логируем результат
|
||||
original_size = len(pdf_bytes) / (1024 * 1024)
|
||||
compressed_size = len(compressed_bytes) / (1024 * 1024)
|
||||
compression_ratio = ((original_size - compressed_size) / original_size) * 100
|
||||
|
||||
print(f"✅ Compressed: {original_size:.2f}MB → {compressed_size:.2f}MB ({compression_ratio:.1f}% reduction)")
|
||||
|
||||
# Возвращаем binary data
|
||||
return {
|
||||
'binary': {
|
||||
'data': compressed_bytes
|
||||
},
|
||||
'json': {
|
||||
'original_size_mb': round(original_size, 2),
|
||||
'compressed_size_mb': round(compressed_size, 2),
|
||||
'compression_ratio': round(compression_ratio, 1),
|
||||
'success': True
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Вариант 2: Execute Command (Ghostscript)
|
||||
|
||||
**Требует:** `ghostscript` установлен в системе
|
||||
|
||||
### Execute Command Node:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
INPUT="/tmp/input_{{ $json.file_id }}.pdf"
|
||||
OUTPUT="/tmp/output_{{ $json.file_id }}.pdf"
|
||||
|
||||
# Сохраняем binary в файл
|
||||
echo "{{ $binary.data }}" | base64 -d > "$INPUT"
|
||||
|
||||
# Сжимаем через Ghostscript
|
||||
gs -sDEVICE=pdfwrite \
|
||||
-dCompatibilityLevel=1.4 \
|
||||
-dPDFSETTINGS=/ebook \
|
||||
-dNOPAUSE \
|
||||
-dQUIET \
|
||||
-dBATCH \
|
||||
-sOutputFile="$OUTPUT" \
|
||||
"$INPUT"
|
||||
|
||||
# Выводим compressed PDF
|
||||
cat "$OUTPUT" | base64
|
||||
|
||||
# Cleanup
|
||||
rm -f "$INPUT" "$OUTPUT"
|
||||
```
|
||||
|
||||
**Параметры `-dPDFSETTINGS`:**
|
||||
- `/screen` - 72 DPI (минимальное качество, максимальное сжатие)
|
||||
- `/ebook` - 150 DPI ⭐ **рекомендуется**
|
||||
- `/printer` - 300 DPI
|
||||
- `/prepress` - 300 DPI (максимальное качество)
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Полный Workflow
|
||||
|
||||
### 1. Webhook (File Upload)
|
||||
|
||||
**Input:**
|
||||
```json
|
||||
{
|
||||
"claim_id": "CLM-2025-10-26-ABC123",
|
||||
"file_type": "policy_scan",
|
||||
"filename": "policy.pdf",
|
||||
"voucher": "E1000-302372730",
|
||||
"session_id": "sess-xyz-456"
|
||||
}
|
||||
```
|
||||
|
||||
**Binary Data:** `data` (PDF file)
|
||||
|
||||
---
|
||||
|
||||
### 2. IF Node: Check File Size
|
||||
|
||||
**Condition:**
|
||||
```
|
||||
{{ $binary.data.length }} > 5242880
|
||||
```
|
||||
(5MB = 5 * 1024 * 1024 bytes)
|
||||
|
||||
---
|
||||
|
||||
### 3a. FALSE → Direct Upload
|
||||
|
||||
**S3 Upload Node** → PostgreSQL
|
||||
|
||||
---
|
||||
|
||||
### 3b. TRUE → Compress First
|
||||
|
||||
```
|
||||
Python Code (compress)
|
||||
↓
|
||||
Set Binary Data
|
||||
↓
|
||||
S3 Upload (compressed)
|
||||
↓
|
||||
PostgreSQL (update file_size)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Результаты сжатия
|
||||
|
||||
| Метод | Скорость | Сжатие | Качество |
|
||||
|-------|----------|--------|----------|
|
||||
| **pypdf** | Быстро | 30-50% | Хорошее ⭐ |
|
||||
| **Ghostscript /ebook** | Средне | 50-70% | Среднее |
|
||||
| **Ghostscript /screen** | Средне | 70-85% | Низкое |
|
||||
| **Frontend (jspdf)** | Моментально | 60-80% | Хорошее ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Итоговая стратегия
|
||||
|
||||
```
|
||||
📱 Пользователь загружает файл
|
||||
↓
|
||||
🔍 Frontend проверка:
|
||||
├─ JPG/PNG → compress + convert → PDF (✅ готово)
|
||||
├─ PDF < 5MB → отправить как есть
|
||||
├─ PDF 5-10MB → отправить (n8n сожмёт)
|
||||
└─ PDF > 10MB → ❌ отклонить
|
||||
|
||||
🚀 n8n workflow:
|
||||
├─ file_size < 5MB → S3 + OCR
|
||||
└─ file_size > 5MB → Python compress → S3 + OCR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Тестирование
|
||||
|
||||
### curl пример:
|
||||
|
||||
```bash
|
||||
# Создаём большой PDF для теста
|
||||
curl -o large.pdf https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf
|
||||
|
||||
# Отправляем в n8n
|
||||
curl -X POST \
|
||||
-F "claim_id=CLM-TEST-001" \
|
||||
-F "file_type=policy_scan" \
|
||||
-F "fileInput=@large.pdf" \
|
||||
-F "voucher=TEST-123" \
|
||||
-F "session_id=sess-test" \
|
||||
https://n8n.clientright.pro/webhook/7e2abc64-eaca-4671-86e4-12786700fe95
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Готово!
|
||||
|
||||
**Frontend:** ✅ Ограничение 10MB + предупреждение
|
||||
**n8n:** ⏳ Нужно добавить Python Code Node
|
||||
|
||||
**Следующий шаг:** Добавить Python Code Node в workflow для файлов > 5MB
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user