# 🏗️ ERV Insurance Platform - Архитектура проекта **Дата создания**: 24.10.2025 **Технологии**: Python FastAPI + React TypeScript **Статус**: 🚧 В разработке --- ## 📊 Обзор архитектуры ### **Технологический стек:** ``` ┌─────────────────────────────────────────────────────────────┐ │ Frontend Layer (React) │ │ ├─ React 18 + TypeScript │ │ ├─ Vite (сборка) │ │ ├─ Ant Design (UI компоненты) │ │ ├─ React Query (API кеш) │ │ ├─ Zustand (state) │ │ └─ WebSocket для real-time │ └─────────────────────────────────────────────────────────────┘ │ REST API + WebSocket ▼ ┌─────────────────────────────────────────────────────────────┐ │ Backend Layer (Python FastAPI) │ │ ├─ FastAPI (асинхронный web framework) │ │ ├─ Pydantic (валидация данных) │ │ ├─ SQLAlchemy (PostgreSQL ORM) │ │ ├─ Redis (кеш, rate limiting) │ │ ├─ RabbitMQ (очереди задач) │ │ ├─ Celery (воркеры) │ │ └─ pytest (тесты) │ └─────────────────────────────────────────────────────────────┘ │ ├─► PostgreSQL (147.45.189.234) - логи, данные ├─► Redis (localhost:6379) - кеш ├─► RabbitMQ (185.197.75.249) - очереди ├─► MySQL (localhost) - проверка полисов ├─► OCR Service (147.45.146.17:8001) ├─► OpenRouter AI (Gemini Vision) ├─► FlightAware API └─► S3 Timeweb Cloud ``` --- ## 📁 Структура проекта ``` erv_platform/ │ ├─ backend/ ← Python FastAPI │ ├─ app/ │ │ ├─ main.py ← Точка входа FastAPI │ │ ├─ config.py ← Настройки из .env │ │ │ │ │ ├─ api/ ← API endpoints │ │ │ ├─ __init__.py │ │ │ ├─ deps.py ← Зависимости (auth, db) │ │ │ └─ endpoints/ │ │ │ ├─ documents.py ← OCR, Vision │ │ │ ├─ flights.py ← Проверка рейсов │ │ │ ├─ claims.py ← Создание обращений │ │ │ ├─ policies.py ← Проверка полисов │ │ │ └─ analytics.py ← Статистика │ │ │ │ │ ├─ services/ ← Бизнес-логика │ │ │ ├─ ocr_service.py │ │ │ ├─ ai_service.py ← OpenRouter Vision │ │ │ ├─ flight_service.py ← FlightAware │ │ │ ├─ s3_service.py ← Timeweb S3 │ │ │ ├─ crm_service.py ← Интеграция с Vtiger │ │ │ ├─ cache_service.py ← Redis │ │ │ └─ queue_service.py ← RabbitMQ │ │ │ │ │ ├─ models/ ← Pydantic модели │ │ │ ├─ claim.py ← Обращение │ │ │ ├─ document.py ← Документ │ │ │ ├─ passport.py ← Паспорт │ │ │ ├─ ticket.py ← Билет │ │ │ └─ flight.py ← Рейс │ │ │ │ │ ├─ db/ ← База данных │ │ │ ├─ postgres.py ← PostgreSQL session │ │ │ ├─ mysql.py ← MySQL session (полисы) │ │ │ └─ models.py ← SQLAlchemy модели │ │ │ │ │ ├─ workers/ ← RabbitMQ воркеры │ │ │ ├─ ocr_worker.py │ │ │ ├─ flight_worker.py │ │ │ └─ notification_worker.py │ │ │ │ │ ├─ core/ ← Утилиты │ │ │ ├─ security.py ← Rate limiting │ │ │ ├─ logger.py ← Логирование │ │ │ └─ exceptions.py │ │ │ │ │ └─ tests/ ← Тесты │ │ ├─ test_api.py │ │ └─ test_services.py │ │ │ ├─ migrations/ ← SQL миграции │ │ ├─ 001_create_logs.sql │ │ ├─ 002_create_documents.sql │ │ └─ 003_create_claims.sql │ │ │ ├─ requirements.txt ← Python зависимости │ ├─ .env.example │ ├─ Dockerfile │ └─ pyproject.toml │ ├─ frontend/ ← React TypeScript │ ├─ src/ │ │ ├─ App.tsx ← Главный компонент │ │ ├─ main.tsx ← Точка входа │ │ │ │ │ ├─ components/ ← UI компоненты │ │ │ ├─ layout/ │ │ │ │ ├─ Header.tsx │ │ │ │ └─ Footer.tsx │ │ │ ├─ form/ │ │ │ │ ├─ FormWizard.tsx ← Многошаговая форма │ │ │ │ ├─ StepPersonal.tsx ← Шаг 1 │ │ │ │ ├─ StepFlight.tsx ← Шаг 2 │ │ │ │ └─ StepDocuments.tsx ← Шаг 3 │ │ │ ├─ upload/ │ │ │ │ ├─ PassportUpload.tsx ← Загрузка паспорта + OCR │ │ │ │ ├─ TicketUpload.tsx ← Загрузка билета + OCR │ │ │ │ └─ DocumentUpload.tsx ← Общий компонент │ │ │ ├─ flight/ │ │ │ │ ├─ FlightChecker.tsx ← Проверка рейса │ │ │ │ └─ FlightStatus.tsx ← Показ статуса │ │ │ └─ common/ │ │ │ ├─ Loading.tsx │ │ │ ├─ ErrorBoundary.tsx │ │ │ └─ Notification.tsx │ │ │ │ │ ├─ pages/ ← Страницы │ │ │ ├─ ClaimForm.tsx ← Главная форма │ │ │ ├─ Success.tsx ← Успех │ │ │ └─ Error.tsx ← Ошибка │ │ │ │ │ ├─ api/ ← API клиент │ │ │ ├─ client.ts ← Axios instance │ │ │ ├─ documents.ts ← API документов │ │ │ ├─ flights.ts ← API рейсов │ │ │ └─ claims.ts ← API обращений │ │ │ │ │ ├─ hooks/ ← React hooks │ │ │ ├─ useOCR.ts │ │ │ ├─ useFlightCheck.ts │ │ │ └─ useWebSocket.ts │ │ │ │ │ ├─ types/ ← TypeScript типы │ │ │ ├─ claim.ts │ │ │ ├─ document.ts │ │ │ └─ flight.ts │ │ │ │ │ └─ utils/ ← Утилиты │ │ ├─ validators.ts │ │ └─ formatters.ts │ │ │ ├─ public/ │ │ ├─ index.html │ │ └─ favicon.ico │ │ │ ├─ package.json │ ├─ tsconfig.json │ ├─ vite.config.ts │ └─ .env.example │ ├─ docker/ │ ├─ Dockerfile.backend │ ├─ Dockerfile.frontend │ ├─ docker-compose.dev.yml │ ├─ docker-compose.prod.yml │ └─ nginx.conf │ ├─ docs/ │ ├─ API.md ← API документация │ ├─ DEPLOYMENT.md ← Деплой │ └─ ARCHITECTURE.md ← Этот файл │ ├─ .gitignore ├─ README.md └─ docker-compose.yml ← Главный compose ``` --- ## 🔄 Поток данных (детально): ### **1. Загрузка паспорта с OCR:** ``` [React] Пользователь загружает passport.jpg ↓ [React] PassportUpload.tsx ↓ FormData [FastAPI] POST /api/v1/documents/scan ↓ [FastAPI] DocumentService ├─► S3Service.upload() → s3_url ├─► RabbitMQ.publish('ocr_queue', {s3_url, type: 'passport'}) └─► return {"job_id": "abc-123", "status": "processing"} ↓ [React] Показывает: "⏳ Распознаём паспорт..." [React] WebSocket подключение: ws://api/ws/jobs/abc-123 ↓ [Worker] OCR Worker получает из RabbitMQ ├─► OCRService.analyze(s3_url) → текст (3 сек) ├─► AIService.extract_passport(text) → JSON (2 сек) ├─► PostgreSQL.log(processing_result) └─► WebSocket.send_to_client(session_id, result) ↓ [React] WebSocket получает результат ↓ [React] Автозаполняет поля: form.setFieldsValue({ lastname: "Иванов", firstname: "Иван", ... }) ↓ [React] Показывает: "✅ Паспорт распознан! Проверьте данные." ``` **Время**: ~5 секунд, но пользователь видит прогресс! --- ### **2. Проверка рейса:** ``` [React] Пользователь ввёл "SU1234" и дату ↓ [React] FlightChecker.tsx ↓ debounce 500ms (не дёргаем API на каждый символ) [FastAPI] GET /api/v1/flights/check?number=SU1234&date=2025-10-24 ↓ [FastAPI] FlightService ├─► Redis.get('flight:SU1234:2025-10-24') │ └─► Если есть → возврат из кеша (0.001 сек) │ └─► Если нет: ├─► FlightAwareAPI.check() (1-2 сек) ├─► Redis.set('flight:...', result, ttl=3600) ← Кеш на час └─► return result ↓ [React] Показывает красивую карточку: ┌─────────────────────────────────────┐ │ ✈️ Рейс SU1234 │ │ Moscow (SVO) → Ufa (UFA) │ │ Статус: ⚠️ Задержка 45 минут │ │ Вылет: 09:25 (план: 08:40) │ └─────────────────────────────────────┘ ``` --- ### **3. Отправка обращения:** ``` [React] Пользователь заполнил всё, жмёт "Подать обращение" ↓ [FastAPI] POST /api/v1/claims/submit { client: {lastname, firstname, ...}, flight: {number, date, type: "delay_flight"}, documents: [s3_urls], sbp_phone: "+79991234567" } ↓ [FastAPI] ClaimService ├─► Валидация (Pydantic) ├─► PostgreSQL: сохранение claim (для аналитики) ├─► Формирование payload для CRM │ { │ "client": {...}, │ "contractor": {...}, ← Из конфига │ "project": {...}, │ "ticket": {...} │ } ├─► POST → https://form.clientright.ru/server_webservice2.php │ └─► PHP Bridge → Vtiger Webservices (PHP 7.3) │ └─► CRM создаёт тикет │ ├─► RabbitMQ.publish('notifications', {...}) ← Email в фоне └─► return {"success": true, "ticket_id": "TT12345"} ↓ [React] Редирект на Success.tsx "✅ Ваше обращение #TT12345 принято!" ``` --- ## 🔌 Интеграции (детально): ### **OCR + Vision Pipeline:** ```python # services/document_processor.py async def process_passport(file_url: str) -> PassportData: # 1. OCR (ваш сервис) ocr_response = await ocr_service.analyze( file_url=file_url, file_type="auto" # Автоопределение ) ocr_text = ocr_response['pages_data'][0]['ocr_text'] # 2. Vision AI (OpenRouter Gemini) vision_prompt = """ Извлеки из текста паспорта РФ следующие данные в формате JSON: { "surname": "...", "name": "...", "patronymic": "...", "birthdate": "DD.MM.YYYY", "passport_series": "XXXX", "passport_number": "XXXXXX" } Текст паспорта: {ocr_text} """ vision_result = await ai_service.extract_structured_data( prompt=vision_prompt.format(ocr_text=ocr_text) ) # 3. Валидация через Pydantic passport = PassportData(**vision_result) # 4. Логирование в PostgreSQL await db.execute(""" INSERT INTO document_processing (file_url, document_type, ocr_text, vision_result, processing_time_ms) VALUES ($1, $2, $3, $4, $5) """, file_url, 'passport', ocr_text, vision_result, elapsed_ms) return passport ``` --- ## 📡 API Спецификация: ### **Базовый URL:** ``` DEV: http://localhost:8000/api/v1 PROD: https://api.erv-claims.clientright.ru/api/v1 ``` ### **Endpoints:** | Method | Endpoint | Описание | Время | |--------|----------|----------|-------| | POST | `/documents/upload` | Загрузка в S3 | ~500ms | | POST | `/documents/scan` | OCR + Vision | ~5s (async) | | GET | `/flights/check` | Проверка рейса | ~200ms (cache) | | GET | `/policies/verify` | Проверка полиса | ~5ms (cache) | | POST | `/claims/submit` | Создание обращения | ~1s | | WS | `/ws/jobs/{job_id}` | Real-time статусы | - | ### **Пример: Scan Passport** **Request:** ```http POST /api/v1/documents/scan Content-Type: application/json { "file_url": "https://s3.twcstorage.ru/.../passport.jpg", "document_type": "passport" } ``` **Response (сразу):** ```json { "job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "processing", "estimated_time_seconds": 5 } ``` **WebSocket update (через 5 сек):** ```json { "job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "completed", "data": { "surname": "Иванов", "name": "Иван", "patronymic": "Иванович", "birthdate": "01.01.1990", "passport_series": "4510", "passport_number": "123456" } } ``` --- ## 🗄️ Структура баз данных: ### **PostgreSQL (новые данные):** ```sql -- Обращения (дубликат для аналитики) CREATE TABLE claims ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), session_id VARCHAR(100), insurance_type VARCHAR(50), -- erv_flight, erv_train, etc. -- Данные клиента (JSONB) client_data JSONB, -- Данные события (JSONB) event_data JSONB, -- Документы documents JSONB, -- [{type, s3_url, ocr_result, vision_result}] -- Статус status VARCHAR(20), -- processing, submitted, completed, error crm_ticket_id VARCHAR(50), -- Метаданные ip_address INET, user_agent TEXT, created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_claims_status ON claims(status); CREATE INDEX idx_claims_created ON claims(created_at); CREATE INDEX idx_claims_crm_ticket ON claims(crm_ticket_id); -- Обработка документов CREATE TABLE document_processing ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), claim_id UUID REFERENCES claims(id), file_url TEXT, s3_url TEXT, document_type VARCHAR(50), -- passport, ticket, etc. -- OCR результат ocr_text TEXT, ocr_confidence NUMERIC, -- Vision результат vision_result JSONB, -- Метрики processing_time_ms INTEGER, ocr_time_ms INTEGER, vision_time_ms INTEGER, created_at TIMESTAMP DEFAULT NOW() ); -- Логи CREATE TABLE logs ( id BIGSERIAL PRIMARY KEY, level VARCHAR(10), -- INFO, WARNING, ERROR logger VARCHAR(50), -- ocr_service, flight_service, etc. message TEXT, context JSONB, session_id VARCHAR(100), ip_address INET, created_at TIMESTAMP DEFAULT NOW() ); CREATE INDEX idx_logs_level ON logs(level); CREATE INDEX idx_logs_created ON logs(created_at DESC); CREATE INDEX idx_logs_session ON logs(session_id); -- Кеш API (fallback) CREATE TABLE api_cache ( cache_key VARCHAR(255) PRIMARY KEY, cache_value JSONB, expires_at TIMESTAMP, created_at TIMESTAMP DEFAULT NOW() ); ``` ### **MySQL (существующая CRM база):** ```sql -- НЕ трогаем! Только читаем ci20465_erv.lexrpiority ├─ voucher ├─ insured_from └─ insured_to ``` --- ## 🚀 Deployment стратегия: ### **Development (сейчас):** ```bash # 1. Backend cd backend python -m venv venv source venv/bin/activate pip install -r requirements.txt uvicorn app.main:app --reload --port 8000 # 2. Frontend cd frontend npm install npm run dev # Vite dev server на :5173 # 3. Доступ: Frontend: http://localhost:5173 Backend: http://localhost:8000 API Docs: http://localhost:8000/docs ← Swagger UI! ``` ### **Production (потом):** ```bash # Вариант 1: Тот же сервер, другой домен cd /var/www/erv-claims.clientright.ru/ git clone http://147.45.146.17:3002/fedya/erv-platform.git . docker-compose -f docker/docker-compose.prod.yml up -d # Nginx конфиг: server { server_name erv-claims.clientright.ru; # Frontend (статика) location / { root /var/www/erv-claims/frontend/dist; try_files $uri $uri/ /index.html; } # Backend API location /api/ { proxy_pass http://localhost:8000; } # WebSocket location /ws/ { proxy_pass http://localhost:8000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } } ``` --- ## 🎯 Что делаю СЕЙЧАС (следующие 2-3 часа): ### **Создаю базу:** 1. ✅ Структура проекта (сделано) 2. ✅ requirements.txt (Python зависимости) 3. ✅ package.json (React зависимости) 4. ✅ .env.example 5. ✅ Базовый FastAPI app 6. ✅ Базовый React app 7. ✅ SQL миграции для PostgreSQL 8. ✅ Docker compose для dev 9. ✅ README с инструкциями **Время**: ~1 час на базовую настройку --- ## 💪 Готов? Сейчас создам всю базовую структуру и запущу оба приложения (FastAPI + React). **Начинаю прямо сейчас!** 🚀