const mongoose = require('mongoose'); const fs = require('fs'); const path = require('path'); const connectDB = require('../config/db'); const Scene = require('../models/Scene'); const Hotspot = require('../models/Hotspot'); /** * Script migration chuẩn hóa trường tourId cho tất cả các Scene dựa trên liên kết Hotspot thực tế. * Đảm bảo tính nhất quán cho các tính năng Privacy Cascading và Cascade Delete. */ const migrateTourIds = async () => { try { console.log('--- Bắt đầu quy trình chuẩn hóa tourId ---'); await connectDB(); // Bước 1: Xóa bỏ các giá trị tourId rác (null, rỗng) để xử lý sạch await Scene.updateMany( { tourId: { $in: [null, ""] } }, { $unset: { tourId: 1 } } ); // Bước 2: Tìm các cảnh gốc (Roots) // Cảnh gốc là cảnh không có bất kỳ hotspot đi tới nào (không tính link quay lại - is_auto_return) const targetSceneIds = await Hotspot.find({ is_auto_return: { $ne: true } }).distinct('target_scene_id'); const rootScenes = await Scene.find({ _id: { $nin: targetSceneIds } }); console.log(`- Tìm thấy ${rootScenes.length} cảnh gốc tiềm năng.`); const processedScenes = new Set(); let updatedCount = 0; for (const root of rootScenes) { const rootIdStr = root._id.toString(); if (processedScenes.has(rootIdStr)) continue; console.log(`- Đang xử lý Tour: ${root.name || root.title || root._id}`); // Cập nhật rootId cho chính nó await Scene.updateOne({ _id: root._id }, { $set: { tourId: root._id } }); processedScenes.add(rootIdStr); updatedCount++; // Duyệt BFS để gán tourId cho toàn bộ cây tour let queue = [root._id]; while (queue.length > 0) { const parentId = queue.shift(); const hotspots = await Hotspot.find({ parent_scene_id: parentId, is_auto_return: { $ne: true } }); for (const hs of hotspots) { if (hs.target_scene_id) { const childIdStr = hs.target_scene_id.toString(); if (!processedScenes.has(childIdStr)) { await Scene.updateOne( { _id: hs.target_scene_id }, { $set: { tourId: root._id } } ); processedScenes.add(childIdStr); updatedCount++; queue.push(hs.target_scene_id); } } } } } // Bước 3: Xử lý các cảnh mồ côi hoặc vòng lặp kín (tự trỏ về chính mình làm gốc) const orphanScenes = await Scene.find({ tourId: { $exists: false } }); let orphanCount = 0; for (const scene of orphanScenes) { await Scene.updateOne({ _id: scene._id }, { $set: { tourId: scene._id } }); orphanCount++; } console.log(`- Đã cập nhật ${updatedCount} cảnh theo luồng tour.`); console.log(`- Đã xử lý ${orphanCount} cảnh mồ côi/vòng lặp tự trỏ về chính mình.`); console.log('--- Hoàn tất migration tourId! ---'); mongoose.connection.close(); process.exit(0); } catch (error) { console.error('Lỗi Migration:', error.message); if (mongoose.connection) mongoose.connection.close(); process.exit(1); } }; migrateTourIds();