Files
crm.clientright.ru/ticket_form/docs/WORKFLOW_ANALYSIS.md
Fedor 52fe013375 feat(ticket_form): unified_id/contact_id передача, исправлен мерж сессии, новая сессия для жалобы
- Добавлены unified_id и contact_id в TicketFormDescriptionRequest
- Исправлен CODE_MERGE_PROJECT_TO_SESSION.js - теперь сохраняются ВСЕ данные из body.other
- Добавлен fallback на получение other из Webhook напрямую
- Генерация новой session_id при создании новой жалобы (сохраняя авторизацию)
- Добавлен SQL_SELECT_CONTACT_WITH_CUSTOM_FIELDS.sql для CRM контактов
- Создан SESSION_LOG_2025-11-25.md с документацией сессии
2025-11-25 20:02:21 +03:00

8.8 KiB
Raw Permalink 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