98 lines
4.6 KiB
JavaScript
98 lines
4.6 KiB
JavaScript
const fs = require('fs');
|
|
const Scene = require('../models/Scene');
|
|
const Asset = require('../models/Asset');
|
|
const Hotspot = require('../models/Hotspot');
|
|
const { logActivity } = require('./logger');
|
|
|
|
/**
|
|
* Xóa dây chuyền một Scene và tất cả các Scene con liên quan (BFS).
|
|
* Tuân thủ logic: Xóa cha thì xóa con, xóa con không xóa cha.
|
|
* @param {string} rootSceneId - ID của Scene cần xóa
|
|
* @param {string} performer - Tên người thực hiện thao tác
|
|
* @returns {Promise<{deletedCount: number}>} Số lượng scene đã xóa
|
|
*/
|
|
const deleteSceneCascade = async (rootSceneId, performer = 'System') => {
|
|
const scene = await Scene.findById(rootSceneId);
|
|
if (!scene) return { deletedCount: 0 };
|
|
|
|
const sceneId = rootSceneId.toString();
|
|
const scenesToDelete = [sceneId];
|
|
|
|
// 1. Thu thập Asset ID
|
|
const assetIds = [scene.assetId].filter(id => id);
|
|
const assets = await Asset.find({ _id: { $in: assetIds } });
|
|
|
|
// 2. Xóa tệp tin vật lý trên đĩa (Bất đồng bộ)
|
|
await Promise.all(assets.map(async asset => {
|
|
if (asset.filePath) await fs.promises.unlink(asset.filePath).catch(() => {});
|
|
}));
|
|
|
|
// 3. Dọn dẹp Hotspots (Link đi và Link đến cảnh này)
|
|
const hotspotCleanup = await Hotspot.deleteMany({
|
|
$or: [
|
|
{ parent_scene_id: sceneId },
|
|
{ target_scene_id: sceneId }
|
|
]
|
|
});
|
|
|
|
// 4. Cập nhật Tour cha (Gỡ bỏ reference và cập nhật rootSceneId)
|
|
if (scene.tourId) {
|
|
const Tour = require('../models/Tour'); // Tránh dependency vòng
|
|
const tour = await Tour.findById(scene.tourId);
|
|
if (tour) {
|
|
tour.scenes = tour.scenes.filter(id => id.toString() !== sceneId);
|
|
|
|
// Nếu cảnh bị xóa là cảnh khởi đầu, gán lại cảnh đầu tiên còn lại hoặc null
|
|
if (tour.rootSceneId && tour.rootSceneId.toString() === sceneId) {
|
|
tour.rootSceneId = tour.scenes.length > 0 ? tour.scenes[0] : null;
|
|
}
|
|
await tour.save();
|
|
|
|
// Cập nhật lại vị trí trung tâm của Tour sau khi một cảnh bị xóa khỏi danh sách
|
|
const tourController = require('../middlewares/TourController');
|
|
if (tourController && tourController.updateTourCenter) {
|
|
await tourController.updateTourCenter(tour._id);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Xóa bản ghi trong Database
|
|
await Asset.deleteMany({ _id: { $in: assetIds } });
|
|
await Scene.deleteOne({ _id: sceneId });
|
|
|
|
const sceneName = scene.name || scene.title || 'Chưa đặt tên';
|
|
await logActivity('DELETE_SCENE', {
|
|
message: `Xóa cảnh [${sceneName}] và các tài nguyên liên quan`,
|
|
sceneId: sceneId,
|
|
cleanedHotspotsCount: hotspotCleanup.deletedCount
|
|
}, performer ? performer.toString() : 'System');
|
|
|
|
return { deletedCount: 1 };
|
|
};
|
|
|
|
/**
|
|
* Lan truyền thiết lập quyền riêng tư cho toàn bộ Tour dựa trên tourId.
|
|
* Đảm bảo tính nhất quán của toàn bộ Tour khi thay đổi quyền truy cập.
|
|
* @param {string} tourId - ID của Tour thực hiện thay đổi
|
|
* @param {Object} privacyData - Dữ liệu quyền riêng tư mới
|
|
* @param {string} performer - ID người thực hiện (mặc định là System)
|
|
*/
|
|
const propagateScenePrivacy = async (tourId, privacyData, performer = 'System') => {
|
|
const { privacy, shareToken, shareTokenExpires, sharedWith, sharedEmails } = privacyData;
|
|
|
|
// 2. Chuẩn bị dữ liệu cập nhật (Chỉ cập nhật Privacy, giữ nguyên tourId)
|
|
const updateFields = { privacy };
|
|
const unsets = {};
|
|
if (privacy === 'shared') { if (shareToken) updateFields.shareToken = shareToken; else unsets.shareToken = 1; updateFields.shareTokenExpires = shareTokenExpires || undefined; updateFields.sharedWith = []; updateFields.sharedEmails = []; } else { unsets.shareToken = 1; unsets.shareTokenExpires = 1; if (privacy !== 'member') { updateFields.sharedWith = []; updateFields.sharedEmails = []; } }
|
|
|
|
const updateQuery = { $set: updateFields };
|
|
if (Object.keys(unsets).length > 0) updateQuery.$unset = unsets;
|
|
|
|
// [BẢO MẬT TUYỆT ĐỐI] Chỉ cập nhật cho các cảnh mang đúng tourId này (Con đẻ).
|
|
// Các cảnh liên kết chéo mang tourId khác nên sẽ được bảo vệ an toàn.
|
|
const result = await Scene.updateMany({ tourId: tourId }, updateQuery);
|
|
|
|
await logActivity('PROPAGATE_PRIVACY_BY_TOUR', { tourId, privacy, affectedCount: result.modifiedCount }, performer ? performer.toString() : 'System');
|
|
};
|
|
|
|
module.exports = { deleteSceneCascade, propagateScenePrivacy }; |