feat(template-folder): move with cycle detection and sibling uniqueness
This commit is contained in:
@@ -67,4 +67,55 @@ public class TemplateFolderService
|
||||
|
||||
return Result<TemplateFolder>.Success(folder);
|
||||
}
|
||||
|
||||
public async Task<Result<TemplateFolder>> MoveFolderAsync(
|
||||
int folderId, int? newParentId, string user,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var folder = await _repository.GetFolderByIdAsync(folderId, cancellationToken);
|
||||
if (folder == null)
|
||||
return Result<TemplateFolder>.Failure($"Folder with ID {folderId} not found.");
|
||||
|
||||
if (newParentId.HasValue)
|
||||
{
|
||||
if (newParentId.Value == folderId)
|
||||
return Result<TemplateFolder>.Failure("Cannot move a folder into itself (cycle).");
|
||||
|
||||
var newParent = await _repository.GetFolderByIdAsync(newParentId.Value, cancellationToken);
|
||||
if (newParent == null)
|
||||
return Result<TemplateFolder>.Failure($"Target folder with ID {newParentId.Value} not found.");
|
||||
|
||||
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
||||
// Walk up from newParentId — if we encounter folderId, the move would create a cycle.
|
||||
var byId = all.ToDictionary(f => f.Id);
|
||||
var cursor = newParentId;
|
||||
while (cursor.HasValue)
|
||||
{
|
||||
if (cursor.Value == folderId)
|
||||
return Result<TemplateFolder>.Failure("Cannot move a folder under one of its descendants (cycle).");
|
||||
cursor = byId.TryGetValue(cursor.Value, out var node) ? node.ParentFolderId : null;
|
||||
}
|
||||
|
||||
// Sibling-name uniqueness in destination.
|
||||
if (all.Any(f => f.Id != folderId
|
||||
&& f.ParentFolderId == newParentId
|
||||
&& string.Equals(f.Name, folder.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
return Result<TemplateFolder>.Failure($"A folder named '{folder.Name}' already exists in the target folder.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var all = await _repository.GetAllFoldersAsync(cancellationToken);
|
||||
if (all.Any(f => f.Id != folderId
|
||||
&& f.ParentFolderId == null
|
||||
&& string.Equals(f.Name, folder.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
return Result<TemplateFolder>.Failure($"A folder named '{folder.Name}' already exists at the root.");
|
||||
}
|
||||
|
||||
folder.ParentFolderId = newParentId;
|
||||
await _repository.UpdateFolderAsync(folder, cancellationToken);
|
||||
await _repository.SaveChangesAsync(cancellationToken);
|
||||
await _auditService.LogAsync(user, "Move", "TemplateFolder", folder.Id.ToString(), folder.Name, folder, cancellationToken);
|
||||
|
||||
return Result<TemplateFolder>.Success(folder);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user