Implement many-to-many relationship between Notes and Categories in the database model and update sync service to handle category IDs accordingly
Despliegue Automático / desplegar (push) Successful in 1m25s
Despliegue Automático / desplegar (push) Successful in 1m25s
This commit is contained in:
+4
-4
@@ -1,7 +1,7 @@
|
||||
const { DataTypes } = require('sequelize');
|
||||
const sequelize = require('../config/database');
|
||||
const User = require('./User');
|
||||
const Category = require('./Category'); // Importamos la nueva tabla
|
||||
const Category = require('./Category');
|
||||
|
||||
const Note = sequelize.define('Note', {
|
||||
id: {
|
||||
@@ -38,8 +38,8 @@ const Note = sequelize.define('Note', {
|
||||
User.hasMany(Note, { foreignKey: 'userId' });
|
||||
Note.belongsTo(User, { foreignKey: 'userId' });
|
||||
|
||||
// Relación: Una Categoría tiene muchas Notas (y una nota puede no tener categoría)
|
||||
Category.hasMany(Note, { foreignKey: 'categoryId', allowNull: true });
|
||||
Note.belongsTo(Category, { foreignKey: 'categoryId', allowNull: true });
|
||||
// 👇 NUEVA Relación: Muchos a Muchos
|
||||
Category.belongsToMany(Note, { through: 'NoteCategories', foreignKey: 'categoryId' });
|
||||
Note.belongsToMany(Category, { through: 'NoteCategories', foreignKey: 'noteId' });
|
||||
|
||||
module.exports = Note;
|
||||
+26
-16
@@ -24,7 +24,8 @@ const normalizeCategory = (category) => ({
|
||||
|
||||
const normalizeNote = (note) => ({
|
||||
id: note.id,
|
||||
categoryId: note.categoryId || null,
|
||||
// 👇 Convertimos la relación de Sequelize en un array simple de IDs de categorías
|
||||
categoryIds: note.Categories ? note.Categories.map(c => c.id) : [],
|
||||
title: note.title,
|
||||
body: note.body,
|
||||
position: note.position,
|
||||
@@ -41,7 +42,7 @@ const buildStoredNoteData = (incomingNote, existingNote = null) => {
|
||||
: (incomingNote.position ?? existingNote?.position ?? 0);
|
||||
|
||||
return {
|
||||
categoryId: incomingNote.categoryId || null,
|
||||
// categoryId eliminado de aquí
|
||||
title: permanentDelete ? '' : incomingNote.title,
|
||||
body: permanentDelete ? '' : incomingNote.body,
|
||||
position,
|
||||
@@ -74,8 +75,8 @@ class SyncService {
|
||||
const incomingNotes = Array.isArray(changes.notes) ? changes.notes : [];
|
||||
|
||||
await sequelize.transaction(async (transaction) => {
|
||||
// --- SINCRONIZACIÓN DE CATEGORÍAS ---
|
||||
for (const incomingCategory of incomingCategories) {
|
||||
|
||||
if (!incomingCategory.id || !incomingCategory.encrypted_name) {
|
||||
throw new Error('Cada categoría debe incluir id y encrypted_name (encriptado)');
|
||||
}
|
||||
@@ -109,16 +110,16 @@ class SyncService {
|
||||
delete: Boolean(incomingCategory.delete),
|
||||
serverVersion: serverVersion + 1
|
||||
}, { transaction });
|
||||
} else {
|
||||
// server wins for categories; do not create conflict copies for categories
|
||||
}
|
||||
}
|
||||
|
||||
// --- SINCRONIZACIÓN DE NOTAS ---
|
||||
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') {
|
||||
return res.status(400).json({ message: "Cada nota debe incluir id, title y body" });
|
||||
throw new Error("Cada nota debe incluir id, title y body");
|
||||
}
|
||||
|
||||
const incomingBaseVersion = getIncomingVersion('nota', incomingNote);
|
||||
@@ -131,12 +132,17 @@ class SyncService {
|
||||
if (!existingNote) {
|
||||
const storedNoteData = buildStoredNoteData(incomingNote);
|
||||
|
||||
await Note.create({
|
||||
const newNote = await Note.create({
|
||||
id: incomingNote.id,
|
||||
userId,
|
||||
...storedNoteData,
|
||||
serverVersion: 1
|
||||
}, { transaction });
|
||||
|
||||
// 👇 Actualizar la relación de muchos a muchos al crear
|
||||
if (Array.isArray(incomingNote.categoryIds)) {
|
||||
await newNote.setCategories(incomingNote.categoryIds, { transaction });
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -149,22 +155,20 @@ class SyncService {
|
||||
...storedNoteData,
|
||||
serverVersion: serverVersion + 1
|
||||
}, { transaction });
|
||||
} else if (serverVersion > incomingBaseVersion) {
|
||||
// Server version is newer -> create a new note with incoming content so user sees a duplicate
|
||||
const storedNoteData = buildStoredNoteData(incomingNote);
|
||||
|
||||
await Note.create({
|
||||
id: randomUUID(),
|
||||
userId,
|
||||
...storedNoteData,
|
||||
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)
|
||||
} else {
|
||||
throw new Error(`serverVersion inválida en la nota ${incomingNote.id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// --- RECOPILAR DATOS PARA ENVIAR DE VUELTA ---
|
||||
const [pulledCategories, pulledNotes] = await Promise.all([
|
||||
Category.findAll({
|
||||
where: {
|
||||
@@ -178,6 +182,12 @@ class SyncService {
|
||||
userId,
|
||||
updatedAt: { [Op.gt]: lastSyncAt }
|
||||
},
|
||||
// 👇 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
|
||||
}],
|
||||
order: [['updatedAt', 'ASC']]
|
||||
})
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user