SeatFlow 架构分层、技术栈、关键设计决策和策略管道概念
本页目录
全部文档
架构总览
项目目标
SeatFlow 是一个 .NET 10 跨平台桌面座位编排系统,面向学校、培训机构和企业等需要批量座位安排的场景。系统核心能力包括:
- 多策略排座管道(固定座位、前排轮换、同桌分组、性别限制、历史防重复等)
- 多种教室布局支持(网格、极坐标、自由点)
- 多数据源导入(CSV、XLSX、JSON)
- 排座结果导出(Excel、CSV、PDF、图片)
- 历史快照与回滚
- 插件化扩展(Assembly 插件 + Lua/C# 脚本)
- 完整的国际化支持(zh-CN/en-US等)
分层架构
采用经典三层架构 + 插件化扩展,遵循严格的分层依赖原则:
┌─────────────────────────────────────┐
│ Presentation.Avalonia │
│ Avalonia UI 12 + CommunityToolkit │
│ MVVM, 编译绑定, i18n │
└─────────────────────────────────────┘
│
┌─────────────────────────────────────┐
│ Application │
│ IApplicationFacade (外观) │
│ StrategyExecutionPipeline │
│ PluginManager + CommandHistory │
│ DI 容器 (Microsoft.Extensions) │
└─────────────────────────────────────┘
│
┌─────────────────────────┼─────────────────────────┐
│ │ │
┌────────▼──────┐ ┌─────────▼────────┐ ┌───────────▼────┐
│ Core │ │ Contracts │ │ Infrastructure │
│ │ │ │ │ │
│ ┌───────────┐ │ │ IPluginSeating │ │ Csv/Xlsx/Json │
│ │ Entities │ │ │ Strategy │ │ Providers │
│ │ Student │ │ │ (插件跨层契约) │ │ Exporters │
│ │ Seat │ │ │ │ │ Repositories │
│ │ Layout │ │ └───────────────────┘ │ LayoutBuilders │
│ ├───────────┤ │ │ MigrationSys │
│ │ Strategies│ │ └────────────────┘
│ │ Interfaces│ │
│ │ 7 Builtin │ │
│ │ Strategies│ │
│ ├───────────┤ │
│ │ Domain │ │
│ │ Services │ │
│ └───────────┘ │
└───────────────┘
各层职责
| 层 | 项目 | 核心职责 |
|---|---|---|
| Core | SeatFlow.Core |
领域实体、策略接口和实现、领域服务、工作区、数据提供者接口 |
| Contracts | SeatFlow.Contracts |
跨层契约,供外部插件引用的插件策略接口 |
| Infrastructure | SeatFlow.Infrastructure |
数据提供者实现、导出器、布局构建器、仓库、文件迁移系统、序列化 |
| Application | SeatFlow.Application |
UI 单一入口(外观)、策略管道执行器、命令模式、插件管理、DI 注册、脚本适配器 |
| Plugins.Sdk | SeatFlow.Plugins.Sdk |
轻量级 SDK 程序集,供外部插件作者引用 |
| Presentation | SeatFlow.Presentation.Avalonia |
Avalonia 12 桌面应用,MVVM 架构 |
依赖关系链
Presentation.Avalonia
└── Application
├── Core
├── Contracts
└── Infrastructure
Plugins.Sdk (仅被外部插件引用,不在主项目依赖链中)
技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| .NET SDK | 10 | 运行时和 SDK |
| Avalonia UI | 12 | 跨平台桌面 UI 框架 |
| CommunityToolkit.Mvvm | 8.4 | MVVM 源码生成器([ObservableProperty], [RelayCommand]) |
| Serilog | 4 | 结构化日志 |
| EPPlus | 8 | XLSX 读写 |
| QuestPDF | — | PDF 导出 |
| Microsoft.Extensions.DependencyInjection | — | DI 容器 |
| xUnit | 3 | 单元测试框架 |
| FluentAssertions | — | 测试断言 |
| NSubstitute | — | 模拟框架 |
关键设计决策
项目采用架构决策记录(ADR)系统文档化重大技术决策,位于 docs/adr/ 目录。
ADR 索引
| ADR | 主题 | 关键结论 |
|---|---|---|
| ADR-001 | 选择 Avalonia UI | 跨平台(Windows/macOS/Linux),原生 MVVM,WPF 开发者友好 |
| ADR-002 | MVVM 框架 | CommunityToolkit.Mvvm 源码生成器 |
| ADR-003 | 分层架构 + 插件化 | 经典三层 + Contracts 层隔离插件接口 |
| ADR-004 | 策略模式 | ISeatingStrategy 作为排座算法通用接口 |
| ADR-005 | 命令模式 | IUndoableCommand + CommandHistory 实现撤销/重做 |
| ADR-006 | 策略管道 | Fill-in-Order 模型,依赖策略在 RandomFill 上下文中执行,能力声明系统 |
| ADR-007 | 多策略插件包 | 双层清单(包级 plugins-manifest.json + 策略 manifest.json) |
| ADR-008 | 引导系统示例数据注入 | 纯内存注入,零磁盘痕迹,DispatcherPriority.Background 延迟 |
核心架构模式
- 外观模式:
IApplicationFacade是 UI 层的单一入口点,封装所有业务操作(40+ 方法) - 策略模式:
ISeatingStrategy和IDependentSeatingStrategy定义排座算法接口 - 命令模式:
IUndoableCommand+CommandHistory提供快照式撤销/重做 - 插件隔离: 通过
AssemblyLoadContext独立加载外部 DLL,支持热插拔 - 依赖注入: Microsoft.Extensions.DependencyInjection 管理组件生命周期
策略管道概念
SeatFlow 的策略管道采用 Fill-in-Order 模型,核心原则:
- 按 Priority 降序执行:Priority 数值越大越先执行,先执行的策略优先挑选空座
- 不存在覆盖语义:先占的座位不会被后执行的策略推翻
- IsFixed 保护:固定座位标记
IsFixed=true,后续GetEmptySeats()自动排除 - 依赖策略:某些策略(DeskMate、GenderRestrictedSeat、NoRepeatDeskMate)不在外部管道执行,而是在 RandomFill 的分配循环中按上下文内部优先级评估
管道执行顺序
独立策略 Priority 降序 →
FixedSeat(100) ← 最先执行:锁定固定座位
FrontRowRotation(50) ← 在非固定空座中填前排
RandomFill(1) ← 兜底填充:
└─ 依赖策略(优先级降序)→
DeskMate(50) ← 检查同桌关系
GenderRestrictedSeat(45) ← 检查性别限制
NoRepeatDeskMate(40) ← 检查历史同桌重复
Defrag(0) ← 碎片整理(默认禁用)
能力声明系统
策略需在 manifest 中声明所需能力(如 MarkFixedSeat),运行时方可调用对应接口。这提供了一个可扩展的约束和日志机制。参见 SeatFlow.Core/Strategies/Capability.cs。
插件系统概念
SeatFlow 支持三种方式扩展排座策略:
- Assembly 插件:编译为 DLL,实现
IPluginSeatingStrategy,通过AssemblyLoadContext隔离加载 - Lua 脚本插件:逻辑由 Lua 脚本文件描述,在受限沙箱中执行
- C# 脚本插件:逻辑由 C# Script 文件描述
插件采用双层清单架构 (docs/adr/ADR-007-multi-strategy-plugin-packages.md):
Plugins/{packageId}/
├── plugins-manifest.json ← 包级清单(元数据 + 加载指令)
├── strategy_id/
│ ├── manifest.json ← 策略级清单(StrategyManifest 格式)
│ └── strategy.dll
└── data/
└── enables.json ← 运行时启用状态