|
|
|
|
# AuroraDesk 项目重构计划
|
|
|
|
|
|
|
|
|
|
## 📋 项目概述
|
|
|
|
|
|
|
|
|
|
**目标项目名**: AuroraDesk
|
|
|
|
|
**原项目名**: MyAvaloniaApp
|
|
|
|
|
**目标架构**: 整洁架构 (Clean Architecture)
|
|
|
|
|
**重点优化**: 减少 ViewModels 耦合,提高代码可维护性
|
|
|
|
|
**环境**: Windows 10
|
|
|
|
|
**注意事项**: 避免使用 Linux 语法,防止乱码问题
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🎯 重构目标
|
|
|
|
|
|
|
|
|
|
### 1. 项目重命名
|
|
|
|
|
- 项目名称:`MyAvaloniaApp` → `AuroraDesk`
|
|
|
|
|
- 解决方案文件:`MyAvaloniaApp.sln` → `AuroraDesk.sln`
|
|
|
|
|
- 项目文件:`MyAvaloniaApp.csproj` → `AuroraDesk.csproj`
|
|
|
|
|
- 命名空间前缀:`MyAvaloniaApp.*` → `AuroraDesk.*`
|
|
|
|
|
|
|
|
|
|
### 2. 架构重构(整洁架构)
|
|
|
|
|
|
|
|
|
|
#### 2.1 整洁架构依赖关系规则
|
|
|
|
|
|
|
|
|
|
整洁架构的核心原则是 **依赖关系必须指向内层**,即依赖方向永远向内(朝向核心层/Domain层)。
|
|
|
|
|
|
|
|
|
|
**关键要点**:
|
|
|
|
|
- **Core(Domain)层定义所有接口**,不是 Application 层
|
|
|
|
|
- **Application 和 Infrastructure 都依赖 Core**,但两者之间不互相依赖
|
|
|
|
|
- **Infrastructure 实现 Core 层定义的接口**
|
|
|
|
|
|
|
|
|
|
##### 📌 基本依赖规则
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
|
|
│ Presentation Layer (表示层) │
|
|
|
|
|
│ - ViewModels, Views, Converters │
|
|
|
|
|
│ ↓ 依赖 ↓ │
|
|
|
|
|
├─────────────────────────────────────────────┤
|
|
|
|
|
│ Application Layer (应用层) │
|
|
|
|
|
│ - Use Cases, DTOs, Application Logic │
|
|
|
|
|
│ ↓ 依赖 ↓ │
|
|
|
|
|
├─────────────────────────────────────────────┤
|
|
|
|
|
│ Core/Domain Layer (核心领域层) │
|
|
|
|
|
│ - Entities, Domain Interfaces │
|
|
|
|
|
│ - 定义所有服务接口 │
|
|
|
|
|
│ (不依赖任何外部层) │
|
|
|
|
|
└─────────────────────────────────────────────┘
|
|
|
|
|
↑ 依赖 ↑ ↑ 实现 ↑
|
|
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
|
|
│ Infrastructure Layer (基础设施层) │
|
|
|
|
|
│ - Service Implementations, Data Access │
|
|
|
|
|
│ - 实现 Core 层定义的接口 │
|
|
|
|
|
│ (不依赖 Application 层) │
|
|
|
|
|
└─────────────────────────────────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
##### ✅ 正确的依赖关系
|
|
|
|
|
|
|
|
|
|
1. **Core(核心领域层 / Domain)**
|
|
|
|
|
- ✅ **无依赖**:不依赖任何其他层
|
|
|
|
|
- ✅ **被依赖**:所有层都可以依赖 Core
|
|
|
|
|
- ✅ **包含**:
|
|
|
|
|
- 领域实体(Entities)
|
|
|
|
|
- **领域接口(如 `INavigationService`, `IDataService` 等)**
|
|
|
|
|
- 领域异常(Exceptions)
|
|
|
|
|
|
|
|
|
|
2. **Application(应用层)**
|
|
|
|
|
- ✅ **依赖**:只依赖 Core 层
|
|
|
|
|
- ✅ **不依赖**:不依赖 Infrastructure 层
|
|
|
|
|
- ✅ **被依赖**:Presentation 层可以依赖 Application
|
|
|
|
|
- ✅ **包含**:
|
|
|
|
|
- 用例逻辑(Use Cases)
|
|
|
|
|
- DTOs(数据传输对象)
|
|
|
|
|
- 应用服务逻辑(使用 Core 接口)
|
|
|
|
|
|
|
|
|
|
3. **Infrastructure(基础设施层)**
|
|
|
|
|
- ✅ **依赖**:只依赖 Core 层
|
|
|
|
|
- ✅ **不依赖**:不依赖 Application 层
|
|
|
|
|
- ✅ **实现**:实现 Core 层定义的接口
|
|
|
|
|
- ✅ **包含**:
|
|
|
|
|
- 服务实现(如 `NavigationService`, `DataService`)
|
|
|
|
|
- 数据访问实现
|
|
|
|
|
- 外部 API 调用
|
|
|
|
|
|
|
|
|
|
4. **Presentation(表示层)**
|
|
|
|
|
- ✅ **依赖**:依赖 Core 和 Application 层
|
|
|
|
|
- ✅ **不依赖**:不直接依赖 Infrastructure 层(通过接口)
|
|
|
|
|
- ✅ **包含**:ViewModels、Views、UI 相关组件
|
|
|
|
|
|
|
|
|
|
##### ❌ 错误的依赖关系(禁止)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
❌ Core → Application // 核心层不能依赖应用层
|
|
|
|
|
❌ Core → Infrastructure // 核心层不能依赖基础设施层
|
|
|
|
|
❌ Core → Presentation // 核心层不能依赖表示层
|
|
|
|
|
|
|
|
|
|
❌ Application → Infrastructure // 应用层不能依赖基础设施层
|
|
|
|
|
❌ Infrastructure → Application // 基础设施层不能依赖应用层(关键!)
|
|
|
|
|
❌ Application → Presentation // 应用层不能依赖表示层
|
|
|
|
|
|
|
|
|
|
❌ Presentation → Infrastructure // 表示层不能直接依赖基础设施层(通过接口)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
##### 🔄 依赖倒置原则(DIP)
|
|
|
|
|
|
|
|
|
|
**关键要点**:
|
|
|
|
|
- **Core 层定义接口**(如 `INavigationService`),不是 Application 层
|
|
|
|
|
- **Infrastructure 层实现 Core 层定义的接口**(如 `NavigationService`)
|
|
|
|
|
- **Application 层使用 Core 层的接口**,但不依赖 Infrastructure
|
|
|
|
|
- **Presentation 层通过接口依赖服务**,不依赖具体实现
|
|
|
|
|
|
|
|
|
|
**正确的示例**:
|
|
|
|
|
```csharp
|
|
|
|
|
// ✅ 正确:Core 层定义接口
|
|
|
|
|
namespace AuroraDesk.Core.Interfaces
|
|
|
|
|
{
|
|
|
|
|
public interface INavigationService
|
|
|
|
|
{
|
|
|
|
|
void Navigate(string pageId);
|
|
|
|
|
ObservableCollection<NavigationItem> GetNavigationItems();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IDataService { }
|
|
|
|
|
public interface IApiService { }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ 正确:Infrastructure 层实现 Core 接口
|
|
|
|
|
namespace AuroraDesk.Infrastructure.Services
|
|
|
|
|
{
|
|
|
|
|
public class NavigationService : INavigationService
|
|
|
|
|
{
|
|
|
|
|
// 实现接口方法
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ 正确:Application 层使用 Core 接口(不定义接口)
|
|
|
|
|
namespace AuroraDesk.Application.Services
|
|
|
|
|
{
|
|
|
|
|
public class NavigationUseCase
|
|
|
|
|
{
|
|
|
|
|
private readonly INavigationService _navigationService; // 使用 Core 接口
|
|
|
|
|
|
|
|
|
|
public NavigationUseCase(INavigationService navigationService)
|
|
|
|
|
{
|
|
|
|
|
_navigationService = navigationService;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ✅ 正确:Presentation 层依赖 Core 接口和 Application 用例
|
|
|
|
|
namespace AuroraDesk.Presentation.ViewModels
|
|
|
|
|
{
|
|
|
|
|
public class MainWindowViewModel
|
|
|
|
|
{
|
|
|
|
|
private readonly INavigationService _navigationService; // 依赖 Core 接口
|
|
|
|
|
private readonly NavigationUseCase _useCase; // 依赖 Application 用例
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**错误的示例**:
|
|
|
|
|
```csharp
|
|
|
|
|
// ❌ 错误:Application 层不应该定义接口
|
|
|
|
|
namespace AuroraDesk.Application.Services
|
|
|
|
|
{
|
|
|
|
|
public interface INavigationService { } // 应该在 Core 层!
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ❌ 错误:Infrastructure 不应该依赖 Application
|
|
|
|
|
namespace AuroraDesk.Infrastructure.Services
|
|
|
|
|
{
|
|
|
|
|
using AuroraDesk.Application; // 错误!不能依赖 Application
|
|
|
|
|
public class NavigationService : INavigationService { }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
##### 📊 本项目依赖关系图(修正后)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
AuroraDesk.Presentation (表示层)
|
|
|
|
|
├─→ 依赖 AuroraDesk.Application (应用用例)
|
|
|
|
|
└─→ 依赖 AuroraDesk.Core (核心实体和接口)
|
|
|
|
|
|
|
|
|
|
AuroraDesk.Infrastructure (基础设施层)
|
|
|
|
|
├─→ 依赖 AuroraDesk.Core (核心实体和接口)
|
|
|
|
|
└─→ 实现 AuroraDesk.Core.Interfaces 的接口
|
|
|
|
|
❌ 不依赖 AuroraDesk.Application
|
|
|
|
|
|
|
|
|
|
AuroraDesk.Application (应用层)
|
|
|
|
|
└─→ 依赖 AuroraDesk.Core (核心实体和接口)
|
|
|
|
|
❌ 不依赖 AuroraDesk.Infrastructure
|
|
|
|
|
|
|
|
|
|
AuroraDesk.Core (核心层/Domain)
|
|
|
|
|
└─→ 无依赖(独立层)
|
|
|
|
|
└─→ 定义所有接口
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
##### 🎯 依赖检查清单(修正后)
|
|
|
|
|
|
|
|
|
|
在重构过程中,确保:
|
|
|
|
|
- [ ] Core 层没有任何 using 语句引用其他层
|
|
|
|
|
- [ ] **Core 层定义所有服务接口**(INavigationService, IDataService 等)
|
|
|
|
|
- [ ] Application 层只引用 Core 层
|
|
|
|
|
- [ ] **Application 层不定义接口,只使用 Core 接口**
|
|
|
|
|
- [ ] Infrastructure 层只引用 Core 层
|
|
|
|
|
- [ ] **Infrastructure 层不依赖 Application 层**
|
|
|
|
|
- [ ] Infrastructure 层实现 Core 层定义的接口
|
|
|
|
|
- [ ] Presentation 层通过接口依赖服务,不直接依赖 Infrastructure
|
|
|
|
|
- [ ] 所有跨层访问都通过接口进行
|
|
|
|
|
- [ ] 没有循环依赖
|
|
|
|
|
|
|
|
|
|
### 3. 解耦 ViewModels 策略
|
|
|
|
|
|
|
|
|
|
#### 3.1 问题分析
|
|
|
|
|
**当前问题**:
|
|
|
|
|
- `MainWindowViewModel` 中直接创建了所有 `PageViewModel` 实例(高耦合)
|
|
|
|
|
- ViewModel 创建逻辑分散在 `InitializeNavigationItems` 方法中
|
|
|
|
|
- 难以进行单元测试
|
|
|
|
|
- 违反单一职责原则
|
|
|
|
|
|
|
|
|
|
**解决方案**:
|
|
|
|
|
1. **引入 PageViewModel 工厂模式**
|
|
|
|
|
- 创建 `IPageViewModelFactory` 接口
|
|
|
|
|
- 实现 `PageViewModelFactory` 类
|
|
|
|
|
- 通过依赖注入管理 ViewModel 创建
|
|
|
|
|
|
|
|
|
|
2. **引入导航服务**
|
|
|
|
|
- 创建 `INavigationService` 接口
|
|
|
|
|
- 封装导航逻辑,减少 MainWindowViewModel 职责
|
|
|
|
|
- 统一管理导航项配置
|
|
|
|
|
|
|
|
|
|
3. **将配置数据外置**
|
|
|
|
|
- 创建导航配置数据类(NavigationConfig)
|
|
|
|
|
- 从 ViewModel 中分离配置数据
|
|
|
|
|
- 配置可在应用启动时注入
|
|
|
|
|
|
|
|
|
|
#### 3.2 重构后的依赖关系
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
MainWindowViewModel
|
|
|
|
|
↓ (依赖)
|
|
|
|
|
INavigationService // 处理导航逻辑
|
|
|
|
|
IPageViewModelFactory // 创建 PageViewModel
|
|
|
|
|
ILogger // 日志记录
|
|
|
|
|
IScreen // ReactiveUI 路由
|
|
|
|
|
|
|
|
|
|
不再直接依赖:
|
|
|
|
|
❌ DashboardPageViewModel
|
|
|
|
|
❌ UsersPageViewModel
|
|
|
|
|
❌ SettingsPageViewModel
|
|
|
|
|
... (所有具体的 PageViewModel)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📝 详细重构步骤
|
|
|
|
|
|
|
|
|
|
### 阶段一:准备工作(不修改代码)
|
|
|
|
|
|
|
|
|
|
#### 步骤 1.1: 创建新项目结构(可选,建议先单项目重构)
|
|
|
|
|
- [ ] 创建分层项目结构(或多文件夹结构)
|
|
|
|
|
- [ ] 规划文件迁移路径
|
|
|
|
|
- [ ] 备份当前代码
|
|
|
|
|
|
|
|
|
|
#### 步骤 1.2: 分析依赖关系
|
|
|
|
|
- [ ] 梳理所有 ViewModel 之间的依赖
|
|
|
|
|
- [ ] 识别所有直接 new 实例化的地方
|
|
|
|
|
- [ ] 记录需要注入的服务
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 阶段二:架构重构(按顺序执行)
|
|
|
|
|
|
|
|
|
|
#### 步骤 2.1: 创建核心领域层
|
|
|
|
|
- [ ] 创建 `AuroraDesk.Core` 项目/文件夹
|
|
|
|
|
- [ ] 迁移实体类:`NavigationItem`, `TabItem` 到 `Core.Entities`
|
|
|
|
|
- [ ] 更新命名空间:`AuroraDesk.Core.Entities`
|
|
|
|
|
- [ ] 移除业务逻辑,只保留数据模型
|
|
|
|
|
|
|
|
|
|
#### 步骤 2.2: 创建核心层接口和应用层
|
|
|
|
|
- [ ] 在 `AuroraDesk.Core.Interfaces` 中定义所有服务接口
|
|
|
|
|
- [ ] `INavigationService` 接口(不是 Application 层!)
|
|
|
|
|
- [ ] `IDataService` 接口
|
|
|
|
|
- [ ] `IApiService` 接口
|
|
|
|
|
- [ ] `IResourceService` 接口
|
|
|
|
|
- [ ] `IPageViewModelFactory` 接口
|
|
|
|
|
- [ ] 创建 `AuroraDesk.Application` 项目/文件夹
|
|
|
|
|
- [ ] 定义用例类(使用 Core 接口)
|
|
|
|
|
- [ ] 定义导航配置 DTO(NavigationConfig)
|
|
|
|
|
|
|
|
|
|
#### 步骤 2.3: 创建基础设施层实现
|
|
|
|
|
- [ ] 创建 `AuroraDesk.Infrastructure` 项目/文件夹
|
|
|
|
|
- [ ] **重要**:Infrastructure 只依赖 Core 层,不依赖 Application 层
|
|
|
|
|
- [ ] 实现 Core 层定义的接口:
|
|
|
|
|
- [ ] `NavigationService` 实现 `INavigationService`
|
|
|
|
|
- [ ] `PageViewModelFactory` 实现 `IPageViewModelFactory`
|
|
|
|
|
- [ ] `DataService` 实现 `IDataService`
|
|
|
|
|
- [ ] `ApiService` 实现 `IApiService`
|
|
|
|
|
- [ ] `ResourceService` 实现 `IResourceService`
|
|
|
|
|
- [ ] 更新服务命名空间为 `AuroraDesk.Infrastructure.Services`
|
|
|
|
|
|
|
|
|
|
#### 步骤 2.4: 重构表示层
|
|
|
|
|
- [ ] 更新 `MainWindowViewModel`,移除直接创建 ViewModel 的代码
|
|
|
|
|
- [ ] 注入 `INavigationService` 和 `IPageViewModelFactory`
|
|
|
|
|
- [ ] 简化 `MainWindowViewModel` 的职责
|
|
|
|
|
- [ ] 更新所有命名空间为 `AuroraDesk.Presentation.*`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 阶段三:项目重命名(谨慎执行)
|
|
|
|
|
|
|
|
|
|
#### 步骤 3.1: 重命名项目文件
|
|
|
|
|
- [ ] 重命名 `.csproj` 文件
|
|
|
|
|
- [ ] 重命名 `.sln` 文件
|
|
|
|
|
- [ ] 更新项目文件中的 `<RootNamespace>` 和 `<AssemblyName>`
|
|
|
|
|
|
|
|
|
|
#### 步骤 3.2: 重命名命名空间(逐个文件修改)
|
|
|
|
|
- [ ] **不要使用批量替换**
|
|
|
|
|
- [ ] 逐个文件修改命名空间声明
|
|
|
|
|
- [ ] 逐个文件更新 using 语句
|
|
|
|
|
- [ ] 每次修改后编译测试
|
|
|
|
|
|
|
|
|
|
#### 步骤 3.3: 更新配置文件
|
|
|
|
|
- [ ] 更新 `appsettings.json` 中的项目相关配置
|
|
|
|
|
- [ ] 更新 `app.manifest` 中的程序集名称
|
|
|
|
|
- [ ] 更新所有批处理文件和脚本
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 阶段四:解耦 ViewModels(重点)
|
|
|
|
|
|
|
|
|
|
#### 步骤 4.1: 在 Core 层定义接口
|
|
|
|
|
```csharp
|
|
|
|
|
// AuroraDesk.Core.Interfaces/IPageViewModelFactory.cs
|
|
|
|
|
public interface IPageViewModelFactory
|
|
|
|
|
{
|
|
|
|
|
T CreatePageViewModel<T>(IScreen screen) where T : IRoutableViewModel;
|
|
|
|
|
IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuroraDesk.Core.Interfaces/INavigationService.cs
|
|
|
|
|
public interface INavigationService
|
|
|
|
|
{
|
|
|
|
|
ObservableCollection<NavigationItem> GetNavigationItems();
|
|
|
|
|
void NavigateToPage(NavigationItem item);
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 步骤 4.2: 在 Infrastructure 层实现接口
|
|
|
|
|
```csharp
|
|
|
|
|
// AuroraDesk.Infrastructure/Services/PageViewModelFactory.cs
|
|
|
|
|
public class PageViewModelFactory : IPageViewModelFactory
|
|
|
|
|
{
|
|
|
|
|
private readonly IServiceProvider _serviceProvider;
|
|
|
|
|
|
|
|
|
|
public IRoutableViewModel CreatePageViewModel(string pageId, IScreen screen)
|
|
|
|
|
{
|
|
|
|
|
return pageId switch
|
|
|
|
|
{
|
|
|
|
|
"dashboard" => _serviceProvider.GetRequiredService<DashboardPageViewModel>(),
|
|
|
|
|
"users" => _serviceProvider.GetRequiredService<UsersPageViewModel>(),
|
|
|
|
|
// ... 其他页面
|
|
|
|
|
_ => throw new ArgumentException($"Unknown page: {pageId}")
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AuroraDesk.Infrastructure/Services/NavigationService.cs
|
|
|
|
|
public class NavigationService : INavigationService
|
|
|
|
|
{
|
|
|
|
|
// 实现接口方法
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 步骤 4.3: 在 Application 层创建用例(可选)
|
|
|
|
|
```csharp
|
|
|
|
|
// AuroraDesk.Application/UseCases/NavigationUseCase.cs
|
|
|
|
|
public class NavigationUseCase
|
|
|
|
|
{
|
|
|
|
|
private readonly INavigationService _navigationService; // 使用 Core 接口
|
|
|
|
|
|
|
|
|
|
public NavigationUseCase(INavigationService navigationService)
|
|
|
|
|
{
|
|
|
|
|
_navigationService = navigationService;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用例逻辑
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### 步骤 4.4: 重构 MainWindowViewModel
|
|
|
|
|
- [ ] 移除 `InitializeNavigationItems` 方法中的 ViewModel 创建逻辑
|
|
|
|
|
- [ ] 注入 `INavigationService`(来自 Core.Interfaces)获取导航项
|
|
|
|
|
- [ ] 使用 `IPageViewModelFactory`(来自 Core.Interfaces)创建 ViewModel
|
|
|
|
|
- [ ] 简化导航逻辑,委托给 `INavigationService`
|
|
|
|
|
- [ ] **重要**:ViewModel 依赖 Core 接口,不直接依赖 Infrastructure
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 阶段五:注册依赖注入
|
|
|
|
|
|
|
|
|
|
#### 步骤 5.1: 更新 ServiceCollectionExtensions
|
|
|
|
|
- [ ] 注册 `IPageViewModelFactory` → `PageViewModelFactory`
|
|
|
|
|
- [ ] 注册 `INavigationService` → `NavigationService`
|
|
|
|
|
- [ ] 注册所有 PageViewModel 为 Transient(或 Scoped)
|
|
|
|
|
- [ ] 更新命名空间引用
|
|
|
|
|
|
|
|
|
|
#### 步骤 5.2: 更新 App.axaml.cs
|
|
|
|
|
- [ ] 确保所有新服务正确注册
|
|
|
|
|
- [ ] 更新 ViewLocator 的命名空间
|
|
|
|
|
- [ ] 测试依赖注入是否正常工作
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
### 阶段六:测试和验证
|
|
|
|
|
|
|
|
|
|
#### 步骤 6.1: 编译测试
|
|
|
|
|
- [ ] 清理解决方案并重新编译
|
|
|
|
|
- [ ] 修复所有编译错误
|
|
|
|
|
- [ ] 修复所有命名空间引用错误
|
|
|
|
|
|
|
|
|
|
#### 步骤 6.2: 运行时测试
|
|
|
|
|
- [ ] 启动应用程序
|
|
|
|
|
- [ ] 测试导航功能
|
|
|
|
|
- [ ] 测试所有页面能否正常加载
|
|
|
|
|
- [ ] 测试标签页功能
|
|
|
|
|
|
|
|
|
|
#### 步骤 6.3: 代码检查
|
|
|
|
|
- [ ] 检查是否还有直接 new ViewModel 的地方
|
|
|
|
|
- [ ] 检查命名空间是否全部更新
|
|
|
|
|
- [ ] 检查是否有循环依赖
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 🔧 技术实现细节
|
|
|
|
|
|
|
|
|
|
### 1. PageViewModel 注册方式
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
// ServiceCollectionExtensions.cs
|
|
|
|
|
services.AddTransient<DashboardPageViewModel>();
|
|
|
|
|
services.AddTransient<UsersPageViewModel>();
|
|
|
|
|
services.AddTransient<SettingsPageViewModel>();
|
|
|
|
|
// ... 其他 PageViewModel
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 2. 导航配置数据
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
// NavigationConfig.cs
|
|
|
|
|
public class NavigationConfig
|
|
|
|
|
{
|
|
|
|
|
public List<NavigationItemConfig> Items { get; set; } = new();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class NavigationItemConfig
|
|
|
|
|
{
|
|
|
|
|
public string Id { get; set; } = string.Empty;
|
|
|
|
|
public string Title { get; set; } = string.Empty;
|
|
|
|
|
public string PageViewModelType { get; set; } = string.Empty;
|
|
|
|
|
public IconType IconType { get; set; }
|
|
|
|
|
public List<NavigationItemConfig>? Children { get; set; }
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 3. MainWindowViewModel 重构示例
|
|
|
|
|
|
|
|
|
|
**重构前**:
|
|
|
|
|
```csharp
|
|
|
|
|
private void InitializeNavigationItems()
|
|
|
|
|
{
|
|
|
|
|
_navigationItems = new ObservableCollection<NavigationItem>
|
|
|
|
|
{
|
|
|
|
|
new NavigationItem { ViewModel = new DashboardPageViewModel(_screen) },
|
|
|
|
|
new NavigationItem { ViewModel = new UsersPageViewModel(_screen) },
|
|
|
|
|
// ...
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**重构后**:
|
|
|
|
|
```csharp
|
|
|
|
|
using AuroraDesk.Core.Interfaces; // 依赖 Core 接口,不依赖 Infrastructure
|
|
|
|
|
|
|
|
|
|
private readonly INavigationService _navigationService;
|
|
|
|
|
|
|
|
|
|
public MainWindowViewModel(
|
|
|
|
|
IScreen screen,
|
|
|
|
|
INavigationService navigationService, // Core 接口
|
|
|
|
|
ILogger<MainWindowViewModel>? logger = null)
|
|
|
|
|
{
|
|
|
|
|
_screen = screen;
|
|
|
|
|
_navigationService = navigationService;
|
|
|
|
|
_logger = logger;
|
|
|
|
|
|
|
|
|
|
NavigationItems = _navigationService.GetNavigationItems();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ⚠️ 注意事项
|
|
|
|
|
|
|
|
|
|
### 1. 不要批量替换
|
|
|
|
|
- ❌ **禁止**:使用 Visual Studio 的全局查找替换功能批量替换命名空间
|
|
|
|
|
- ✅ **正确做法**:逐个文件修改,每次修改后编译测试
|
|
|
|
|
|
|
|
|
|
### 2. Windows 10 环境
|
|
|
|
|
- ❌ **避免**:使用 Linux 风格的路径分隔符或命令
|
|
|
|
|
- ✅ **使用**:Windows 路径分隔符 `\` 或使用 `Path.Combine`
|
|
|
|
|
- ✅ **使用**:PowerShell 脚本而非 Bash 脚本
|
|
|
|
|
|
|
|
|
|
### 3. 编译顺序
|
|
|
|
|
- 先编译被依赖的层:
|
|
|
|
|
- **Core**(最底层,无依赖)
|
|
|
|
|
- **Application** 和 **Infrastructure**(都依赖 Core,可并行编译)
|
|
|
|
|
- **Presentation**(依赖 Core 和 Application)
|
|
|
|
|
- 每次修改一层后立即编译测试
|
|
|
|
|
|
|
|
|
|
### 4. Git 版本控制
|
|
|
|
|
- 每个阶段完成后提交一次代码
|
|
|
|
|
- 使用有意义的提交信息
|
|
|
|
|
- 如果出现问题可以回退到上一个阶段
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📊 预期成果
|
|
|
|
|
|
|
|
|
|
### 代码质量提升
|
|
|
|
|
- ✅ ViewModels 耦合度降低 80%+
|
|
|
|
|
- ✅ 代码可测试性提升
|
|
|
|
|
- ✅ 符合整洁架构原则
|
|
|
|
|
- ✅ 易于扩展新功能
|
|
|
|
|
|
|
|
|
|
### 项目结构
|
|
|
|
|
- ✅ 清晰的分层架构
|
|
|
|
|
- ✅ 统一的命名空间规范(Aurora 前缀)
|
|
|
|
|
- ✅ 更好的代码组织
|
|
|
|
|
|
|
|
|
|
### 维护性
|
|
|
|
|
- ✅ 新页面添加只需注册,无需修改 MainWindowViewModel
|
|
|
|
|
- ✅ 导航逻辑集中管理
|
|
|
|
|
- ✅ 配置与代码分离
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📅 执行时间估算
|
|
|
|
|
|
|
|
|
|
- **阶段一**(准备):0.5 天
|
|
|
|
|
- **阶段二**(架构重构):2-3 天
|
|
|
|
|
- **阶段三**(重命名):1-2 天
|
|
|
|
|
- **阶段四**(解耦):2-3 天
|
|
|
|
|
- **阶段五**(DI注册):0.5 天
|
|
|
|
|
- **阶段六**(测试):1 天
|
|
|
|
|
|
|
|
|
|
**总计**:约 7-10 个工作日
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ✅ 检查清单
|
|
|
|
|
|
|
|
|
|
在每个阶段完成后,使用此清单验证:
|
|
|
|
|
|
|
|
|
|
- [ ] 所有文件命名空间已更新
|
|
|
|
|
- [ ] 所有 using 语句已更新
|
|
|
|
|
- [ ] 项目文件已更新
|
|
|
|
|
- [ ] 解决方案文件已更新
|
|
|
|
|
- [ ] 代码编译无错误
|
|
|
|
|
- [ ] 应用程序可以正常启动
|
|
|
|
|
- [ ] 主要功能正常工作
|
|
|
|
|
- [ ] 已更新 modify.md 记录修改
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## 📝 修改记录
|
|
|
|
|
|
|
|
|
|
每次完成一个重要步骤后,请在 `modify.md` 文件中记录:
|
|
|
|
|
- 修改日期
|
|
|
|
|
- 修改内容
|
|
|
|
|
- 修改的文件列表
|
|
|
|
|
- 遇到的问题及解决方案
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
**文档创建日期**: 2025年1月
|
|
|
|
|
**最后更新**: 2025年1月
|
|
|
|
|
**状态**: 计划阶段
|