2025-11-19 18:46:48 +03:00
|
|
|
|
# Исправленный SQL для ноды `claimsave_final`
|
|
|
|
|
|
|
|
|
|
|
|
## Текущая проблема
|
|
|
|
|
|
|
|
|
|
|
|
Нода `claimsave_final` использует `$2::uuid`, но получает строку `"CLM-2025-11-18-GEQ3KL"`, что вызывает ошибку.
|
|
|
|
|
|
|
|
|
|
|
|
## Особенности `claimsave_final`
|
|
|
|
|
|
|
|
|
|
|
|
1. Используется **после конвертации файлов в PDF** и загрузки в S3
|
|
|
|
|
|
2. Работает с `file_url` (URL файла в S3)
|
|
|
|
|
|
3. Обновляет только `documents_meta` в payload (не трогает `answers`)
|
|
|
|
|
|
4. Использует динамический префикс таблицы (для разных схем)
|
|
|
|
|
|
|
|
|
|
|
|
## Исправленный SQL запрос
|
|
|
|
|
|
|
|
|
|
|
|
```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;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Изменения
|
|
|
|
|
|
|
|
|
|
|
|
1. **`$2::text` вместо `$2::uuid`**: Принимает строковый `claim_id`
|
|
|
|
|
|
2. **`claim_lookup` CTE**: Находит UUID по строковому `claim_id` из `payload->>'claim_id'`
|
|
|
|
|
|
3. **`claim_created` CTE**: Создает запись, если её нет (на всякий случай)
|
|
|
|
|
|
4. **`claim_final` CTE**: Получает финальный UUID (из созданной или существующей записи)
|
|
|
|
|
|
5. **`docs` CTE**: Преобразует UUID в строку для `clpr_claim_documents` (т.к. там `claim_id` имеет тип `character varying`)
|
|
|
|
|
|
6. **Убраны динамические префиксы**: Используется `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")
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Если нужен динамический префикс
|
|
|
|
|
|
|
|
|
|
|
|
Если всё-таки нужен динамический префикс таблицы (как в оригинале), можно использовать:
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- Вместо clpr_claims использовать:
|
|
|
|
|
|
{{ $('Edit Fields').item.json.propertyName.prefix }}claims
|
|
|
|
|
|
|
|
|
|
|
|
-- Вместо clpr_claim_documents использовать:
|
|
|
|
|
|
{{ $('Edit Fields').item.json.propertyName.prefix }}claim_documents
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Но для `ticket_form` это не нужно, т.к. мы всегда работаем с `clpr_*` таблицами.
|
|
|
|
|
|
|
|
|
|
|
|
## Отличия от `claimsave`
|
|
|
|
|
|
|
|
|
|
|
|
1. **`claimsave`**: Сохраняет данные визарда (answers, wizard_plan, wizard_answers)
|
|
|
|
|
|
2. **`claimsave_final`**: Обновляет только `documents_meta` после обработки файлов, добавляет `file_url`
|
|
|
|
|
|
|
|
|
|
|
|
Оба запроса теперь используют строковый `claim_id` и правильно находят UUID.
|
|
|
|
|
|
|
2025-11-21 15:57:18 +03:00
|
|
|
|
|
2025-11-24 13:36:14 +03:00
|
|
|
|
|