diff --git a/src/Jiaowu.Api/Controllers/SchedulesController.cs b/src/Jiaowu.Api/Controllers/SchedulesController.cs index 2ed149e..07a361d 100644 --- a/src/Jiaowu.Api/Controllers/SchedulesController.cs +++ b/src/Jiaowu.Api/Controllers/SchedulesController.cs @@ -354,6 +354,25 @@ public sealed class SchedulesController( ToResponse(job)); } + [HttpGet("plans/{planId:guid}/preflight")] + public async Task Preflight(Guid planId, CancellationToken cancellationToken) + { + var plan = await DraftPlanAsync(planId, cancellationToken); + if (plan is null) return NotFound(); + var tasks = await db.TeachingTasks.AsNoTracking() + .Where(x => x.AcademicTermId == plan.AcademicTermId && + x.Status == TeachingTaskStatus.Published && + x.SchedulingMode == TeachingTaskSchedulingMode.Standard) + .Select(x => new { x.Id, x.Name, CourseName = x.Course!.Name }) + .ToListAsync(cancellationToken); + var scheduled = await db.ScheduleEntries.AsNoTracking() + .Where(x => x.SchedulePlanId == planId) + .Select(x => x.TeachingTaskId).Distinct().ToListAsync(cancellationToken); + var missing = tasks.Where(x => !scheduled.Contains(x.Id)) + .Select(x => $"《{x.CourseName}》{x.Name}").Take(20).ToList(); + return Ok(new { totalTasks = tasks.Count, scheduledTasks = scheduled.Count, unscheduledTasks = missing.Count, messages = missing }); + } + [HttpGet("auto-schedule-jobs/{jobId:guid}")] public async Task> GetAutomaticScheduleJob( diff --git a/web/src/views/SchedulesView.vue b/web/src/views/SchedulesView.vue index 9d18275..c66f135 100644 --- a/web/src/views/SchedulesView.vue +++ b/web/src/views/SchedulesView.vue @@ -807,6 +807,16 @@ function changeEntryKind() { } } +async function preflightSchedule() { + try { + const { data } = await http.get(`/schedules/plans/${selected.value.id}/preflight`) + const message = data.unscheduledTasks + ? `尚有 ${data.unscheduledTasks} 个教学班未安排:\n${data.messages.join('\n')}` + : '当前草稿已覆盖全部需要排课的教学班。' + await ElMessageBox.alert(message, `排课前检查 · 已覆盖 ${data.scheduledTasks}/${data.totalTasks}`, { confirmButtonText: '知道了' }) + } catch (error) { ElMessage.error(apiErrorMessage(error)) } +} + async function saveEntry() { if (!entryForm.teachingTaskId || ((entryForm.kind === 'Experiment' || @@ -937,6 +947,13 @@ onBeforeUnmount(() => { > 复制调整 + + 排课前检查 +