103 lines
4.4 KiB
C#
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);
|
|
}
|
|
}
|