Files
aiform_dev/SESSION_LOG_2025-11-20.md
AI Assistant d6b17baa7d feat: Add PostgreSQL fields and workflow for form without files
Database changes:
- Add unified_id, contact_id, phone columns to clpr_claims table
- Create indexes for fast lookup by these fields
- Migrate existing data from payload to new columns
- SQL migration: docs/SQL_ALTER_CLPR_CLAIMS_ADD_FIELDS.sql

SQL improvements:
- Simplify claimsave query: remove complex claim_lookup logic
- Use UPSERT (INSERT ON CONFLICT) with known claim_id
- Always return claim (fix NULL issue)
- Store unified_id, contact_id, phone directly in table columns
- SQL: docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql

Workflow enhancements:
- Add branch for form submissions WITHOUT files
- Create 6 new nodes: extract, prepare, save, redis, respond
- Separate flow for has_files=false in IF node
- Instructions: docs/N8N_FORM_GET_NO_FILES_INSTRUCTIONS.md
- Node config: docs/N8N_FORM_GET_NO_FILES_BRANCH.json

Migration stats:
- Total claims: 81
- With unified_id: 77
- Migrated from payload: 2

Next steps:
1. Add 6 nodes to n8n workflow form_get (ID: 8ZVMTsuH7Cmw7snw)
2. Connect TRUE branch of IF node to extract_webhook_data_no_files
3. Test form submission without files
4. Verify PostgreSQL save and Redis event
2025-11-21 15:57:18 +03:00

