Files
Academic-Affairs-System/src/Jiaowu.Api/Infrastructure/Timetables/ClassroomReservationAvailabilityService.cs
T
2026-07-26 20:11:56 +08:00

103 lines
4.4 KiB
C#

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<HashSet<Guid>> GetOccupiedClassroomIdsAsync(
AcademicTerm term,
DateOnly reservationDate,
int startPeriod,
int periodCount,
Guid? excludedReservationId,
CancellationToken cancellationToken)
{
var occupiedIds = new HashSet<Guid>();
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 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 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);
}
}