Files
aiform_dev/docs/WORKFLOW_ANALYSIS.md
AI Assistant 0978e485dc 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

8.8 KiB
Raw Blame History

Анализ workflow 8ZVMTsuH7Cmw7snw и предложения

Текущая структура

Основные ноды PostgreSQL:

  1. claimsave (строка 190-210)

    • Использует обновленный SQL с $2::text (строка claim_id)
    • ПРОБЛЕМА: SQL запрос не использует claim_final CTE, который я добавил в исправленной версии
    • Это основная нода для сохранения данных визарда
  2. claimsave_final (строка 428-450)

    • Использует другой SQL запрос с $2::uuid
    • Используется после конвертации файлов в PDF
    • ПРОБЛЕМА: Ожидает UUID, но может получать строку
  3. claimsave1 (строка 634-655)

    • Использует старый SQL запрос с $2::uuid
    • ПРОБЛЕМА: Не работает со строковым claim_id

Проблемы

1. SQL запрос в claimsave неполный

Текущий SQL в ноде claimsave:

  • Использует $2::text (правильно)
  • Имеет claim_lookup и claim_created CTE
  • НЕ использует claim_final CTE (который я добавил в исправленной версии)
  • Использует claim_lookup.claim_uuid напрямую, что может не работать, если запись была создана в claim_created

2. Несоответствие типов данных

  • claimsave ожидает строку ($2::text)
  • claimsave_final ожидает UUID ($2::uuid)
  • claimsave1 ожидает UUID ($2::uuid)

Но везде передается claim_id как строка "CLM-2025-11-18-GEQ3KL".

3. Проблема с existing CTE

В текущем SQL запросе existing может не найти запись, если она была создана в claim_created, потому что:

  • claim_lookup выполняется ДО claim_created
  • existing использует claim_lookup.claim_uuid, но запись может быть создана в claim_created

Решения

Решение 1: Обновить SQL в ноде claimsave

Заменить SQL запрос на исправленную версию из FIXED_SQL_QUERY.md:

Ключевые изменения:

  1. Добавить claim_final CTE для получения правильного UUID
  2. Использовать claim_final.claim_uuid вместо claim_lookup.claim_uuid
  3. Исправить old CTE, чтобы он всегда возвращал строку

Решение 2: Унифицировать типы данных

Вариант A: Все ноды используют строку claim_id

  • Изменить claimsave_final и claimsave1 на $2::text
  • Добавить логику поиска UUID по строке claim_id

Вариант B: Все ноды используют UUID

  • Перед SQL запросами добавить Code Node, который:
    • Находит запись в clpr_claims по payload->>'claim_id'
    • Извлекает её id (UUID)
    • Передает UUID в SQL запрос

Рекомендую Вариант A (использовать строку везде), т.к.:

  • Проще реализовать
  • Меньше изменений в workflow
  • claim_id в формате CLM-YYYY-MM-DD-XXXXXX - это основной идентификатор

Решение 3: Упростить логику

Можно упростить SQL запрос, убрав сложную логику слияния:

WITH partial AS (
  SELECT $1::jsonb AS p, $2::text AS claim_id_str
),

-- Находим или создаем запись
claim_final AS (
  SELECT 
    COALESCE(
      (SELECT id FROM clpr_claims WHERE payload->>'claim_id' = partial.claim_id_str LIMIT 1),
      gen_random_uuid()
    ) AS claim_uuid
  FROM partial
),

-- Создаем запись, если её нет
claim_created AS (
  INSERT INTO clpr_claims (
    id, session_token, channel, type_code, status_code, payload, created_at, updated_at, expires_at
  )
  SELECT 
    claim_final.claim_uuid,
    COALESCE(partial.p->>'session_id', 'sess-' || gen_random_uuid()::text),
    'web_form',
    COALESCE(partial.p->>'type_code', 'consumer'),
    'draft',
    jsonb_build_object(
      'claim_id', partial.claim_id_str,
      'answers', COALESCE(partial.p->'answers', '{}'::jsonb),
      'documents_meta', COALESCE(partial.p->'documents_meta', '[]'::jsonb),
      'wizard_plan', partial.p->'wizard_plan',
      'wizard_answers', partial.p->'wizard_answers',
      'form_data', partial.p
    ),
    now(), now(), now() + interval '14 days'
  FROM partial, claim_final
  WHERE NOT EXISTS (SELECT 1 FROM clpr_claims WHERE id = claim_final.claim_uuid)
  ON CONFLICT (id) DO NOTHING
  RETURNING id
),

