From f12416a901b7eefd6d931b01c9923198c48f7b2c Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Wed, 29 Oct 2025 15:12:20 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=A1=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=20?= =?UTF-8?q?=D0=BB=D0=BE=D0=B3=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=2029?= =?UTF-8?q?=20=D0=BE=D0=BA=D1=82=D1=8F=D0=B1=D1=80=D1=8F=20-=20=D1=80?= =?UTF-8?q?=D0=B5=D1=84=D0=B0=D0=BA=D1=82=D0=BE=D1=80=D0=B8=D0=BD=D0=B3=20?= =?UTF-8?q?=D0=B2=D0=B8=D0=B7=D0=B0=D1=80=D0=B4=D0=B0=20=D0=BD=D0=B0=20?= =?UTF-8?q?=D0=B4=D0=B8=D0=BD=D0=B0=D0=BC=D0=B8=D1=87=D0=B5=D1=81=D0=BA?= =?UTF-8?q?=D0=B8=D0=B5=20=D1=88=D0=B0=D0=B3=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Документирован полный рефакторинг визарда (Вариант B) - Каждый документ = отдельный шаг в прогресс-баре - Созданы Step2EventType.tsx, StepDocumentUpload.tsx, documentConfigs.ts - Переделан ClaimForm.tsx на динамические шаги через useMemo - Исправлены проблемы: URL n8n, FormData структура, SSE логирование - Исправлены прогресс и навигация через useCallback - Всего 9 коммитов, ~1500 строк кода --- SESSION_LOG_2025-10-29.md | 644 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 644 insertions(+) create mode 100644 SESSION_LOG_2025-10-29.md diff --git a/SESSION_LOG_2025-10-29.md b/SESSION_LOG_2025-10-29.md new file mode 100644 index 0000000..c94c9d9 --- /dev/null +++ b/SESSION_LOG_2025-10-29.md @@ -0,0 +1,644 @@ +# 📋 Лог сессии: Рефакторинг визарда на динамические шаги + +**Дата:** 29 октября 2025 (12:00 - 15:00 MSK) +**Задача:** Переделка визарда - каждый документ отдельным шагом (Вариант B) +**Статус:** ✅ Успешно завершено + +--- + +## 🎯 Основная задача + +Переделать структуру визарда так, чтобы **каждый документ был отдельным шагом** в прогресс-баре: + +### Было (inline документы): +``` +[1. Полис] → [2. Детали + все документы] → [3. Оплата] +``` + +### Стало (каждый документ = шаг): +``` +[1. Полис] → [2. Тип] → [3. Док 1] → [4. Док 2] → [5. Док 3] → [N. Оплата] +``` + +--- + +## ✅ Выполненные задачи + +### 1. Создан Step2EventType.tsx +**Назначение:** Выбор типа страхового случая + +**Функционал:** +- Выпадающий список с иконками (✈️, 🚂, ⛴️) +- 7 типов событий: delay_flight, cancel_flight, miss_connection, emergency_landing, delay_train, cancel_train, delay_ferry +- Alert с подтверждением выбора +- DEV MODE кнопка для быстрого выбора "Отмена рейса" + +**Файл:** `frontend/src/components/form/Step2EventType.tsx` + +--- + +### 2. Создан StepDocumentUpload.tsx +**Назначение:** Универсальный компонент для загрузки одного документа + +**Функционал:** +- Прогресс-бар: "Документ X из Y" + процент завершения +- Upload компонент для выбора файлов +- Автоматическая загрузка на n8n webhook +- SSE для получения результатов AI обработки +- Модалка "Обрабатываем документ..." с результатами +- Проверка `isAlreadyUploaded` для пропуска повторной загрузки +- Кнопки: "Назад", "Загрузить", "Пропустить" (для необязательных) +- DEV MODE: "Назад" и "Пропустить [dev]" + +**Props:** +```typescript +{ + documentConfig: DocumentConfig; // Конфигурация документа + formData: any; // Данные формы + updateFormData: (data) => void; // Обновление данных + onNext: () => void; // Следующий шаг + onPrev: () => void; // Предыдущий шаг + isLastDocument: boolean; // Последний документ? + currentDocNumber: number; // Номер текущего документа + totalDocs: number; // Всего документов +} +``` + +**Файл:** `frontend/src/components/form/StepDocumentUpload.tsx` + +--- + +### 3. Создан constants/documentConfigs.ts +**Назначение:** Централизованная конфигурация документов для всех типов событий + +**Структура:** +```typescript +export interface DocumentConfig { + name: string; // Название документа + field: string; // Поле в formData + file_type: string; // Уникальный идентификатор для n8n + required: boolean; // Обязательный? + maxFiles: number; // Максимум файлов + description: string; // Описание для пользователя +} + +export const DOCUMENT_CONFIGS: Record = { + delay_flight: [...], + cancel_flight: [...], + miss_connection: [...], + delay_train: [...], + cancel_train: [...], + delay_ferry: [...], + emergency_landing: [...] +}; +``` + +**Пример для отмены рейса:** +```typescript +cancel_flight: [ + { + name: "Билет", + field: "ticket", + file_type: "flight_cancel_ticket", + required: true, + maxFiles: 1, + description: "Ticket/booking confirmation" + }, + { + name: "Уведомление об отмене", + field: "cancellation_notice", + file_type: "flight_cancel_notice", + required: true, + maxFiles: 3, + description: "Email, SMS или скриншот из приложения АК" + } +] +``` + +**Функции:** +- `getDocumentsForEventType(eventType)` - получить список документов +- `getTotalDocumentsCount(eventType)` - количество документов + +**Файл:** `frontend/src/constants/documentConfigs.ts` + +--- + +### 4. Переделан ClaimForm.tsx на динамические шаги + +**Изменения:** + +#### 4.1. Импорты +```typescript +import { useState, useMemo, useCallback } from 'react'; +import Step2EventType from '../components/form/Step2EventType'; +import StepDocumentUpload from '../components/form/StepDocumentUpload'; +import { getDocumentsForEventType } from '../constants/documentConfigs'; +``` + +#### 4.2. FormData интерфейс +```typescript +interface FormData { + // Шаг 1: Policy + voucher: string; + claim_id?: string; + session_id?: string; + + // Шаг 2: Event Type + eventType?: string; + + // Шаги 3+: Documents + documents?: Record; + + // Последний шаг: Payment + fullName?: string; + email?: string; + phone?: string; + paymentMethod?: string; + // ... +} +``` + +#### 4.3. Динамическое определение документов +```typescript +const documentConfigs = formData.eventType + ? getDocumentsForEventType(formData.eventType) + : []; +const totalDocumentSteps = documentConfigs.length; +``` + +#### 4.4. useCallback для функций навигации +```typescript +const nextStep = useCallback(() => { + console.log('⏩ nextStep called'); + setCurrentStep((prev) => { + console.log('📍 Current step:', prev, '→ Next:', prev + 1); + return prev + 1; + }); +}, []); + +const prevStep = useCallback(() => { + console.log('⏪ prevStep called'); + setCurrentStep((prev) => { + console.log('📍 Current step:', prev, '→ Prev:', prev - 1); + return prev - 1; + }); +}, []); + +const updateFormData = useCallback((data: Partial) => { + setFormData((prev) => ({ ...prev, ...data })); +}, []); +``` + +**Почему useCallback критично:** +- Без useCallback функции пересоздаются при каждом рендере +- Компоненты получают новые ссылки → ререндер → closure захватывает старые значения +- `prevStep` вызывался, но `setCurrentStep` не срабатывал + +#### 4.5. Динамическая генерация шагов через useMemo +```typescript +const steps = useMemo(() => { + const stepsArray: any[] = []; + + // Шаг 1: Policy (всегда) + stepsArray.push({ + title: 'Проверка полиса', + description: 'Полис ERV', + content: + }); + + // Шаг 2: Event Type (всегда) + stepsArray.push({ + title: 'Тип события', + description: 'Выбор случая', + content: + }); + + // Шаги 3+: Documents (динамически) + if (formData.eventType && documentConfigs.length > 0) { + documentConfigs.forEach((docConfig, index) => { + stepsArray.push({ + title: `Документ ${index + 1}`, + description: docConfig.name, + content: + }); + }); + } + + // Последний шаг: Payment (всегда) + stepsArray.push({ + title: 'Оплата', + description: 'Контакты и выплата', + content: + }); + + return stepsArray; +}, [formData, documentConfigs, isPhoneVerified, claimId, sessionId, + nextStep, prevStep, updateFormData, handleSubmit, + setIsPhoneVerified, addDebugEvent]); +``` + +#### 4.6. Прогресс-бар с описаниями +```typescript + + {steps.map((item, index) => ( + + ))} + +``` + +**Файл:** `frontend/src/pages/ClaimForm.tsx` + +--- + +### 5. Бэкап старых версий + +- `Step2Details.OLD_MANUAL_INPUT.tsx` - версия с ручным вводом полей +- `Step2Details.OLD_WIZARD_INLINE.tsx` - версия с inline загрузкой документов + +--- + +## 🐛 Исправленные проблемы + +### Проблема 1: Неправильный URL n8n webhook + +**Симптом:** +``` +POST https://n8n.clientright.ru/webhook/erv-upload +net::ERR_NAME_NOT_RESOLVED +``` + +**Причина:** Использовался несуществующий домен `n8n.clientright.ru` + +**Решение:** +```diff +- https://n8n.clientright.ru/webhook/erv-upload ++ https://n8n.clientright.pro/webhook/7e2abc64-eaca-4671-86e4-12786700fe95 +``` + +**Commit:** `4e5bc76` + +--- + +### Проблема 2: Неправильная структура FormData + +**Симптом:** n8n получал данные в неправильном формате + +**Было:** +```javascript +formDataToSend.append('files', file); // множественное число +// Нет filename и upload_timestamp +``` + +**Стало:** +```javascript +formDataToSend.append('claim_id', claimId); +formDataToSend.append('file_type', documentConfig.file_type); +formDataToSend.append('filename', file.name); // ✅ +formDataToSend.append('voucher', formData.voucher); +formDataToSend.append('session_id', sessionId); +formDataToSend.append('upload_timestamp', new Date().toISOString()); // ✅ +formDataToSend.append('file', file.originFileObj); // ✅ единственное число +``` + +**Commit:** `4ad6b78` + +--- + +### Проблема 3: Ложные ошибки SSE в консоли + +**Симптом:** +``` +❌ SSE connection error: Event {...} +``` + +**Причина:** Backend закрывает SSE после отправки результата → браузер триггерит `onerror` → выводится красная ошибка + +**Решение:** +```javascript +eventSource.onerror = (error) => { + console.log('🔌 SSE connection closed'); + + setProcessingModalContent((prev) => { + if (prev && prev !== 'loading') { + console.log('✅ SSE закрыто после получения результата - всё ОК'); + return prev; // Не затираем результат + } + console.error('❌ SSE ошибка: не получили данные', error); + return { success: false, message: 'Ошибка подключения' }; + }); +}; +``` + +**Commit:** `67f054d` + +--- + +### Проблема 4: Неправильный расчёт прогресса + +**Симптом:** "Документ 2/2" показывал "100%" ДО загрузки + +**Было:** +```javascript +percent = (currentDocNumber / totalDocs) * 100 +// Документ 2/2 = 100% (неправильно!) +``` + +**Стало:** +```javascript +percent = ((currentDocNumber - 1) / totalDocs) * 100 +// Документ 1/2: 0% (до) → 50% (после) +// Документ 2/2: 50% (до) → 100% (после) +``` + +**Commit:** `145a9bd` + +--- + +### Проблема 5: Кнопки "Назад" не кликабельны + +**Симптом:** Кнопки "Назад" серые (disabled), хотя в коде `disabled` не было + +**Решение:** Явно установил `disabled={false}` и добавил логирование: +```javascript + +``` + +**Commit:** `d727b74` + +--- + +### Проблема 6: Навигация назад не работает + +**Симптом:** Клик регистрируется в консоли, но `currentStep` не изменяется + +**Причина:** +- Функции `nextStep`, `prevStep` пересоздавались при каждом рендере +- Компоненты получали новые ссылки → ререндер +- Closure захватывал старое значение `currentStep` + +**Решение:** Обернул в `useCallback` + functional update: +```javascript +const prevStep = useCallback(() => { + console.log('⏪ prevStep called'); + setCurrentStep((prev) => { + console.log('📍 Current step:', prev, '→ Prev:', prev - 1); + return prev - 1; // Functional update! + }); +}, []); +``` + +**Commit:** `9f39847` + +--- + +## 📦 Git История + +```bash +# Commit history (от старого к новому) +6fe1459 - backup: Сохранён старый Step2Details с ручным вводом полей +122af07 - feat: Умная форма Step2 с автоматическим распознаванием документов +9084d75 - feat: Пошаговая загрузка документов с модалкой на Step 2 +2999951 - fix: Удалён дублирующийся код в Step1Policy.tsx +1207222 - fix: Удалён дублирующийся код в Step3Payment.tsx +6c19392 - docs: Обновлён лог сессии - добавлена вторая часть (умная форма Step 2) + +# Сессия 29 октября (рефакторинг на динамические шаги) +1f25301 - feat: Переделан визард на динамические шаги - каждый документ отдельный Step +f06105d - fix: Исправлена работа Upload и кнопки Назад в StepDocumentUpload +4e5bc76 - fix: Исправлен URL n8n webhook на правильный домен +4ad6b78 - fix: Исправлена структура FormData для загрузки документов +67f054d - fix: Улучшено логирование SSE - убраны ложные ошибки +145a9bd - fix: Исправлен расчёт прогресса загрузки документов +d727b74 - fix: Явно установлен disabled=false для всех кнопок Назад +9f39847 - fix: Исправлена навигация назад через useCallback +``` + +**Push:** ✅ `origin/main` (все коммиты) + +--- + +## 🎨 Примеры визуализации + +### Пример 1: Отмена рейса (2 документа) +``` +Шаг 1: Проверка полиса + └─ Полис ERV + +Шаг 2: Тип события + └─ ✈️❌ Отмена авиарейса + +Шаг 3: Документ 1 (0% → 50%) + └─ Билет + └─ Upload → n8n → AI → SSE → Модалка с результатами + +Шаг 4: Документ 2 (50% → 100%) + └─ Уведомление об отмене + └─ Upload → n8n → AI → SSE → Модалка с результатами + +Шаг 5: Оплата + └─ Контакты и выплата +``` + +### Пример 2: Пропуск стыковки (3 документа, 1 опциональный) +``` +[1.Полис] → [2.Тип] → [3.Посадочный талон прибытия] → +[4.Билет отправления] → [5.Доказательство задержки (опционально)] → +[6.Оплата] +``` + +--- + +## 🔧 Технические детали + +### Data Flow для одного документа + +``` +Frontend (StepDocumentUpload) +│ +├─ User selects file +│ └─ Upload component → setFileList([file]) +│ +├─ User clicks "Загрузить и обработать" +│ └─ handleUpload() called +│ +├─ FormData creation +│ ├─ claim_id +│ ├─ file_type (уникальный для каждого документа) +│ ├─ filename +│ ├─ voucher +│ ├─ session_id +│ ├─ upload_timestamp +│ └─ file (originFileObj) +│ +├─ POST to n8n webhook +│ └─ https://n8n.clientright.pro/webhook/7e2abc64-... +│ +├─ SSE connection opens +│ └─ GET /events/{claim_id}?event_type={file_type}_processed +│ +├─ Show modal "Обрабатываем документ..." +│ └─ Spin + "Извлекаем данные с помощью AI" +│ +│ [n8n workflow] +│ ├─ Upload to S3 +│ ├─ PostgreSQL UPSERT (claims + claim_files) +│ ├─ OCR Service (147.45.146.17:8001) +│ ├─ AI Vision (OpenRouter Gemini 2.0 Flash) +│ └─ Redis PUBLISH to ocr_events:{claim_id} +│ +├─ Backend receives Redis message +│ └─ SSE sends event to frontend +│ +├─ Frontend receives SSE message +│ └─ eventSource.onmessage +│ └─ setProcessingModalContent(result.data) +│ +├─ Modal shows results +│ ├─ ✅ Документ обработан +│ ├─ JSON with extracted data +│ └─ Button: "Продолжить к следующему документу →" +│ +├─ User clicks "Продолжить" +│ └─ handleContinue() +│ ├─ setProcessingModalVisible(false) +│ ├─ setUploading(false) +│ ├─ eventSource.close() +│ └─ onNext() → nextStep() → setCurrentStep(prev => prev + 1) +│ +└─ Next document step renders (or Payment if last) +``` + +### Уникальные file_type для n8n + +| Событие | Документ | file_type | event_type | +|---------|----------|-----------|------------| +| Задержка рейса | Талон/билет | `flight_delay_boarding_or_ticket` | `flight_delay_boarding_or_ticket_processed` | +| Задержка рейса | Подтверждение | `flight_delay_confirmation` | `flight_delay_confirmation_processed` | +| Отмена рейса | Билет | `flight_cancel_ticket` | `flight_cancel_ticket_processed` | +| Отмена рейса | Уведомление | `flight_cancel_notice` | `flight_cancel_notice_processed` | +| Пропуск стыковки | Талон прибытия | `connection_arrival_boarding` | `connection_arrival_boarding_processed` | +| Пропуск стыковки | Талон/билет отправления | `connection_departure_boarding_or_ticket` | `connection_departure_boarding_or_ticket_processed` | +| Пропуск стыковки | Доказательство задержки | `connection_delay_proof` | `connection_delay_proof_processed` | + +**Формула:** `event_type = file_type + "_processed"` + +--- + +## 📊 Метрики + +**Время выполнения сессии:** ~3 часа +**Количество коммитов:** 9 +**Созданных файлов:** 3 +- `Step2EventType.tsx` +- `StepDocumentUpload.tsx` +- `constants/documentConfigs.ts` + +**Изменённых файлов:** 2 +- `ClaimForm.tsx` (полная переделка логики) +- `StepDocumentUpload.tsx` (множество фиксов) + +**Строк добавлено:** ~1500 +**Строк удалено:** ~50 +**Frontend rebuilds:** 9 +**Тестовых загрузок:** 5 + +--- + +## 🔗 Ссылки + +- **Frontend:** http://147.45.146.17:5173 +- **Backend API:** http://localhost:8100 +- **Gitea:** http://147.45.146.17:3002/negodiy/erv-platform +- **n8n Production:** https://n8n.clientright.pro +- **n8n Dev:** http://147.45.146.17:5678 +- **n8n Webhook:** https://n8n.clientright.pro/webhook/7e2abc64-eaca-4671-86e4-12786700fe95 + +--- + +## 📝 Важные заметки + +### Redis Configuration +``` +Host: crm.clientright.ru +Port: 6379 +Password: CRM_Redis_Pass_2025_Secure! +Channel pattern: ocr_events:{claim_id} +``` + +### DEV MODE во всех шагах +Для ускорения разработки и тестирования добавлены кнопки быстрой навигации: +- **Step 1:** "Далее → (Step 2) [пропустить]" +- **Step 2:** "Далее → [Отмена рейса]" +- **Step 3+:** "Пропустить [dev] →" +- **Step Payment:** "✅ Автоподтверждение телефона [dev]", "🚀 Отправить [пропустить]" + +### Ant Design Warnings (не критично) +В консоли показываются deprecation warnings: +- `headStyle` → `styles.header` +- `bodyStyle` → `styles.body` +- `Timeline.Item` → `items` + +Эти warning в `DebugPanel.tsx` - не влияют на работу, можно исправить позже. + +--- + +## ✅ Итоговый результат + +### Что работает: +1. ✅ Динамические шаги на основе выбранного `eventType` +2. ✅ Каждый документ загружается на отдельном шаге +3. ✅ Прогресс-бар показывает все шаги с описаниями +4. ✅ Upload → n8n → S3 → PostgreSQL → OCR → AI → Redis → SSE +5. ✅ Модалка показывает процесс обработки и результаты +6. ✅ Навигация вперёд/назад работает корректно +7. ✅ DEV MODE кнопки на всех шагах +8. ✅ Логирование в консоль для отладки + +### Архитектура: +``` +ClaimForm (главный компонент) +├─ useMemo для динамической генерации steps +├─ useCallback для стабильных функций навигации +│ +├─ Step 1: Step1Policy +│ └─ Загрузка и OCR полиса +│ +├─ Step 2: Step2EventType +│ └─ Выбор типа события +│ +├─ Steps 3...N-1: StepDocumentUpload (динамически) +│ └─ Для каждого документа из DOCUMENT_CONFIGS +│ ├─ Прогресс: "Документ X из Y" +│ ├─ Upload компонент +│ ├─ POST to n8n → S3 → DB → OCR → AI +│ ├─ SSE для получения результата +│ └─ Модалка с извлечёнными данными +│ +└─ Step N: Step3Payment + └─ Контакты и выплата +``` + +--- + +**Статус:** ✅ Успешно завершено +**Автор:** AI Assistant (Claude Sonnet 4.5) +**Дата:** 29 октября 2025, 15:00 MSK +