跳转到主内容

SeatFlow.Core 层的领域实体、策略接口、领域服务和能力系统

Core 层详解

SeatFlow.Core 是 SeatFlow 的领域核心层,负责定义业务实体、策略接口和领域服务。它不依赖任何其他项目,仅引用标准 .NET 库。

领域实体

Student (SeatFlow.Core/Models/Student.cs)

学生是排座系统的基本操作对象。

public class Student
{
    public string Id { get; set; }                         // 唯一标识
    public string Name { get; set; }
    public float? Height { get; set; }                     // 身高(cm),用于前排策略筛选
    public Gender? Gender { get; set; }
    public bool NeedsFrontRow { get; set; }                // 是否需要前排(CSV/XLSX 导入字段)

    // 轮换记录(环形缓冲区,容量 10)
    public CircularHistory<string> RecentSeatHistory { get; set; } = new(10);

    // 扩展数据挂载点(插件使用)
    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; }              // 逻辑 ID
    public abstract SeatType Type { get; }      // Grid / Polar / Freeform
    public string LogicalGroup { get; set; }    // 逻辑分组
    public abstract object 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 LayoutType { get; set; }       // "Grid" | "Polar" | "Freeform"
    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)。

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);

    // 能力系统
    public void RegisterCapabilities(IEnumerable<string> capabilities);
    public bool TryMarkFixed(string seatId, string studentId, string strategyId,
                             string displayName, out string error);
}

SeatingPlan (SeatFlow.Core/Models/SeatingPlan.cs)

管道执行的最终输出,包含所有座位到学生的分配映射。

其他模型类型

策略配置声明相关的模型(SeatFlow.Core.Models):

  • StrategyParameterDefinition — 策略参数定义
  • StrategyCodeBlock — 策略配置块声明
  • StrategyFieldDefinition — 字段定义
  • StrategyDatasetConfig / StrategyConfigRow — 持久化配置行
  • 枚举:StrategyFieldTypeStrategyDataTypeStrategyDisplayMode

策略接口

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

7 条内置策略

策略 Priority 类型 文件 职责
FixedSeatStrategy 100 独立 Strategies/FixedSeatStrategy.cs 锁定固定座位 (IsFixed=true),后续策略自动排除
FrontRowRotationStrategy 50 独立 Strategies/FrontRowRotationStrategy.cs 按需求分数选出学生,Fisher-Yates 洗牌后随机分布前排
DeskMateStrategy 50 (上下文) 依赖 Strategies/DeskMateStrategy.cs 在 RandomFill 中检查同桌关系,协调同行邻列分配
GenderRestrictedSeatStrategy 45 (上下文) 依赖 Strategies/GenderRestrictedSeatStrategy.cs 检查座位性别限制,不匹配时重定向到匹配空座
NoRepeatDeskMateStrategy 40 (上下文) 依赖 Strategies/NoRepeatDeskMateStrategy.cs 检查相邻已占座是否与历史同桌重复
RandomFillStrategy 1 独立+宿主 Strategies/RandomFillStrategy.cs 兜底填充,托管依赖策略执行
DefragStrategy 0 独立 Strategies/DefragStrategy.cs 后置碎片整理(默认禁用)

策略管道编排

FixedSeat(100) → FrontRowRotation(50) → RandomFill(1) → Defrag(0)
                                             │
                                    ┌────────┼────────┐
                                    ▼        ▼        ▼
                              DeskMate  Gender   NoRepeat
                                (50)   Restrict  DeskMate
                                        (45)      (40)

策略配置清单

每种策略的 UI 配置由 manifest JSON 文件声明式驱动(SeatFlow.Core/Strategies/Manifests/{Id}.json),包含:

  • visible — 策略可见性(默认 true,false 时完全排除)
  • isIndependent — 是否为独立策略(默认 true)
  • manifestVersion — 清单格式版本
  • capabilities[] — 策略能力声明
  • parameters[] — 策略级全局参数
  • codeBlocks[] — 按数据集/会场的配置块
  • messages — 策略执行消息 i18n 模板

领域服务

位于 SeatFlow.Core/DomainServices/

服务 文件 职责
ObstacleProcessor ObstacleProcessor.cs 处理教室中的障碍物(柱子、讲台等),调整座位布局
SeatGeometryHelper SeatGeometryHelper.cs 座位几何计算,如相邻判断、距离计算
StrategyManifestProvider StrategyManifestProvider.cs 加载和提供策略 manifest JSON(嵌入资源),解析声明式配置
SeatAdjacencyHelper SeatAdjacencyHelper.cs 座位相邻关系辅助,判断同行、同桌、邻座等

工具类

位于 SeatFlow.Core/Utilities/

工具 文件 用途
CircularHistory<T> CircularHistory.cs 环形缓冲区,固定容量(Student.RecentSeatHistory 容量为 10),自动覆盖最旧记录
AttributeBag AttributeBag.cs 扩展数据挂载点,允许插件向实体附加自定义数据

数据提供者接口

位于 SeatFlow.Core/Abstractions/

接口 用途
IStudentProvider 学生数据加载(从文件/流读取学生列表)
IVenueRepository 会场数据持久化
IStudentDatasetRepository 学生数据集管理
ISeatingPlanExporter 排座结果导出

这些接口由 Infrastructure 层实现,通过 DI 注入到 Application 层使用。

能力系统

位于 SeatFlow.Core/Strategies/Capability.cs

Capability 提供了一种声明式的能力约束机制——策略需在 manifest 中声明能力,运行时方可调用对应接口。

public static class Capability
{
    public const string MarkFixedSeat = "MarkFixedSeat";
    // 未来在此追加新常量
}

public interface IFixedSeatCapability
{
    bool TryMarkFixed(string seatId, string studentId,
                      string strategyId, string displayName, out string error);
}

执行链:

  1. ApplicationFacade 从 manifest 读取 capabilities
  2. 调用 workspace.RegisterCapabilities(capabilities) 注册
  3. 策略调用 TryMarkFixed() 时,workspace 校验声明状态
  4. 未声明则拒绝并记录警告

SeatingWorkspace 实现 IFixedSeatCapabilityIPluginWorkspace 直接暴露 TryMarkFixed 方法供插件使用。