-- ============================================================================ -- SQL запрос для n8n: Сохранение черновика (НОВЫЙ ФЛОУ с документами) -- ============================================================================ -- Назначение: Сохранить черновик сразу после анализа описания проблемы -- AI Agent возвращает facts + docs (список документов) -- -- Вход от AI Agent: -- output: { facts_short, facts_full, problem, recommendation, docs: [...] } -- propertyName: { session_id, phone, unified_id, contact_id, ФИО и т.д. } -- -- Параметры: -- $1 = payload_json (jsonb) - полный payload с output и propertyName -- $2 = session_token (text) - сессия пользователя (из propertyName.session_id) -- $3 = unified_id (text) - unified_id пользователя -- $4 = problem_description (text) - исходное описание проблемы от пользователя -- -- Возвращает: -- claim - объект с claim_id, session_token, status_code, documents_required -- ============================================================================ WITH input_data AS ( SELECT $1::jsonb AS payload, $2::text AS session_token_str, NULLIF($3::text, '') AS unified_id_str, NULLIF($4::text, '') AS problem_desc ), -- Извлекаем данные из payload parsed_data AS ( SELECT input_data.*, input_data.payload->'output' AS ai_output, input_data.payload->'propertyName' AS user_data, input_data.payload->'output'->'docs' AS documents_required FROM input_data ), -- Проверяем существующий черновик по session_token existing_claim AS ( SELECT id, payload FROM clpr_claims WHERE session_token = (SELECT session_token_str FROM input_data) LIMIT 1 ), -- Генерируем или используем существующий UUID claim_id_resolved AS ( SELECT COALESCE( (SELECT id FROM existing_claim), gen_random_uuid() ) AS claim_uuid ), -- INSERT или UPDATE черновика upserted_claim AS ( INSERT INTO clpr_claims ( id, session_token, unified_id, channel, type_code, status_code, payload, created_at, updated_at, expires_at ) SELECT claim_id_resolved.claim_uuid, parsed_data.session_token_str, COALESCE(parsed_data.unified_id_str, parsed_data.user_data->>'unified_id'), 'web_form', 'consumer', 'draft_new', -- ✅ Новый статус: только описание + документы jsonb_build_object( 'claim_id', claim_id_resolved.claim_uuid::text, 'problem_description', COALESCE(parsed_data.problem_desc, parsed_data.user_data->>'problem_description'), -- AI анализ 'ai_analysis', jsonb_build_object( 'facts_short', parsed_data.ai_output->>'facts_short', 'facts_full', parsed_data.ai_output->>'facts_full', 'problem', parsed_data.ai_output->>'problem', 'recommendation', parsed_data.ai_output->>'recommendation' ), -- ✅ Список необходимых документов (новое!) 'documents_required', COALESCE(parsed_data.documents_required, '[]'::jsonb), 'documents_uploaded', '[]'::jsonb, 'documents_skipped', '[]'::jsonb, 'current_doc_index', 0, -- Данные пользователя 'phone', COALESCE(parsed_data.user_data->>'phone', ''), 'email', COALESCE(parsed_data.user_data->>'email', ''), 'contact_id', parsed_data.user_data->>'contact_id', -- ФИО и паспортные данные (для заявления) 'applicant', jsonb_build_object( 'lastname', parsed_data.user_data->>'lastname', 'firstname', parsed_data.user_data->>'firstname', 'middle_name', parsed_data.user_data->>'middle_name', 'birthday', parsed_data.user_data->>'birthday', 'birthplace', parsed_data.user_data->>'birthplace', 'inn', parsed_data.user_data->>'inn', 'address', parsed_data.user_data->>'mailingstreet', 'zip', parsed_data.user_data->>'mailingzip' ), -- Telegram ID если есть 'tg_id', parsed_data.user_data->>'tg_id', -- Флаги готовности 'wizard_ready', false, 'claim_ready', false ), now(), now(), now() + interval '14 days' FROM parsed_data, claim_id_resolved ON CONFLICT (id) DO UPDATE SET unified_id = COALESCE(EXCLUDED.unified_id, clpr_claims.unified_id), status_code = 'draft_new', payload = clpr_claims.payload || EXCLUDED.payload, updated_at = now(), expires_at = now() + interval '14 days' RETURNING id, session_token, status_code, payload ) -- Возвращаем результат для n8n SELECT jsonb_build_object( 'claim_id', upserted_claim.id::text, 'session_token', upserted_claim.session_token, 'status_code', upserted_claim.status_code, 'documents_required', upserted_claim.payload->'documents_required', 'documents_count', jsonb_array_length(COALESCE(upserted_claim.payload->'documents_required', '[]'::jsonb)) ) AS claim FROM upserted_claim; -- ============================================================================ -- Пример вызова в n8n (PostgreSQL Node): -- ============================================================================ -- -- Параметры: -- $1 = {{ JSON.stringify($json) }} -- Весь payload от AI Agent -- $2 = {{ $json.propertyName.session_id }} -- session_token -- $3 = {{ $json.propertyName.unified_id }} -- unified_id -- $4 = {{ $node["Redis Trigger"].json.description }} -- Исходное описание проблемы -- -- После выполнения SQL, в Code Node пушим в Redis: -- -- const result = $input.first().json.claim; -- -- return { -- json: { -- channel: `ocr_events:${result.session_token}`, -- event: { -- event_type: 'documents_list_ready', -- claim_id: result.claim_id, -- session_id: result.session_token, -- documents_required: result.documents_required, -- documents_count: result.documents_count, -- timestamp: new Date().toISOString() -- } -- } -- }; -- ============================================================================ -- SQL запрос для n8n: Сохранение черновика (НОВЫЙ ФЛОУ с документами) -- ============================================================================ -- Назначение: Сохранить черновик сразу после анализа описания проблемы -- AI Agent возвращает facts + docs (список документов) -- -- Вход от AI Agent: -- output: { facts_short, facts_full, problem, recommendation, docs: [...] } -- propertyName: { session_id, phone, unified_id, contact_id, ФИО и т.д. } -- -- Параметры: -- $1 = payload_json (jsonb) - полный payload с output и propertyName -- $2 = session_token (text) - сессия пользователя (из propertyName.session_id) -- $3 = unified_id (text) - unified_id пользователя -- $4 = problem_description (text) - исходное описание проблемы от пользователя -- -- Возвращает: -- claim - объект с claim_id, session_token, status_code, documents_required -- ============================================================================ WITH input_data AS ( SELECT $1::jsonb AS payload, $2::text AS session_token_str, NULLIF($3::text, '') AS unified_id_str, NULLIF($4::text, '') AS problem_desc ), -- Извлекаем данные из payload parsed_data AS ( SELECT input_data.*, input_data.payload->'output' AS ai_output, input_data.payload->'propertyName' AS user_data, input_data.payload->'output'->'docs' AS documents_required FROM input_data ), -- Проверяем существующий черновик по session_token existing_claim AS ( SELECT id, payload FROM clpr_claims WHERE session_token = (SELECT session_token_str FROM input_data) LIMIT 1 ), -- Генерируем или используем существующий UUID claim_id_resolved AS ( SELECT COALESCE( (SELECT id FROM existing_claim), gen_random_uuid() ) AS claim_uuid ), -- INSERT или UPDATE черновика upserted_claim AS ( INSERT INTO clpr_claims ( id, session_token, unified_id, channel, type_code, status_code, payload, created_at, updated_at, expires_at ) SELECT claim_id_resolved.claim_uuid, parsed_data.session_token_str, COALESCE(parsed_data.unified_id_str, parsed_data.user_data->>'unified_id'), 'web_form', 'consumer', 'draft_new', -- ✅ Новый статус: только описание + документы jsonb_build_object( 'claim_id', claim_id_resolved.claim_uuid::text, 'problem_description', COALESCE(parsed_data.problem_desc, parsed_data.user_data->>'problem_description'), -- AI анализ 'ai_analysis', jsonb_build_object( 'facts_short', parsed_data.ai_output->>'facts_short', 'facts_full', parsed_data.ai_output->>'facts_full', 'problem', parsed_data.ai_output->>'problem', 'recommendation', parsed_data.ai_output->>'recommendation' ), -- ✅ Список необходимых документов (новое!) 'documents_required', COALESCE(parsed_data.documents_required, '[]'::jsonb), 'documents_uploaded', '[]'::jsonb, 'documents_skipped', '[]'::jsonb, 'current_doc_index', 0, -- Данные пользователя 'phone', COALESCE(parsed_data.user_data->>'phone', ''), 'email', COALESCE(parsed_data.user_data->>'email', ''), 'contact_id', parsed_data.user_data->>'contact_id', -- ФИО и паспортные данные (для заявления) 'applicant', jsonb_build_object( 'lastname', parsed_data.user_data->>'lastname', 'firstname', parsed_data.user_data->>'firstname', 'middle_name', parsed_data.user_data->>'middle_name', 'birthday', parsed_data.user_data->>'birthday', 'birthplace', parsed_data.user_data->>'birthplace', 'inn', parsed_data.user_data->>'inn', 'address', parsed_data.user_data->>'mailingstreet', 'zip', parsed_data.user_data->>'mailingzip' ), -- Telegram ID если есть 'tg_id', parsed_data.user_data->>'tg_id', -- Флаги готовности 'wizard_ready', false, 'claim_ready', false ), now(), now(), now() + interval '14 days' FROM parsed_data, claim_id_resolved ON CONFLICT (id) DO UPDATE SET unified_id = COALESCE(EXCLUDED.unified_id, clpr_claims.unified_id), status_code = 'draft_new', payload = clpr_claims.payload || EXCLUDED.payload, updated_at = now(), expires_at = now() + interval '14 days' RETURNING id, session_token, status_code, payload ) -- Возвращаем результат для n8n SELECT jsonb_build_object( 'claim_id', upserted_claim.id::text, 'session_token', upserted_claim.session_token, 'status_code', upserted_claim.status_code, 'documents_required', upserted_claim.payload->'documents_required', 'documents_count', jsonb_array_length(COALESCE(upserted_claim.payload->'documents_required', '[]'::jsonb)) ) AS claim FROM upserted_claim; -- ============================================================================ -- Пример вызова в n8n (PostgreSQL Node): -- ============================================================================ -- -- Параметры: -- $1 = {{ JSON.stringify($json) }} -- Весь payload от AI Agent -- $2 = {{ $json.propertyName.session_id }} -- session_token -- $3 = {{ $json.propertyName.unified_id }} -- unified_id -- $4 = {{ $node["Redis Trigger"].json.description }} -- Исходное описание проблемы -- -- После выполнения SQL, в Code Node пушим в Redis: -- -- const result = $input.first().json.claim; -- -- return { -- json: { -- channel: `ocr_events:${result.session_token}`, -- event: { -- event_type: 'documents_list_ready', -- claim_id: result.claim_id, -- session_id: result.session_token, -- documents_required: result.documents_required, -- documents_count: result.documents_count, -- timestamp: new Date().toISOString() -- } -- } -- }; -- ============================================================================