Files
aiform_dev/SESSION_LOG_2025-11-20.md

347 lines
12 KiB
Markdown
Raw Permalink Normal View History

# Лог сессии: 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
**Статус:** ✅ Завершено
feat: Add claim plan confirmation flow via Redis SSE Problem: - After wizard form submission, need to wait for claim data from n8n - Claim data comes via Redis channel claim:plan:{session_token} - Need to display confirmation form with claim data Solution: 1. Backend: Added SSE endpoint /api/v1/claim-plan/{session_token} - Subscribes to Redis channel claim:plan:{session_token} - Streams claim data from n8n to frontend - Handles timeouts and errors gracefully 2. Frontend: Added subscription to claim:plan channel - StepWizardPlan: After form submission, subscribes to SSE - Waits for claim_plan_ready event - Shows loading message while waiting - On success: saves claimPlanData and shows confirmation step 3. New component: StepClaimConfirmation - Displays claim confirmation form in iframe - Receives claimPlanData from parent - Generates HTML form (placeholder - should call n8n for real HTML) - Handles confirmation/cancellation via postMessage 4. ClaimForm: Added conditional step for confirmation - Shows StepClaimConfirmation when showClaimConfirmation=true - Step appears after StepWizardPlan - Only visible when claimPlanData is available Flow: 1. User fills wizard form → submits 2. Form data sent to n8n via /api/v1/claims/wizard 3. Frontend subscribes to SSE /api/v1/claim-plan/{session_token} 4. n8n processes data → publishes to Redis claim:plan:{session_token} 5. Backend receives → streams to frontend via SSE 6. Frontend receives → shows StepClaimConfirmation 7. User confirms → proceeds to next step Files: - backend/app/api/events.py: Added stream_claim_plan endpoint - frontend/src/components/form/StepWizardPlan.tsx: Added subscribeToClaimPlan - frontend/src/components/form/StepClaimConfirmation.tsx: New component - frontend/src/pages/ClaimForm.tsx: Added confirmation step to steps array
2025-11-24 13:36:14 +03:00