- Исправлен N8N_CODE_PROCESS_UPLOADED_FILES_FIXED.js: использовать uploads_field_labels[0] вместо [grp] - Создан SQL_CLAIMSAVE_FIXED_NEW_FLOW_DEDUP.sql с дедупликацией documents_meta - Создан SQL_CLEANUP_DOCUMENTS_META_DUPLICATES.sql для очистки существующих дубликатов - Создан полный уникальный индекс idx_document_texts_hash_unique на document_texts(file_hash) - Добавлен SESSION_LOG_2025-11-28_documents_dedup.md с описанием всех изменений Fixes: - field_label теперь корректно отображает 'Переписка' вместо 'group-2' - documents_meta не накапливает дубликаты при повторных сохранениях - ON CONFLICT (file_hash) теперь работает для document_texts
125 lines
5.7 KiB
Python
125 lines
5.7 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Детальная проверка документов в черновике
|
||
"""
|
||
import asyncio
|
||
import asyncpg
|
||
import json
|
||
|
||
POSTGRES_HOST = "147.45.189.234"
|
||
POSTGRES_PORT = 5432
|
||
POSTGRES_DB = "default_db"
|
||
POSTGRES_USER = "gen_user"
|
||
POSTGRES_PASSWORD = "2~~9_^kVsU?2\\S"
|
||
|
||
CLAIM_ID = "bddb6815-8e17-4d54-a721-5e94382942c7"
|
||
|
||
async def check_documents_detailed():
|
||
conn = await asyncpg.connect(
|
||
host=POSTGRES_HOST,
|
||
port=POSTGRES_PORT,
|
||
database=POSTGRES_DB,
|
||
user=POSTGRES_USER,
|
||
password=POSTGRES_PASSWORD
|
||
)
|
||
|
||
try:
|
||
row = await conn.fetchrow("""
|
||
SELECT id, status_code, payload, updated_at
|
||
FROM clpr_claims
|
||
WHERE id::text = $1 OR payload->>'claim_id' = $1
|
||
ORDER BY updated_at DESC
|
||
LIMIT 1
|
||
""", CLAIM_ID)
|
||
|
||
if not row:
|
||
print(f"❌ Черновик {CLAIM_ID} не найден!")
|
||
return
|
||
|
||
payload = row['payload'] if isinstance(row['payload'], dict) else json.loads(row['payload'])
|
||
|
||
print("=" * 80)
|
||
print(f"📋 Статус жалобы: {CLAIM_ID}")
|
||
print("=" * 80)
|
||
print(f"Status Code: {row['status_code']}")
|
||
print(f"Обновлён: {row['updated_at']}")
|
||
|
||
# Статистика документов
|
||
documents_required = payload.get('documents_required', [])
|
||
documents_uploaded = payload.get('documents_uploaded', [])
|
||
documents_skipped = payload.get('documents_skipped', [])
|
||
current_doc_index = payload.get('current_doc_index', 0)
|
||
|
||
print(f"\n📊 Статистика документов:")
|
||
print(f" - Требуется документов: {len(documents_required)}")
|
||
print(f" - Загружено документов: {len(documents_uploaded)}")
|
||
print(f" - Пропущено документов: {len(documents_skipped)}")
|
||
print(f" - Current Doc Index: {current_doc_index}")
|
||
|
||
# Анализ статуса
|
||
print(f"\n🔍 Анализ статуса:")
|
||
status = row['status_code']
|
||
uploaded = len(documents_uploaded)
|
||
skipped = len(documents_skipped)
|
||
required = len(documents_required)
|
||
|
||
if status == 'draft_docs_complete':
|
||
print(" ✅ Все документы обработаны (загружены или пропущены)")
|
||
print(" 📝 Должно происходить: формирование заявления (wizard generation)")
|
||
elif status == 'draft_docs_progress':
|
||
print(" ⏳ Документы загружаются")
|
||
remaining = required - uploaded - skipped
|
||
print(f" 📊 Осталось обработать: {remaining} документов")
|
||
elif status == 'draft_new':
|
||
print(" 🆕 Новая жалоба, только описание")
|
||
elif status == 'draft_claim_ready':
|
||
print(" ✅ Заявление готово к отправке")
|
||
else:
|
||
print(f" ⚠️ Статус: {status}")
|
||
|
||
print("=" * 80)
|
||
print(f"\n📋 documents_meta ({len(payload.get('documents_meta', []))} шт.):")
|
||
for i, doc in enumerate(payload.get('documents_meta', [])):
|
||
print(f" {i+1}. {doc.get('field_label', 'N/A')}")
|
||
print(f" file_id: {doc.get('file_id', 'N/A')[:80]}...")
|
||
print(f" field_name: {doc.get('field_name', 'N/A')}")
|
||
|
||
print(f"\n📋 documents_uploaded ({len(payload.get('documents_uploaded', []))} шт.):")
|
||
for i, doc in enumerate(payload.get('documents_uploaded', [])):
|
||
print(f" {i+1}. Тип: {doc.get('type', 'N/A')} / {doc.get('id', 'N/A')}")
|
||
print(f" file_id: {doc.get('file_id', 'N/A')[:80]}...")
|
||
print(f" original_file_name: {doc.get('original_file_name', 'N/A')}")
|
||
|
||
print(f"\n📋 documents_required ({len(payload.get('documents_required', []))} шт.):")
|
||
for i, doc in enumerate(payload.get('documents_required', [])):
|
||
print(f" {i+1}. {doc.get('name', 'N/A')} (id: {doc.get('id', 'N/A')})")
|
||
|
||
print(f"\n📋 current_doc_index: {payload.get('current_doc_index', 'N/A')}")
|
||
|
||
# Проверяем уникальность file_id
|
||
print(f"\n🔍 Проверка уникальности file_id:")
|
||
documents_meta = payload.get('documents_meta', [])
|
||
file_ids_meta = [doc.get('file_id') for doc in documents_meta if doc.get('file_id')]
|
||
unique_file_ids_meta = list(set(file_ids_meta))
|
||
print(f" documents_meta: всего {len(file_ids_meta)}, уникальных {len(unique_file_ids_meta)}")
|
||
if len(file_ids_meta) != len(unique_file_ids_meta):
|
||
print(f" ⚠️ ЕСТЬ ДУБЛИКАТЫ!")
|
||
from collections import Counter
|
||
duplicates = [fid for fid, count in Counter(file_ids_meta).items() if count > 1]
|
||
for dup in duplicates:
|
||
print(f" - {dup[:80]}... (встречается {Counter(file_ids_meta)[dup]} раз)")
|
||
|
||
documents_uploaded = payload.get('documents_uploaded', [])
|
||
file_ids_uploaded = [doc.get('file_id') for doc in documents_uploaded if doc.get('file_id')]
|
||
unique_file_ids_uploaded = list(set(file_ids_uploaded))
|
||
print(f" documents_uploaded: всего {len(file_ids_uploaded)}, уникальных {len(unique_file_ids_uploaded)}")
|
||
if len(file_ids_uploaded) != len(unique_file_ids_uploaded):
|
||
print(f" ⚠️ ЕСТЬ ДУБЛИКАТЫ!")
|
||
|
||
finally:
|
||
await conn.close()
|
||
|
||
if __name__ == "__main__":
|
||
asyncio.run(check_documents_detailed())
|
||
|