using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Infrastructure.Grades; public static class ExperimentGradeAggregationService { public static async Task CalculateAsync( AppDbContext db, Guid teachingTaskId, IReadOnlyCollection studentIds, CancellationToken cancellationToken) { var sheets = await db.ExperimentGradeSheets.AsNoTracking() .Where(x => x.Status == Domain.Academic.ExperimentGradeSheetStatus.Published && x.ExperimentProject!.TeachingTaskId == teachingTaskId) .OrderBy(x => x.ExperimentProject!.Code) .Select(x => new PublishedExperimentSheet( x.Id, x.ExperimentProject!.Code, x.ExperimentProject.Name, x.ContributionWeight, x.Records.Select(record => new PublishedExperimentScore( record.StudentId, record.TotalScore)).ToList())) .AsSplitQuery() .ToListAsync(cancellationToken); var scores = new Dictionary(); foreach (var studentId in studentIds.Distinct()) { decimal weightedTotal = 0; decimal totalWeight = 0; var complete = sheets.Count > 0; foreach (var sheet in sheets) { var score = sheet.Scores.FirstOrDefault(x => x.StudentId == studentId); if (score?.TotalScore is not decimal totalScore) { complete = false; break; } weightedTotal += totalScore * sheet.ContributionWeight; totalWeight += sheet.ContributionWeight; } scores[studentId] = complete && totalWeight > 0 ? Math.Round( weightedTotal / totalWeight, 1, MidpointRounding.AwayFromZero) : null; } return new ExperimentGradeAggregateResult( sheets.Count, sheets.Select(x => new ExperimentGradeAggregateProject( x.Id, x.Code, x.Name, x.ContributionWeight)).ToList(), scores); } private sealed record PublishedExperimentSheet( Guid Id, string Code, string Name, decimal ContributionWeight, List Scores); private sealed record PublishedExperimentScore( Guid StudentId, decimal? TotalScore); } public sealed record ExperimentGradeAggregateResult( int PublishedProjectCount, IReadOnlyList Projects, IReadOnlyDictionary Scores); public sealed record ExperimentGradeAggregateProject( Guid Id, string Code, string Name, decimal ContributionWeight);