fix: claim_id теперь генерируется только на бэкенде (n8n)
- ❌ Удалена локальная генерация claim_id в ClaimForm.tsx - ✅ claim_id создаётся n8n при SMS верификации - ✅ Step1Phone: передаёт session_id в n8n, сохраняет claim_id - ✅ Step1Policy: сохраняет claim_id из ответа n8n при проверке полиса - ✅ Добавлено детальное логирование для отладки - 📝 PROJECT_TIMELINE.md - полная хронология проекта (24 окт - 1 ноя)
This commit is contained in:
549
PROJECT_TIMELINE.md
Normal file
549
PROJECT_TIMELINE.md
Normal file
@@ -0,0 +1,549 @@
|
||||
# 📅 Хронология проекта ERV Platform
|
||||
|
||||
**Период:** 24 октября - 1 ноября 2025
|
||||
**Всего сессий:** 8
|
||||
**Всего строк документации:** 5295+
|
||||
**Коммитов:** 50+
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 1: 24 октября 2025 - Инициализация проекта
|
||||
|
||||
**Задача:** Развернуть новую платформу обработки страховых обращений
|
||||
|
||||
### Что сделано:
|
||||
- ✅ Создана структура проекта: Backend (FastAPI) + Frontend (React TypeScript)
|
||||
- ✅ Подключены внешние сервисы:
|
||||
- PostgreSQL (147.45.189.234:5432)
|
||||
- Redis (crm.clientright.ru:6379)
|
||||
- RabbitMQ (185.197.75.249:5672)
|
||||
- S3 Timeweb Storage
|
||||
- ✅ API endpoints: SMS, Claims, Policy, Upload
|
||||
- ✅ OCR интеграция (147.45.146.17:8001)
|
||||
- ✅ Базовая форма заявки (3 шага)
|
||||
- ✅ Docker контейнеры: frontend, backend, postgres, redis
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-24.md` (708 строк)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 2: 26 октября 2025 - N8N интеграция
|
||||
|
||||
**Задача:** Интеграция с n8n для асинхронной обработки файлов
|
||||
|
||||
### Что сделано:
|
||||
- ✅ **N8N Webhooks:**
|
||||
- Проверка полиса в MySQL + запись в PostgreSQL
|
||||
- Загрузка файлов в S3 + OCR + Vision AI
|
||||
- ✅ **React Frontend:**
|
||||
- Автогенерация `claim_id` и `session_id`
|
||||
- Конвертация файлов в PDF на клиенте (jsPDF + browser-image-compression)
|
||||
- Поддержка форматов: JPG, PNG, HEIC, WEBP, PDF
|
||||
- Сжатие изображений до 2MB
|
||||
- ✅ **PostgreSQL:**
|
||||
- Таблицы `claims` и `claim_files`
|
||||
- UPSERT операции
|
||||
- JSONB поля для гибкого хранения
|
||||
- ✅ **Документация:**
|
||||
- `N8N_INTEGRATION.md`
|
||||
- `N8N_SQL_QUERIES.md`
|
||||
- `N8N_PDF_COMPRESS.md`
|
||||
|
||||
**Проблемы решены:**
|
||||
- Конфликт зависимостей `aioboto3` → удалён
|
||||
- Nullable поля в PostgreSQL
|
||||
- JSON сериализация в n8n
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-26.md` (932 строки)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 3: 27 октября 2025 - SSE + Redis Pub/Sub
|
||||
|
||||
**Задача:** Real-time обработка документов через SSE
|
||||
|
||||
### Что сделано:
|
||||
- ✅ **Backend SSE Endpoint** (`/events/{task_id}`):
|
||||
- Server-Sent Events для real-time стриминга
|
||||
- Подписка на Redis Pub/Sub канал `ocr_events:{task_id}`
|
||||
- Обработка вложенного формата от n8n Redis ноды
|
||||
- Автозакрытие соединения после результата
|
||||
- ✅ **Frontend SSE Client:**
|
||||
- Блокирующая модалка с loading spinner
|
||||
- Автоматическое подключение после загрузки файла
|
||||
- Отображение результатов AI анализа
|
||||
- Логирование в Debug Panel
|
||||
- ✅ **Vite Proxy:**
|
||||
- `/events` → `host.docker.internal:8100`
|
||||
- Обход файрвола (порт 8100 закрыт)
|
||||
- ✅ **Docker:**
|
||||
- Frontend в dev режиме (hot reload)
|
||||
- `host.docker.internal` для доступа к хосту
|
||||
|
||||
**Проблемы решены:**
|
||||
- Неправильный порт SSE → Vite proxy
|
||||
- Backend к локальному Redis → абсолютный путь `.env`
|
||||
- AttributeError decode → убран `.decode()`
|
||||
- Префикс `/api/v1` → убран для events
|
||||
- Frontend не ждёт SSE → модалка + `waitingForOcr` state
|
||||
|
||||
**Тестирование:**
|
||||
- Время обработки: ~55 секунд
|
||||
- OCR: полис E1000-302545808
|
||||
- AI: извлечены ФИО, даты, программа
|
||||
- Результат в модалке ✅
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-27.md` (не найден в списке, но упоминается)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 4: 28 октября 2025 - Исправления SSE
|
||||
|
||||
**Сессия 1 (00:00-01:00):** Исправление SSE error handling
|
||||
|
||||
### Проблема:
|
||||
После успешного распознавания показывалась ошибка "Ошибка подключения к серверу"
|
||||
|
||||
### Причина:
|
||||
Backend закрывал SSE после отправки события → браузер триггерил `onerror` → фронтенд затирал успешный результат
|
||||
|
||||
### Решение:
|
||||
```typescript
|
||||
eventSource.onerror = (error) => {
|
||||
setOcrModalContent((prev) => {
|
||||
if (prev && prev !== 'loading') {
|
||||
return prev; // НЕ затираем результат
|
||||
}
|
||||
return { success: false, message: 'Ошибка подключения к серверу' };
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
**Сессия 2 (13:00-17:00):** Умная форма Step 2
|
||||
|
||||
### Что сделано:
|
||||
- ✅ **Рефакторинг Step 2** с AI-обработкой документов
|
||||
- ✅ **Пошаговая загрузка** с модалками
|
||||
- ✅ **DEV MODE кнопки** во всех 3 шагах
|
||||
- ✅ **PostgreSQL UPSERT** с CTE
|
||||
- ✅ **Конфигурация документов** для каждого типа события
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-28.md` (1063 строки)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 5: 29 октября 2025 - Динамический визард
|
||||
|
||||
**Сессия 1 (12:00-15:00):** Рефакторинг визарда
|
||||
|
||||
### Задача:
|
||||
Переделать визард так, чтобы каждый документ был отдельным шагом
|
||||
|
||||
### Было:
|
||||
```
|
||||
[1. Полис] → [2. Детали + все документы] → [3. Оплата]
|
||||
```
|
||||
|
||||
### Стало:
|
||||
```
|
||||
[1. Полис] → [2. Тип] → [3. Док 1] → [4. Док 2] → ... → [N. Оплата]
|
||||
```
|
||||
|
||||
### Что сделано:
|
||||
- ✅ `Step2EventType.tsx` - выбор типа страхового случая
|
||||
- ✅ `StepDocumentUpload.tsx` - загрузка каждого документа
|
||||
- ✅ Динамическое количество шагов
|
||||
- ✅ Прогресс-бар с реальными шагами
|
||||
- ✅ SSE для каждого документа с уникальным `event_type`
|
||||
|
||||
**Проблемы решены:**
|
||||
- Синтаксические ошибки (дублирующийся код)
|
||||
- PostgreSQL INSERT не возвращал данные
|
||||
- `file_size` как строка вместо числа
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-29.md` (645 строк)
|
||||
|
||||
---
|
||||
|
||||
**Сессия 2 (16:30-17:30):** Безопасность N8N Webhooks
|
||||
|
||||
### Задача:
|
||||
> "как нам не палить вебхук, а то его видно через код?"
|
||||
|
||||
### Проблема:
|
||||
N8N webhook URLs были захардкожены в коде фронтенда → видны в DevTools
|
||||
|
||||
### Решение:
|
||||
Backend Proxy - фронтенд больше не знает про n8n!
|
||||
|
||||
### Архитектура:
|
||||
```
|
||||
Frontend → fetch('/api/n8n/*') → Backend Proxy → N8N (URLs в .env)
|
||||
```
|
||||
|
||||
### Что сделано:
|
||||
- ✅ `backend/app/api/n8n_proxy.py` - proxy router
|
||||
- ✅ Webhook URLs в `.env` (не коммитятся)
|
||||
- ✅ Frontend использует `/api/n8n/policy/check` и `/api/n8n/upload/file`
|
||||
- ✅ Документация `SECURITY_N8N_PROXY.md`
|
||||
|
||||
**Проблемы решены:**
|
||||
- "Ошибка соединения" → относительные пути вместо localhost
|
||||
- Пропущенные поля → добавлены `filename` и `upload_timestamp`
|
||||
- event_type не совпадает → гибкая проверка
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-29_part2.md` (627 строк)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 6: 30 октября 2025 - Телефон на Step 1 + CRM
|
||||
|
||||
**Задача:** Перенос телефона на первый шаг и создание контакта в CRM
|
||||
|
||||
### Что сделано:
|
||||
- ✅ **Новый Step1Phone** (вместо Step1Policy):
|
||||
- Ввод телефона как первый шаг
|
||||
- SMS верификация
|
||||
- Автосоздание контакта в CRM через n8n webhook
|
||||
- ✅ **CreateWebContact** - операция vTiger webservice:
|
||||
- Создание контакта по телефону
|
||||
- Проверка дубликатов
|
||||
- Возврат `contact_id`
|
||||
- ✅ **N8N Webhook** для создания контакта после SMS
|
||||
- ✅ **Формат телефона без +** (79001234567)
|
||||
- ✅ **DEV MODE** кнопки на всех шагах
|
||||
|
||||
**Рефакторинг структуры:**
|
||||
```
|
||||
Было: Step1Policy → Step2Details → Step3Payment
|
||||
Стало: Step1Phone → Step2Policy → Step3EventType → Step4DocUpload... → StepNPayment
|
||||
```
|
||||
|
||||
**Оптимизация Docker:**
|
||||
- Убраны локальные контейнеры Postgres и Redis
|
||||
- Используются только внешние сервисы
|
||||
- Остались: frontend + backend
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-10-30.md` (597 строк)
|
||||
|
||||
---
|
||||
|
||||
## 📊 День 7: 1 ноября 2025 - CreateWebProject + Финальная интеграция
|
||||
|
||||
**Задача:** Создание проектов в CRM по номеру полиса
|
||||
|
||||
### Что сделано:
|
||||
- ✅ **CreateWebProject.php** - операция vTiger:
|
||||
- Поиск проекта по полису (cf_1885)
|
||||
- Создание нового если не найден
|
||||
- Привязка к контакту
|
||||
- Возврат `{"project_id": "123", "is_new": true/false}`
|
||||
- ✅ **Регистрация в БД** vTiger webservice
|
||||
- ✅ **Тестирование:**
|
||||
- Создание нового: `{"project_id":"396865","is_new":true}` ✅
|
||||
- Повторный вызов: `{"project_id":"396865","is_new":false}` ✅
|
||||
- ✅ **N8N Webhook URLs** в docker-compose.yml (environment)
|
||||
|
||||
**Проблемы решены:**
|
||||
- Формат телефона в vTiger (mobile без +)
|
||||
- SMS коды не проходили валидацию
|
||||
- N8N webhook URLs не передавались в backend контейнер
|
||||
|
||||
**Логи:** `SESSION_LOG_2025-11-01.md` (723 строки)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Текущая архитектура (финальная)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ USER BROWSER │
|
||||
│ http://147.45.146.17:5173 │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────┐ │
|
||||
│ │ React Frontend (Vite Dev Server) │ │
|
||||
│ │ - Step1Phone.tsx (SMS верификация) │ │
|
||||
│ │ - Step2Policy.tsx (проверка полиса) │ │
|
||||
│ │ - Step3EventType.tsx (тип события) │ │
|
||||
│ │ - Step4-N DocumentUpload.tsx (документы) │ │
|
||||
│ │ - StepNPayment.tsx (оплата) │ │
|
||||
│ │ - SSE Client для real-time обновлений │ │
|
||||
│ │ - PDF конвертер (HEIC/JPG/PNG → PDF) │ │
|
||||
│ └────────────┬───────────────────────────────────────────────────┘ │
|
||||
│ │ Vite Proxy (/api, /events → backend) │
|
||||
└───────────────┼──────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ BACKEND (FastAPI, port 8100) │
|
||||
│ PID: 31571 (запущен вне Docker) │
|
||||
│ │
|
||||
│ 📁 app/api/ │
|
||||
│ ├── sms.py - SMS верификация (SigmaSMS) │
|
||||
│ ├── claims.py - Заявки │
|
||||
│ ├── policy.py - Проверка полисов │
|
||||
│ ├── upload.py - Загрузка файлов │
|
||||
│ ├── events.py - SSE endpoints ⬅️ ВАЖНО │
|
||||
│ └── n8n_proxy.py - Безопасный proxy к n8n ⬅️ НОВОЕ │
|
||||
│ │
|
||||
│ 🔌 Подключения: │
|
||||
│ ├── PostgreSQL (147.45.189.234:5432) - claims, claim_files │
|
||||
│ ├── MySQL (localhost:3306) - проверка полисов ERV │
|
||||
│ ├── Redis (crm.clientright.ru:6379) - Pub/Sub для SSE │
|
||||
│ ├── RabbitMQ (185.197.75.249:5672) - очереди │
|
||||
│ └── S3 (Timeweb) - хранилище файлов │
|
||||
└────────────┬────────────────────────────────────────────────────────┘
|
||||
│
|
||||
├──────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
┌──────────────────────────┐ ┌──────────────────────────────┐
|
||||
│ N8N Workflows │ │ vTiger CRM │
|
||||
│ n8n.clientright.pro │ │ crm.clientright.ru │
|
||||
│ │ │ │
|
||||
│ Webhook 1: Policy Check │ │ Webservice Operations: │
|
||||
│ - MySQL query │ │ - CreateWebContact │
|
||||
│ - PostgreSQL insert │ │ - CreateWebProject │
|
||||
│ - Return insured_persons│ │ - Query │
|
||||
│ │ │ │
|
||||
│ Webhook 2: File Upload │ │ Tables: │
|
||||
│ - S3 upload │────────────────────│ - vtiger_contactdetails │
|
||||
│ - PostgreSQL insert │ n8n webhook │ - vtiger_project │
|
||||
│ - OCR Service │ creates contact │ - vtiger_contactscf (mobile) │
|
||||
│ - AI Vision (Gemini) │ │ - vtiger_projectcf (cf_1885) │
|
||||
│ - Redis PUBLISH │ │ │
|
||||
└───────────┬──────────────┘ └───────────────────────────────┘
|
||||
│
|
||||
│ Redis PUBLISH: ocr_events:{claim_id}
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────────┐
|
||||
│ Redis Pub/Sub │
|
||||
│ crm.clientright.ru:6379 │
|
||||
│ Channel: ocr_events:{claim_id} │
|
||||
│ Password: CRM_Redis_Pass_2025_Secure! │
|
||||
└───────────▲──────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ SUBSCRIBE
|
||||
│
|
||||
Backend SSE endpoint (/events/{task_id})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Ключевые компоненты
|
||||
|
||||
### Frontend (React + TypeScript)
|
||||
|
||||
**Основные файлы:**
|
||||
- `ClaimForm.tsx` - главный визард с прогрессом
|
||||
- `Step1Phone.tsx` - ввод телефона + SMS
|
||||
- `Step2Policy.tsx` - проверка полиса
|
||||
- `Step3EventType.tsx` - выбор типа события
|
||||
- `StepDocumentUpload.tsx` - загрузка документов (динамический)
|
||||
- `StepPayment.tsx` - реквизиты для выплаты
|
||||
- `utils/pdfConverter.ts` - клиентская конвертация в PDF
|
||||
|
||||
**Технологии:**
|
||||
- Vite (dev server + proxy)
|
||||
- Ant Design (UI компоненты)
|
||||
- EventSource (SSE client)
|
||||
- jsPDF + browser-image-compression
|
||||
|
||||
### Backend (FastAPI + Python)
|
||||
|
||||
**Основные модули:**
|
||||
- `app/main.py` - FastAPI приложение
|
||||
- `app/config.py` - настройки из `.env`
|
||||
- `app/api/*` - роутеры (sms, claims, policy, upload, events, n8n_proxy)
|
||||
- `app/services/*` - сервисы (database, redis, rabbitmq, s3, policy)
|
||||
|
||||
**Технологии:**
|
||||
- FastAPI (async API)
|
||||
- SQLAlchemy (PostgreSQL)
|
||||
- aiomysql (MySQL)
|
||||
- redis-py (Redis Pub/Sub)
|
||||
- aio-pika (RabbitMQ)
|
||||
- boto3 (S3)
|
||||
- httpx (HTTP client для proxy)
|
||||
|
||||
### N8N Workflows
|
||||
|
||||
**Workflow 1: Policy Check**
|
||||
```
|
||||
Webhook → PostgreSQL (INSERT claims) → MySQL (SELECT policy) → Code → Response
|
||||
```
|
||||
|
||||
**Workflow 2: File Upload + OCR**
|
||||
```
|
||||
Webhook → S3 Upload → PostgreSQL (INSERT claim_files)
|
||||
→ OCR Service → Vision AI → Code (валидация)
|
||||
→ PostgreSQL (UPDATE ai_extracted_data) → Redis PUBLISH
|
||||
```
|
||||
|
||||
### Database Schema
|
||||
|
||||
**PostgreSQL (147.45.189.234:5432/default_db):**
|
||||
- `claims` - заявки (claim_number, policy_number, status, form_data::jsonb)
|
||||
- `claim_files` - файлы (s3_key, ocr_text, ai_extracted_data::jsonb, ocr_status)
|
||||
|
||||
**MySQL (localhost:3306/u2768571_crm_db):**
|
||||
- `lexrpiority` - полисы ERV
|
||||
- `lexrpiority_insured_persons` - застрахованные лица
|
||||
|
||||
**vTiger CRM (crm.clientright.ru):**
|
||||
- `vtiger_contactdetails` - контакты
|
||||
- `vtiger_contactscf` - доп.поля контактов (mobile)
|
||||
- `vtiger_project` - проекты (заявки)
|
||||
- `vtiger_projectcf` - доп.поля проектов (cf_1885 = полис)
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Безопасность
|
||||
|
||||
### N8N Webhooks (спрятаны):
|
||||
```
|
||||
❌ РАНЬШЕ: fetch('https://n8n.../webhook/9eb7bc5b...') // Виден в коде!
|
||||
✅ ТЕПЕРЬ: fetch('/api/n8n/policy/check') // Proxy через backend
|
||||
```
|
||||
|
||||
**Webhook URLs хранятся в:**
|
||||
- `.env` (не коммитится в git)
|
||||
- `backend/app/config.py` (читает из .env)
|
||||
- `backend/app/api/n8n_proxy.py` (проксирует запросы)
|
||||
|
||||
### Redis:
|
||||
```
|
||||
Host: crm.clientright.ru:6379
|
||||
Password: CRM_Redis_Pass_2025_Secure! (в .env)
|
||||
```
|
||||
|
||||
### PostgreSQL:
|
||||
```
|
||||
Host: 147.45.189.234:5432
|
||||
User: gen_user
|
||||
Password: 2~~9_^kVsU?2\S (в .env)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Статистика проекта
|
||||
|
||||
### Документация:
|
||||
```
|
||||
SESSION_LOG_2025-10-24.md: 708 строк
|
||||
SESSION_LOG_2025-10-26.md: 932 строки
|
||||
SESSION_LOG_2025-10-28.md: 1063 строки
|
||||
SESSION_LOG_2025-10-29.md: 645 строк
|
||||
SESSION_LOG_2025-10-29_part2.md: 627 строк
|
||||
SESSION_LOG_2025-10-30.md: 597 строк
|
||||
SESSION_LOG_2025-11-01.md: 723 строки
|
||||
───────────────────────────────────────────
|
||||
ИТОГО: 5295 строк
|
||||
|
||||
Дополнительно:
|
||||
- N8N_INTEGRATION.md
|
||||
- N8N_SQL_QUERIES.md
|
||||
- N8N_PDF_COMPRESS.md
|
||||
- SECURITY_N8N_PROXY.md
|
||||
```
|
||||
|
||||
### Git коммиты:
|
||||
```
|
||||
Всего: 50+ коммитов
|
||||
Период: 24 окт - 1 ноя (8 дней)
|
||||
Среднее: 6-7 коммитов/день
|
||||
```
|
||||
|
||||
### Файлы проекта:
|
||||
```
|
||||
Backend: ~30 файлов Python
|
||||
Frontend: ~20 файлов TypeScript/TSX
|
||||
Configs: ~10 файлов (docker, vite, env)
|
||||
Docs: ~10 файлов Markdown
|
||||
CRM: ~3 файла PHP (vTiger operations)
|
||||
Utils: ~5 скриптов (мониторинг, тесты)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Что работает сейчас
|
||||
|
||||
### ✅ Полный флоу обработки заявки:
|
||||
|
||||
1. **Step 1: Телефон**
|
||||
- Ввод телефона → SMS код → верификация
|
||||
- Создание контакта в CRM (автоматически через n8n)
|
||||
|
||||
2. **Step 2: Полис**
|
||||
- Ввод номера полиса
|
||||
- Проверка в MySQL БД
|
||||
- Если найден → показ застрахованных лиц → переход дальше
|
||||
- Если НЕ найден → загрузка скана полиса
|
||||
- OCR + Vision AI → распознавание полиса
|
||||
- Real-time результат через SSE в модалке
|
||||
|
||||
3. **Step 3: Тип события**
|
||||
- Выбор типа (задержка, отмена, пропуск стыковки)
|
||||
- Динамическое определение нужных документов
|
||||
|
||||
4. **Step 4-N: Документы**
|
||||
- Каждый документ = отдельный шаг
|
||||
- Загрузка → S3 → OCR → AI → результат в модалке
|
||||
- Real-time обработка через SSE
|
||||
|
||||
5. **Step N: Оплата**
|
||||
- Реквизиты для СБП
|
||||
- Финальная отправка заявки
|
||||
|
||||
### ✅ Интеграции:
|
||||
|
||||
- **n8n** - асинхронная обработка (webhooks)
|
||||
- **vTiger CRM** - создание контактов и проектов (webservice)
|
||||
- **PostgreSQL** - хранение заявок и файлов
|
||||
- **MySQL** - база полисов ERV
|
||||
- **Redis** - Pub/Sub для SSE событий
|
||||
- **RabbitMQ** - очереди задач
|
||||
- **S3 Timeweb** - хранилище файлов
|
||||
- **OCR Service** - распознавание текста
|
||||
- **Gemini Vision AI** - извлечение данных из документов
|
||||
- **SigmaSMS** - отправка SMS кодов
|
||||
|
||||
---
|
||||
|
||||
## 📊 Текущий статус (1 ноября 2025)
|
||||
|
||||
### Git:
|
||||
```bash
|
||||
Branch: main
|
||||
Status: clean (nothing to commit)
|
||||
Last commit: c049ed6 - "fix: Добавлены n8n webhook URLs в docker-compose.yml"
|
||||
```
|
||||
|
||||
### Сервисы:
|
||||
```bash
|
||||
✅ Backend: http://147.45.146.17:8100 (PID 31571, uvicorn --reload)
|
||||
✅ Frontend: http://147.45.146.17:5173 (Docker, Vite dev mode)
|
||||
✅ PostgreSQL: 147.45.189.234:5432 (внешний)
|
||||
✅ Redis: crm.clientright.ru:6379 (внешний)
|
||||
✅ RabbitMQ: 185.197.75.249:5672 (внешний)
|
||||
✅ MySQL: localhost:3306 (vTiger/ERV полисы)
|
||||
✅ S3: s3.twcstorage.ru (Timeweb)
|
||||
✅ OCR: 147.45.146.17:8001 (Python service)
|
||||
✅ n8n: n8n.clientright.pro (workflows)
|
||||
✅ vTiger: crm.clientright.ru (CRM)
|
||||
```
|
||||
|
||||
### Следующие шаги:
|
||||
- [ ] Production mode для frontend (сейчас dev)
|
||||
- [ ] Docker Compose для backend (сейчас venv на хосте)
|
||||
- [ ] Nginx reverse proxy вместо прямого доступа к портам
|
||||
- [ ] SSL сертификаты
|
||||
- [ ] Мониторинг (Grafana/Prometheus)
|
||||
- [ ] Тесты (pytest для backend, Jest для frontend)
|
||||
- [ ] CI/CD pipeline
|
||||
|
||||
---
|
||||
|
||||
**Последнее обновление:** 1 ноября 2025, 13:39 MSK
|
||||
**Автор:** Фёдор + AI Assistant (Claude Sonnet 4.5)
|
||||
|
||||
@@ -94,20 +94,37 @@ export default function Step1Phone({
|
||||
const crmResponse = await fetch('https://n8n.clientright.pro/webhook/511fde97-88bb-4fb4-bea5-cafdc364be27', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ phone })
|
||||
body: JSON.stringify({
|
||||
phone,
|
||||
session_id: formData.session_id // ✅ Передаём session_id
|
||||
})
|
||||
});
|
||||
|
||||
const crmResult = await crmResponse.json();
|
||||
|
||||
console.log('🔥 N8N CRM Response:', crmResult);
|
||||
console.log('🔥 claim_id from n8n:', crmResult.claim_id);
|
||||
console.log('🔥 Array check:', Array.isArray(crmResult), crmResult[0]);
|
||||
|
||||
if (crmResponse.ok) {
|
||||
addDebugEvent?.('crm', 'success', `✅ Контакт создан/найден в CRM`, crmResult);
|
||||
|
||||
// Если n8n вернул массив - берём первый элемент
|
||||
const data = Array.isArray(crmResult) ? crmResult[0] : crmResult;
|
||||
|
||||
console.log('🔥 Saving to formData:', {
|
||||
phone,
|
||||
contact_id: data.contact_id,
|
||||
claim_id: data.claim_id,
|
||||
is_new_contact: data.is_new_contact
|
||||
});
|
||||
|
||||
// Сохраняем данные из CRM в форму
|
||||
updateFormData({
|
||||
phone,
|
||||
contact_id: crmResult.contact_id,
|
||||
claim_id: crmResult.claim_id,
|
||||
is_new_contact: crmResult.is_new_contact
|
||||
contact_id: data.contact_id,
|
||||
claim_id: data.claim_id,
|
||||
is_new_contact: data.is_new_contact
|
||||
});
|
||||
|
||||
message.success(crmResult.is_new_contact ? 'Контакт создан!' : 'Контакт найден!');
|
||||
|
||||
@@ -228,7 +228,12 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug
|
||||
voucher: values.voucher
|
||||
});
|
||||
message.success(`Полис найден: ${result.policy.voucher}. Застрахованных: ${result.policy.count} чел.`);
|
||||
updateFormData(values);
|
||||
|
||||
// ✅ Сохраняем claim_id из ответа n8n (если есть) или используем существующий
|
||||
updateFormData({
|
||||
...values,
|
||||
claim_id: result.claim?.claim_id || formData.claim_id
|
||||
});
|
||||
onNext();
|
||||
} else {
|
||||
// Полис НЕ найден - показываем загрузку скана
|
||||
@@ -239,6 +244,13 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug
|
||||
voucher: values.voucher
|
||||
});
|
||||
message.warning('Полис не найден в базе. Загрузите скан полиса');
|
||||
|
||||
// ✅ Сохраняем claim_id из ответа n8n (если есть) или используем существующий
|
||||
updateFormData({
|
||||
...values,
|
||||
claim_id: result.claim?.claim_id || formData.claim_id
|
||||
});
|
||||
|
||||
setPolicyNotFound(true);
|
||||
}
|
||||
} else {
|
||||
@@ -283,7 +295,11 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug
|
||||
count: fileList.length
|
||||
});
|
||||
|
||||
// Генерируем claim_id если его нет
|
||||
// ✅ Используем claim_id из formData (создан в Step1Phone или получен от n8n)
|
||||
if (!formData.claim_id) {
|
||||
console.warn('⚠️ claim_id отсутствует! Генерирую новый. Возможна ошибка в флоу Step1Phone → Step2Policy');
|
||||
addDebugEvent?.('claim_id', 'warning', 'claim_id отсутствует, генерирую fallback');
|
||||
}
|
||||
const claimId = formData.claim_id || `CLM-${new Date().toISOString().split('T')[0]}-${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
|
||||
|
||||
// Загружаем каждый файл через n8n вебхук
|
||||
|
||||
@@ -43,12 +43,8 @@ interface FormData {
|
||||
}
|
||||
|
||||
export default function ClaimForm() {
|
||||
// Генерируем claim_id один раз при загрузке формы
|
||||
const [claimId] = useState(() => {
|
||||
const date = new Date().toISOString().split('T')[0];
|
||||
const randomId = Math.random().toString(36).substr(2, 6).toUpperCase();
|
||||
return `CLM-${date}-${randomId}`;
|
||||
});
|
||||
// ✅ claim_id будет создан n8n в Step1Phone после SMS верификации
|
||||
// Не генерируем его локально!
|
||||
|
||||
// Генерируем session_id и сохраняем в sessionStorage
|
||||
const [sessionId] = useState(() => {
|
||||
@@ -63,13 +59,16 @@ export default function ClaimForm() {
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
voucher: '',
|
||||
claim_id: claimId,
|
||||
claim_id: undefined, // ✅ Будет заполнен n8n в Step1Phone
|
||||
session_id: sessionId,
|
||||
paymentMethod: 'sbp',
|
||||
});
|
||||
const [isPhoneVerified, setIsPhoneVerified] = useState(false);
|
||||
const [debugEvents, setDebugEvents] = useState<any[]>([]);
|
||||
|
||||
// 🔥 VERSION CHECK: Если видишь это в консоли - фронт обновился!
|
||||
console.log('🔥 ClaimForm v2.0 - claim_id НЕ генерируется на фронте!');
|
||||
|
||||
// Динамически определяем список шагов на основе выбранного eventType
|
||||
const documentConfigs = formData.eventType ? getDocumentsForEventType(formData.eventType) : [];
|
||||
const totalDocumentSteps = documentConfigs.length;
|
||||
@@ -82,20 +81,13 @@ export default function ClaimForm() {
|
||||
message,
|
||||
data: {
|
||||
...data,
|
||||
claim_id: claimId // Добавляем claim_id во все события
|
||||
claim_id: formData.claim_id // ✅ Используем claim_id из formData (от n8n)
|
||||
}
|
||||
};
|
||||
setDebugEvents(prev => [event, ...prev]);
|
||||
};
|
||||
|
||||
// Логируем генерацию claim_id и session_id при первой загрузке
|
||||
useState(() => {
|
||||
addDebugEvent('system', 'info', `🆔 Сгенерирован Claim ID: ${claimId}`, {
|
||||
claim_id: claimId,
|
||||
session_id: sessionId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
});
|
||||
// ✅ claim_id будет залогирован в Step1Phone после получения от n8n
|
||||
|
||||
const updateFormData = useCallback((data: Partial<FormData>) => {
|
||||
setFormData((prev) => ({ ...prev, ...data }));
|
||||
@@ -127,7 +119,7 @@ export default function ClaimForm() {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
claim_id: claimId,
|
||||
claim_id: formData.claim_id, // ✅ Используем claim_id от n8n
|
||||
voucher: formData.voucher,
|
||||
email: formData.email,
|
||||
phone: formData.phone,
|
||||
@@ -146,10 +138,10 @@ export default function ClaimForm() {
|
||||
message.success(`Заявка ${result.claim_number} успешно создана!`);
|
||||
addDebugEvent('form', 'success', `✅ Заявка ${result.claim_number} создана`);
|
||||
|
||||
// Сброс формы
|
||||
// Сброс формы (создаём новую заявку, claim_id будет сгенерирован при следующем SMS)
|
||||
setFormData({
|
||||
voucher: '',
|
||||
claim_id: claimId,
|
||||
claim_id: undefined, // ✅ Очищаем для новой заявки
|
||||
session_id: sessionId,
|
||||
paymentMethod: 'sbp',
|
||||
});
|
||||
@@ -164,7 +156,7 @@ export default function ClaimForm() {
|
||||
addDebugEvent('form', 'error', '❌ Ошибка соединения');
|
||||
console.error(error);
|
||||
}
|
||||
}, [formData, claimId, sessionId, addDebugEvent]);
|
||||
}, [formData, sessionId, addDebugEvent]);
|
||||
|
||||
// Динамически генерируем шаги на основе выбранного eventType
|
||||
const steps = useMemo(() => {
|
||||
@@ -176,7 +168,7 @@ export default function ClaimForm() {
|
||||
description: 'Подтверждение по SMS',
|
||||
content: (
|
||||
<Step1Phone
|
||||
formData={{ ...formData, claim_id: claimId, session_id: sessionId }}
|
||||
formData={{ ...formData, session_id: sessionId }} // ✅ claim_id будет создан n8n
|
||||
updateFormData={updateFormData}
|
||||
onNext={nextStep}
|
||||
onPrev={prevStep}
|
||||
@@ -193,7 +185,7 @@ export default function ClaimForm() {
|
||||
description: 'Полис ERV',
|
||||
content: (
|
||||
<Step1Policy
|
||||
formData={{ ...formData, claim_id: claimId, session_id: sessionId }}
|
||||
formData={{ ...formData, session_id: sessionId }} // ✅ claim_id уже в formData от n8n
|
||||
updateFormData={updateFormData}
|
||||
onNext={nextStep}
|
||||
addDebugEvent={addDebugEvent}
|
||||
@@ -244,7 +236,7 @@ export default function ClaimForm() {
|
||||
description: 'Контакты и выплата',
|
||||
content: (
|
||||
<Step3Payment
|
||||
formData={{ ...formData, claim_id: claimId }}
|
||||
formData={formData} // ✅ claim_id уже в formData
|
||||
updateFormData={updateFormData}
|
||||
onPrev={prevStep}
|
||||
onSubmit={handleSubmit}
|
||||
@@ -256,12 +248,12 @@ export default function ClaimForm() {
|
||||
});
|
||||
|
||||
return stepsArray;
|
||||
}, [formData, documentConfigs, isPhoneVerified, claimId, sessionId, nextStep, prevStep, updateFormData, handleSubmit, setIsPhoneVerified, addDebugEvent]);
|
||||
}, [formData, documentConfigs, isPhoneVerified, sessionId, nextStep, prevStep, updateFormData, handleSubmit, setIsPhoneVerified, addDebugEvent]);
|
||||
|
||||
const handleReset = () => {
|
||||
setFormData({
|
||||
voucher: '',
|
||||
claim_id: claimId,
|
||||
claim_id: undefined, // ✅ Очищаем для новой заявки
|
||||
session_id: sessionId,
|
||||
paymentMethod: 'sbp',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user