跳转到主内容

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 层只依赖接口定义。