Files
DCIT/DCIT.Core/Services/BatchExecutionPlanner.cs
2026-07-13 10:04:36 +08:00

123 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using DCIT.Core.Models;
namespace DCIT.Core.Services
{
public sealed class BatchExecutionPlanner
{
public BatchExecutionPlan CreatePlan(IList<FileScanItem> selectedItems, int processorCount)
{
var executionItems = new List<FileScanItem>();
var skippedItems = new List<BatchExecutionSkipItem>();
var sourcePaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var outputPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
var diffPaths = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
if (selectedItems != null)
{
foreach (var item in selectedItems)
{
if (item == null)
{
continue;
}
var sourcePath = NormalizePath(item.SourcePath);
var outputPath = NormalizePath(item.OutputPath);
var diffPath = NormalizePath(item.DiffPath);
var collision = GetCollision(sourcePath, sourcePaths, BatchExecutionSkipReason.DuplicateSourcePath);
if (collision == null)
{
collision = GetCollision(outputPath, outputPaths, BatchExecutionSkipReason.DuplicateOutputPath);
}
if (collision == null && !string.IsNullOrWhiteSpace(diffPath))
{
collision = GetCollision(diffPath, diffPaths, BatchExecutionSkipReason.DuplicateDiffPath);
}
if (collision != null)
{
collision.Item = item;
skippedItems.Add(collision);
continue;
}
AddIfPresent(sourcePaths, sourcePath);
AddIfPresent(outputPaths, outputPath);
AddIfPresent(diffPaths, diffPath);
executionItems.Add(item);
}
}
var requiresSerialExecution = executionItems.Any(item => Path.GetExtension(item.SourcePath).Equals(".doc", StringComparison.OrdinalIgnoreCase));
var degreeOfParallelism = DetermineDegreeOfParallelism(executionItems.Count, processorCount, requiresSerialExecution);
return new BatchExecutionPlan
{
ExecutionItems = executionItems,
SkippedItems = skippedItems,
DegreeOfParallelism = degreeOfParallelism,
RequiresSerialExecution = requiresSerialExecution,
};
}
private static BatchExecutionSkipItem GetCollision(string path, HashSet<string> existingPaths, BatchExecutionSkipReason reason)
{
if (string.IsNullOrWhiteSpace(path) || !existingPaths.Contains(path))
{
return null;
}
return new BatchExecutionSkipItem
{
Reason = reason,
ConflictingPath = path,
ExistingPath = path,
};
}
private static void AddIfPresent(HashSet<string> set, string path)
{
if (!string.IsNullOrWhiteSpace(path))
{
set.Add(path);
}
}
private static int DetermineDegreeOfParallelism(int itemCount, int processorCount, bool requiresSerialExecution)
{
if (itemCount <= 0 || requiresSerialExecution)
{
return 1;
}
var normalizedProcessorCount = processorCount > 0 ? processorCount : Environment.ProcessorCount;
normalizedProcessorCount = Math.Max(1, normalizedProcessorCount);
return Math.Max(1, Math.Min(itemCount, normalizedProcessorCount));
}
private static string NormalizePath(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return string.Empty;
}
try
{
return Path.GetFullPath(path)
.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
catch
{
return path.Trim();
}
}
}
}