fix: Исправление загрузки документов и SQL запросов

- Исправлена потеря документов при обновлении черновика (SQL объединяет вместо перезаписи)
- Исправлено определение типа документа (приоритет field_label над field_name)
- Исправлены дубликаты в documents_meta и documents_uploaded
- Добавлена передача group_index с фронтенда для правильного field_name
- Исправлены все документы в таблице clpr_claim_documents с правильными field_name
- Обновлены SQL запросы: claimsave и claimsave_final для нового флоу
- Добавлена поддержка multi-file upload для одного документа
- Исправлены дубликаты в списке загруженных документов на фронтенде

Файлы:
- SQL: SQL_CLAIMSAVE_FIXED_NEW_FLOW.sql, SQL_CLAIMSAVE_FINAL_FIXED_NEW_FLOW_WITH_UPLOADED.sql
- n8n: N8N_CODE_PROCESS_UPLOADED_FILES_FIXED.js (поддержка group_index)
- Backend: documents.py (передача group_index в n8n)
- Frontend: StepWizardPlan.tsx (передача group_index, исправление дубликатов)
- Скрипты: fix_claim_documents_field_names.py, fix_documents_meta_duplicates.py

Результат: документы больше не теряются, имеют правильные типы и field_name
This commit is contained in:
AI Assistant
2025-11-26 19:54:51 +03:00
parent 1d6c9d1f52
commit 02689e65db
42 changed files with 8314 additions and 232 deletions

View File

