成绩分析报告

This commit is contained in:
2026-08-09 11:45:06 +08:00 Unverified
parent ca8232ad5e
commit 1229f3163d
6 changed files with 639 additions and 4 deletions
@@ -3,6 +3,7 @@ using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Domain.System;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Caching;
using Jiaowu.Api.Infrastructure.Grades;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -112,6 +113,50 @@ public sealed class GradeAnalyticsController(
return Ok(report);
}
[HttpGet("teaching-classes/{gradeSheetId:guid}/report.docx")]
public async Task<ActionResult> ExportTeachingClassAnalysisReport(
Guid gradeSheetId,
CancellationToken cancellationToken)
{
var sheet = await db.GradeSheets.AsNoTracking()
.Where(x => x.Id == gradeSheetId &&
VisibleTeachingTasks().Any(task => task.Id == x.TeachingTaskId))
.Select(x => new AnalysisTarget(
x.Id,
x.TeachingTaskId,
x.TeachingTask!.CourseId,
x.TeachingTask.AcademicTermId,
x.TeachingTask.Course!.CollegeId,
x.TeachingTask.TaskNumber,
x.TeachingTask.Name,
x.TeachingTask.Course.Code,
x.TeachingTask.Course.Name,
x.TeachingTask.AcademicTerm!.Name))
.FirstOrDefaultAsync(cancellationToken);
if (sheet is null) return NotFound();
var report = await (cache ?? NoOpAppCache.Instance).GetOrCreateAsync(
AppCacheKeys.TeachingTaskGradeAnalytics(gradeSheetId),
token => BuildReportAsync(sheet, token),
AppCacheProfile.Analytics,
[AppCacheTags.CourseGradeStatistics],
cancellationToken);
if (report.IsRefreshing || report.Summary is null)
return Conflict(new ProblemDetails
{
Title = "成绩统计尚未生成",
Detail = "请先重新计算当前教学班,待统计完成后再导出。",
Status = StatusCodes.Status409Conflict
});
var content = GradeAnalysisWordReportGenerator.Generate(report, DateTime.Now);
var fileName = $"{SanitizeFileName(report.CourseCode)}-{SanitizeFileName(report.TaskNumber)}-成绩分析报告.docx";
return File(
content,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
fileName);
}
[HttpPost("teaching-classes/{gradeSheetId:guid}/refresh")]
public async Task<ActionResult> RefreshTeachingClassAnalysis(
Guid gradeSheetId,
@@ -373,6 +418,12 @@ public sealed class GradeAnalyticsController(
return source.Where(_ => false);
}
private static string SanitizeFileName(string value)
{
var invalid = Path.GetInvalidFileNameChars();
return string.Concat(value.Select(character => invalid.Contains(character) ? '_' : character));
}
private sealed record AnalysisTarget(
Guid GradeSheetId,
Guid TeachingTaskId,