每个实验项目独立建成绩单,支持评分项、参与状态、补做次数、安全违规、报告地址和教师评语。 流程为:录入 → 学院审核 → 教务发布;学生只能查看已发布成绩。 自主预约实验支持同步有效预约名单,已评分记录不会被误删。 课程成绩可将已发布实验成绩汇总为快照导入指定分项;导入后锁定,不能手工篡改。 实验项目成绩不完整时,对应课程分项保持空白,不按 0 分处理。 一级“实验管理”菜单下增加“项目与场次”“实验成绩”;学生端增加“实验安排”“实验成绩”。 已生成 MySQL 正式迁移及 SQLite 开发迁移,尚未操作生产数据库。 界面采用“实验项目 → 独立评分 → 审核发布”工作台设计。
89 lines
3.0 KiB
C#
89 lines
3.0 KiB
C#
using Jiaowu.Api.Infrastructure.Persistence;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace Jiaowu.Api.Infrastructure.Grades;
|
|
|
|
public static class ExperimentGradeAggregationService
|
|
{
|
|
public static async Task<ExperimentGradeAggregateResult> CalculateAsync(
|
|
AppDbContext db,
|
|
Guid teachingTaskId,
|
|
IReadOnlyCollection<Guid> 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<Guid, decimal?>();
|
|
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<PublishedExperimentScore> Scores);
|
|
|
|
private sealed record PublishedExperimentScore(
|
|
Guid StudentId,
|
|
decimal? TotalScore);
|
|
}
|
|
|
|
public sealed record ExperimentGradeAggregateResult(
|
|
int PublishedProjectCount,
|
|
IReadOnlyList<ExperimentGradeAggregateProject> Projects,
|
|
IReadOnlyDictionary<Guid, decimal?> Scores);
|
|
|
|
public sealed record ExperimentGradeAggregateProject(
|
|
Guid Id,
|
|
string Code,
|
|
string Name,
|
|
decimal ContributionWeight);
|