补考、缓考成绩自动替换
This commit is contained in:
1 parent
d7d2560587
commit
51b83e9d78
7 files changed
+122
-20
No files matched your search
@@ -754,6 +754,27 @@ public sealed class MakeupExamsController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var enrollmentByStudent = session.Enrollments.ToDictionary(e => e.StudentId);
|
var enrollmentByStudent = session.Enrollments.ToDictionary(e => e.StudentId);
|
||||||
|
var unresolvedStudentIds = scores
|
||||||
|
.Where(x => enrollmentByStudent.TryGetValue(x.StudentId, out var enrollment) &&
|
||||||
|
!enrollment.SourceGradeRecordId.HasValue)
|
||||||
|
.Select(x => x.StudentId)
|
||||||
|
.Distinct()
|
||||||
|
.ToArray();
|
||||||
|
var fallbackGradeRecordIds = unresolvedStudentIds.Length == 0
|
||||||
|
? new Dictionary<Guid, Guid>()
|
||||||
|
: (await db.GradeRecords.AsNoTracking()
|
||||||
|
.Where(x => x.GradeSheet!.TeachingTaskId == session.TeachingTaskId)
|
||||||
|
.WhereIn(unresolvedStudentIds, x => x.StudentId)
|
||||||
|
.OrderByDescending(x => x.UpdatedAt)
|
||||||
|
.Select(x => new { x.StudentId, x.Id })
|
||||||
|
.ToListAsync(cancellationToken))
|
||||||
|
.GroupBy(x => x.StudentId)
|
||||||
|
.ToDictionary(x => x.Key, x => x.First().Id);
|
||||||
|
var missingReplacementTargets = unresolvedStudentIds
|
||||||
|
.Where(studentId => !fallbackGradeRecordIds.ContainsKey(studentId))
|
||||||
|
.ToArray();
|
||||||
|
if (missingReplacementTargets.Length > 0)
|
||||||
|
return ConflictProblem("存在无法关联原课程成绩的补考考生,不能录入补考成绩。请先建立该教学班成绩单后重试。");
|
||||||
|
|
||||||
foreach (var entry in scores)
|
foreach (var entry in scores)
|
||||||
{
|
{
|
||||||
@@ -762,22 +783,54 @@ public sealed class MakeupExamsController(
|
|||||||
|
|
||||||
enrollment.MakeupScore = entry.Score;
|
enrollment.MakeupScore = entry.Score;
|
||||||
|
|
||||||
|
// Backfill legacy deferred-exam enrollments created before they
|
||||||
|
// carried SourceGradeRecordId, then use the same replacement
|
||||||
|
// path as absent and failed candidates.
|
||||||
|
if (!enrollment.SourceGradeRecordId.HasValue &&
|
||||||
|
fallbackGradeRecordIds.TryGetValue(entry.StudentId, out var fallbackGradeRecordId))
|
||||||
|
enrollment.SourceGradeRecordId = fallbackGradeRecordId;
|
||||||
|
|
||||||
// Update the grade record — 补考合格按60分记
|
// Update the grade record — 补考合格按60分记
|
||||||
if (enrollment.SourceGradeRecordId.HasValue)
|
if (enrollment.SourceGradeRecordId.HasValue)
|
||||||
{
|
{
|
||||||
var record = await db.GradeRecords
|
var record = await db.GradeRecords
|
||||||
|
.Include(x => x.GradeSheet)
|
||||||
|
.ThenInclude(x => x!.Items)
|
||||||
|
.Include(x => x.ItemScores)
|
||||||
.FirstOrDefaultAsync(x => x.Id == enrollment.SourceGradeRecordId.Value,
|
.FirstOrDefaultAsync(x => x.Id == enrollment.SourceGradeRecordId.Value,
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
if (record is not null)
|
if (record is not null)
|
||||||
{
|
{
|
||||||
record.ExamStatus = GradeExamStatus.Makeup;
|
if (enrollment.Reason == MakeupReason.DeferredApproved &&
|
||||||
// Cap passing score at 60
|
record.GradeSheet is not null)
|
||||||
var cappedScore = entry.Score >= 60 ? 60m : entry.Score;
|
{
|
||||||
record.TotalScore = cappedScore;
|
// A deferred exam is a later sitting of the normal
|
||||||
record.GradePoint = GradeCalculator.CalculateGradePoint(cappedScore);
|
// final. Its score therefore combines with the
|
||||||
record.Notes = entry.Score >= 60
|
// already-entered regular and component scores.
|
||||||
? $"补考合格(原始{entry.Score}分,按60分记)"
|
record.ExamStatus = GradeExamStatus.Deferred;
|
||||||
: $"补考不合格({entry.Score}分)";
|
record.FinalScore = entry.Score;
|
||||||
|
record.TotalScore = GradeCalculator.CalculateTotal(
|
||||||
|
record.RegularScore,
|
||||||
|
record.FinalScore,
|
||||||
|
record.ItemScores.ToList(),
|
||||||
|
record.GradeSheet.RegularWeight,
|
||||||
|
record.GradeSheet.FinalWeight,
|
||||||
|
record.GradeSheet.Items.ToList(),
|
||||||
|
record.ExamStatus);
|
||||||
|
record.GradePoint = GradeCalculator.CalculateGradePoint(record.TotalScore);
|
||||||
|
record.Notes = $"缓考成绩(期末{entry.Score}分,已按正常权重计入平时分)";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
record.ExamStatus = GradeExamStatus.Makeup;
|
||||||
|
// Cap passing score at 60
|
||||||
|
var cappedScore = entry.Score >= 60 ? 60m : entry.Score;
|
||||||
|
record.TotalScore = cappedScore;
|
||||||
|
record.GradePoint = GradeCalculator.CalculateGradePoint(cappedScore);
|
||||||
|
record.Notes = entry.Score >= 60
|
||||||
|
? $"补考合格(原始{entry.Score}分,按60分记)"
|
||||||
|
: $"补考不合格({entry.Score}分)";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,13 +44,33 @@ public sealed class MakeupExamEligibilityService(AppDbContext db)
|
|||||||
})
|
})
|
||||||
.ToListAsync(cancellationToken);
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
// A deferred exam replaces the same course-grade record after the
|
||||||
|
// makeup score is entered. Keep that link at enrollment time instead
|
||||||
|
// of leaving deferred candidates without a score-replacement target.
|
||||||
|
var deferredStudentIds = deferredStudents.Select(x => x.StudentId).ToArray();
|
||||||
|
var deferredGradeRecordIds = deferredStudentIds.Length == 0
|
||||||
|
? new Dictionary<Guid, Guid>()
|
||||||
|
: (await db.GradeRecords.AsNoTracking()
|
||||||
|
.Where(x => x.GradeSheet!.TeachingTaskId == teachingTaskId)
|
||||||
|
.WhereIn(deferredStudentIds, x => x.StudentId)
|
||||||
|
.OrderByDescending(x => x.UpdatedAt)
|
||||||
|
.Select(x => new { x.StudentId, x.Id })
|
||||||
|
.ToListAsync(cancellationToken))
|
||||||
|
.GroupBy(x => x.StudentId)
|
||||||
|
.ToDictionary(x => x.Key, x => x.First().Id);
|
||||||
|
|
||||||
foreach (var s in deferredStudents)
|
foreach (var s in deferredStudents)
|
||||||
{
|
{
|
||||||
if (!results.ContainsKey(s.StudentId))
|
if (!results.ContainsKey(s.StudentId))
|
||||||
{
|
{
|
||||||
|
var sourceGradeRecordId = deferredGradeRecordIds.TryGetValue(
|
||||||
|
s.StudentId, out var gradeRecordId)
|
||||||
|
? gradeRecordId
|
||||||
|
: (Guid?)null;
|
||||||
results[s.StudentId] = new EligibleStudent(
|
results[s.StudentId] = new EligibleStudent(
|
||||||
s.StudentId, s.StudentNumber, s.Name, s.ClassName,
|
s.StudentId, s.StudentNumber, s.Name, s.ClassName,
|
||||||
MakeupReason.DeferredApproved, null,
|
MakeupReason.DeferredApproved,
|
||||||
|
SourceGradeRecordId: sourceGradeRecordId,
|
||||||
SourceDeferredExamId: s.Id);
|
SourceDeferredExamId: s.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public static class GradeCalculator
|
|||||||
IReadOnlyCollection<GradeItem> items,
|
IReadOnlyCollection<GradeItem> items,
|
||||||
GradeExamStatus examStatus)
|
GradeExamStatus examStatus)
|
||||||
{
|
{
|
||||||
if (examStatus != GradeExamStatus.Normal && examStatus != GradeExamStatus.Makeup)
|
if (examStatus is not (GradeExamStatus.Normal or GradeExamStatus.Deferred or GradeExamStatus.Makeup))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (regularWeight > 0 && !regularScore.HasValue)
|
if (regularWeight > 0 && !regularScore.HasValue)
|
||||||
|
|||||||
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