优化学业预警检测:
通知阶段移除逐条查询学生、规则、辅导员的 N+1 数据库访问。 不及格预警从“每个学生一次统计 + 一次查重”改为批量分组统计与一次性已有记录集合查询。 保持原有阈值、去重、通知内容与权限行为。
This commit is contained in:
@@ -101,16 +101,22 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
||||
db.WarningRecords.AddRange(generated);
|
||||
await db.SaveChangesAsync(ct);
|
||||
|
||||
var studentsById = studentInfos.ToDictionary(student => student.Id);
|
||||
var rulesByType = rules.ToDictionary(rule => rule.Type);
|
||||
var classIds = studentInfos.Select(student => student.ClassId).Distinct().ToArray();
|
||||
var counselorsByClass = await db.AdministrativeClasses.AsNoTracking()
|
||||
.WhereIn(classIds, item => item.Id)
|
||||
.Where(item => item.CounselorUserId != null)
|
||||
.Select(item => new { item.Id, CounselorUserId = item.CounselorUserId!.Value })
|
||||
.ToDictionaryAsync(item => item.Id, item => item.CounselorUserId, ct);
|
||||
foreach (var w in generated)
|
||||
{
|
||||
var si = studentInfos.First(s => s.Id == w.StudentId);
|
||||
var r = rules.First(r => r.Type == w.Type);
|
||||
var si = studentsById[w.StudentId];
|
||||
var r = rulesByType[w.Type];
|
||||
if (r.NotifyStudent && si.UserId.HasValue)
|
||||
await NotificationService.SendAsync(db, si.UserId.Value, "学业预警", w.Detail, "/warnings", ct, NotificationCategory.Warning);
|
||||
if (r.NotifyCounselor)
|
||||
if (r.NotifyCounselor && counselorsByClass.TryGetValue(si.ClassId, out var counselorId))
|
||||
{
|
||||
var counselorId = await db.AdministrativeClasses.Where(c => c.Id == si.ClassId && c.CounselorUserId != null).Select(c => c.CounselorUserId!.Value).FirstOrDefaultAsync(ct);
|
||||
if (counselorId != default)
|
||||
await NotificationService.SendAsync(db, counselorId, "学生学业预警", $"{si.Name}:{w.Detail}", "/warnings", ct, NotificationCategory.Warning);
|
||||
}
|
||||
}
|
||||
@@ -197,17 +203,31 @@ public sealed class WarningsController(AppDbContext db, ICurrentUserDataScope sc
|
||||
// ═══════════ Detection logic ═══════════
|
||||
private async Task<List<WarningRecord>> DetectFailedCredits(WarningRule rule, Guid termId, List<StudentInfo> students, CancellationToken ct)
|
||||
{
|
||||
var records = new List<WarningRecord>();
|
||||
foreach (var s in students)
|
||||
var studentIds = students.Select(student => student.Id).ToArray();
|
||||
var existingStudentIds = await db.WarningRecords.AsNoTracking()
|
||||
.Where(record => record.AcademicTermId == termId && record.Type == rule.Type)
|
||||
.Select(record => record.StudentId)
|
||||
.ToHashSetAsync(ct);
|
||||
var failedByStudent = await db.GradeRecords.AsNoTracking()
|
||||
.Where(record => studentIds.Contains(record.StudentId) &&
|
||||
record.GradeSheet!.TeachingTask!.AcademicTermId == termId &&
|
||||
record.GradeSheet.Status == GradeSheetStatus.Published &&
|
||||
record.TotalScore < 60)
|
||||
.GroupBy(record => record.StudentId)
|
||||
.Select(group => new { StudentId = group.Key, Failed = group.Count() })
|
||||
.ToListAsync(ct);
|
||||
return failedByStudent
|
||||
.Where(item => item.Failed >= rule.Threshold &&
|
||||
!existingStudentIds.Contains(item.StudentId))
|
||||
.Select(item => new WarningRecord
|
||||
{
|
||||
var failed = await db.GradeRecords.CountAsync(x => x.StudentId == s.Id && x.GradeSheet!.TeachingTask!.AcademicTermId == termId && x.GradeSheet.Status == GradeSheetStatus.Published && x.TotalScore < 60, ct);
|
||||
if (failed >= rule.Threshold)
|
||||
{
|
||||
if (await db.WarningRecords.AnyAsync(x => x.StudentId == s.Id && x.AcademicTermId == termId && x.Type == WarningType.FailedCredits, ct)) continue;
|
||||
records.Add(new WarningRecord { StudentId = s.Id, Type = rule.Type, TriggerValue = failed, Detail = $"不及格课程 {failed} 门,达到预警阈值 {rule.Threshold} 门。", AcademicTermId = termId });
|
||||
}
|
||||
}
|
||||
return records;
|
||||
StudentId = item.StudentId,
|
||||
Type = rule.Type,
|
||||
TriggerValue = item.Failed,
|
||||
Detail = $"不及格课程 {item.Failed} 门,达到预警阈值 {rule.Threshold} 门。",
|
||||
AcademicTermId = termId
|
||||
})
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<List<WarningRecord>> DetectLowGPA(WarningRule rule, Guid termId, List<StudentInfo> students, CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user