学业预警自动化

This commit is contained in:
2026-07-25 17:28:53 +08:00 Unverified
parent d23b5156b4
commit 3d4d9953e8
7 changed files with 120 additions and 146 deletions
@@ -21,7 +21,7 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
[Authorize(Roles = Managers)]
public async Task<ActionResult> GetRules(Guid academicTermId, CancellationToken ct) =>
Ok(await db.WarningRules.AsNoTracking().Where(x => x.AcademicTermId == academicTermId)
.OrderBy(x => x.Type).Select(x => new { x.Id, x.Type, x.Name, x.Threshold, x.IsEnabled, x.NotifyStudent, x.NotifyCounselor, x.Description })
.OrderBy(x => x.Type).Select(x => new { x.Id, x.Type, x.Name, x.Threshold, x.IsEnabled, x.NotifyStudent, x.NotifyCounselor, x.Description, x.AutoCheckEnabled, CheckDayOfWeek = x.CheckDayOfWeek ?? 0, x.CheckHour, x.CheckMinute, x.LastCheckAt })
.ToListAsync(ct));
[HttpPut("rules")]
@@ -37,7 +37,11 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
AcademicTermId = academicTermId, Type = r.Type, Name = r.Name.Trim(),
Threshold = r.Threshold, IsEnabled = r.IsEnabled,
NotifyStudent = r.NotifyStudent, NotifyCounselor = r.NotifyCounselor,
Description = r.Description?.Trim()
Description = r.Description?.Trim(),
AutoCheckEnabled = r.AutoCheckEnabled,
CheckDayOfWeek = r.CheckDayOfWeek == 0 ? null : r.CheckDayOfWeek,
CheckHour = r.CheckHour,
CheckMinute = r.CheckMinute
});
}
await db.SaveChangesAsync(ct);
@@ -115,26 +119,6 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
return Ok(await q.OrderByDescending(x => x.CreatedAt).Select(x => new { x.Id, x.StudentId, StudentName = x.Student!.Name, StudentNumber = x.Student.StudentNumber, ClassName = x.Student.AdministrativeClass!.Name, x.Type, x.Status, x.TriggerValue, x.Detail, x.AcknowledgedAt, x.AcknowledgeComment, x.CreatedAt }).ToListAsync(ct));
}
// ═══════════ Schedule ═══════════
[HttpGet("schedule")]
[Authorize(Roles = Managers)]
public async Task<ActionResult> GetSchedule(Guid academicTermId, CancellationToken ct)
{
var s = await db.WarningSchedules.AsNoTracking().FirstOrDefaultAsync(x => x.AcademicTermId == academicTermId, ct);
return Ok(s is null ? new { isEnabled = false, dayOfWeek = 1, hour = 8, minute = 0, lastRunAt = (DateTime?)null } : new { s.IsEnabled, s.DayOfWeek, s.Hour, s.Minute, s.LastRunAt });
}
[HttpPut("schedule")]
[Authorize(Roles = Managers)]
public async Task<ActionResult> SaveSchedule(Guid academicTermId, [FromBody] ScheduleDto dto, CancellationToken ct)
{
var s = await db.WarningSchedules.FirstOrDefaultAsync(x => x.AcademicTermId == academicTermId, ct);
if (s is null) { s = new WarningSchedule { AcademicTermId = academicTermId }; db.WarningSchedules.Add(s); }
s.IsEnabled = dto.IsEnabled; s.DayOfWeek = dto.DayOfWeek; s.Hour = dto.Hour; s.Minute = dto.Minute;
await db.SaveChangesAsync(ct);
return NoContent();
}
// ═══════════ Student ═══════════
[HttpGet("my-warnings")]
[Authorize(Roles = SystemRoles.Student)]
@@ -232,6 +216,5 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
}
public sealed record StudentInfo(Guid Id, Guid? UserId, string Name, Guid ClassId);
public sealed record WarningRuleDto(WarningType Type, [MaxLength(100)] string Name, decimal Threshold, bool IsEnabled, bool NotifyStudent, bool NotifyCounselor, [MaxLength(300)] string? Description);
public sealed record ScheduleDto(bool IsEnabled, int DayOfWeek, int Hour, int Minute);
public sealed record WarningRuleDto(WarningType Type, [MaxLength(100)] string Name, decimal Threshold, bool IsEnabled, bool NotifyStudent, bool NotifyCounselor, [MaxLength(300)] string? Description, bool AutoCheckEnabled, int? CheckDayOfWeek, int CheckHour, int CheckMinute);
public sealed record AckBody([MaxLength(300)] string? Comment);