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
5.3 KiB
5.3 KiB
Схема базы данных clpr_*
Основные таблицы
1. clpr_users - Основная таблица пользователей
id (integer, PK)
universal_id (uuid)
unified_id (varchar) ← КЛЮЧЕВОЕ ПОЛЕ для связи
phone (varchar)
created_at, updated_at
2. clpr_user_accounts - Связь пользователей с каналами
id (integer, PK)
user_id (integer) → FK на clpr_users.id
channel (text) - 'telegram', 'web_form'
channel_user_id (text) - ID в канале (telegram_id для telegram, phone для web_form)
Связь:
clpr_user_accounts.user_id→clpr_users.id- Уникальность:
(channel, channel_user_id)- один пользователь может быть в нескольких каналах
3. clpr_claims - Заявки/черновики
id (uuid, PK)
session_token (varchar)
unified_id (varchar) ← СВЯЗЬ С clpr_users.unified_id (должен заполняться n8n!)
telegram_id (bigint)
channel (text) - 'telegram', 'web_form'
user_id (integer) - возможно FK на clpr_users.id
type_code (text)
status_code (text) - 'draft', 'in_work', etc.
policy_number (text)
payload (jsonb) - содержит phone, claim_id, wizard_plan, answers, documents_meta и т.д.
is_confirmed (boolean)
created_at, updated_at, expires_at
Связь:
clpr_claims.unified_id→clpr_users.unified_id(логическая связь)clpr_claims.user_id→clpr_users.id(возможно, не всегда заполнено)
4. clpr_users_tg - Данные Telegram пользователей
telegram_id (bigint, PK)
unified_id (varchar) → clpr_users.unified_id
phone_number (varchar)
first_name_tg, last_name_tg, username, language_code, is_premium
first_name, last_name, middle_name, birth_date, etc.
5. clpr_claim_documents - Документы заявок
id (uuid, PK)
claim_id (varchar) → clpr_claims.id (логическая связь через payload->>'claim_id')
field_name (text)
file_id (text)
uploaded_at (timestamp)
file_name, original_file_name
6. clpr_documents - Хранилище документов
id (uuid, PK)
source (text)
content (text)
metadata (jsonb)
created_at
Логика работы с черновиками для web_form
Шаг 1: Проверка пользователя в CRM
- n8n вызывает
CreateWebContactс phone - Получает
contact_idиз CRM
Шаг 2: Поиск/создание пользователя в PostgreSQL
SQL запрос (аналогично Telegram):
WITH existing AS (
SELECT u.id AS user_id, u.unified_id
FROM clpr_user_accounts ua
JOIN clpr_users u ON u.id = ua.user_id
WHERE ua.channel = 'web_form'
AND ua.channel_user_id = '{phone}'
LIMIT 1
),
create_user AS (
INSERT INTO clpr_users (unified_id, phone, created_at, updated_at)
SELECT 'usr_' || gen_random_uuid()::text, '{phone}', now(), now()
WHERE NOT EXISTS (SELECT 1 FROM existing)
RETURNING id AS user_id, unified_id
),
final_user AS (
SELECT * FROM existing
UNION ALL
SELECT * FROM create_user
),
create_account AS (
INSERT INTO clpr_user_accounts(user_id, channel, channel_user_id)
SELECT
(SELECT user_id FROM final_user),
'web_form',
'{phone}'
ON CONFLICT (channel, channel_user_id) DO NOTHING
)
SELECT unified_id FROM final_user LIMIT 1;
Шаг 3: Создание/обновление заявки
- n8n создает/обновляет запись в
clpr_claims - ВАЖНО: заполняет
unified_idиз результата шага 2 - Сохраняет
phoneвpayload->>'phone' channel = 'web_form'status_code = 'draft'для черновиков
Шаг 4: Поиск черновиков
SELECT
c.id,
c.payload->>'claim_id' as claim_id,
c.session_token,
c.status_code,
c.payload,
c.created_at,
c.updated_at
FROM clpr_claims c
WHERE c.status_code = 'draft'
AND c.channel = 'web_form'
AND c.unified_id = '{unified_id}' -- ← ПОИСК ПО unified_id!
ORDER BY c.updated_at DESC
LIMIT 20;
Проблема в текущей реализации
Текущее состояние:
- В
clpr_claimsполеunified_idПУСТОЕ для всех черновиков web_form - Поиск идет по
payload->>'phone'илиsession_token, что не надежно
Решение:
- n8n должен заполнять
unified_idпри создании/обновлении заявки - Backend должен искать черновики по
unified_id, а не по phone/session_id
Связи между таблицами
clpr_users (unified_id)
↑
| (через unified_id)
|
clpr_claims (unified_id)
|
| (через user_id)
↓
clpr_user_accounts (user_id → clpr_users.id)
|
| (channel='web_form', channel_user_id=phone)
↓
clpr_claims (payload->>'phone')
Для Telegram (для сравнения)
clpr_users (unified_id)
↑
| (через unified_id)
|
clpr_users_tg (unified_id)
|
| (telegram_id)
↓
clpr_user_accounts (channel='telegram', channel_user_id=telegram_id)
|
| (user_id)
↓
clpr_users (id)