Refactor giai đoạn 1: test các tính năng vừa thay đổi như tour, scene...
This commit is contained in:
@@ -7,76 +7,77 @@ 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 gốc cần xóa
|
||||
* @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') => {
|
||||
// 0. Xác định tourId của scene gốc để thiết lập biên giới xóa
|
||||
const rootScene = await Scene.findById(rootSceneId);
|
||||
if (!rootScene) return { deletedCount: 0 };
|
||||
const tourId = rootScene.tourId ? rootScene.tourId.toString() : null;
|
||||
const scene = await Scene.findById(rootSceneId);
|
||||
if (!scene) return { deletedCount: 0 };
|
||||
|
||||
// [BIÊN GIỚI TOUR] Xác định danh sách cần xóa
|
||||
const isRoot = tourId && tourId === rootSceneId.toString();
|
||||
let scenesToDelete = [];
|
||||
|
||||
if (isRoot) {
|
||||
// Nếu xóa gốc: Xóa mọi thứ thuộc tourId này (Bao gồm con đẻ, loại trừ liên kết)
|
||||
const tourScenes = await Scene.find({ tourId: rootScene.tourId }).select('_id');
|
||||
scenesToDelete = tourScenes.map(s => s._id.toString());
|
||||
} else {
|
||||
// Nếu xóa cảnh con lẻ: Chỉ xóa đúng nó
|
||||
scenesToDelete = [rootSceneId.toString()];
|
||||
}
|
||||
const sceneId = rootSceneId.toString();
|
||||
const scenesToDelete = [sceneId];
|
||||
|
||||
// 1. Thu thập Asset ID
|
||||
// 2. Thu thập tất cả Asset ID liên quan
|
||||
const scenes = await Scene.find({ _id: { $in: scenesToDelete } });
|
||||
const assetIds = scenes.map(s => s.assetId).filter(id => id);
|
||||
const assetIds = [scene.assetId].filter(id => id);
|
||||
const assets = await Asset.find({ _id: { $in: assetIds } });
|
||||
|
||||
// 3. Xóa tệp tin vật lý trên đĩa (Bất đồng bộ)
|
||||
// 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(() => {});
|
||||
}));
|
||||
|
||||
// 4. Dọn dẹp Hotspots (Cả link đi từ cảnh bị xóa và link từ các tour khác trỏ ĐẾN cảnh này)
|
||||
const hotspotCleanup = await Hotspot.deleteMany({
|
||||
// 3. Dọn dẹp Hotspots (Link đi và Link đến cảnh này)
|
||||
const hotspotCleanup = await Hotspot.deleteMany({
|
||||
$or: [
|
||||
{ parent_scene_id: { $in: scenesToDelete } },
|
||||
{ target_scene_id: { $in: scenesToDelete } }
|
||||
]
|
||||
{ parent_scene_id: sceneId },
|
||||
{ target_scene_id: sceneId }
|
||||
]
|
||||
});
|
||||
|
||||
// 5. Xóa bản ghi trong Database
|
||||
const assetCleanup = await Asset.deleteMany({ _id: { $in: assetIds } });
|
||||
const sceneCleanup = await Scene.deleteMany({ _id: { $in: scenesToDelete } });
|
||||
// 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();
|
||||
|
||||
const tourName = rootScene.name || rootScene.title || 'Chưa đặt tên';
|
||||
const childCount = scenesToDelete.length > 0 ? scenesToDelete.length - 1 : 0;
|
||||
await logActivity('CASCADE_DELETE_SCENE', {
|
||||
message: isRoot ? `Xóa trọn bộ Tour [${tourName}] và ${childCount} cảnh con` : `Xóa cảnh lẻ [${tourName}] khỏi Tour`,
|
||||
deletedScenesCount: scenesToDelete.length,
|
||||
// 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: scenesToDelete.length };
|
||||
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} rootSceneId - ID của cảnh gốc thực hiện thay đổi
|
||||
* @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 (rootSceneId, privacyData, performer = 'System') => {
|
||||
const rootScene = await Scene.findById(rootSceneId);
|
||||
if (!rootScene) return;
|
||||
|
||||
const tourId = rootScene.tourId || rootScene._id;
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user