From a26cb772f574e999f867030e8f0ac7d5ec319a88 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Sat, 25 Oct 2025 10:12:41 +0300 Subject: [PATCH] =?UTF-8?q?fix:=203=20=D0=BA=D1=80=D0=B8=D1=82=D0=B8=D1=87?= =?UTF-8?q?=D0=B5=D1=81=D0=BA=D0=B8=D1=85=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20-=20OCR=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B3=D1=80=D0=B5=D1=81=D1=81,=20=D1=83=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=B2=D0=BD=D1=8B=D0=B5=20=D0=BF=D0=BE=D0=BB=D1=8F,=20?= =?UTF-8?q?=D1=83=D0=B1=D1=80=D0=B0=D0=BD=20=D0=BD=D0=B5=D0=BA=D0=BE=D1=80?= =?UTF-8?q?=D1=80=D0=B5=D0=BA=D1=82=D0=BD=D1=8B=D0=B9=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=82=D1=83=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ✅ OCR Progress Bar: - Добавлен polling OCR результатов каждые 3 сек - Визуальный индикатор: 🔍 Обработка OCR... (1/10) - Progress bar с анимацией - Статусы: 🔄 Запуск → 🔍 Обработка → ✅ Завершен - Gemini Vision результаты в Debug панели 2. ✅ Убран некорректный 'Полис найден': - Было: показывался сразу после загрузки файла - Проблема: OCR еще не закончился, может быть шляпа - Решение: убрана зеленая плашка с Step2 - Статус полиса только после реальной проверки 3. ✅ Условные поля для стыковочного рейса: - Если выбран 'miss_connection' → показываются 4 доп поля: • Номер рейса прибытия • Дата рейса прибытия • Номер рейса отправления • Дата рейса отправления - Если выбран 'cancel_flight' → доп поле: • Подтверждение отмены от АК - Для обычных рейсов: только номер рейса Frontend изменения: - Step1Policy: OCR polling, progress bar - Step2Details: условная логика полей (как в erv_ticket) - useState для eventType - handleEventTypeChange для динамики Теперь: ✅ Видно прогресс OCR ✅ Видно результаты Gemini Vision ✅ Условные поля работают ✅ Нет ложных статусов --- frontend/src/components/form/Step1Policy.tsx | 116 +++++++++++---- frontend/src/components/form/Step2Details.tsx | 138 ++++++++++++++---- 2 files changed, 202 insertions(+), 52 deletions(-) diff --git a/frontend/src/components/form/Step1Policy.tsx b/frontend/src/components/form/Step1Policy.tsx index 22a75e8..6192ca7 100644 --- a/frontend/src/components/form/Step1Policy.tsx +++ b/frontend/src/components/form/Step1Policy.tsx @@ -1,6 +1,6 @@ import { useState } from 'react'; -import { Form, Input, Button, message, Upload } from 'antd'; -import { FileProtectOutlined, UploadOutlined } from '@ant-design/icons'; +import { Form, Input, Button, message, Upload, Progress } from 'antd'; +import { FileProtectOutlined, UploadOutlined, LoadingOutlined } from '@ant-design/icons'; import type { UploadFile } from 'antd/es/upload/interface'; interface Props { @@ -56,6 +56,7 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug const [policyNotFound, setPolicyNotFound] = useState(false); const [fileList, setFileList] = useState([]); const [uploading, setUploading] = useState(false); + const [ocrProgress, setOcrProgress] = useState(''); // Обработчик изменения поля полиса с автозаменой и маской const handleVoucherChange = (e: React.ChangeEvent) => { @@ -130,6 +131,60 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug setFileList(newFileList); }; + // Polling для получения OCR результатов + const pollOcrResults = async (fileIds: string[]) => { + if (fileIds.length === 0) return; + + const maxAttempts = 10; + const interval = 3000; // 3 секунды + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + await new Promise(resolve => setTimeout(resolve, interval)); + + setOcrProgress(`🔍 Обработка OCR... (${attempt + 1}/${maxAttempts})`); + + for (const fileId of fileIds) { + try { + const response = await fetch(`http://147.45.146.17:8100/api/v1/upload/ocr-result/${fileId}`); + const result = await response.json(); + + if (result.found && result.ocr_result) { + const ocr = result.ocr_result; + + addDebugEvent?.('ocr', 'success', `📄 OCR завершен: ${ocr.ocr_text?.length || 0} символов`, { + text: ocr.ocr_text?.substring(0, 300) + }); + + if (ocr.ai_analysis || ocr.document_type) { + const isGarbage = ocr.document_type === 'garbage'; + + addDebugEvent?.( + 'ai_analysis', + isGarbage ? 'warning' : 'success', + isGarbage + ? `🗑️ ШЛЯПА DETECTED! (пользователю не говорим)` + : `🤖 Gemini Vision: ${ocr.document_type}, confidence: ${(ocr.confidence * 100).toFixed(0)}%`, + { + document_type: ocr.document_type, + is_valid: ocr.is_valid, + confidence: ocr.confidence, + extracted_data: ocr.extracted_data + } + ); + + setOcrProgress(`✅ OCR завершен: ${ocr.document_type}`); + return; // Готово + } + } + } catch (error) { + console.error('OCR polling error:', error); + } + } + } + + setOcrProgress('⏱️ OCR обрабатывается в фоне...'); + }; + const handleSubmitWithScan = async () => { if (fileList.length === 0) { message.error('Загрузите скан полиса'); @@ -173,6 +228,10 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug // Проверяем OCR результаты if (uploadResult.files && uploadResult.files.length > 0) { + const fileIds = uploadResult.files + .filter((f: any) => f.file_id) + .map((f: any) => f.file_id); + const firstFile = uploadResult.files[0]; addDebugEvent?.('ocr', 'pending', `🔍 Запущен OCR для: ${firstFile.filename}`, { @@ -180,32 +239,10 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug filename: firstFile.filename }); - // Если есть OCR результат - if (firstFile.ocr_result) { - const ocr = firstFile.ocr_result; - - addDebugEvent?.('ocr', 'success', `📄 OCR завершен: ${ocr.ocr_text?.length || 0} символов`, { - text: ocr.ocr_text?.substring(0, 300) - }); + setOcrProgress('🔄 Запуск OCR...'); - if (ocr.ai_analysis) { - const isGarbage = ocr.document_type === 'garbage'; - - addDebugEvent?.( - 'ai_analysis', - isGarbage ? 'warning' : 'success', - isGarbage - ? `🗑️ ШЛЯПА DETECTED! (пользователю не говорим)` - : `🤖 AI: ${ocr.document_type}, confidence: ${(ocr.confidence * 100).toFixed(0)}%`, - { - document_type: ocr.document_type, - is_valid: ocr.is_valid, - confidence: ocr.confidence, - extracted_data: ocr.extracted_data - } - ); - } - } + // Запускаем polling в фоне (не блокируем переход) + pollOcrResults(fileIds); } updateFormData({ @@ -327,6 +364,31 @@ export default function Step1Policy({ formData, updateFormData, onNext, addDebug + {/* OCR Progress */} + {ocrProgress && ( +
+
+ {ocrProgress.includes('🔍') || ocrProgress.includes('🔄') ? ( + + ) : null} + {ocrProgress} +
+ {ocrProgress.includes('Обработка') && ( + + )} +
+ )} +
+ + + )}