diff --git a/Models/ShellModels.cs b/Models/ShellModels.cs
index 90dac1c..213000c 100644
--- a/Models/ShellModels.cs
+++ b/Models/ShellModels.cs
@@ -194,7 +194,7 @@ namespace RevitHttpControl.Models
///
/// 处理耗时(秒)
///
- public double ProcessingTimeSeconds { get; set; }
+ public int ProcessingTimeSeconds { get; set; }
}
///
diff --git a/Services/ShellOptimizer.cs b/Services/ShellOptimizer.cs
index f8cc17b..f01be99 100644
--- a/Services/ShellOptimizer.cs
+++ b/Services/ShellOptimizer.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -38,7 +38,7 @@ namespace RevitHttpControl.Services
result.OriginalSize = FormatFileSize(originalSize);
// 2. 分析模型获取删除列表
- var analysis = _analyzer.AnalyzeModel(doc, mode);
+ _analyzer.AnalyzeModel(doc, mode);
var elementsToDelete = GetElementsToDelete(doc, mode);
// 3. 创建备份(如果需要)
@@ -49,7 +49,8 @@ namespace RevitHttpControl.Services
// 4. 执行删除操作
var deletedCount = DeleteElements(doc, elementsToDelete, mode);
- result.RemovedCount = deletedCount;
+ var purgedCount = PurgeUnusedFamilyData(doc);
+ result.RemovedCount = deletedCount + purgedCount;
// 5. 保存文档并计算优化后大小
SaveDocument(doc);
@@ -68,14 +69,14 @@ namespace RevitHttpControl.Services
}
stopwatch.Stop();
- result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
+ result.ProcessingTimeSeconds = (int)Math.Round(stopwatch.Elapsed.TotalSeconds, MidpointRounding.AwayFromZero);
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
- result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
+ result.ProcessingTimeSeconds = (int)Math.Round(stopwatch.Elapsed.TotalSeconds, MidpointRounding.AwayFromZero);
// 如果有备份且操作失败,可以考虑恢复备份
throw new InvalidOperationException($"薄壳优化执行失败: {ex.Message}", ex);
@@ -111,9 +112,10 @@ namespace RevitHttpControl.Services
result.BackupPath = CreateBackup(doc);
}
- // 自定义删除沿用标准安全检查逻辑(非 EnvelopeOnly)
+ // Reuse Standard safety checks for custom category deletion.
var deletedCount = DeleteElements(doc, elementsToDelete, ShellOptimizeMode.Standard);
- result.RemovedCount = deletedCount;
+ var purgedCount = PurgeUnusedFamilyData(doc);
+ result.RemovedCount = deletedCount + purgedCount;
SaveDocument(doc);
var optimizedSize = GetDocumentFileSize(doc);
@@ -130,14 +132,14 @@ namespace RevitHttpControl.Services
}
stopwatch.Stop();
- result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
+ result.ProcessingTimeSeconds = (int)Math.Round(stopwatch.Elapsed.TotalSeconds, MidpointRounding.AwayFromZero);
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
- result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
+ result.ProcessingTimeSeconds = (int)Math.Round(stopwatch.Elapsed.TotalSeconds, MidpointRounding.AwayFromZero);
throw new InvalidOperationException($"自定义类别删除执行失败: {ex.Message}", ex);
}
}
@@ -219,6 +221,7 @@ namespace RevitHttpControl.Services
using (var transaction = new Transaction(doc, "薄壳优化删除构件"))
{
transaction.Start();
+ ApplyWarningSuppression(transaction);
try
{
@@ -404,6 +407,12 @@ namespace RevitHttpControl.Services
return;
}
+ // Prefer compact save for normal local documents to reclaim free space.
+ if (TryCompactSaveInPlace(doc))
+ {
+ return;
+ }
+
try
{
doc.Save();
@@ -421,6 +430,38 @@ namespace RevitHttpControl.Services
}
}
+ private bool TryCompactSaveInPlace(Document doc)
+ {
+ if (doc == null)
+ return false;
+
+ if (doc.IsWorkshared)
+ return false;
+
+ var originalPath = doc.PathName;
+ if (string.IsNullOrWhiteSpace(originalPath))
+ return false;
+
+ if (!File.Exists(originalPath))
+ return false;
+
+ try
+ {
+ var options = new SaveAsOptions
+ {
+ OverwriteExistingFile = true,
+ Compact = true
+ };
+
+ doc.SaveAs(originalPath, options);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
private bool IsReadOnlySaveError(Exception ex)
{
if (ex == null) return false;
@@ -453,6 +494,118 @@ namespace RevitHttpControl.Services
doc.SaveAs(outputPath, saveAsOptions);
}
+ ///
+ /// Remove unused family symbols and empty families after instance deletion.
+ ///
+ private int PurgeUnusedFamilyData(Document doc)
+ {
+ if (doc == null)
+ return 0;
+
+ var usedSymbolIds = new HashSet();
+ var instances = new FilteredElementCollector(doc)
+ .OfClass(typeof(FamilyInstance))
+ .WhereElementIsNotElementType()
+ .Cast();
+
+ foreach (var instance in instances)
+ {
+ var symbolId = instance?.Symbol?.Id?.IntegerValue;
+ if (symbolId.HasValue)
+ {
+ usedSymbolIds.Add(symbolId.Value);
+ }
+ }
+
+ var unusedSymbolIds = new FilteredElementCollector(doc)
+ .OfClass(typeof(FamilySymbol))
+ .Cast()
+ .Where(symbol => symbol != null && !usedSymbolIds.Contains(symbol.Id.IntegerValue))
+ .Select(symbol => symbol.Id)
+ .ToList();
+
+ var removedSymbolCount = DeleteElementIdsInBatches(doc, unusedSymbolIds, "清理未使用族类型");
+
+ var emptyFamilyIds = new FilteredElementCollector(doc)
+ .OfClass(typeof(Family))
+ .Cast()
+ .Where(family => family != null && !family.GetFamilySymbolIds().Any())
+ .Select(family => family.Id)
+ .ToList();
+
+ var removedFamilyCount = DeleteElementIdsInBatches(doc, emptyFamilyIds, "清理空族定义");
+ return removedSymbolCount + removedFamilyCount;
+ }
+
+ private int DeleteElementIdsInBatches(Document doc, List elementIds, string transactionName)
+ {
+ if (doc == null || elementIds == null || elementIds.Count == 0)
+ return 0;
+
+ const int batchSize = 100;
+ int totalRemoved = 0;
+
+ for (int i = 0; i < elementIds.Count; i += batchSize)
+ {
+ var batchIds = elementIds.Skip(i).Take(batchSize).ToList();
+ if (batchIds.Count == 0)
+ continue;
+
+ using (var transaction = new Transaction(doc, transactionName))
+ {
+ transaction.Start();
+ ApplyWarningSuppression(transaction);
+
+ try
+ {
+ var deletedIds = doc.Delete(batchIds);
+ transaction.Commit();
+ totalRemoved += deletedIds.Count;
+ }
+ catch
+ {
+ transaction.RollBack();
+ }
+ }
+ }
+
+ return totalRemoved;
+ }
+
+ private void ApplyWarningSuppression(Transaction transaction)
+ {
+ if (transaction == null)
+ return;
+
+ var options = transaction.GetFailureHandlingOptions();
+ options.SetFailuresPreprocessor(new WarningFailurePreprocessor());
+ options.SetClearAfterRollback(true);
+ transaction.SetFailureHandlingOptions(options);
+ }
+
+ private class WarningFailurePreprocessor : IFailuresPreprocessor
+ {
+ public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
+ {
+ if (failuresAccessor == null)
+ return FailureProcessingResult.Continue;
+
+ var failures = failuresAccessor.GetFailureMessages();
+ if (failures == null || failures.Count == 0)
+ return FailureProcessingResult.Continue;
+
+ foreach (var failure in failures)
+ {
+ if (failure != null && failure.GetSeverity() == FailureSeverity.Warning)
+ {
+ failuresAccessor.DeleteWarning(failure);
+ }
+ }
+
+ return FailureProcessingResult.Continue;
+ }
+ }
+
///
/// 获取文档文件大小
///
diff --git a/app.config b/app.config
index 1729791..1ec619e 100644
--- a/app.config
+++ b/app.config
@@ -1,8 +1,8 @@
-
-
+
+