From 80f1b7ad284b7fad0c3e7536074d26dd60d240ba Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 18 Aug 2026 09:23:18 +0200 Subject: [PATCH] Refactor sync service to handle conflicted category and note IDs during synchronization --- src/services/syncService.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/services/syncService.js b/src/services/syncService.js index b0d0108..814a84a 100644 --- a/src/services/syncService.js +++ b/src/services/syncService.js @@ -41,7 +41,6 @@ const buildStoredNoteData = (incomingNote, existingNote = null) => { : (incomingNote.position ?? existingNote?.position ?? 0); return { - // categoryId eliminado de aquí title: permanentDelete ? '' : incomingNote.title, body: permanentDelete ? '' : incomingNote.body, position, @@ -73,6 +72,10 @@ class SyncService { const incomingCategories = Array.isArray(changes.categories) ? changes.categories : []; const incomingNotes = Array.isArray(changes.notes) ? changes.notes : []; + // ARRAYS PARA IDs DESFASADOS + const conflictedCategoryIds = []; + const conflictedNoteIds = []; + await sequelize.transaction(async (transaction) => { // --- SINCRONIZACIÓN DE CATEGORÍAS --- for (const incomingCategory of incomingCategories) { @@ -109,6 +112,8 @@ class SyncService { delete: Boolean(incomingCategory.delete), serverVersion: serverVersion + 1 }, { transaction }); + } else if (serverVersion > incomingBaseVersion) { + conflictedCategoryIds.push(existingCategory.id); } } @@ -116,7 +121,6 @@ class SyncService { for (const incomingNote of incomingNotes) { const permanentDelete = Boolean(incomingNote.permanentDelete); - // Se verifica que sea 'string' para aceptar strings vacíos if (!incomingNote.id || typeof incomingNote.title !== 'string' || typeof incomingNote.body !== 'string') { throw new Error("Cada nota debe incluir id, title y body"); } @@ -138,7 +142,6 @@ class SyncService { serverVersion: 1 }, { transaction }); - // 👇 Actualizar la relación de muchos a muchos al crear if (Array.isArray(incomingNote.categoryIds)) { await newNote.setCategories(incomingNote.categoryIds, { transaction }); } @@ -155,12 +158,11 @@ class SyncService { serverVersion: serverVersion + 1 }, { transaction }); - // 👇 Actualizar la relación de muchos a muchos al editar if (Array.isArray(incomingNote.categoryIds)) { await existingNote.setCategories(incomingNote.categoryIds, { transaction }); } } else if (serverVersion > incomingBaseVersion) { - // Conflicto de versiones... (implementación de duplicados omitida por ahora) + conflictedNoteIds.push(existingNote.id); } else { throw new Error(`serverVersion inválida en la nota ${incomingNote.id}`); } @@ -172,20 +174,25 @@ class SyncService { Category.findAll({ where: { userId, - updatedAt: { [Op.gt]: lastSyncAt } + [Op.or]: [ + { updatedAt: { [Op.gt]: lastSyncAt } }, + { id: { [Op.in]: conflictedCategoryIds } } + ] }, order: [['updatedAt', 'ASC']] }), Note.findAll({ where: { userId, - updatedAt: { [Op.gt]: lastSyncAt } + [Op.or]: [ + { updatedAt: { [Op.gt]: lastSyncAt } }, + { id: { [Op.in]: conflictedNoteIds } } + ] }, - // 👇 Le decimos a Sequelize que incluya la tabla Category para poder sacar los IDs include: [{ model: Category, - attributes: ['id'], // Solo necesitamos el ID para el array de Flutter - through: { attributes: [] } // Omite los datos de la tabla intermedia en el JSON final + attributes: ['id'], + through: { attributes: [] } }], order: [['updatedAt', 'ASC']] })