Files
aiform_prod/docs/SQL_CLAIMSAVE_PRIMARY_DRAFT.sql

219 lines
7.3 KiB
MySQL
Raw Normal View History

-- ============================================================================
-- SQL запрос для n8n: Сохранение первичного черновика заявки
-- ============================================================================
-- Назначение: Сохранить первичный черновик сразу после генерации wizard_plan
-- Использует session_token для связи (claim_id генерируется позже)
--
-- Параметры:
-- $1 = payload_json (jsonb) - полный payload с wizard_plan, problem_description,
-- AI Agent1, AI Agent13 и т.д.
-- $2 = session_token (text) - сессия пользователя
-- $3 = unified_id (text, опционально) - unified_id пользователя
--
-- Возвращает:
-- claim - объект с claim_id (UUID), session_token, status_code, payload
--
-- Использование в n8n:
-- 1. PostgreSQL node
-- 2. Query Type: Execute Query
-- 3. Parameters:
-- $1 = {{ JSON.stringify({...}) }}
-- $2 = {{ $('Edit Fields11').first().json.session_token }}
-- $3 = {{ $('Edit Fields10').first().json.unified_id || null }}
-- ============================================================================
WITH partial AS (
SELECT
$1::jsonb AS p,
$2::text AS session_token_str,
NULLIF($3::text, '') AS unified_id_str
),
-- Находим существующую запись по session_token или создаем новую
claim_lookup AS (
SELECT
COALESCE(
(SELECT id FROM clpr_claims WHERE session_token = partial.session_token_str LIMIT 1),
gen_random_uuid()
) AS claim_uuid
FROM partial
),
-- Если записи нет, создаем её
claim_created AS (
INSERT INTO clpr_claims (
id,
session_token,
unified_id,
channel,
type_code,
status_code,
payload,
created_at,
updated_at,
expires_at
)
SELECT
claim_lookup.claim_uuid,
partial.session_token_str,
partial.unified_id_str,
'web_form',
COALESCE(partial.p->>'type_code', 'consumer'),
'draft',
jsonb_build_object(
-- claim_id будет сгенерирован позже, пока NULL
'claim_id', NULL,
'problem_description', partial.p->>'problem_description',
'wizard_plan',
CASE
WHEN partial.p->>'wizard_plan' IS NOT NULL
THEN (partial.p->>'wizard_plan')::jsonb
WHEN partial.p->'wizard_plan' IS NOT NULL AND jsonb_typeof(partial.p->'wizard_plan') = 'object'
THEN partial.p->'wizard_plan'
ELSE NULL
END,
'answers_prefill',
CASE
WHEN partial.p->>'answers_prefill' IS NOT NULL
THEN (partial.p->>'answers_prefill')::jsonb
WHEN partial.p->'answers_prefill' IS NOT NULL AND jsonb_typeof(partial.p->'answers_prefill') = 'array'
THEN partial.p->'answers_prefill'
ELSE '[]'::jsonb
END,
'coverage_report',
CASE
WHEN partial.p->>'coverage_report' IS NOT NULL
THEN (partial.p->>'coverage_report')::jsonb
WHEN partial.p->'coverage_report' IS NOT NULL AND jsonb_typeof(partial.p->'coverage_report') = 'object'
THEN partial.p->'coverage_report'
ELSE NULL
END,
-- Данные из AI Agent1 (факты)
'ai_agent1_facts',
CASE
WHEN partial.p->'ai_agent1_facts' IS NOT NULL AND jsonb_typeof(partial.p->'ai_agent1_facts') = 'object'
THEN partial.p->'ai_agent1_facts'
ELSE NULL
END,
-- Данные из AI Agent13 (RAG ответ)
'ai_agent13_rag',
CASE
WHEN partial.p->>'ai_agent13_rag' IS NOT NULL
THEN (partial.p->>'ai_agent13_rag')::jsonb
WHEN partial.p->'ai_agent13_rag' IS NOT NULL AND jsonb_typeof(partial.p->'ai_agent13_rag') = 'object'
THEN partial.p->'ai_agent13_rag'
ELSE NULL
END,
'phone', partial.p->>'phone',
'email', partial.p->>'email'
),
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
),
-- Обновляем существующую запись (если есть)
upd AS (
UPDATE clpr_claims c
SET
unified_id = COALESCE(partial.unified_id_str, c.unified_id),
payload = jsonb_set(
jsonb_set(
jsonb_set(
jsonb_set(
jsonb_set(
COALESCE(c.payload, '{}'::jsonb),
'{wizard_plan}',
COALESCE(
CASE
WHEN partial.p->>'wizard_plan' IS NOT NULL
THEN (partial.p->>'wizard_plan')::jsonb
WHEN partial.p->'wizard_plan' IS NOT NULL AND jsonb_typeof(partial.p->'wizard_plan') = 'object'
THEN partial.p->'wizard_plan'
ELSE NULL
END,
c.payload->'wizard_plan'
),
true
),
'{ai_agent1_facts}',
COALESCE(
CASE
WHEN partial.p->'ai_agent1_facts' IS NOT NULL AND jsonb_typeof(partial.p->'ai_agent1_facts') = 'object'
THEN partial.p->'ai_agent1_facts'
ELSE NULL
END,
c.payload->'ai_agent1_facts'
),
true
),
'{ai_agent13_rag}',
COALESCE(
CASE
WHEN partial.p->>'ai_agent13_rag' IS NOT NULL
THEN (partial.p->>'ai_agent13_rag')::jsonb
WHEN partial.p->'ai_agent13_rag' IS NOT NULL AND jsonb_typeof(partial.p->'ai_agent13_rag') = 'object'
THEN partial.p->'ai_agent13_rag'
ELSE NULL
END,
c.payload->'ai_agent13_rag'
),
true
),
'{problem_description}',
COALESCE(partial.p->>'problem_description', c.payload->>'problem_description'),
true
),
'{answers_prefill}',
COALESCE(
CASE
WHEN partial.p->>'answers_prefill' IS NOT NULL
THEN (partial.p->>'answers_prefill')::jsonb
WHEN partial.p->'answers_prefill' IS NOT NULL AND jsonb_typeof(partial.p->'answers_prefill') = 'array'
THEN partial.p->'answers_prefill'
ELSE '[]'::jsonb
END,
c.payload->'answers_prefill',
'[]'::jsonb
),
true
),
updated_at = now(),
expires_at = now() + interval '14 days'
FROM partial, claim_final
WHERE c.id = claim_final.claim_uuid
AND EXISTS (SELECT 1 FROM claim_lookup WHERE claim_uuid = c.id)
RETURNING c.id, c.payload
)
SELECT
(SELECT jsonb_build_object(
'claim_id', COALESCE(u.id::text, cf.claim_uuid::text),
'session_token', partial.session_token_str,
'status_code', 'draft',
'payload', COALESCE(u.payload, jsonb_build_object())
)
FROM claim_final cf, partial
LEFT JOIN upd u ON true
LIMIT 1) AS claim;
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