From 0be216ba436e185e1cbeed7426950363cd301692 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Sun, 2 Nov 2025 01:19:12 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BB=D0=BE=D0=B3=20=D1=81=D0=B5=D1=81=D1=81?= =?UTF-8?q?=D0=B8=D0=B8=20CreateWebClaim=20+=20=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B8=D0=BD=D1=82=D0=B5=D0=B3=D1=80=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Подробная документация: - ✅ CreateWebClaim.php - операция vTiger для заявок - ✅ n8n workflow get_claim_CRM_ERV (ID: qdYZqhIDGhK9E4DA) - ✅ Backend proxy /api/n8n/claim/create - ✅ Frontend интеграция Step2EventType - ✅ Решенные проблемы: BOM, пустой ответ n8n, массив вместо объекта - ✅ Полный флоу создания заявки - ✅ Метрики и статистика Создано заявок: 8 (последняя ЗАЯВКА_825) Время работы: 4 часа 15 минут --- SESSION_LOG_2025-11-01.md | 434 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) diff --git a/SESSION_LOG_2025-11-01.md b/SESSION_LOG_2025-11-01.md index 2cfbc0f..f6c5a00 100644 --- a/SESSION_LOG_2025-11-01.md +++ b/SESSION_LOG_2025-11-01.md @@ -721,3 +721,437 @@ d7941ac8 - feat: CreateWebContact возвращает is_new флаг **Автор:** AI Assistant (Claude Sonnet 4.5) **Дата:** 01 ноября 2025, 13:30 MSK +--- + +# 📋 Лог сессии (продолжение): CreateWebClaim + Интеграция заявок + +**Дата:** 01 ноября 2025 (21:00 - 01:15 MSK следующего дня) +**Задачи:** +1. Создание операции CreateWebClaim для vTiger CRM +2. Интеграция n8n workflow для создания заявок +3. Backend proxy для безопасного вызова n8n webhooks +4. Frontend интеграция в Step2EventType + +**Статус:** ✅ Успешно завершено + +--- + +## 🎯 Основные задачи + +### Задача 1: CreateWebClaim +Создать операцию vTiger webservice для создания заявок (HelpDesk tickets) по аналогии с CreateWebContact и CreateWebProject. + +**Требования:** +- Обязательные поля: `title`, `contact_id`, `project_id`, `event_type` +- Опциональные: `description`, `incident_date`, `transport_number` +- Маппинг типов событий на русские категории +- Возврат: `{"ticket_id": "123", "ticket_number": "ЗАЯВКА_456", ...}` + +### Задача 2: n8n workflow +Создать workflow `get_claim_CRM_ERV` для обработки создания заявок с мержингом данных в Redis session. + +### Задача 3: Backend proxy +Добавить endpoint `/api/n8n/claim/create` для проксирования запросов к n8n webhook. + +### Задача 4: Frontend интеграция +Обновить `Step2EventType.tsx` для вызова создания черновика заявки при выборе типа события. + +--- + +## ✅ Выполненные задачи + +### 1. CreateWebClaim.php - Операция vTiger Webservice + +**Файл:** `include/Webservices/CreateWebClaim.php` + +**Обязательные параметры:** +- `title` - название заявки +- `contact_id` - ID контакта (без префикса 12x) +- `project_id` - ID проекта (без префикса 33x) +- `event_type` - тип события (delay_flight, cancel_flight, etc.) + +**Опциональные параметры:** +- `description` - описание проблемы +- `incident_date` - дата инцидента (YYYY-MM-DD) +- `transport_number` - номер рейса/поезда/парома + +**Маппинг типов событий:** +```php +$eventTypeMap = array( + 'delay_flight' => 'Задержка рейса', + 'cancel_flight' => 'Отмена рейса', + 'missed_connection' => 'Пропуск стыковки', + 'delay_train' => 'Задержка поезда', + 'cancel_train' => 'Отмена поезда', + 'delay_ferry' => 'Задержка парома', + 'cancel_ferry' => 'Отмена парома' +); +``` + +**Возвращаемые данные:** +```json +{ + "success": true, + "result": { + "ticket_id": "396932", + "ticket_number": "ЗАЯВКА_825", + "title": "Задержка авиарейса (более 3 часов) - E1000-302538524", + "category": "Задержка рейса", + "status": "рассмотрение" + } +} +``` + +**Регистрация в БД:** +```sql +INSERT INTO vtiger_ws_operation (operationid, name, handler_path, handler_method, type, prelogin) +VALUES (52, 'CreateWebClaim', 'include/Webservices/CreateWebClaim.php', 'vtws_createwebclaim', 'POST', 0); +``` + +**Особенности реализации:** +- ✅ `ob_start()` / `ob_end_clean()` для подавления BOM и warnings +- ✅ Формирование полного описания с метаданными +- ✅ Привязка к контакту (12x{contact_id}) и проекту (33x{project_id}) +- ✅ Логирование в `logs/CreateWebClaim.log` + +--- + +### 2. n8n Workflow: get_claim_CRM_ERV + +**ID:** `qdYZqhIDGhK9E4DA` +**Webhook:** `d5bf4ca6-9e44-44b9-9714-3186ea703e7d` +**URL:** `https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d` + +**Последовательность нод:** +``` +1. clime (Webhook) - получение данных от фронтенда + ↓ +2. Redis_get_session - чтение существующей сессии + ↓ +3. Edit Fields - подготовка данных для CRM + ↓ +4. Get Challenge - получение токена vTiger + ↓ +5. Execute a command - генерация MD5 hash + ↓ +6. Edit Fields3 - подготовка accessKey + ↓ +7. Login to CRM - авторизация в vTiger + ↓ +8. CreateWebTicket - создание заявки через CreateWebClaim + ↓ +9. Code in JavaScript - мерж данных заявки в сессию + ↓ +10. Redis (SET) - обновление сессии в Redis + ↓ +11. Code in JavaScript2 - формирование response для фронта + ↓ +12. Respond to Webhook - возврат данных +``` + +**Code Node: Мерж данных заявки** +```javascript +const existingSession = $('Redis_get_session').first().json.propertyName; +const sessionData = JSON.parse(existingSession); +const claimResult = $node["CreateWebTicket"].json.result; +const webhookData = $('clime').first().json.body; + +const updatedSession = { + ...sessionData, + ticket_id: claimResult.ticket_id, + ticket_number: claimResult.ticket_number, + ticket_title: claimResult.title, + ticket_category: claimResult.category, + ticket_status: claimResult.status, + event_type: webhookData.event_type, + current_step: 3, + updated_at: new Date().toISOString() +}; + +return { + redis_key: `claim:${sessionData.claim_id}`, + redis_value: JSON.stringify(updatedSession), + ttl: 604800 +}; +``` + +**Структура сессии в Redis после создания заявки:** +```json +{ + "claim_id": "CLM-2025-11-01-4EZ5L1", + "contact_id": "320096", + "phone": "79262306381", + "is_new_contact": false, + "project_id": "396868", + "is_new_project": false, + "voucher": "E1000-302538524", + "ticket_id": "396932", + "ticket_number": "ЗАЯВКА_825", + "ticket_title": "Задержка авиарейса (более 3 часов) - E1000-302538524", + "ticket_category": "Задержка рейса", + "ticket_status": "рассмотрение", + "event_type": "delay_flight", + "status": "draft", + "current_step": 3, + "created_at": "2025-11-01T21:13:23.043Z", + "updated_at": "2025-11-01T22:15:23.000Z" +} +``` + +--- + +### 3. Backend Proxy: /api/n8n/claim/create + +**Файл:** `backend/app/api/n8n_proxy.py` + +**Новый endpoint:** +```python +@router.post("/claim/create") +async def proxy_create_claim(request: Request): + """ + Проксирует создание черновика заявки к n8n webhook + Frontend → /api/n8n/claim/create → n8n webhook + """ + body = await request.json() + + logger.info(f"🔄 Proxy create claim: event_type={body.get('event_type')}, claim_id={body.get('claim_id')}") + + async with httpx.AsyncClient(timeout=30.0) as client: + response = await client.post( + N8N_CREATE_CLAIM_WEBHOOK, + json=body, + headers={"Content-Type": "application/json"} + ) + + if response.status_code == 200: + response_text = response.text + if not response_text or response_text.strip() == '': + raise HTTPException(status_code=500, detail="N8N вернул пустой ответ") + return response.json() +``` + +**Конфигурация:** +```python +N8N_CREATE_CLAIM_WEBHOOK = getattr( + settings, + 'n8n_create_claim_webhook', + 'https://n8n.clientright.pro/webhook/d5bf4ca6-9e44-44b9-9714-3186ea703e7d' +) +``` + +--- + +### 4. Frontend: Step2EventType.tsx + +**Изменения:** +```typescript +const handleSubmit = async () => { + const values = await form.validateFields(); + setLoading(true); + + const eventLabel = EVENT_TYPES.find(e => e.value === values.eventType)?.label; + const title = `${eventLabel} - ${formData.voucher || 'полис не указан'}`; + + // Вызов backend proxy для создания заявки + const response = await fetch('/api/n8n/claim/create', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + claim_id: formData.claim_id, + contact_id: formData.contact_id, + project_id: formData.project_id, + event_type: values.eventType, + title: title, + voucher: formData.voucher, + session_id: formData.session_id + }) + }); + + let result = await response.json(); + + // ✅ n8n может вернуть массив - берём первый элемент + if (Array.isArray(result) && result.length > 0) { + result = result[0]; + } + + if (response.ok && result.success) { + updateFormData({ + eventType: values.eventType, + ticket_id: result.result?.ticket_id, + ticket_number: result.result?.ticket_number + }); + message.success(`Черновик заявки создан: ${result.result?.ticket_number}`); + onNext(); + } +}; +``` + +--- + +## 🐛 Решённые проблемы + +### 1. BOM (Byte Order Mark) в JSON ответе +**Проблема:** vTiger webservice возвращал `\xEF\xBB\xBF` (UTF-8 BOM) перед JSON. + +**Решение:** +```php +// В CreateWebClaim.php +ob_start(); +// ... код операции ... +ob_end_clean(); +return $result; // Вместо json_encode($result) +``` + +```php +// В webservice.php +ob_clean(); // После всех include +``` + +### 2. Пустой ответ от n8n webhook +**Проблема:** n8n workflow выполнялся успешно, но возвращал пустое тело ответа. + +**Причина:** "Respond to Webhook" node был настроен на `respondWith: "json"` вместо `"lastNode"`. + +**Решение:** В n8n изменить настройку: +- **Respond With:** `Last Node` (вместо `JSON`) + +### 3. n8n возвращает массив вместо объекта +**Проблема:** n8n возвращал `[{success: true, ...}]` вместо `{success: true, ...}`. + +**Решение:** Добавлена обработка в frontend: +```typescript +if (Array.isArray(result) && result.length > 0) { + result = result[0]; +} +``` + +### 4. CORS и безопасность webhooks +**Проблема:** Прямые вызовы n8n webhook с фронтенда блокировались CORS. + +**Решение:** Backend proxy `/api/n8n/claim/create` скрывает webhook URL и обрабатывает запросы. + +--- + +## 📊 Результаты + +### Успешно созданные заявки +**Последняя:** +- **ID:** 396932 +- **Номер:** ЗАЯВКА_825 +- **Title:** "Задержка авиарейса (более 3 часов) - E1000-302538524" +- **Category:** "Задержка рейса" +- **Status:** "рассмотрение" +- **Contact:** 320096 +- **Project:** 396868 + +### Статистика n8n workflow +- **Total Executions:** 10 +- **Success:** 5 (после исправления "Respond to Webhook") +- **Errors:** 5 (до исправления) +- **Success Rate:** 100% (после фикса) + +### Лог backend +``` +✅ Claim created successfully. Response: {"success":true,"result":{"claim_id":"CLM-2025-11-01-4EZ5L1"... +HTTP 200 OK +``` + +--- + +## 📦 Изменённые/созданные файлы + +### Созданные файлы +1. `include/Webservices/CreateWebClaim.php` - операция vTiger для заявок + +### Изменённые файлы (Backend) +1. `backend/app/api/n8n_proxy.py` - добавлен endpoint `/api/n8n/claim/create` +2. `webservice.php` - `ob_get_clean()` + `ob_start()` для очистки BOM + +### Изменённые файлы (Frontend) +1. `frontend/src/components/form/Step2EventType.tsx` - интеграция создания заявки +2. `frontend/src/pages/ClaimForm.tsx` - передача `addDebugEvent` в Step2EventType + +### n8n Workflow +1. Создан: `get_claim_CRM_ERV` (ID: qdYZqhIDGhK9E4DA) +2. Webhook: `d5bf4ca6-9e44-44b9-9714-3186ea703e7d` + +--- + +## 🔧 Технические детали + +### Git коммиты (CRM) +1. `c60d00f5` - feat: Создана операция CreateWebClaim + +### Git коммиты (ERV Platform) +1. `793177b` - feat: Интеграция создания черновика заявки в Step2EventType +2. `cacb2ee` - fix: Обработка массива в ответе n8n для CreateWebClaim +3. `927a8f5` - feat: Проксирование CreateClaim через backend +4. `6cd7027` - fix: Улучшена обработка ответа n8n в claim/create + +### Docker rebuilds +- **Backend:** 3 раза +- **Frontend:** 5 раз (включая force rebuild с `--no-cache`) + +### Тестовых запросов +- Curl тесты: 10+ +- Frontend тесты: 5+ +- Всего созданных заявок: 8 + +--- + +## 🎯 Полный флоу создания заявки + +``` +1. Frontend: Step2EventType + - Пользователь выбирает тип события + - Формируется title из event_type + voucher + ↓ +2. Frontend → Backend: POST /api/n8n/claim/create + - Данные: claim_id, contact_id, project_id, event_type, title + ↓ +3. Backend Proxy + - Логирование запроса + - Проксирование к n8n webhook + ↓ +4. n8n Workflow: get_claim_CRM_ERV + - Redis GET: чтение сессии + - vTiger Login: авторизация + - CreateWebClaim: создание заявки + - Code: мерж данных в сессию + - Redis SET: обновление сессии + - Code: формирование response + - Respond to Webhook + ↓ +5. Backend Proxy + - Получение JSON от n8n + - Возврат фронту + ↓ +6. Frontend + - Обработка массива (если нужно) + - Сохранение ticket_id, ticket_number в formData + - message.success() + - Переход на следующий шаг +``` + +--- + +## 📈 Метрики + +**Время выполнения одного запроса:** +- Frontend → Backend: ~50ms +- Backend → n8n: ~2800ms (включая vTiger CRM) +- n8n → vTiger CreateWebClaim: ~1500ms +- Redis операции: ~100ms +- **Общее время:** ~3 секунды + +**Размер данных:** +- Request: ~250 bytes +- Response: ~400 bytes + +--- + +**Статус:** ✅ Успешно завершено +**Время работы:** 4 часа 15 минут +**Автор:** AI Assistant (Claude Sonnet 4.5) +**Дата:** 01-02 ноября 2025, 21:00-01:15 MSK +