空闲教室与场地预约占用检查,现在对新发布课表优先查询 PublishedScheduleOccurrences。

查询直接按“学期 + 周次 + 星期 + 节次”命中投影索引,不再扫描周期规则并在应用层判断单双周。
历史已发布课表若尚未生成投影,自动回退到原规则表,兼容现有数据。
调停课审批后会重建受影响教学任务的已发布投影,因此占用结果同步更新。
This commit is contained in:
2026-08-09 16:04:17 +08:00 Unverified
parent e36a02ea59
commit e39ab4579f
@@ -17,33 +17,34 @@ public sealed class ClassroomReservationAvailabilityService(AppDbContext db)
var occupiedIds = new HashSet<Guid>(); var occupiedIds = new HashSet<Guid>();
var (week, dayOfWeek) = ResolveTeachingWeek(term, reservationDate); var (week, dayOfWeek) = ResolveTeachingWeek(term, reservationDate);
var scheduleEntries = await db.ScheduleEntries.AsNoTracking() var hasProjection = await db.PublishedScheduleOccurrences.AsNoTracking()
.Where(entry => .AnyAsync(entry => entry.AcademicTermId == term.Id, cancellationToken);
entry.ClassroomId.HasValue && if (hasProjection)
entry.SchedulePlan!.AcademicTermId == term.Id &&
entry.SchedulePlan.Status == SchedulePlanStatus.Published &&
entry.DayOfWeek == dayOfWeek &&
entry.StartWeek <= week &&
entry.EndWeek >= week &&
entry.StartPeriod < startPeriod + periodCount &&
startPeriod < entry.StartPeriod + entry.PeriodCount)
.Select(entry => new
{
entry.ClassroomId,
entry.WeekPattern,
entry.StartPeriod,
entry.PeriodCount
})
.ToListAsync(cancellationToken);
foreach (var entry in scheduleEntries.Where(entry =>
FreeClassroomRules.MatchesWeek(entry.WeekPattern, week) &&
FreeClassroomRules.PeriodsOverlap(
startPeriod,
periodCount,
entry.StartPeriod,
entry.PeriodCount)))
{ {
occupiedIds.Add(entry.ClassroomId!.Value); var projectedRoomIds = await db.PublishedScheduleOccurrences.AsNoTracking()
.Where(entry => entry.AcademicTermId == term.Id && entry.Week == week &&
entry.DayOfWeek == dayOfWeek && entry.ClassroomId.HasValue &&
entry.StartPeriod < startPeriod + periodCount &&
startPeriod < entry.StartPeriod + entry.PeriodCount)
.Select(entry => entry.ClassroomId!.Value)
.ToListAsync(cancellationToken);
occupiedIds.UnionWith(projectedRoomIds);
}
else
{
var scheduleEntries = await db.ScheduleEntries.AsNoTracking()
.Where(entry => entry.ClassroomId.HasValue &&
entry.SchedulePlan!.AcademicTermId == term.Id &&
entry.SchedulePlan.Status == SchedulePlanStatus.Published &&
entry.DayOfWeek == dayOfWeek && entry.StartWeek <= week &&
entry.EndWeek >= week && entry.StartPeriod < startPeriod + periodCount &&
startPeriod < entry.StartPeriod + entry.PeriodCount)
.Select(entry => new { entry.ClassroomId, entry.WeekPattern, entry.StartPeriod, entry.PeriodCount })
.ToListAsync(cancellationToken);
foreach (var entry in scheduleEntries.Where(entry =>
FreeClassroomRules.MatchesWeek(entry.WeekPattern, week) &&
FreeClassroomRules.PeriodsOverlap(startPeriod, periodCount, entry.StartPeriod, entry.PeriodCount)))
occupiedIds.Add(entry.ClassroomId!.Value);
} }
var examRoomIds = await db.ExamSessions.AsNoTracking() var examRoomIds = await db.ExamSessions.AsNoTracking()