- Добавлены логи в frontend (ClaimForm.tsx) для отслеживания unified_id и запросов к API - Добавлены логи в backend (claims.py) для отладки SQL запросов - Создан лог сессии с описанием проблемы и текущего состояния - Проблема: API возвращает 0 черновиков, хотя в БД есть данные
213 lines
7.9 KiB
JavaScript
213 lines
7.9 KiB
JavaScript
// Code node (JavaScript). Input: items[0].json = либо объект, либо массив таких объектов, как ты прислал.
|
||
// Output: по одному нормализованному объекту на кейс.
|
||
// Никаких внешних зависимостей, всё на ванильном JS.
|
||
|
||
function toNullish(v) {
|
||
if (v === undefined || v === null) return null;
|
||
if (typeof v === 'string' && v.trim() === '') return null;
|
||
return v;
|
||
}
|
||
|
||
function pick(o, path, def = null) {
|
||
try {
|
||
return toNullish(path.split('.').reduce((acc, k) => (acc == null ? undefined : acc[k]), o));
|
||
} catch {
|
||
return def;
|
||
}
|
||
}
|
||
|
||
function mapDocuments(docs = []) {
|
||
// Проверяем, что docs не null и является массивом
|
||
if (!docs || !Array.isArray(docs)) return [];
|
||
return docs.map(d => ({
|
||
id: toNullish(d.id),
|
||
claim_document_id: toNullish(d.id), // у тебя id = claim_document_id
|
||
file_id: toNullish(d.file_id),
|
||
file_url: toNullish(d.file_url),
|
||
file_name: toNullish(d.file_name),
|
||
original_file_name: toNullish(d.original_file_name),
|
||
field_name: toNullish(d.field_name),
|
||
upload_description: toNullish(d.upload_description),
|
||
uploaded_at: toNullish(d.uploaded_at),
|
||
filename_for_upload: toNullish(d.filename_for_upload),
|
||
}));
|
||
}
|
||
|
||
function mapVisionDocs(vds = []) {
|
||
// Проверяем, что vds не null и является массивом
|
||
if (!vds || !Array.isArray(vds)) return [];
|
||
return vds.map(v => ({
|
||
claim_document_id: toNullish(v.claim_document_id),
|
||
vision_document_id: toNullish(v.vision_document_id),
|
||
pages: toNullish(v.pages),
|
||
content_sha256: toNullish(v.content_sha256),
|
||
vision_text: toNullish(v.vision_text),
|
||
vision_pages: Array.isArray(v.vision_pages)
|
||
? v.vision_pages.map(p => ({
|
||
page: toNullish(p.page),
|
||
uid: toNullish(p.uid),
|
||
}))
|
||
: null,
|
||
}));
|
||
}
|
||
|
||
function mapCombinedDocs(cds = []) {
|
||
// Проверяем, что cds не null и является массивом
|
||
if (!cds || !Array.isArray(cds)) return [];
|
||
return cds.map(c => ({
|
||
claim_document_id: toNullish(c.claim_document_id),
|
||
combined_document_id: toNullish(c.combined_document_id),
|
||
pages: toNullish(c.pages),
|
||
content_sha256: toNullish(c.content_sha256),
|
||
combined_text: toNullish(c.combined_text),
|
||
page_summaries: Array.isArray(c.page_summaries)
|
||
? c.page_summaries.map(ps => ({
|
||
page: toNullish(ps.page),
|
||
chars: toNullish(ps.chars),
|
||
uid: toNullish(ps.uid),
|
||
image_url: toNullish(ps.image_url),
|
||
}))
|
||
: null,
|
||
}));
|
||
}
|
||
|
||
function mapDialogHistory(h = []) {
|
||
// ИСПРАВЛЕНО: Проверяем, что h не null и является массивом
|
||
if (!h || !Array.isArray(h)) return [];
|
||
return h.map(m => ({
|
||
id: toNullish(m.id),
|
||
role: toNullish(m.role),
|
||
message: toNullish(m.message),
|
||
message_type: toNullish(m.message_type),
|
||
tg_message_id: toNullish(m.tg_message_id),
|
||
created_at: toNullish(m.created_at),
|
||
}));
|
||
}
|
||
|
||
function mapCoverageReport(cr = null) {
|
||
if (!cr) return null;
|
||
return {
|
||
questions: Array.isArray(cr.questions)
|
||
? cr.questions.map(q => ({
|
||
name: toNullish(q.name),
|
||
value: toNullish(q.value),
|
||
status: toNullish(q.status),
|
||
source: toNullish(q.source),
|
||
confidence: toNullish(q.confidence),
|
||
}))
|
||
: null,
|
||
docs_missing: Array.isArray(cr.docs_missing) ? cr.docs_missing : null,
|
||
docs_received: Array.isArray(cr.docs_received) ? cr.docs_received : null,
|
||
};
|
||
}
|
||
|
||
function normalizeOne(src) {
|
||
const claim = src.claim ?? {};
|
||
const userInfo = src.user_info ?? {};
|
||
const propertyName = claim.propertyName ?? {};
|
||
|
||
// answers_parsed уже есть в claim; не мудрим — возвращаем как есть, пустоты -> null
|
||
const answersParsed = claim.answers_parsed
|
||
? Object.fromEntries(
|
||
Object.entries(claim.answers_parsed).map(([k, v]) => [k, toNullish(v)])
|
||
)
|
||
: null;
|
||
|
||
// wizard план (часто нужен на фронте) — оставим ключевые поля
|
||
let wizard = null;
|
||
try {
|
||
const parsed = typeof claim.wizard_plan === 'string'
|
||
? JSON.parse(claim.wizard_plan)
|
||
: (claim.wizard_plan_parsed ?? null);
|
||
if (parsed) {
|
||
wizard = {
|
||
version: toNullish(parsed.version),
|
||
case_type: toNullish(parsed.case_type),
|
||
goals: Array.isArray(parsed.goals) ? parsed.goals : null,
|
||
documents: Array.isArray(parsed.documents) ? parsed.documents : null,
|
||
questions: Array.isArray(parsed.questions) ? parsed.questions : null,
|
||
risks: Array.isArray(parsed.risks) ? parsed.risks : null,
|
||
deadlines: Array.isArray(parsed.deadlines) ? parsed.deadlines : null,
|
||
ask_order: Array.isArray(parsed.ask_order) ? parsed.ask_order : null,
|
||
notes: toNullish(parsed.notes),
|
||
user_text: toNullish(parsed.user_text),
|
||
};
|
||
}
|
||
} catch {
|
||
wizard = null;
|
||
}
|
||
|
||
// Склеиваем user — берём user_info, плюс propertyName на всякий, и то, что лежит в диалогах
|
||
const user = {
|
||
channel: toNullish(userInfo.channel ?? propertyName.channel),
|
||
user_id: toNullish(userInfo.user_id ?? propertyName.user_id),
|
||
unified_id: toNullish(userInfo.unified_id ?? propertyName.unified_id),
|
||
telegram_id: toNullish(userInfo.telegram_id ?? propertyName.telegram_id ?? claim.telegram_id),
|
||
session_token: toNullish(userInfo.session_token ?? propertyName.session_token ?? claim.session_token),
|
||
};
|
||
|
||
// Собираем
|
||
const out = {
|
||
case: {
|
||
id: toNullish(pick(claim, 'id')),
|
||
prefix: toNullish(pick(claim, 'prefix')),
|
||
channel: toNullish(pick(claim, 'channel')),
|
||
type_code: toNullish(pick(claim, 'type_code')),
|
||
status_code: toNullish(pick(claim, 'status_code')),
|
||
created_at: toNullish(pick(claim, 'created_at')),
|
||
updated_at: toNullish(pick(claim, 'updated_at')),
|
||
telegram_id: toNullish(pick(claim, 'telegram_id')),
|
||
session_token: toNullish(pick(claim, 'session_token')),
|
||
unified_id: toNullish(pick(claim, 'unified_id')),
|
||
case_type: toNullish(pick(claim, 'case_type')),
|
||
},
|
||
|
||
user, // см. выше
|
||
|
||
answers: answersParsed,
|
||
|
||
// что загрузили
|
||
documents: mapDocuments(src.documents),
|
||
|
||
// OCR/Vision/Combined, если есть
|
||
vision_docs: mapVisionDocs(src.vision_docs),
|
||
combined_docs: mapCombinedDocs(src.combined_docs),
|
||
|
||
// что там в "coverage_report" (кто что заполнил/не заполнил в мастере)
|
||
coverage_report: mapCoverageReport(pick(claim, 'coverage_report')),
|
||
|
||
// история чата (ID, роли, тексты)
|
||
dialog_history: mapDialogHistory(src.dialog_history),
|
||
|
||
// на всякий — куда и что складывали на S3 в момент сохранения
|
||
s3_manifest: {
|
||
session_token: toNullish(pick(claim, 'session_token')),
|
||
documents_meta: Array.isArray(claim.documents_meta) ? claim.documents_meta : null,
|
||
},
|
||
|
||
// флаги/риски, что засетили при сохранении
|
||
risks: Array.isArray(claim.risks) ? claim.risks : null,
|
||
|
||
// план (wizard), как есть — пригодится фронту и валидаторам
|
||
wizard_plan: wizard,
|
||
};
|
||
|
||
return out;
|
||
}
|
||
|
||
// === entrypoint ===
|
||
const raw = items[0]?.json ?? {};
|
||
const arr = Array.isArray(raw) ? raw : [raw];
|
||
|
||
// опциональный фильтр по claim_id, если в item передадут { claim_id: "..." }
|
||
const claimIdFilter = items[0]?.json?.claim_id || items[0]?.json?.claimId || null;
|
||
|
||
// Прогоняем всё, отдаём по одному Item на кейс
|
||
const results = arr
|
||
.map(normalizeOne)
|
||
.filter(obj => (claimIdFilter ? obj.case.id === claimIdFilter : true))
|
||
.map(obj => ({ json: obj }));
|
||
|
||
return results.length ? results : [{ json: null }];
|
||
|