using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using Autodesk.Revit.DB;
using RevitHttpControl.Models;
namespace RevitHttpControl.Services
{
///
/// 薄壳优化执行服务
///
public class ShellOptimizer
{
private readonly ShellAnalyzer _analyzer;
public ShellOptimizer()
{
_analyzer = new ShellAnalyzer();
}
///
/// 执行薄壳优化
///
public ShellOptimizeResult ExecuteOptimization(Document doc, ShellOptimizeMode mode, bool backupOriginal = true)
{
if (doc == null)
throw new ArgumentNullException(nameof(doc));
var stopwatch = Stopwatch.StartNew();
var result = new ShellOptimizeResult();
try
{
// 1. 获取原始文件大小
var originalSize = GetDocumentFileSize(doc);
result.OriginalSize = FormatFileSize(originalSize);
// 2. 分析模型获取删除列表
var analysis = _analyzer.AnalyzeModel(doc, mode);
var elementsToDelete = GetElementsToDelete(doc, mode);
// 3. 创建备份(如果需要)
if (backupOriginal)
{
result.BackupPath = CreateBackup(doc);
}
// 4. 执行删除操作
var deletedCount = DeleteElements(doc, elementsToDelete, mode);
result.RemovedCount = deletedCount;
// 5. 保存文档并计算优化后大小
SaveDocument(doc);
var optimizedSize = GetDocumentFileSize(doc);
result.OptimizedSize = FormatFileSize(optimizedSize);
// 6. 计算减少百分比
if (originalSize > 0)
{
var reduction = ((double)(originalSize - optimizedSize) / originalSize) * 100;
result.Reduction = $"{reduction:F1}%";
}
else
{
result.Reduction = "0%";
}
stopwatch.Stop();
result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
// 如果有备份且操作失败,可以考虑恢复备份
throw new InvalidOperationException($"薄壳优化执行失败: {ex.Message}", ex);
}
}
///
/// 按类别执行自定义删除
///
public ShellOptimizeResult ExecuteOptimizationByCategories(Document doc, IEnumerable categoryIds, bool backupOriginal = true)
{
if (doc == null)
throw new ArgumentNullException(nameof(doc));
if (categoryIds == null)
throw new ArgumentNullException(nameof(categoryIds));
var categorySet = new HashSet(categoryIds);
if (categorySet.Count == 0)
throw new ArgumentException("至少需要指定一个要删除的类别", nameof(categoryIds));
var stopwatch = Stopwatch.StartNew();
var result = new ShellOptimizeResult();
try
{
var originalSize = GetDocumentFileSize(doc);
result.OriginalSize = FormatFileSize(originalSize);
var elementsToDelete = GetElementsToDeleteByCategories(doc, categorySet);
if (backupOriginal)
{
result.BackupPath = CreateBackup(doc);
}
// 自定义删除沿用标准安全检查逻辑(非 EnvelopeOnly)
var deletedCount = DeleteElements(doc, elementsToDelete, ShellOptimizeMode.Standard);
result.RemovedCount = deletedCount;
SaveDocument(doc);
var optimizedSize = GetDocumentFileSize(doc);
result.OptimizedSize = FormatFileSize(optimizedSize);
if (originalSize > 0)
{
var reduction = ((double)(originalSize - optimizedSize) / originalSize) * 100;
result.Reduction = $"{reduction:F1}%";
}
else
{
result.Reduction = "0%";
}
stopwatch.Stop();
result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
return result;
}
catch (Exception ex)
{
stopwatch.Stop();
result.ProcessingTimeSeconds = stopwatch.Elapsed.TotalSeconds;
throw new InvalidOperationException($"自定义类别删除执行失败: {ex.Message}", ex);
}
}
///
/// 根据分析结果获取需要删除的构件
///
private List GetElementsToDelete(Document doc, ShellOptimizeMode mode)
{
var elementsToDelete = new List();
// 获取所有需要删除的构件
var allElements = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.ToElements();
foreach (var element in allElements)
{
if (_analyzer.GetElementAction(element, mode) == ElementAction.Remove)
{
elementsToDelete.Add(element.Id);
}
}
return elementsToDelete;
}
///
/// 按类别获取待删除构件
///
private List GetElementsToDeleteByCategories(Document doc, HashSet categoryIds)
{
var elementsToDelete = new List();
var allElements = new FilteredElementCollector(doc)
.WhereElementIsNotElementType()
.WhereElementIsViewIndependent()
.ToElements();
foreach (var element in allElements)
{
var categoryId = element?.Category?.Id?.IntegerValue;
if (categoryId.HasValue && categoryIds.Contains(categoryId.Value))
{
elementsToDelete.Add(element.Id);
}
}
return elementsToDelete;
}
///
/// 批量删除构件
///
private int DeleteElements(Document doc, List elementIds, ShellOptimizeMode mode)
{
if (elementIds == null || elementIds.Count == 0)
return 0;
int deletedCount = 0;
const int batchSize = 100; // 批量处理大小
// 分批删除以提高性能和稳定性
for (int i = 0; i < elementIds.Count; i += batchSize)
{
var batch = elementIds.Skip(i).Take(batchSize).ToList();
deletedCount += DeleteElementBatch(doc, batch, mode);
}
return deletedCount;
}
///
/// 删除一批构件
///
private int DeleteElementBatch(Document doc, List elementIds, ShellOptimizeMode mode)
{
using (var transaction = new Transaction(doc, "薄壳优化删除构件"))
{
transaction.Start();
try
{
// 过滤出可以删除的构件
var deletableIds = new List();
foreach (var id in elementIds)
{
var element = doc.GetElement(id);
if (element != null && CanDeleteElement(element, mode))
{
deletableIds.Add(id);
}
}
// 执行删除
if (deletableIds.Count > 0)
{
var deletedIds = doc.Delete(deletableIds);
transaction.Commit();
return deletedIds.Count;
}
else
{
transaction.RollBack();
return 0;
}
}
catch (Exception ex)
{
transaction.RollBack();
// 记录错误但继续处理其他构件
System.Diagnostics.Debug.WriteLine($"删除构件批次失败: {ex.Message}");
return 0;
}
}
}
///
/// 检查构件是否可以安全删除
///
private bool CanDeleteElement(Element element, ShellOptimizeMode mode)
{
try
{
// 检查构件是否被锁定
if (element.Pinned)
return false;
// 检查构件是否在工作集中被其他用户编辑
var worksetId = element.WorksetId;
if (worksetId != WorksetId.InvalidWorksetId)
{
var doc = element.Document;
// 仅对工作共享文档执行“他人占用”校验,避免本地/样例文件误判。
if (doc.IsWorkshared)
{
var worksetTable = doc.GetWorksetTable();
var workset = worksetTable.GetWorkset(worksetId);
var owner = workset?.Owner;
var currentUser = doc.Application?.Username;
if (workset != null &&
workset.IsOpen &&
!string.IsNullOrWhiteSpace(owner) &&
!string.Equals(owner, currentUser, StringComparison.OrdinalIgnoreCase))
{
return false;
}
}
}
// 检查是否有依赖关系(如主体构件)
// EnvelopeOnly: 允许删除“宿主”类元素(如内墙),由Revit级联删除其托管构件
if (mode != ShellOptimizeMode.EnvelopeOnly && HasCriticalDependencies(element))
return false;
return true;
}
catch
{
// 如果检查过程出错,为安全起见不删除
return false;
}
}
///
/// 检查构件是否有关键依赖关系
///
private bool HasCriticalDependencies(Element element)
{
try
{
// 对于墙体,检查是否有门窗等构件托管在上面
if (element is Wall wall)
{
var doc = wall.Document;
var collector = new FilteredElementCollector(doc);
var familyInstances = collector.OfClass(typeof(FamilyInstance)).ToElements();
// 检查是否有门窗托管在这面墙上
foreach (Element e in familyInstances)
{
var familyInstance = e as FamilyInstance;
if (familyInstance?.Host?.Id == wall.Id)
{
return true; // 有构件托管在这面墙上,不能删除
}
}
}
return false;
}
catch
{
return true; // 无法确定时,为安全起见认为有依赖
}
}
///
/// 创建文件备份
///
private string CreateBackup(Document doc)
{
try
{
var originalPath = doc.PathName;
if (string.IsNullOrEmpty(originalPath))
{
// 如果文档未保存,先保存
throw new InvalidOperationException("文档必须先保存才能创建备份");
}
var fileName = Path.GetFileNameWithoutExtension(originalPath);
var extension = Path.GetExtension(originalPath);
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
var backupFileName = $"{fileName}_backup_{timestamp}{extension}";
// 先尝试写入原目录;若无权限则回退到用户本地目录。
var sourceDirectory = Path.GetDirectoryName(originalPath);
if (!string.IsNullOrWhiteSpace(sourceDirectory))
{
var backupPathInSource = Path.Combine(sourceDirectory, backupFileName);
try
{
File.Copy(originalPath, backupPathInSource, false);
return backupPathInSource;
}
catch (UnauthorizedAccessException)
{
// Ignore and fallback.
}
catch (IOException ex) when (File.Exists(backupPathInSource))
{
throw new InvalidOperationException($"备份文件已存在: {backupPathInSource}", ex);
}
}
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var fallbackDirectory = Path.Combine(localAppData, "RevitHttpControl", "Backups");
Directory.CreateDirectory(fallbackDirectory);
var backupPathInFallback = Path.Combine(fallbackDirectory, backupFileName);
File.Copy(originalPath, backupPathInFallback, false);
return backupPathInFallback;
}
catch (Exception ex)
{
throw new InvalidOperationException($"创建备份失败: {ex.Message}", ex);
}
}
///
/// 保存文档
///
private void SaveDocument(Document doc)
{
try
{
if (!doc.IsModified)
{
return;
}
try
{
doc.Save();
return;
}
catch (Exception ex) when (IsReadOnlySaveError(ex))
{
SaveDocumentAsWritableCopy(doc);
return;
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"保存文档失败: {ex.Message}", ex);
}
}
private bool IsReadOnlySaveError(Exception ex)
{
if (ex == null) return false;
var message = ex.Message ?? string.Empty;
return message.IndexOf("read-only", StringComparison.OrdinalIgnoreCase) >= 0 ||
message.IndexOf("只读", StringComparison.OrdinalIgnoreCase) >= 0;
}
private void SaveDocumentAsWritableCopy(Document doc)
{
var originalPath = doc.PathName;
if (string.IsNullOrWhiteSpace(originalPath))
throw new InvalidOperationException("文档为只读且无法获取原始路径,不能自动另存为");
var fileName = Path.GetFileNameWithoutExtension(originalPath);
var extension = Path.GetExtension(originalPath);
var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
var outputDirectory = Path.Combine(localAppData, "RevitHttpControl", "Optimized");
Directory.CreateDirectory(outputDirectory);
var outputPath = Path.Combine(outputDirectory, $"{fileName}_optimized_{timestamp}{extension}");
var saveAsOptions = new SaveAsOptions
{
OverwriteExistingFile = false,
Compact = true
};
doc.SaveAs(outputPath, saveAsOptions);
}
///
/// 获取文档文件大小
///
private long GetDocumentFileSize(Document doc)
{
try
{
var filePath = doc.PathName;
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
return 0;
var fileInfo = new FileInfo(filePath);
return fileInfo.Length;
}
catch
{
return 0;
}
}
///
/// 格式化文件大小显示
///
private string FormatFileSize(long bytes)
{
if (bytes == 0) return "0 字节";
string[] sizes = { "字节", "KB", "MB", "GB", "TB" };
int order = 0;
double size = bytes;
while (size >= 1024 && order < sizes.Length - 1)
{
order++;
size /= 1024;
}
return $"{size:F2} {sizes[order]}";
}
}
}