@@ -17,6 +17,18 @@ import './ClaimForm.css';
const { Step } = Steps;
/**
* Генерация UUID v4
* Формат: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
*/
function generateUUIDv4(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
interface FormData {
// Шаг 1: Phone
phone?: string;
@@ -633,12 +645,33 @@ export default function ClaimForm() {
console.log('🔄 Загрузка черновика: session_id из черновика:', claim.session_token);
console.log('🔄 Загрузка черновика: текущий sessionIdRef.current:', sessionIdRef.current);
console.log('🔄 Загрузка черновика: текущий formData.session_id:', formData.session_id);
const actualSessionId = sessionIdRef.current || formData.session_id;
// ✅ При загрузке черновика используем session_id из черновика (для продолжения работы с той же жалобой)
// Если session_id из черновика есть - используем его, иначе текущий
const actualSessionId = claim.session_token || sessionIdRef.current || formData.session_id;
console.log('🔄 Загрузка черновика: ИСПОЛЬЗУЕМ session_id:', actualSessionId);
// ✅ Обновляем sessionIdRef на сессию из черновика (если есть)
if (claim.session_token && claim.session_token !== sessionIdRef.current) {
sessionIdRef.current = claim.session_token;
console.log('🔄 Обновляем sessionIdRef на сессию из черновика:', claim.session_token);
}
// ✅ НОВЫЙ ФЛОУ: Извлекаем documents_required из payload
const documentsRequired = body.documents_required || payload.documents_required || [];
const documentsUploaded = body.documents_uploaded || payload.documents_uploaded || [];
const documentsSkipped = body.documents_skipped || payload.documents_skipped || [];
const currentDocIndex = body.current_doc_index ?? payload.current_doc_index ?? 0;
console.log('📋 Загрузка черновика - documents_required:', documentsRequired.length, 'шт.');
console.log('📋 Загрузка черновика - body.documents_required:', body.documents_required);
console.log('📋 Загрузка черновика - payload.documents_required:', payload.documents_required);
console.log('📋 Загрузка черновика - status_code:', claim.status_code);
console.log('📋 Загрузка черновика - все ключи payload:', Object.keys(payload));
updateFormData({
claim_id: finalClaimId, // ✅ Используем извлечённый claim_id
session_id: actualSessionId, // ✅ Используем ТЕКУЩИЙ session_id, а не старый из черновика
session_id: actualSessionId, // ✅ Используем session_id из черновика (если есть) или текущий
phone: body.phone || payload.phone || formData.phone,
email: body.email || payload.email || formData.email,
problemDescription: problemDescription || formData.problemDescription,
@@ -661,6 +694,11 @@ export default function ClaimForm() {
contact_id: body.contact_id || payload.contact_id || formData.contact_id,
project_id: body.project_id || payload.project_id || formData.project_id,
unified_id: formData.unified_id, // ✅ Сохраняем unified_id
// ✅ НОВЫЙ ФЛОУ: Документы
documents_required: documentsRequired,
documents_uploaded: documentsUploaded,
documents_skipped: documentsSkipped,
current_doc_index: currentDocIndex,
});
setSelectedDraftId(finalClaimId);
@@ -703,11 +741,16 @@ export default function ClaimForm() {
let targetStep = 1; // По умолчанию - описание (шаг 1)
if (wizardPlan) {
// ✅ Если есть wizard_plan - переходим к визарду (шаг 2)
// ✅ НОВЫЙ ФЛОУ: Если есть documents_required, показываем загрузку документов
if (documentsRequired.length > 0) {
targetStep = 2;
console.log('✅ Переходим к StepWizardPlan (шаг 2) - НОВЫЙ ФЛОУ: есть documents_required, показываем загрузку документов');
console.log('✅ documents_required:', documentsRequired.length, 'документов');
} else if (wizardPlan) {
// ✅ СТАРЫЙ ФЛОУ: Если есть wizard_plan - переходим к визарду (шаг 2)
// Пользователь уже описывал проблему, и есть план вопросов
targetStep = 2;
console.log('✅ Переходим к StepWizardPlan (шаг 2) - есть wizard_plan');
console.log('✅ Переходим к StepWizardPlan (шаг 2) - СТАРЫЙ ФЛОУ: есть wizard_plan');
console.log('✅ answers в черновике:', answers ? 'есть (показываем заполненную форму)' : 'нет (показываем пустую форму)');
} else if (problemDescription) {
// Если есть описание, но нет плана - переходим к визарду (шаг 2), чтобы получить план
@@ -793,12 +836,27 @@ export default function ClaimForm() {
console.log('🆕 Текущий currentStep:', currentStep);
console.log('🆕 isPhoneVerified:', isPhoneVerified);
// ✅ Генерируем НОВУЮ сессию для новой жалобы
const newSessionId = 'sess_' + generateUUIDv4();
console.log('🆕 Генерируем новую сессию для жалобы:', newSessionId);
console.log('🆕 Старая сессия:', sessionIdRef.current);
// ✅ Обновляем sessionIdRef на новую сессию
sessionIdRef.current = newSessionId;
// ✅ session_token в localStorage остаётся ПРЕЖНИМ (авторизация сохраняется)
const savedSessionToken = localStorage.getItem('session_token');
console.log('🆕 session_token в localStorage (авторизация):', savedSessionToken || '(не сохранён)');
console.log('🆕 Авторизация сохранена: unified_id=', formData.unified_id, 'phone=', formData.phone);
setShowDraftSelection(false);
setSelectedDraftId(null);
setHasDrafts(false); // ✅ Сбрасываем флаг наличия черновиков
// Очищаем данные формы, кроме телефона и session_id
// Очищаем данные формы и устанавливаем НОВЫЙ session_id
// unified_id, phone, contact_id остаются прежними - авторизация сохранена!
updateFormData({
session_id: newSessionId, // ✅ Новая сессия для новой жалобы
claim_id: undefined,
problemDescription: undefined,
wizardPlan: undefined,
@@ -809,6 +867,7 @@ export default function ClaimForm() {
wizardUploads: undefined,
wizardSkippedDocuments: undefined,
eventType: undefined,
// ✅ unified_id, phone, contact_id НЕ очищаем - авторизация сохраняется!
});
console.log('🆕 Переходим к шагу описания проблемы (пропускаем Phone и DraftSelection)');
@@ -819,7 +878,7 @@ export default function ClaimForm() {
// Шаг 1 - Description (сюда переходим)
// Шаг 2 - WizardPlan
setCurrentStep(1); // ✅ Переходим к описанию (индекс 1)
}, [updateFormData, currentStep, isPhoneVerified]);
}, [updateFormData, currentStep, isPhoneVerified, formData.unified_id, formData.phone]);
const handleSubmit = useCallback(async () => {
try {