-- Сохраняем документы
inserted_docs AS (
  INSERT INTO clpr_claim_documents
    (claim_id, field_name, file_id, uploaded_at, file_name, original_file_name)
  SELECT
    claim_final.claim_uuid::text,
    doc.field_name, doc.file_id,
    (doc.uploaded_at)::timestamptz,
    doc.file_name, doc.original_file_name
  FROM partial, claim_final
  CROSS JOIN LATERAL jsonb_to_recordset(
    COALESCE(partial.p->'documents_meta','[]'::jsonb)
  ) AS doc(field_name text, file_id text, file_name text, original_file_name text, uploaded_at text)
  ON CONFLICT (claim_id, field_name) DO UPDATE
    SET file_id = EXCLUDED.file_id,
        uploaded_at = EXCLUDED.uploaded_at,
        file_name = EXCLUDED.file_name,
        original_file_name = EXCLUDED.original_file_name
  RETURNING id, claim_id, field_name, file_id
),

-- Обновляем запись (простое слияние)
upd AS (
  UPDATE clpr_claims c
  SET 
    payload = COALESCE(c.payload, '{}'::jsonb) || partial.p,
    status_code = CASE
      WHEN (partial.p->'answers'->>'docs_exist' = 'true') THEN 'in_work'
      ELSE COALESCE(c.status_code, 'draft')
    END,
    updated_at = now(),
    expires_at = now() + interval '14 days'
  FROM partial, claim_final
  WHERE c.id = claim_final.claim_uuid
  RETURNING c.id, c.status_code, c.payload
)

SELECT
  (SELECT jsonb_build_object(
    'claim_id', u.id::text,
    'claim_id_str', (u.payload->>'claim_id'),
    'status_code', u.status_code,
    'payload', u.payload
  ) FROM upd u LIMIT 1) AS claim,
  (SELECT jsonb_agg(jsonb_build_object(
    'id', id,
    'field_name', field_name,
    'file_id', file_id
  )) FROM inserted_docs) AS documents;

Рекомендации

Немедленные действия:

  1. Обновить SQL в ноде claimsave

    • Заменить на исправленную версию из FIXED_SQL_QUERY.md
    • Или использовать упрощенную версию выше
  2. Проверить параметры

    • Убедиться, что queryReplacement правильный: ={{ $json.payload_partial_json }}, {{ $json.claim_id }}
    • payload_partial_json должен быть JSON объектом
    • claim_id должен быть строкой
  3. Протестировать

    • Запустить workflow с тестовыми данными
    • Проверить, что claim не возвращает null
    • Проверить, что документы сохраняются правильно

Долгосрочные улучшения:

  1. Унифицировать все SQL запросы

    • Привести claimsave_final и claimsave1 к единому формату
    • Использовать строковый claim_id везде
  2. Добавить обработку ошибок

    • Проверять результат SQL запроса
    • Логировать ошибки
    • Возвращать понятные сообщения об ошибках
  3. Оптимизировать workflow

    • Упростить логику слияния payload
    • Использовать транзакции для атомарности операций

Готовый SQL для копирования

Полный исправленный SQL запрос находится в файле FIXED_SQL_QUERY.md.

Основные изменения:

  • Использует claim_final CTE для правильного получения UUID
  • old CTE всегда возвращает строку (даже если запись не найдена)
  • Все подзапросы используют LIMIT 1 для гарантии одной строки
  • Правильное слияние answers и documents_meta