Database changes: - Add unified_id, contact_id, phone columns to clpr_claims table - Create indexes for fast lookup by these fields - Migrate existing data from payload to new columns - SQL migration: docs/SQL_ALTER_CLPR_CLAIMS_ADD_FIELDS.sql SQL improvements: - Simplify claimsave query: remove complex claim_lookup logic - Use UPSERT (INSERT ON CONFLICT) with known claim_id - Always return claim (fix NULL issue) - Store unified_id, contact_id, phone directly in table columns - SQL: docs/SQL_CLAIMSAVE_UPSERT_SIMPLE.sql Workflow enhancements: - Add branch for form submissions WITHOUT files - Create 6 new nodes: extract, prepare, save, redis, respond - Separate flow for has_files=false in IF node - Instructions: docs/N8N_FORM_GET_NO_FILES_INSTRUCTIONS.md - Node config: docs/N8N_FORM_GET_NO_FILES_BRANCH.json Migration stats: - Total claims: 81 - With unified_id: 77 - Migrated from payload: 2 Next steps: 1. Add 6 nodes to n8n workflow form_get (ID: 8ZVMTsuH7Cmw7snw) 2. Connect TRUE branch of IF node to extract_webhook_data_no_files 3. Test form submission without files 4. Verify PostgreSQL save and Redis event
6.9 KiB
6.9 KiB
Исправленный SQL для ноды claimsave_final
Текущая проблема
Нода claimsave_final использует $2::uuid, но получает строку "CLM-2025-11-18-GEQ3KL", что вызывает ошибку.
Особенности claimsave_final
- Используется после конвертации файлов в PDF и загрузки в S3
- Работает с
file_url(URL файла в S3) - Обновляет только
documents_metaв payload (не трогаетanswers) - Использует динамический префикс таблицы (для разных схем)
Исправленный SQL запрос
-- $1 = payload_partial_json (jsonb)
-- $2 = claim_id (text, например "CLM-2025-11-18-GEQ3KL")
WITH partial AS (
SELECT $1::jsonb AS p, $2::text AS claim_id_str
),
-- Находим UUID по строковому claim_id
claim_lookup 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_lookup.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,
'documents_meta', COALESCE(partial.p->'documents_meta', '[]'::jsonb)
),
now(),
now(),
now() + interval '14 days'
FROM partial, claim_lookup
WHERE NOT EXISTS (
SELECT 1 FROM clpr_claims WHERE id = claim_lookup.claim_uuid
)
ON CONFLICT (id) DO NOTHING
RETURNING id
),
-- Получаем финальный UUID
claim_final AS (
SELECT
CASE
WHEN EXISTS (SELECT 1 FROM claim_created)
THEN (SELECT id FROM claim_created LIMIT 1)
ELSE claim_lookup.claim_uuid
END AS claim_uuid
FROM claim_lookup
),
-- Извлекаем документы из payload
docs AS (
SELECT
claim_final.claim_uuid::text AS claim_id, -- преобразуем UUID в строку для clpr_claim_documents
doc.field_name::text,
doc.file_id::text,
doc.file_name::text,
doc.original_file_name::text,
(doc.uploaded_at)::timestamptz AS uploaded_at,
doc.file_url::text
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,
file_url text
)
),
-- Сохраняем/обновляем документы
upsert_docs AS (
INSERT INTO clpr_claim_documents
(claim_id, field_name, file_id, uploaded_at, file_name, original_file_name)
SELECT
claim_id,
field_name,
file_id,
uploaded_at,
file_name,
original_file_name
FROM docs
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
),
-- Обновляем payload (только documents_meta, не трогаем answers)
upd_claim AS (
UPDATE clpr_claims c
SET
payload = jsonb_set(
COALESCE(c.payload, '{}'::jsonb),
'{documents_meta}',
COALESCE((SELECT p->'documents_meta' FROM partial), '[]'::jsonb),
true
),
updated_at = now(),
expires_at = now() + interval '14 days'
FROM partial, claim_final
WHERE c.id = claim_final.claim_uuid
RETURNING c.id, c.payload
)
SELECT
(SELECT jsonb_build_object(
'claim_id', u.id::text,
'claim_id_str', (u.payload->>'claim_id'),
'payload', u.payload
) FROM upd_claim u LIMIT 1) AS claim,
(
SELECT jsonb_agg(
jsonb_build_object(
'id', u.id,
'field_name', u.field_name,
'file_id', u.file_id,
'file_url', d.file_url,
'file_name', d.file_name,
'original_file_name', d.original_file_name,
'uploaded_at', d.uploaded_at,
-- имя, которое безопасно отдавать во внешний API
'filename_for_upload',
COALESCE(
NULLIF(d.original_file_name, ''),
NULLIF(d.file_name, ''),
regexp_replace(d.file_id, '^.*/', '') -- хвост пути как запасной
)
)
)
FROM upsert_docs u
JOIN docs d
ON d.claim_id = u.claim_id
AND d.field_name = u.field_name
WHERE d.file_url IS NOT NULL AND d.file_url <> '' -- не показываем без URL
) AS documents;
Изменения
$2::textвместо$2::uuid: Принимает строковыйclaim_idclaim_lookupCTE: Находит UUID по строковомуclaim_idизpayload->>'claim_id'claim_createdCTE: Создает запись, если её нет (на всякий случай)claim_finalCTE: Получает финальный UUID (из созданной или существующей записи)docsCTE: Преобразует UUID в строку дляclpr_claim_documents(т.к. тамclaim_idимеет типcharacter varying)- Убраны динамические префиксы: Используется
clpr_claimsиclpr_claim_documentsнапрямую
Параметры запроса
В n8n PostgreSQL Node:
Parameters:
$1 = {{ $json.payload_partial_json }} (JSONB)
$2 = {{ $json.claim_id }} (TEXT, строка "CLM-2025-11-18-GEQ3KL")
Если нужен динамический префикс
Если всё-таки нужен динамический префикс таблицы (как в оригинале), можно использовать:
-- Вместо clpr_claims использовать:
{{ $('Edit Fields').item.json.propertyName.prefix }}claims
-- Вместо clpr_claim_documents использовать:
{{ $('Edit Fields').item.json.propertyName.prefix }}claim_documents
Но для ticket_form это не нужно, т.к. мы всегда работаем с clpr_* таблицами.
Отличия от claimsave
claimsave: Сохраняет данные визарда (answers, wizard_plan, wizard_answers)claimsave_final: Обновляет толькоdocuments_metaпосле обработки файлов, добавляетfile_url
Оба запроса теперь используют строковый claim_id и правильно находят UUID.