346 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Лог сессии: 2025-11-20 - Session Persistence & Draft Management
## Дата: 20 ноября 2025
---
## 🎯 Основные задачи
1. ✅ Реализация сохранения сессии в Redis
2. ✅ Восстановление сессии из localStorage после перезагрузки страницы
3. ✅ Исправление передачи `unified_id` и `claim_id` в n8n при отправке визарда
4. ✅ Исправление приоритета `session_id` при загрузке черновика
---
## 📝 Выполненные изменения
### 1. Backend - Session API (`/api/v1/session`)
**Файл:** `ticket_form/backend/app/api/session.py`
**Создан новый роутер** для управления сессиями в Redis:
- `POST /api/v1/session/create` - создание сессии с TTL 24 часа
- `POST /api/v1/session/verify` - проверка валидности сессии
- `POST /api/v1/session/logout` - удаление сессии
**Данные сессии:**
```python
{
"session_token": "sess_...",
"unified_id": "usr_...",
"phone": "79262306381",
"contact_id": "320096",
"verified_at": "2025-11-20T14:54:01.279Z",
"expires_at": "2025-11-21T14:54:01.279Z"
}
```
**Файл:** `ticket_form/backend/app/main.py`
- Добавлен импорт `session` роутера
- Подключен роутер: `app.include_router(session.router)`
---
### 2. Frontend - Session Management
**Файл:** `ticket_form/frontend/src/components/form/Step1Phone.tsx`
**Версия:** v2.0 - 2025-11-20 14:40
**Изменения:**
- После успешной SMS-верификации вызывается `POST /api/v1/session/create`
- `session_token` сохраняется в `localStorage`
- Добавлены подробные debug логи для отладки сессии
**Код:**
```typescript
// После получения unified_id от n8n
const sessionPayload = {
session_token: finalSessionId,
unified_id: unifiedIdToPass,
phone: formData.phone!,
contact_id: result.contact_id,
};
const sessionResponse = await fetch('/api/v1/session/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(sessionPayload),
});
if (sessionResponse.ok) {
localStorage.setItem('session_token', finalSessionId);
}
```
---
### 3. Frontend - Session Restoration
**Файл:** `ticket_form/frontend/src/pages/ClaimForm.tsx`
**Версия:** v3.8 - 2025-11-20 15:10
**Изменения:**
#### A. Проверка сессии при загрузке компонента:
```typescript
useEffect(() => {
const sessionToken = localStorage.getItem('session_token');
if (!sessionToken) return;
// Проверяем валидность сессии
fetch('/api/v1/session/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken }),
})
.then(res => res.json())
.then(data => {
if (data.success && data.valid) {
// Восстанавливаем данные сессии
updateFormData({
unified_id: data.unified_id,
phone: data.phone,
contact_id: data.contact_id,
});
setIsPhoneVerified(true);
checkDrafts(data.unified_id, data.phone, formData.session_id);
} else {
localStorage.removeItem('session_token');
}
});
}, []);
```
#### B. Кнопка "Выход":
```typescript
const handleExitToList = () => {
const sessionToken = localStorage.getItem('session_token');
if (sessionToken) {
fetch('/api/v1/session/logout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken }),
});
localStorage.removeItem('session_token');
}
// Сброс формы
updateFormData({
unified_id: undefined,
phone: '',
contact_id: '',
});
setIsPhoneVerified(false);
setCurrentStep(0);
};
```
#### C. Исправление приоритета `session_id` при загрузке черновика:
**До:**
```typescript
session_id: claim.session_token || sessionIdRef.current, // ❌ Старый из черновика
```
**После:**
```typescript
session_id: sessionIdRef.current || formData.session_id, // ✅ Текущий актуальный
```
**Причина:** При загрузке черновика старый `session_id` из БД перезаписывал новый, полученный от n8n после SMS-верификации.
---
### 4. Frontend - Wizard Payload Fix
**Файл:** `ticket_form/frontend/src/components/form/StepWizardPlan.tsx`
**Версия:** v1.4 - 2025-11-20 15:00
**Проблема:** При отправке визарда в n8n не передавались `unified_id` и `claim_id`.
**Исправление:**
```typescript
// Добавляем unified_id и claim_id (если есть)
if (formData.unified_id) formPayload.append('unified_id', formData.unified_id);
if (formData.claim_id) formPayload.append('claim_id', formData.claim_id);
```
**Debug лог:**
```typescript
console.log('📤 Отправка в n8n:', {
session_id: formData.session_id,
unified_id: formData.unified_id,
claim_id: formData.claim_id,
contact_id: formData.contact_id,
phone: formData.phone,
});
```
---
### 5. Docker Volumes для Hot Module Replacement
**Файл:** `ticket_form/docker-compose.yml`
**Добавлен volume для фронтенда:**
```yaml
ticket_form_frontend:
volumes:
- ./frontend/src:/app/src:ro
```
**Цель:** Включить live reload (HMR) при изменении файлов фронтенда без пересборки контейнера.
---
## 🔄 Workflow изменений
### Полный цикл работы с сессией:
1. **Пользователь вводит телефон и SMS-код**
- → Step1Phone вызывает n8n для верификации
- → n8n возвращает `unified_id`, `contact_id`, `session_id`
- → Step1Phone создаёт сессию в Redis через `POST /api/v1/session/create`
-`session_token` сохраняется в `localStorage`
2. **Пользователь закрывает/обновляет страницу**
- → ClaimForm при загрузке проверяет `localStorage`
- → Вызывается `POST /api/v1/session/verify`
- → Если сессия валидна, восстанавливаются `unified_id`, `phone`, `contact_id`
- → Автоматически загружаются черновики
3. **Пользователь продолжает черновик**
- → При загрузке черновика используется ТЕКУЩИЙ `session_id` (не старый из БД)
- → При отправке визарда передаются `unified_id`, `claim_id`, актуальный `session_id`
4. **Пользователь нажимает "Выход"**
- → Вызывается `POST /api/v1/session/logout`
- → Сессия удаляется из Redis
-`session_token` удаляется из `localStorage`
- → Редирект на Step1Phone
---
## 🐛 Исправленные проблемы
### Проблема #1: Session token not found in localStorage
**Причина:** Backend эндпоинт `/api/v1/session/create` не был подключен.
**Решение:** Добавлен импорт и подключение роутера в `main.py`.
### Проблема #2: unified_id не передавался в n8n
**Причина:** В `StepWizardPlan.tsx` не было строки `formPayload.append('unified_id', ...)`.
**Решение:** Добавлена передача `unified_id` и `claim_id` в FormData.
### Проблема #3: Старый session_id перезаписывал новый
**Причина:** При загрузке черновика приоритет был у `claim.session_token` из БД.
**Решение:** Изменён приоритет на `sessionIdRef.current` (текущая сессия).
### Проблема #4: Frontend не обновлялся без пересборки
**Причина:** Docker контейнер не монтировал исходники фронтенда.
**Решение:** Добавлен volume `./frontend/src:/app/src:ro` для HMR.
---
## 📊 Результаты
### Payload в n8n после исправлений:
```json
{
"stage": "wizard",
"form_id": "ticket_form",
"session_id": "sess_e6e3f447-8770-47af-ae87-8c022c686d9f", Актуальный от n8n
"unified_id": "usr_90599ff2-ac79-4236-b950-0df85395096c", Добавлен
"claim_id": "19572ab7-cad5-4f8d-a622-4617487c07ce", Добавлен
"contact_id": "320096",
"phone": "79262306381",
"wizard_plan": "{...}",
"wizard_answers": "{...}",
"wizard_skipped_documents": "[]"
}
```
### Сессия в Redis (TTL 24 часа):
```
Key: crm:session:sess_e6e3f447-8770-47af-ae87-8c022c686d9f
Value: {
"session_token": "sess_e6e3f447-8770-47af-ae87-8c022c686d9f",
"unified_id": "usr_90599ff2-ac79-4236-b950-0df85395096c",
"phone": "79262306381",
"contact_id": "320096",
"verified_at": "2025-11-20T14:54:01.279Z",
"expires_at": "2025-11-21T14:54:01.279Z"
}
TTL: 86400 секунд
```
---
## 📦 Изменённые файлы
1.`ticket_form/backend/app/api/session.py` (создан)
2.`ticket_form/backend/app/main.py` (добавлен импорт session)
3.`ticket_form/frontend/src/components/form/Step1Phone.tsx` (v2.0)
4.`ticket_form/frontend/src/pages/ClaimForm.tsx` (v3.8)
5.`ticket_form/frontend/src/components/form/StepWizardPlan.tsx` (v1.4)
6.`ticket_form/docker-compose.yml` (добавлен volume)
---
## 🧪 Тестирование
### Сценарий 1: Новая сессия
- ✅ Ввод телефона и SMS-кода
- ✅ Создание сессии в Redis
- ✅ Сохранение session_token в localStorage
- ✅ Отображение черновиков (если есть)
### Сценарий 2: Восстановление сессии
- ✅ Ctrl+F5 (hard refresh)
- ✅ Автоматическая верификация сессии
- ✅ Восстановление unified_id, phone, contact_id
- ✅ Автоматическое отображение черновиков
### Сценарий 3: Продолжение черновика
- ✅ Выбор черновика из списка
- ✅ Загрузка данных черновика
- ✅ Сохранение актуального session_id (не старого из БД)
- ✅ Отправка в n8n с unified_id, claim_id, session_id
### Сценарий 4: Выход
- ✅ Нажатие кнопки "🚪 Выход"
- ✅ Удаление сессии из Redis
- ✅ Удаление session_token из localStorage
- ✅ Редирект на Step1Phone
---
## 🎉 Итоги
Реализован полноценный механизм управления сессиями:
- Персистентность через Redis (TTL 24 часа)
- Восстановление после перезагрузки страницы
- Корректная передача идентификаторов в n8n
- Безопасный выход с очисткой данных
Все изменения протестированы и готовы к продакшену! 🚀
---
## 📝 Следующие шаги (опционально)
1. Добавить обновление TTL сессии при активности пользователя
2. Реализовать уведомление о скором истечении сессии (за 5 минут)
3. Добавить мониторинг активных сессий в админке
4. Реализовать "запомнить меня" с увеличенным TTL (7 дней)
---
**Автор:** AI Assistant
**Дата:** 2025-11-20
**Статус:** ✅ Завершено