SeatFlow.Core 层的领域实体、策略接口、领域服务和存储抽象
本页目录
全部文档
Core 层详解
SeatFlow.Core 是 SeatFlow 的领域核心层,负责定义业务实体、策略接口和领域服务。它不依赖任何其他项目,仅引用标准 .NET 库。
领域实体
Student (SeatFlow.Core/Models/Student.cs)
学生是排座系统的基本操作对象。
public class Student
{
public string Id { get; set; } = Guid.NewGuid().ToString(); // 唯一标识
public string Name { get; set; } = string.Empty;
public float? Height { get; set; } // 身高(cm),用于前排策略筛选
public Gender? Gender { get; set; }
public bool NeedsFrontRow { get; set; } // 是否需要前排(CSV/XLSX 导入字段)
// 轮换记录(环形缓冲区,容量 10,不序列化)
[JsonIgnore]
public CircularHistory<string> RecentSeatHistory { get; set; } = new(10);
// 前排偏好分数,由轮换策略动态调整,分数越高越优先
public int FrontRowPreferenceScore { get; set; }
// 扩展数据挂载点(不序列化)
[JsonIgnore]
public AttributeBag Extensions { get; set; } = new();
}
Seat 体系 (SeatFlow.Core/Models/Seat.cs)
座位采用抽象类和派生类的多态设计:
Seat (abstract)
├── GridSeat — 网格布局(Row, Column)
├── PolarSeat — 极坐标布局(Ring, Angle)
└── FreeformSeat — 自由点布局(X, Y)
核心属性:
public abstract class Seat
{
public string Id { get; set; } = Guid.NewGuid().ToString();
public abstract SeatType Type { get; } // Grid / Polar / Freeform
public string LogicalGroup { get; set; } = string.Empty;
public abstract ISeatGeometry GeometryData { get; } // 行列 / 半径角度坐标
public bool IsAvailable { get; set; } = true;
public bool IsFixed { get; set; } // 固定座位标记
public string? OccupantId { get; set; } // 就坐学生 ID
public AttributeBag Extensions { get; set; } = new();
}
多态序列化通过 SeatJsonConverter(在 SeatFlow.Infrastructure 中)实现,使用 Type 鉴别器字段(“Grid” / “Polar” / “Freeform”)。
ClassroomLayoutDefinition (SeatFlow.Core/Models/ClassroomLayoutDefinition.cs)
教室布局的完整定义,支持三种布局类型:
public class ClassroomLayoutDefinition
{
public string Id { get; set; }
public string Name { get; set; }
public LayoutType LayoutType { get; set; } // 枚举 Grid | Polar | Freeform
public string LayoutTypeString { get; set; } // 字符串版本,反序列化兼容
public LayoutMetadata Metadata { get; set; } // 布局特定元数据
public List<Seat> Seats { get; set; } = [];
public List<Obstacle> Obstacles { get; set; } = []; // 柱子、讲台等障碍物
}
布局类型同时序列化为数字(layoutType: 0=Grid, 1=Polar, 2=Freeform)和字符串(layoutTypeString);迁移器优先读取字符串。
LayoutMetadata (SeatFlow.Core/Models/LayoutMetadata.cs)
LayoutMetadata 为多态基类,按布局类型派生;默认值直接决定座位坐标生成:
| 元数据类型 | 关键默认值 |
|---|---|
GridLayoutMetadata |
Rows/Columns 0、SeatsPerDesk 2、IntraDeskSpacing 12、InterDeskSpacing 32(默认桌间距)、AisleWidth 60、FrontRowCount 1、HasPodium true |
PolarLayoutMetadata |
Rings 1、SeatsPerRing 8、RadiusStep 40、EndAngleDegrees 180(半圆)、FrontRowCount 1、HasPodium true |
FreeformLayoutMetadata |
无额外参数,依赖 Seats 中的显式坐标点 |
默认桌间距:2.1.0 起
GridLayoutMetadata.InterDeskSpacing默认值统一为 32(相邻桌边界间距),与设计规范一致。
SeatingWorkspace (SeatFlow.Core/Workspace/SeatingWorkspace.cs)
排座工作区,是所有策略操作的核心上下文。提供可修改的座位视图并记录操作消息。
public class SeatingWorkspace
{
public IReadOnlyList<Student> Students { get; }
// 核心操作
public bool TryAssignSeat(string seatId, string studentId, out string error);
public IEnumerable<Seat> GetEmptySeats();
public IEnumerable<Seat> FindSeats(Func<Seat, bool> predicate);
public SeatingPlan BuildSeatingPlan();
// 消息系统
public IReadOnlyList<StrategyMessage> Messages { get; }
public void LogWarning(string strategyId, string displayName, string messageKey, params object?[] args);
public void LogError(string strategyId, string displayName, string messageKey, params object?[] args);
}
SeatingPlan (SeatFlow.Core/Models/SeatingPlan.cs)
管道执行的最终输出,包含所有座位到学生的分配映射。
其他模型类型
策略配置声明相关的模型(SeatFlow.Core.Models):
StrategyParameterDefinition— 策略参数定义StrategyCodeBlock— 策略配置块声明StrategyFieldDefinition— 字段定义StrategyDatasetConfig/StrategyConfigRow— 持久化配置行- 枚举:
StrategyFieldType、StrategyDataType、StrategyDisplayMode
策略接口
ISeatingStrategy (独立策略)
public interface ISeatingStrategy
{
string Id { get; }
string Name { get; }
int Priority { get; set; }
bool IsEnabled { get; set; }
Task<StrategyExecutionResult> ExecuteAsync(
SeatingWorkspace workspace, CancellationToken cancellationToken);
ValidationResult ValidateConfiguration();
}
独立策略由外部管道编排执行,按 Priority 降序依次操作同一个 SeatingWorkspace。
IDependentSeatingStrategy (依赖策略)
public interface IDependentSeatingStrategy
{
string Id { get; }
string Name { get; }
string DisplayName { get; }
int Priority { get; set; }
bool IsEnabled { get; set; }
Task<DependentEvaluationResult> EvaluateAsync(
SeatingWorkspace workspace, Student student, Seat targetSeat,
IRandomFillContext context, CancellationToken ct);
ValidationResult ValidateConfiguration();
/// <summary>
/// 告知策略在 RandomFill 循环开始前已被前序策略分配的学生 ID。
/// </summary>
void SetPriorAssignedStudentIds(HashSet<string> ids) { }
/// <summary>
/// 返回此策略认为有特殊约束、应优先分配以减少重掷的学生 ID 集合。
/// </summary>
HashSet<string> GetConstrainedStudentIds() => [];
}
依赖策略不在外部管道中执行,而是在 RandomFillStrategy 的分配循环中运行。每次 RandomFill 选出 (student, seat) 对后,按内部 Priority 降序依次调用 EvaluateAsync。
DependentEvaluationResult 有三种结果:
- Approve — 批准分配
- Reject — 请求重掷(RandomFill 选择下一个随机座位重试)
- Handled — 依赖策略已自行完成分配(含连携修改),RandomFill 跳过
TryAssignSeat
11 条内置策略
| 策略 | Priority | 类型 | 文件 | 职责 |
|---|---|---|---|---|
| FixedSeatStrategy | 100 | 独立 | Strategies/FixedSeatStrategy.cs |
锁定固定座位 (IsFixed=true),后续策略自动排除 |
| HeightPriorityStrategy | 60 | 独立 | Strategies/HeightPriorityStrategy.cs |
身高从矮到高优先填前排;无身高者不参与 |
| FrontRowRotationStrategy | 50 | 独立 | Strategies/FrontRowRotationStrategy.cs |
按需求分数选出学生,Fisher-Yates 洗牌后随机分布前排 |
| DeskMateStrategy | 50 (上下文) | 依赖 | Strategies/DeskMateStrategy.cs |
在 RandomFill 中检查同桌关系,协调同行邻列分配 |
| GenderRestrictedSeatStrategy | 45 (上下文) | 依赖 | Strategies/GenderRestrictedSeatStrategy.cs |
检查座位性别限制,不匹配时重定向到匹配空座 |
| GenderDeskMateStrategy | 44 (上下文) | 依赖 | Strategies/GenderDeskMateStrategy.cs |
同桌性别搭配:默认男女搭配,可切同性同桌 |
| NoDeskMateStrategy | 42 (上下文) | 依赖 | Strategies/NoDeskMateStrategy.cs |
搭配组内任意两人不互为同桌(多选选择器配置) |
| NoRepeatDeskMateStrategy | 40 (上下文) | 依赖 | Strategies/NoRepeatDeskMateStrategy.cs |
检查相邻已占座是否与历史同桌重复 |
| SnakeOrderStrategy | 10 | 独立 | Strategies/SnakeOrderStrategy.cs |
按排序规则(名单/姓名/身高/性别)确定性填充,可配置蛇形方向 |
| RandomFillStrategy | 1 | 独立+宿主 | Strategies/RandomFillStrategy.cs |
兜底填充,托管依赖策略执行 |
| DefragStrategy | 0 | 独立 | Strategies/DefragStrategy.cs |
后置碎片整理(默认禁用) |
策略管道编排
FixedSeat(100) → HeightPriority(60) → FrontRowRotation(50) → SnakeOrder(10) → RandomFill(1) → Defrag(0)
│
┌────────┬────────┬────────┬────────┐
▼ ▼ ▼ ▼ ▼
DeskMate Gender Gender NoDesk NoRepeat
(50) Restrict DeskMate (42) DeskMate
(45) (44) (40)
策略配置清单
每种策略的 UI 配置由 manifest JSON 文件声明式驱动(SeatFlow.Core/Strategies/Manifests/{Id}.json),包含:
visible— 策略可见性(默认 true,false 时完全排除)isIndependent— 是否为独立策略(默认 true)manifestVersion— 清单格式版本parameters[]— 策略级全局参数codeBlocks[]— 按数据集/会场的配置块messages— 策略执行消息 i18n 模板
领域服务与辅助类
| 服务 | 文件 | 职责 |
|---|---|---|
| ObstacleProcessor | DomainServices/ObstacleProcessor.cs |
处理教室中的障碍物(柱子、讲台等),调整座位布局 |
| SeatGeometryHelper | DomainServices/SeatGeometryHelper.cs |
座位几何计算,如相邻判断、距离计算(SeatingCanvas 复用同一几何) |
| StrategyManifestProvider | Services/StrategyManifestProvider.cs |
加载和提供策略 manifest JSON(嵌入资源),解析声明式配置 |
| SeatAdjacencyHelper | Strategies/SeatAdjacencyHelper.cs |
座位相邻关系辅助,判断同行、同桌、邻座等 |
工具类
位于 SeatFlow.Core/Utilities/:
| 工具 | 文件 | 用途 |
|---|---|---|
CircularHistory<T> |
CircularHistory.cs |
环形缓冲区,固定容量(Student.RecentSeatHistory 容量为 10),自动覆盖最旧记录 |
AttributeBag |
AttributeBag.cs |
扩展数据挂载点,允许向实体附加自定义数据 |
AppEnvironment |
AppEnvironment.cs |
OS 标准数据目录解析(DefaultDataDirectory)与运行环境辅助 |
数据提供者接口
位于 SeatFlow.Core/Providers/:
| 接口 | 用途 |
|---|---|
IStudentProvider |
学生数据加载(从文件/流读取学生列表) |
IStudentWriter |
学生数据写回(JSON/CSV/XLSX) |
IVenueRepository |
会场数据持久化 |
IStudentDatasetRepository |
学生数据集管理 |
IAppSettingsRepository |
应用设置持久化 |
ISeatingSnapshotRepository |
排座快照持久化 |
ISeatingPlanExporter(排座结果导出)位于 SeatFlow.Core/Exporters/,ISeatSetsService(数据包打包/解包)位于 SeatFlow.Core/Interfaces/。这些接口均由 Infrastructure 层实现,通过 DI 注入到 Application 层使用。
存储抽象
位于 SeatFlow.Core/Storage/ILocalDataStore.cs:
ILocalDataStore 是平台无关的本地数据存储接口,向上层屏蔽文件系统差异,使同一套业务逻辑可运行在桌面与浏览器:
public interface ILocalDataStore
{
// 平台无关的读取、写入、删除与枚举操作
}
- 桌面端由
FileSystemDataStore实现,底层为应用数据目录的文件系统 - 浏览器端由
IndexedDbDataStore实现,底层为浏览器 IndexedDB - 仓库、文件迁移、SeatSets 打包/解包等 Infrastructure 服务均通过该抽象访问本地数据
具体实现由各启动壳在 DI 中注册,Core 层只依赖接口定义。