排课优化 #3
No files matched your search
@@ -14,6 +14,46 @@ namespace Jiaowu.Api.Controllers;
|
|||||||
[Route("api/schedules")]
|
[Route("api/schedules")]
|
||||||
public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache) : ControllerBase
|
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")]
|
[HttpGet("time-slots")]
|
||||||
public async Task<ActionResult> GetTimeSlots(
|
public async Task<ActionResult> GetTimeSlots(
|
||||||
Guid academicTermId,
|
Guid academicTermId,
|
||||||
@@ -32,6 +72,17 @@ public sealed class ScheduleSettingsController(AppDbContext db, IAppCache cache)
|
|||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken));
|
.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}")]
|
[HttpPut("time-slots/{academicTermId:guid}")]
|
||||||
public async Task<ActionResult> ReplaceTimeSlots(
|
public async Task<ActionResult> ReplaceTimeSlots(
|
||||||
Guid academicTermId,
|
Guid academicTermId,
|
||||||
@@ -544,6 +595,15 @@ public sealed record ScheduleTimeSlotRequest(
|
|||||||
TimeOnly EndsAt,
|
TimeOnly EndsAt,
|
||||||
bool IsEnabled);
|
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(
|
public sealed record TeachingTaskScheduleConstraintRequest(
|
||||||
TeachingTaskSchedulingMode SchedulingMode,
|
TeachingTaskSchedulingMode SchedulingMode,
|
||||||
bool RequiresClassroom,
|
bool RequiresClassroom,
|
||||||
|
|||||||
@@ -43,6 +43,26 @@ public sealed class ScheduleTimeSlot : EntityBase
|
|||||||
public bool IsEnabled { get; set; } = true;
|
public bool IsEnabled { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学期级自动排课的软约束权重。所有值为 0 时表示关闭对应偏好;
|
||||||
|
/// 硬约束(冲突、容量和课程可用范围)始终优先于这些设置。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ScheduleOptimizationSettings : EntityBase
|
||||||
|
{
|
||||||
|
public Guid AcademicTermId { get; set; }
|
||||||
|
public AcademicTerm? AcademicTerm { get; set; }
|
||||||
|
public int TeacherSameDayWeight { get; set; } = 120;
|
||||||
|
public int ClassSameDayWeight { get; set; } = 100;
|
||||||
|
public int ClassGapWeight { get; set; } = 240;
|
||||||
|
public int ConsecutiveClassWeight { get; set; } = 60;
|
||||||
|
public int EarlyPeriodPenalty { get; set; } = 30;
|
||||||
|
public int LatePeriodPenalty { get; set; } = 30;
|
||||||
|
public int RoomCapacityWasteWeight { get; set; } = 1;
|
||||||
|
|
||||||
|
public static ScheduleOptimizationSettings CreateDefault(Guid academicTermId) =>
|
||||||
|
new() { AcademicTermId = academicTermId };
|
||||||
|
}
|
||||||
|
|
||||||
public sealed class TeachingTaskScheduleConstraint : EntityBase
|
public sealed class TeachingTaskScheduleConstraint : EntityBase
|
||||||
{
|
{
|
||||||
public Guid TeachingTaskId { get; set; }
|
public Guid TeachingTaskId { get; set; }
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
|||||||
public DbSet<ScheduleEntry> ScheduleEntries => Set<ScheduleEntry>();
|
public DbSet<ScheduleEntry> ScheduleEntries => Set<ScheduleEntry>();
|
||||||
public DbSet<PublishedScheduleOccurrence> PublishedScheduleOccurrences => Set<PublishedScheduleOccurrence>();
|
public DbSet<PublishedScheduleOccurrence> PublishedScheduleOccurrences => Set<PublishedScheduleOccurrence>();
|
||||||
public DbSet<ScheduleTimeSlot> ScheduleTimeSlots => Set<ScheduleTimeSlot>();
|
public DbSet<ScheduleTimeSlot> ScheduleTimeSlots => Set<ScheduleTimeSlot>();
|
||||||
|
public DbSet<ScheduleOptimizationSettings> ScheduleOptimizationSettings =>
|
||||||
|
Set<ScheduleOptimizationSettings>();
|
||||||
public DbSet<TeachingTaskScheduleConstraint> TeachingTaskScheduleConstraints =>
|
public DbSet<TeachingTaskScheduleConstraint> TeachingTaskScheduleConstraints =>
|
||||||
Set<TeachingTaskScheduleConstraint>();
|
Set<TeachingTaskScheduleConstraint>();
|
||||||
public DbSet<TeachingTaskAllowedClassroom> TeachingTaskAllowedClassrooms =>
|
public DbSet<TeachingTaskAllowedClassroom> TeachingTaskAllowedClassrooms =>
|
||||||
@@ -518,6 +520,15 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
|||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.Entity<ScheduleOptimizationSettings>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasIndex(x => x.AcademicTermId).IsUnique();
|
||||||
|
entity.HasOne(x => x.AcademicTerm)
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey(x => x.AcademicTermId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
builder.Entity<TeachingTaskScheduleConstraint>(entity =>
|
builder.Entity<TeachingTaskScheduleConstraint>(entity =>
|
||||||
{
|
{
|
||||||
entity.Property(x => x.AllowedDayOfWeeks).HasMaxLength(20);
|
entity.Property(x => x.AllowedDayOfWeeks).HasMaxLength(20);
|
||||||
|
|||||||
Loaded 3 of 10 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user