新增按学期配置的“自动排课优化”页签,可调教师/行政班同日集中、行政班空档与连堂、首末节、教室容量冗余等权重。

自动排课在所有硬约束满足后,对候选排法应用这些软约束评分;不会改动既有课程或已发布课表。
This commit is contained in:
biss committed 2026-08-17 10:51:10 +08:00
1 parent 8074d59e82
commit 0513e251df
10 files changed
+7588 -2

No files matched your search

@@ -14,6 +14,46 @@ namespace Jiaowu.Api.Controllers;
[Route("api/schedules")]
public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache) : ControllerBase
{
[HttpGet("optimization-settings")]
public async Task<ActionResult> GetOptimizationSettings(
Guid academicTermId,
CancellationToken cancellationToken)
{
var settings = await db.ScheduleOptimizationSettings.AsNoTracking()
.FirstOrDefaultAsync(x => x.AcademicTermId == academicTermId, cancellationToken)
?? ScheduleOptimizationSettings.CreateDefault(academicTermId);
return Ok(ToOptimizationSettingsResponse(settings));
}
[HttpPut("optimization-settings/{academicTermId:guid}")]
public async Task<ActionResult> SaveOptimizationSettings(
Guid academicTermId,
ScheduleOptimizationSettingsRequest request,
CancellationToken cancellationToken)
{
if (!await db.AcademicTerms.AnyAsync(
x => x.Id == academicTermId && x.IsEnabled,
cancellationToken))
return ValidationProblem("学期不存在或已停用。");
var settings = await db.ScheduleOptimizationSettings
.FirstOrDefaultAsync(x => x.AcademicTermId == academicTermId, cancellationToken);
if (settings is null)
{
settings = ScheduleOptimizationSettings.CreateDefault(academicTermId);
db.ScheduleOptimizationSettings.Add(settings);
}
settings.TeacherSameDayWeight = request.TeacherSameDayWeight;
settings.ClassSameDayWeight = request.ClassSameDayWeight;
settings.ClassGapWeight = request.ClassGapWeight;
settings.ConsecutiveClassWeight = request.ConsecutiveClassWeight;
settings.EarlyPeriodPenalty = request.EarlyPeriodPenalty;
settings.LatePeriodPenalty = request.LatePeriodPenalty;
settings.RoomCapacityWasteWeight = request.RoomCapacityWasteWeight;
await db.SaveChangesAsync(cancellationToken);
return Ok(ToOptimizationSettingsResponse(settings));
}
[HttpGet("time-slots")]
public async Task<ActionResult> GetTimeSlots(
Guid academicTermId,
@@ -32,6 +72,17 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
})
.ToListAsync(cancellationToken));
private static object ToOptimizationSettingsResponse(ScheduleOptimizationSettings settings) => new
{
settings.TeacherSameDayWeight,
settings.ClassSameDayWeight,
settings.ClassGapWeight,
settings.ConsecutiveClassWeight,
settings.EarlyPeriodPenalty,
settings.LatePeriodPenalty,
settings.RoomCapacityWasteWeight
};
[HttpPut("time-slots/{academicTermId:guid}")]
public async Task<ActionResult> ReplaceTimeSlots(
Guid academicTermId,
@@ -544,6 +595,15 @@ public sealed record ScheduleTimeSlotRequest(
TimeOnly EndsAt,
bool IsEnabled);
public sealed record ScheduleOptimizationSettingsRequest(
[Range(0, 10_000)] int TeacherSameDayWeight,
[Range(0, 10_000)] int ClassSameDayWeight,
[Range(0, 10_000)] int ClassGapWeight,
[Range(0, 10_000)] int ConsecutiveClassWeight,
[Range(0, 10_000)] int EarlyPeriodPenalty,
[Range(0, 10_000)] int LatePeriodPenalty,
[Range(0, 10_000)] int RoomCapacityWasteWeight);
public sealed record TeachingTaskScheduleConstraintRequest(
TeachingTaskSchedulingMode SchedulingMode,
bool RequiresClassroom,