using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Timetables; public sealed class PublishedTimetableProjectionService(AppDbContext db) { private const int WriteBatchSize = 2_000; public async Task RebuildPublishedPlansForTaskAsync(Guid teachingTaskId, CancellationToken cancellationToken) { var plans = await db.SchedulePlans .Where(plan => plan.Status == SchedulePlanStatus.Published && plan.Entries.Any(entry => entry.TeachingTaskId == teachingTaskId)) .Include(plan => plan.Entries) .ToListAsync(cancellationToken); foreach (var plan in plans) await RebuildAsync(plan, cancellationToken); } public async Task RebuildAsync(SchedulePlan plan, CancellationToken cancellationToken) { await db.PublishedScheduleOccurrences .Where(x => x.SchedulePlanId == plan.Id) .ExecuteDeleteAsync(cancellationToken); var rows = new List(WriteBatchSize); foreach (var entry in plan.Entries) for (var week = entry.StartWeek; week <= entry.EndWeek; week++) { if (entry.WeekPattern == WeekPattern.Odd && week % 2 == 0 || entry.WeekPattern == WeekPattern.Even && week % 2 != 0) continue; rows.Add(new PublishedScheduleOccurrence { SchedulePlanId = plan.Id, AcademicTermId = plan.AcademicTermId, ScheduleEntryId = entry.Id, TeachingTaskId = entry.TeachingTaskId, ClassroomId = entry.ClassroomId, Week = week, DayOfWeek = entry.DayOfWeek, StartPeriod = entry.StartPeriod, PeriodCount = entry.PeriodCount, Kind = entry.Kind }); if (rows.Count == WriteBatchSize) await WriteBatchAsync(rows, cancellationToken); } if (rows.Count > 0) await WriteBatchAsync(rows, cancellationToken); } private async Task WriteBatchAsync( List rows, CancellationToken cancellationToken) { db.PublishedScheduleOccurrences.AddRange(rows); await db.SaveChangesAsync(cancellationToken); foreach (var row in rows) db.Entry(row).State = EntityState.Detached; rows.Clear(); } }