docs: Добавлен лог сессии CreateWebClaim + полная интеграция
Подробная документация: - ✅ 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 минут
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user