using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Timetables; public sealed class ClassroomReservationAvailabilityService(AppDbContext db) { public async Task> GetOccupiedClassroomIdsAsync( AcademicTerm term, DateOnly reservationDate, int startPeriod, int periodCount, Guid? excludedReservationId, CancellationToken cancellationToken) { var occupiedIds = new HashSet(); var (week, dayOfWeek) = ResolveTeachingWeek(term, reservationDate); 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() .Where(session => session.ClassroomId.HasValue && session.ExamPlan!.AcademicTermId == term.Id && session.ExamPlan.Status == ExamPlanStatus.Published && session.ExamDate == reservationDate && session.StartPeriod < startPeriod + periodCount && startPeriod < session.StartPeriod + session.PeriodCount) .Select(session => session.ClassroomId!.Value) .ToListAsync(cancellationToken); occupiedIds.UnionWith(examRoomIds); var mixedExamRoomIds = await db.ExamRooms.AsNoTracking() .Where(room => room.ExamPlan!.AcademicTermId == term.Id && room.ExamPlan.Status == ExamPlanStatus.Published && room.ExamDate == reservationDate && room.StartPeriod < startPeriod + periodCount && startPeriod < room.StartPeriod + room.PeriodCount) .Select(room => room.ClassroomId) .ToListAsync(cancellationToken); occupiedIds.UnionWith(mixedExamRoomIds); var makeupExamRoomIds = await db.MakeupExamSessions.AsNoTracking() .Where(session => session.ClassroomId.HasValue && session.MakeupExamPlan!.AcademicTermId == term.Id && session.MakeupExamPlan.Status == MakeupExamPlanStatus.Published && session.ExamDate == reservationDate && session.StartPeriod < startPeriod + periodCount && startPeriod < session.StartPeriod + session.PeriodCount) .Select(session => session.ClassroomId!.Value) .ToListAsync(cancellationToken); occupiedIds.UnionWith(makeupExamRoomIds); var experimentRoomIds = await db.ExperimentSessions.AsNoTracking() .Where(session => session.ExperimentProject!.TeachingTask!.AcademicTermId == term.Id && session.ExperimentProject.Status != ExperimentProjectStatus.Closed && session.Status == ExperimentSessionStatus.Scheduled && session.SessionDate == reservationDate && session.StartPeriod < startPeriod + periodCount && startPeriod < session.StartPeriod + session.PeriodCount) .Select(session => session.ClassroomId) .ToListAsync(cancellationToken); occupiedIds.UnionWith(experimentRoomIds); var reservationQuery = db.ClassroomReservations.AsNoTracking() .Where(reservation => reservation.AcademicTermId == term.Id && reservation.Status == ClassroomReservationStatus.Approved && reservation.ReservationDate == reservationDate && reservation.StartPeriod < startPeriod + periodCount && startPeriod < reservation.StartPeriod + reservation.PeriodCount); if (excludedReservationId.HasValue) reservationQuery = reservationQuery.Where(reservation => reservation.Id != excludedReservationId.Value); var reservationRoomIds = await reservationQuery .Select(reservation => reservation.ClassroomId) .ToListAsync(cancellationToken); occupiedIds.UnionWith(reservationRoomIds); return occupiedIds; } public static (int Week, int DayOfWeek) ResolveTeachingWeek( AcademicTerm term, DateOnly date) { var termStartDay = (int)term.StartDate.DayOfWeek; var daysSinceMonday = (termStartDay + 6) % 7; var firstWeekMonday = term.StartDate.AddDays(-daysSinceMonday); var week = (date.DayNumber - firstWeekMonday.DayNumber) / 7 + 1; var dayOfWeek = ((int)date.DayOfWeek + 6) % 7 + 1; return (week, dayOfWeek); } }