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
186 lines
5.3 KiB
Markdown
186 lines
5.3 KiB
Markdown
# Схема базы данных 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):
|
||
```sql
|
||
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: Поиск черновиков
|
||
```sql
|
||
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)
|
||
```
|
||
|
||
|
||
|