已改成“普通排课与实验学时分离”:

普通课表学时 = 总学时 − 实践学时。
自动排课、手工排课、课表发布都会阻止把实践学时重复排入。
全部为实践学时的课程应设为“非排时课程”,再由实验管理安排。
教学任务和排课页面会显示“常规 / 实践学时”。
已有教学任务即使仍保存旧周学时,排课时也会按拆分后的课程学时计算。
无需数据库迁移。
This commit is contained in:
2026-07-29 09:57:49 +08:00 Unverified
parent 356c410788
commit 692b521453
14 changed files with 525 additions and 30 deletions
@@ -12,7 +12,7 @@ public sealed class SchedulePublishJobProcessorTests
[Fact]
public async Task Processor_validates_and_publishes_plan_atomically()
{
var result = await RunPublishAsync(weeklyHours: 2);
var result = await RunPublishAsync(weeklyHours: 2, scheduledHours: 2);
Assert.Equal(SchedulePublishJobStatus.Succeeded, result.JobStatus);
Assert.Equal(SchedulePlanStatus.Published, result.PlanStatus);
@@ -26,17 +26,46 @@ public sealed class SchedulePublishJobProcessorTests
[Fact]
public async Task Processor_keeps_draft_when_validation_fails()
{
var result = await RunPublishAsync(weeklyHours: 4);
var result = await RunPublishAsync(weeklyHours: 2, scheduledHours: 1);
Assert.Equal(SchedulePublishJobStatus.Failed, result.JobStatus);
Assert.Equal(SchedulePlanStatus.Draft, result.PlanStatus);
Assert.Null(result.ActiveAcademicTermId);
Assert.Equal("检查未通过", result.CurrentStep);
Assert.Contains("尚未达到每周 4 学时", result.ErrorMessage);
Assert.Contains("尚未达到每周 2 个普通排课学时", result.ErrorMessage);
Assert.Null(result.PublishedAt);
}
private static async Task<PublishResult> RunPublishAsync(int weeklyHours)
[Fact]
public async Task Processor_excludes_practice_hours_for_existing_tasks()
{
var result = await RunPublishAsync(
weeklyHours: 2,
scheduledHours: 1,
practiceHours: 16);
Assert.Equal(SchedulePublishJobStatus.Succeeded, result.JobStatus);
Assert.Equal(SchedulePlanStatus.Published, result.PlanStatus);
}
[Fact]
public async Task Processor_rejects_practice_hours_already_added_to_draft()
{
var result = await RunPublishAsync(
weeklyHours: 2,
scheduledHours: 2,
practiceHours: 16);
Assert.Equal(SchedulePublishJobStatus.Failed, result.JobStatus);
Assert.Equal(SchedulePlanStatus.Draft, result.PlanStatus);
Assert.Contains("普通课表应为 1 学时", result.ErrorMessage);
Assert.Contains("删除已包含的实践学时", result.ErrorMessage);
}
private static async Task<PublishResult> RunPublishAsync(
int weeklyHours,
int scheduledHours,
int practiceHours = 0)
{
var databasePath = Path.Combine(
Path.GetTempPath(),
@@ -75,7 +104,8 @@ public sealed class SchedulePublishJobProcessorTests
College = college,
Credits = 1,
TotalHours = 32,
LectureHours = 32
LectureHours = 32 - practiceHours,
PracticeHours = practiceHours
};
var task = new TeachingTask
{
@@ -100,7 +130,7 @@ public sealed class SchedulePublishJobProcessorTests
TeachingTask = task,
DayOfWeek = 1,
StartPeriod = 1,
PeriodCount = 2,
PeriodCount = scheduledHours,
StartWeek = 1,
EndWeek = 16,
WeekPattern = WeekPattern.All