SeatFlow.Application 层的外观模式、策略管道、命令模式、插件管理和 DI 注册
本页目录
全部文档
Application 层详解
SeatFlow.Application 是 SeatFlow 的应用编排层,作为 UI 层和领域层之间的桥梁。它负责编排数据加载、策略执行、插件管理和命令历史。所有业务逻辑的协调入口均在这一层。
IApplicationFacade (外观接口)
IApplicationFacade 是 UI 层的单一入口点(外观模式),封装 40+ 方法,涵盖:
public interface IApplicationFacade
{
// 数据管理
Task<IReadOnlyList<StudentDatasetInfo>> ListStudentDatasetsAsync(CancellationToken ct = default);
Task ImportStudentsAsync(string filePath, CancellationToken ct = default);
// 排座执行
Task<SeatingWorkspace> GenerateSeatingAsync(SeatingRequest request,
IProgress<SeatingProgress>? progress = null, CancellationToken ct = default);
// 导出
Task ExportSeatingPlanAsync(ExportOptions options, SeatingWorkspace workspace,
ClassroomLayoutDefinition layout, CancellationToken ct = default);
// 快照
Task<IReadOnlyList<SeatingSnapshot>> GetSnapshotsAsync(string venueId, CancellationToken ct = default);
Task RollbackToSnapshotAsync(string snapshotId, CancellationToken ct = default);
// 命令历史(撤销/重做)
Task<bool> ExecuteCommandAsync(IUndoableCommand command, CancellationToken ct = default);
Task<bool> UndoAsync(CancellationToken ct = default);
Task<bool> RedoAsync(CancellationToken ct = default);
// 策略配置
Task<IReadOnlyList<StrategyDisplayInfo>> GetStrategiesAsync(CancellationToken ct = default);
Task SaveStrategyConfigAsync(string id, StrategyConfig config, CancellationToken ct = default);
// ... 更多方法
}
ViewModel 通过构造函数注入 IApplicationFacade,不直接依赖任何领域或基础设施类型。
StrategyExecutionPipeline (策略管道)
策略管道采用 Fill-in-Order 模型执行独立策略:
public class StrategyExecutionPipeline
{
public async Task<SeatingPlan> ExecuteAsync(
SeatingWorkspace workspace, CancellationToken cancellationToken)
{
// 按 Priority 降序执行每个启用的独立策略
foreach (var strategy in _strategies
.OrderByDescending(s => s.Priority)
.Where(s => s.IsEnabled))
{
var result = await strategy.ExecuteAsync(workspace, cancellationToken);
if (!result.Success)
{
workspace.LogError(strategy.Id, strategy.Name,
"Pipeline_ExecFailed", result.Message);
}
}
return workspace.BuildSeatingPlan();
}
}
执行流程(ApplicationFacade 在构建策略列表时已排除 visible=false 的策略,管道中仅处理已纳入的策略):
- 按 Priority 降序排列
IsEnabled为 true 的独立策略 - 依次执行每个独立策略,所有策略操作同一个
SeatingWorkspace - 依赖策略不在外部管道中执行——它们在
RandomFillStrategy的分配循环中按上下文内部优先级评估
RandomFill 上下文循环
RandomFillStrategy 作为依赖策略的宿主,其分配循环:
while 还有未分配学生 AND 空座位:
随机选 (student, seat)
rerollCount = 0
loop:
依次调用依赖策略 EvaluateAsync (按内部 Priority 降序)
if Reject → rerollCount++
if >= maxRerolls 兜底强制分配
else 换随机座位重试
if Handled → 依赖策略已自行分配,跳过 TryAssignSeat
if 全部 Approve → TryAssignSeat, 刷新列表
约束学生(DeskMate 分组)优先分配以减少重掷次数。逐出操作尊重先前策略(FixedSeat、FrontRowRotation)的分配结果。
命令模式
IUndoableCommand 和 CommandHistory 提供快照式撤销/重做:
public interface IUndoableCommand
{
string Description { get; }
Task<bool> ExecuteAsync(CancellationToken ct = default);
Task<bool> UndoAsync(CancellationToken ct = default);
}
CommandHistory 管理操作栈,支持撤销和重做导航。采用快照式实现——每个命令保存操作前后的状态快照,而非增量操作记录。
插件管理
PluginManager
位于 SeatFlow.Application/PluginManagement/:
| 组件 | 职责 |
|---|---|
PluginManager |
插件发现(扫描插件目录)、加载、卸载、运行时启用/禁用 |
PluginLoadContext |
基于 AssemblyLoadContext 的隔离加载上下文,外部 DLL 独立于主程序域 |
PluginStrategyAdapter |
将 IPluginSeatingStrategy 适配为 ISeatingStrategy |
关键特性:
RefreshPackageAsync(packageId)— 热插拔:卸载 → 重新扫描 → 加载SetStrategyEnabledAsync(strategyId, enabled)— 运行时即时生效FindStrategy(strategyId)— 判断策略来源,自动路由配置存储后端
插件隔离
通过自定义 AssemblyLoadContext 加载外部插件程序集,确保:
- 插件 DLL 版本冲突不影响主程序
- 插件可安全卸载
- 受限的 API 访问权限
插件配置路由
| 配置类型 | 内置策略 | 插件策略 |
|---|---|---|
| 运行时配置 | {AppData}/StrategyConfig/{id}.config.json |
Plugins/{pkgId}/{path}/{id}.config.json |
| 数据集配置 | {AppData}/StrategyConfig/{id}/... |
Plugins/{pkgId}/{path}/{id}/... |
| 启用状态 | 在 StrategyConfig.IsEnabled 中 |
Plugins/{pkgId}/data/enables.json |
脚本适配器
位于 SeatFlow.Application/Scripting/:
| 适配器 | 支持语言 | 说明 |
|---|---|---|
LuaScriptPluginAdapter |
Lua | 通过受限沙箱执行 Lua 脚本,禁用 IO/网络 |
CSharpScriptPluginAdapter |
C# Script | 编译和执行 C# 脚本,同样受限 |
脚本适配器将脚本逻辑包装为 IPluginSeatingStrategy,使其能以与其他插件相同的方式在管道中执行。
DI 注册
Application 层注册
ServiceCollectionExtensions.AddSeatFlowApplication(snapshotBasePath, pluginsPath) 注册所有 Application、Core 和 Infrastructure 层的服务:
public static IServiceCollection AddSeatFlowApplication(
this IServiceCollection services,
string snapshotBasePath,
string pluginsPath)
注册的服务包括:
- 策略(内置 7 条策略 + 依赖策略)
- 导出器
- 数据提供者
- 仓库
- 插件管理器
- 策略执行管道
- 外观
UI 层补充注册
在 Program.cs 中调用 AddSeatFlowApplication() 后,UI 层补充注册自己的单例:
services.AddSingleton<INavigationService, NavigationService>();
services.AddSingleton<IFileService, FileService>();
services.AddSingleton<IDialogService, DialogService>();
services.AddSingleton<WatchdogService>();
services.AddSingleton<MainWindow>();
services.AddSingleton<MainShellViewModel>();
// 所有页面 ViewModel
services.AddSingleton<HomeViewModel>();
services.AddSingleton<MemberManagementViewModel>();
// ...
日志
- 使用 Serilog 4 +
Microsoft.Extensions.Logging.ILogger<T> - 日志输出到文件(
Serilog.Sinks.File) - Application 服务通过构造函数注入
ILogger<T> - 可选日志参数回退到
NullLogger<T>.Instance
public class StrategyExecutionPipeline
{
private readonly ILogger<StrategyExecutionPipeline> _logger;
public StrategyExecutionPipeline(
IEnumerable<ISeatingStrategy> strategies,
ILogger<StrategyExecutionPipeline>? logger = null)
{
_strategies = strategies;
_logger = logger ?? NullLogger<StrategyExecutionPipeline>.Instance;
}
}