排课版本新建、编辑、复制、发布、归档和删除。

周课表视图,支持12节课、单双周和起止周。
自动校验教师、行政班、教室、时间段冲突。
校验教室容量、教学任务状态和学期范围。
SQLite 开发环境自动升级,MySQL 提供正式 EF Core 迁移。
Vue 已编译为静态文件并随 ASP.NET Core 发布,无需 npm run dev。
桌面端和390px移动端页面均已验收。
This commit is contained in:
2026-07-24 14:27:01 +08:00 Unverified
parent 628944f64d
commit 0b463fa4f2
19 changed files with 2950 additions and 9 deletions
@@ -0,0 +1,62 @@
using Jiaowu.Api.Domain.Academic;
namespace Jiaowu.Api.Infrastructure.Scheduling;
public static class ScheduleConflictDetector
{
public static string? FindConflict(IReadOnlyList<ScheduleEntry> entries)
{
for (var i = 0; i < entries.Count; i++)
{
for (var j = i + 1; j < entries.Count; j++)
{
var first = entries[i];
var second = entries[j];
if (!TimeOverlaps(first, second)) continue;
var reason = ConflictReason(first, second);
if (reason is not null)
return $"“{first.TeachingTask!.Name}”与“{second.TeachingTask!.Name}”存在{reason}冲突。";
}
}
return null;
}
public static bool TimeOverlaps(ScheduleEntry first, ScheduleEntry second) =>
first.DayOfWeek == second.DayOfWeek &&
first.StartPeriod < second.StartPeriod + second.PeriodCount &&
second.StartPeriod < first.StartPeriod + first.PeriodCount &&
WeeksOverlap(first, second);
public static string? ConflictReason(ScheduleEntry first, ScheduleEntry second)
{
if (first.ClassroomId == second.ClassroomId) return "教室";
var firstTeachers = first.TeachingTask!.Teachers.Select(x => x.TeacherId).ToHashSet();
if (second.TeachingTask!.Teachers.Any(x => firstTeachers.Contains(x.TeacherId)))
return "教师";
var firstClasses = first.TeachingTask.Classes
.Select(x => x.AdministrativeClassId)
.ToHashSet();
return second.TeachingTask.Classes.Any(x =>
firstClasses.Contains(x.AdministrativeClassId))
? "行政班"
: null;
}
private static bool WeeksOverlap(ScheduleEntry first, ScheduleEntry second)
{
var start = Math.Max(first.StartWeek, second.StartWeek);
var end = Math.Min(first.EndWeek, second.EndWeek);
for (var week = start; week <= end; week++)
{
if (IncludesWeek(first.WeekPattern, week) &&
IncludesWeek(second.WeekPattern, week))
return true;
}
return false;
}
private static bool IncludesWeek(WeekPattern pattern, int week) =>
pattern == WeekPattern.All ||
pattern == WeekPattern.Odd && week % 2 == 1 ||
pattern == WeekPattern.Even && week % 2 == 0;
}