Files
test_demo/MX-PD-盘古 - new/PanGu.DieBonderApp/plans/recipe-code-review.md
Shi.Ji e31d3560bb 添加 MX-PD-盘古 项目文件
将 MX-PD-盘古 - new 目录下的所有文件添加到主仓库
2026-05-18 11:43:09 +08:00

216 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Recipe 模块代码审阅报告
## 一、架构概览
配方模块采用 MVVM 架构分为四层配方体系Carrier → Substrate → Wafer → Process形成级联关系。使用 Stylet 框架 + JSON 文件持久化。
```mermaid
graph TD
A[CarrierRecipe 载具配方] --> B[SubstrateRecipe 基板配方]
B --> C[WaferRecipe 芯片配方]
C --> D[ProcessRecipe 工艺配方]
E[RecipeManager] --> A
E --> B
E --> C
E --> D
F[RecipeWrapManager] --> G[RecipeWrap 列表]
H[RecipeViewModel] --> I[CarrierRecipeViewModel]
H --> J[SubstrateRecipeViewModel]
H --> K[WaferRecipeViewModel]
H --> L[ProcessRecipeViewModel]
```
---
## 二、发现的问题
### 问题 1ViewModel 层存在严重的代码重复(高优先级)
`CarrierRecipeViewModel``SubstrateRecipeViewModel``WaferRecipeViewModel``ProcessRecipeViewModel` 四个类中,以下逻辑几乎完全相同:
- `SelectedRecipeWrap` 属性的 setter 逻辑(离开检查 + 加载配方 + 快照)
- `RegisterButtonGroupEvents()` 方法18 行事件注册代码完全一致)
- `TryLeaveCurrentRecipeContext()` 方法
- `SaveCurrentRecipe()` / `CaptureSavedSnapshot()` / `IsRecipeDirty()` / `SelectRecipeWrapInternal()` 方法
- `OnCreateNewRecipe` / `OnDeleteRecipe` / `OnOpenRecipe` / `OnSaveRecipe` / `OnClearRecipe` 等事件处理
**改进建议**:提取一个泛型基类 `RecipeViewModelBase<TRecipe> where TRecipe : RecipeBase`,将公共逻辑下沉,子类只需覆写差异部分(如加载配方的具体类型、级联逻辑等)。
---
### 问题 2RecipeButtonGroupViewModel 在 ViewModel 中直接创建 View中优先级
`RecipeButtonGroupViewModel.ShowRecipeWindow<TWindow, TViewModel>()` 方法直接 `new TWindow()`,违反了 MVVM 的核心原则——ViewModel 不应直接创建和管理 View。
**改进建议**:使用 Stylet 的 `IWindowManager.ShowDialog()` 来显示对话框窗口,或引入一个 `IDialogService` 接口来解耦。
---
### 问题 3RecipeNameWindowModel 与 RecipeReNameWindowModel 代码重复(中优先级)
这两个类的验证逻辑、`OkCommand`/`CancelCommand``UpdateValidationMessage()` 方法几乎完全一致,`RecipeReNameWindowModel` 仅多了一个 `OldRecipeName` 属性。
**改进建议**:让 `RecipeReNameWindowModel` 继承 `RecipeNameWindowModel`,或提取公共基类。
---
### 问题 4RecipeBase 同时实现 INotifyPropertyChanged 和继承 JsonFileWritableBase中优先级
`RecipeBase` 手动实现了 `INotifyPropertyChanged`(包含 `SetProperty<T>` 辅助方法),但其子类的子属性模型(如 `SubstrateInfo``MarkData` 等)继承的是 Stylet 的 `PropertyChangedBase`,使用 `SetAndNotify`。这导致项目中存在两套属性变更通知机制。
**改进建议**:统一通知机制。如果 `JsonFileWritableBase` 不能继承 `PropertyChangedBase`,考虑用组合代替继承,或让 `RecipeBase` 也使用 Stylet 的通知基础设施。
---
### 问题 5CameraBaseViewModel 放在 Models 目录下(低优先级)
`CameraBaseViewModel` 是一个 ViewModel 基类,但放在了 `Recipe/Models/` 目录下,违反了项目的分层约定。
**改进建议**:移动到 `Recipe/ViewModel/` 目录。
---
### 问题 6命名拼写错误低优先级
- `SubtrateMarkPars` → 应为 `SubstrateMarkPars`(缺少 `s`
- `SubtrateMarkParameterInfo` → 应为 `SubstrateMarkParameterInfo`
- `ThickNess` → 应为 `Thickness`(大小写不一致)
- `PIDOperater` → 应为 `PIDOperator`
**改进建议**:统一修正拼写,使用 IDE 的重命名功能确保所有引用同步更新。
---
### 问题 7PIDOperater 使用反射遍历属性,且通过 IoC.Get 静态调用获取硬件(中优先级)
`PIDOperater.SendPIDParameters()``ReadPIDParametersFromDevice()` 使用反射遍历所有属性来读写 PID 参数,性能不佳且不易维护。同时 `GetControler()` 通过 `IoC.Get<HardwareManager>()` 静态调用获取依赖,违反了依赖注入原则。
**改进建议**
-`IMotionController``HardwareManager` 通过构造函数注入
- 考虑用显式的参数列表或字典代替反射遍历
---
### 问题 8RecipeManager 的删除操作缺少异常处理(中优先级)
`DeleteCarrierRecipe` / `DeleteSubstrateRecipe` / `DeleteWaferRecipe` / `DeleteProcessRecipe` 直接调用 `Directory.Delete(path, true)` 而没有任何异常处理。如果目录不存在或被占用,会直接抛出异常。
**改进建议**:添加 try-catch 和存在性检查,返回操作结果或错误信息。
---
### 问题 9OnCopyRecipe / OnImportRecipe / OnExportRecipe 为空实现(低优先级)
多个 ViewModel 中的 `OnCopyRecipe``OnImportRecipe``OnExportRecipe` 方法体为空,功能未实现。
**改进建议**:要么实现这些功能,要么在 UI 上禁用对应按钮(通过 `CanExecute` 返回 false避免用户点击后无反应。
---
### 问题 10CarrierRecipeViewModel.OnViewLoaded 中直接访问 RecipeWraps[0] 可能越界(高优先级)
```csharp
// CarrierRecipeViewModel.cs:248
RecipeWraps[0].IsInUse = true;
```
如果 `RecipeWraps` 为空集合,这行代码会抛出 `ArgumentOutOfRangeException`
**改进建议**:添加空集合检查。
---
### 问题 11SubstrateRecipeViewModel.OnViewLoaded 中 SelectedRecipeWrap 可能为 null高优先级
```csharp
// SubstrateRecipeViewModel.cs:498-499
SelectedRecipeWrap = RecipeWraps.Where(...).FirstOrDefault();
SelectedRecipeWrap.IsInUse = true; // 可能 NullReferenceException
```
`FirstOrDefault()` 可能返回 null直接访问 `.IsInUse` 会崩溃。
**改进建议**:添加 null 检查。
---
### 问题 12RecipeWrapManager.Handle 方法中复用同一个 CarrierRecipe 实例(中优先级)
```csharp
// RecipeWrapManager.cs:74
CarrierRecipe carrierRecipe = new CarrierRecipe();
foreach (var carrierWrap in CarrierRecipeWraps)
{
carrierRecipe.RecipeName = carrierWrap.RecipeName;
carrierRecipe.Read();
...
}
```
在循环中复用同一个 `CarrierRecipe` 实例,每次 `Read()` 会覆盖上一次的数据。虽然功能上可能没问题,但语义不清晰,且如果 `Read()` 失败可能导致残留旧数据。
**改进建议**:每次循环创建新实例。
---
### 问题 13MarkTeachViewModel 直接持有 Window 引用(中优先级)
```csharp
private MarkCoordinatePreviewWindow _coordinatePreviewWindow;
```
ViewModel 直接持有 `Window` 实例引用,违反 MVVM 原则。
**改进建议**:使用 `IWindowManager` 管理窗口生命周期,或通过 Messenger/EventAggregator 通知 View 层打开窗口。
---
### 问题 14事件参数类散落在不同位置低优先级
- `RecipeEventArgs` / `RecipeRenameEventArgs``Recipe/Models/RecipeEventArgs.cs`
- `SubstrateNameChangedEventArgs` 等在 `EventArgsFolder/RecipeNameChangedEventArgs.cs` 中,且命名空间为根命名空间 `MainShell`
**改进建议**:统一事件参数的命名空间和存放位置,建议放在 `Recipe/Models/` 或专门的 `Recipe/Events/` 目录下。
---
### 问题 15SubstrateHeightMeasureSetting 和 SubstrateHeightMeasurePoint 未实现 IParameterItem低优先级
其他模型类(如 `SubstrateInfo``CarrierInfo`)都实现了 `IParameterItem` 接口以支持 `Clone()`,但 `SubstrateHeightMeasureSetting``SubstrateHeightMeasurePoint` 没有实现,可能导致序列化/克隆时行为不一致。
**改进建议**:统一实现 `IParameterItem` 接口。
---
### 问题 16ActiveRecipeService 未被使用(低优先级)
`IActiveRecipeService<T>``ActiveRecipeService<T>` 定义了但在 Recipe 模块中未见使用。
**改进建议**:确认是否为未来预留,如果不需要则移除以减少代码噪音。
---
## 三、改进优先级汇总
| 优先级 | 问题编号 | 简述 |
|--------|---------|------|
| 高 | 1 | 四个 RecipeViewModel 严重代码重复,提取泛型基类 |
| 高 | 10 | CarrierRecipeViewModel.OnViewLoaded 数组越界风险 |
| 高 | 11 | SubstrateRecipeViewModel.OnViewLoaded 空引用风险 |
| 中 | 2 | ButtonGroupViewModel 直接创建 View |
| 中 | 3 | 两个命名窗口 Model 代码重复 |
| 中 | 4 | 两套属性变更通知机制并存 |
| 中 | 7 | PIDOperater 反射 + 静态 IoC 调用 |
| 中 | 8 | 删除操作缺少异常处理 |
| 中 | 12 | RecipeWrapManager 循环中复用实例 |
| 中 | 13 | MarkTeachViewModel 持有 Window 引用 |
| 低 | 5 | CameraBaseViewModel 放错目录 |
| 低 | 6 | 命名拼写错误 |
| 低 | 9 | 空实现的操作方法 |
| 低 | 14 | 事件参数类位置分散 |
| 低 | 15 | 部分模型未实现 IParameterItem |
| 低 | 16 | ActiveRecipeService 未使用 |