学业预警自动化
This commit is contained in:
1 parent
d23b5156b4
commit
3d4d9953e8
7 files changed
+120
-146
No files matched your search
@@ -21,7 +21,7 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
|||||||
[Authorize(Roles = Managers)]
|
[Authorize(Roles = Managers)]
|
||||||
public async Task<ActionResult> GetRules(Guid academicTermId, CancellationToken ct) =>
|
public async Task<ActionResult> GetRules(Guid academicTermId, CancellationToken ct) =>
|
||||||
Ok(await db.WarningRules.AsNoTracking().Where(x => x.AcademicTermId == academicTermId)
|
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));
|
.ToListAsync(ct));
|
||||||
|
|
||||||
[HttpPut("rules")]
|
[HttpPut("rules")]
|
||||||
@@ -37,7 +37,11 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
|||||||
AcademicTermId = academicTermId, Type = r.Type, Name = r.Name.Trim(),
|
AcademicTermId = academicTermId, Type = r.Type, Name = r.Name.Trim(),
|
||||||
Threshold = r.Threshold, IsEnabled = r.IsEnabled,
|
Threshold = r.Threshold, IsEnabled = r.IsEnabled,
|
||||||
NotifyStudent = r.NotifyStudent, NotifyCounselor = r.NotifyCounselor,
|
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);
|
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));
|
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 ═══════════
|
// ═══════════ Student ═══════════
|
||||||
[HttpGet("my-warnings")]
|
[HttpGet("my-warnings")]
|
||||||
[Authorize(Roles = SystemRoles.Student)]
|
[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 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 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 ScheduleDto(bool IsEnabled, int DayOfWeek, int Hour, int Minute);
|
|
||||||
public sealed record AckBody([MaxLength(300)] string? Comment);
|
public sealed record AckBody([MaxLength(300)] string? Comment);
|
||||||
@@ -13,6 +13,12 @@ public sealed class WarningRule : EntityBase
|
|||||||
public Guid AcademicTermId { get; set; }
|
public Guid AcademicTermId { get; set; }
|
||||||
public AcademicTerm? AcademicTerm { get; set; }
|
public AcademicTerm? AcademicTerm { get; set; }
|
||||||
public string? Description { get; set; }
|
public string? Description { get; set; }
|
||||||
|
// Auto-check schedule (per-type, independent)
|
||||||
|
public bool AutoCheckEnabled { get; set; }
|
||||||
|
public int? CheckDayOfWeek { get; set; } // null = daily
|
||||||
|
public int CheckHour { get; set; } = 8;
|
||||||
|
public int CheckMinute { get; set; }
|
||||||
|
public DateTime? LastCheckAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class WarningRecord : EntityBase
|
public sealed class WarningRecord : EntityBase
|
||||||
@@ -36,17 +42,6 @@ public enum WarningType
|
|||||||
GraduationDelay = 4
|
GraduationDelay = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class WarningSchedule : EntityBase
|
|
||||||
{
|
|
||||||
public Guid AcademicTermId { get; set; }
|
|
||||||
public AcademicTerm? AcademicTerm { get; set; }
|
|
||||||
public bool IsEnabled { get; set; }
|
|
||||||
public int DayOfWeek { get; set; } = 1;
|
|
||||||
public int Hour { get; set; } = 8;
|
|
||||||
public int Minute { get; set; }
|
|
||||||
public DateTime? LastRunAt { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum WarningStatus
|
public enum WarningStatus
|
||||||
{
|
{
|
||||||
Active = 1,
|
Active = 1,
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
|||||||
public DbSet<CourseSubstitution> CourseSubstitutions => Set<CourseSubstitution>();
|
public DbSet<CourseSubstitution> CourseSubstitutions => Set<CourseSubstitution>();
|
||||||
public DbSet<WarningRule> WarningRules => Set<WarningRule>();
|
public DbSet<WarningRule> WarningRules => Set<WarningRule>();
|
||||||
public DbSet<WarningRecord> WarningRecords => Set<WarningRecord>();
|
public DbSet<WarningRecord> WarningRecords => Set<WarningRecord>();
|
||||||
public DbSet<WarningSchedule> WarningSchedules => Set<WarningSchedule>();
|
|
||||||
public DbSet<Notification> Notifications => Set<Notification>();
|
public DbSet<Notification> Notifications => Set<Notification>();
|
||||||
public DbSet<StudentStatusChange> StudentStatusChanges =>
|
public DbSet<StudentStatusChange> StudentStatusChanges =>
|
||||||
Set<StudentStatusChange>();
|
Set<StudentStatusChange>();
|
||||||
@@ -731,12 +730,6 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
|||||||
entity.HasOne(x => x.Student).WithMany()
|
entity.HasOne(x => x.Student).WithMany()
|
||||||
.HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict);
|
.HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict);
|
||||||
});
|
});
|
||||||
builder.Entity<WarningSchedule>(entity =>
|
|
||||||
{
|
|
||||||
entity.HasIndex(x => x.AcademicTermId).IsUnique();
|
|
||||||
entity.HasOne(x => x.AcademicTerm).WithMany()
|
|
||||||
.HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.Entity<CourseSubstitution>(entity =>
|
builder.Entity<CourseSubstitution>(entity =>
|
||||||
{
|
{
|
||||||
|
|||||||
Loaded 3 of 7 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user