# 数据库兼容性 / 剖面盒导出修复报告

## 修复目标

1. 打开旧版本数据库时崩溃（schema 不兼容）
2. NWF 多文档剖面盒导出时，无命中子树泄露到导出文件
3. 反复剖面盒导出后崩溃（COM 跨线程访问）
4. 导出后剖面盒被关闭，状态栏按钮不同步

---

## 问题一：数据库 schema 不兼容导致启动崩溃

### 现象
打开带有旧版本 `.db` 文件的模型时，插件初始化报错：
```
SQL logic error: no such column: DetectionRecordId
```

### 根因
`CreateTables()` 使用 `CREATE TABLE IF NOT EXISTS` + `CREATE INDEX IF NOT EXISTS` 组合。旧数据库表已存在但 schema 是旧版（缺 `DetectionRecordId` 列），`CREATE TABLE IF NOT EXISTS` 静默跳过，紧随的 `CREATE INDEX ... ON ...(DetectionRecordId)` 直接炸。

同时 `PathDatabase.InitializeDatabase()` 还有以下问题：
- `PRAGMA user_version` 只写入、从未读取
- `CreateTables()` 无异常保护
- `BatchQueueItems` 的 `ALTER TABLE ADD COLUMN` 用裸 `try-catch` 吞异常
- `PathPlanningManager.DatabaseInitialize()` 的 `catch` 里 `throw;` 导致同一错误双重日志

### 修复
**PathDatabase.cs：**
- 新增 `SafeCreateIndex()` 方法：建索引前用 `PRAGMA table_info` 检查列是否存在，缺列记 warning 跳过，不崩
- 新增 `EnsureColumnExists()` 方法：用 `PRAGMA table_info` 检查列，缺了才 `ALTER TABLE ADD COLUMN`，不吞其他异常
- 删除无用的 `PRAGMA user_version` 写入
- `InitializeDatabase()` 内用 `try-catch` 包裹 `CreateTables()` + `EnsurePathRoutesTableExtended()`，失败时关闭连接
- 清理注释中重复/杂乱的编号
- `CollisionDetectionRecords` 建表处加外键依赖注释

**PathPlanningManager.cs：**
- 去掉 `catch` 后的 `throw;`，外层 `OnModelsCollectionChanged` 已有独立日志

---

## 问题二：NWF 多文档剖面盒导出时空白子树泄露

### 现象
NWF 文件引用多个 NWD（各为一个独立 `Model`），剖面盒只覆盖其中一个子树的元素，导出后其他平行子树仍然出现在 NWD 中。

### 根因
`SectionBoxExporter.CalculateHiddenItems()` 通过"兄弟节点算法"确定隐藏项：收集可见项（剖面盒内元素及其祖先）的父节点，隐藏同父节点下的不可见兄弟。当模型有多个平行 `RootItem`（来自不同 `Model`）时，完全空白的子树没有任何节点进入 `visibleSet`，其 `RootItem` 不会出现在 `uniqueParents` 中，整棵子树被遗漏。

### 修复
**SectionBoxExporter.GetObjectsAndHiddenItems()：**
在 `CalculateHiddenItems()` 之后补一步——遍历所有 `document.Models` 的 `RootItem`，不在 `visibleSet` 中的直接加入 `ItemsToHide`。`SetHidden` 会级联隐藏所有后代，无需递归。

---

## 问题三：反复剖面盒导出崩溃

### 现象
连续多次"设置剖面盒 → 导出"操作，前几次正常，后续某次崩溃（无 managed 异常日志）。

### 根因
`LayerManagementViewModel` 中有两处不必要的 `await Task.Run()`：
- `GetObjectsAndHiddenItems()` 被包在 `Task.Run` 中，在后台线程调用 Navisworks COM API（`Model.HasGeometry`、`BoundingBox()` 等）
- `NwdExportHelper.ExportToNwdWithPrecomputedHiddenItems()` 同样被包在 `Task.Run` 中

Navisworks COM API 要求 STA 线程访问，跨线程调用在少量使用时不崩，多次使用后触发 STA 违规导致进程崩溃。

### 修复
**LayerManagementViewModel.cs：**
- 去掉 `GetObjectsAndHiddenItems` 外的 `await Task.Run()` 包装，直接在 UI 线程调用
- 去掉 `ExportToNwdWithPrecomputedHiddenItems` 外的 `await Task.Run()` 包装，该方法内部已有 `Dispatcher.Invoke`，无需外层再切换线程

---

## 涉及文件

| 文件 | 改动 |
|------|------|
| `src/Core/PathDatabase.cs` | 新增 `SafeCreateIndex`、`EnsureColumnExists`；`InitializeDatabase` 加 try-catch；删除 user_version；清理注释 |
| `src/Core/PathPlanningManager.cs` | 去掉 `DatabaseInitialize` catch 中的 re-throw |
| `src/Core/SectionBoxExporter.cs` | `GetObjectsAndHiddenItems` 补空白 RootItem 隐藏逻辑 |
| `src/Utils/NwdExportHelper.cs` | 调试日志（已清理） |
| `src/Utils/VisibilityHelper.cs` | 调试日志（已清理） |
| `src/UI/WPF/ViewModels/LayerManagementViewModel.cs` | 去掉两处 `Task.Run` |
| `deploy-plugin.bat` | `powershell` → `pwsh`（兼容环境） |
| `AGENTS.md` | 补充编译/部署调用方式说明 |
| `src/Utils/SectionClipHelper.cs` | 新增 `RestoreClipBox` 公开方法 |

---

## 问题四：导出后剖面盒被关闭，状态栏按钮不同步

### 现象
导出剖面盒后，Navisworks `ExportToNwd` 内部会关闭剖分功能，但状态栏两个剖面盒图标按钮仍保持激活状态。

### 根因
`ExportToNwd` 是 Navisworks 内部行为，绕过我们的 `SetClipBoxState`。ViewModel 的 `IsClipBoxEnabled`/`IsSelectionClipBoxEnabled` 未感知到变化。

### 修复
**VisibilityHelper.ExecuteWithPrecomputedHiddenItems：**
导出前用 `SectionClipHelper.IsClipBoxEnabled` + `TryGetCurrentClipBox` 保存剖面盒状态，导出后检查并在被关闭时恢复。

**SectionClipHelper：**
新增 `RestoreClipBox(BoundingBox3D)` 公开方法。
