“教学班成绩分析”页面新增“定时刷新设置”,支持启停、刷新间隔、单次处理上限、查看上次/下次扫描时间。
配置保存在独立数据库表 CourseGradeStatisticsRefreshSettings,不是修改 appsettings.json。 后台每 10 秒读取配置,仅到期扫描;成绩录入、导入、审批时不再立即创建统计任务。 扫描发现过期数据后,仍通过持久任务、Outbox 和 RabbitMQ 执行;Redis统计缓存由处理任务统一刷新。
This commit is contained in:
1 parent
e8261714da
commit
cd073885b5
17 files changed
+7661
-46
No files matched your search
@@ -251,13 +251,6 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s
|
||||
// Auto-apply: update grade record
|
||||
gm.GradeRecord!.TotalScore = gm.RequestedScore;
|
||||
gm.GradeRecord.GradePoint = GradeCalculator.CalculateGradePoint(gm.RequestedScore);
|
||||
var statisticsJob = new CourseGradeStatisticsRefreshJob
|
||||
{
|
||||
GradeSheetId = gm.GradeRecord.GradeSheetId
|
||||
};
|
||||
db.CourseGradeStatisticsRefreshJobs.Add(statisticsJob);
|
||||
db.BackgroundJobOutboxMessages.Add(BackgroundJobOutboxMessage.Create(
|
||||
BackgroundJobKind.CourseGradeStatisticsRefresh, statisticsJob.Id));
|
||||
await db.SaveChangesAsync(ct);
|
||||
await NotificationService.SendAsync(
|
||||
db,
|
||||
|
||||
@@ -25,6 +25,69 @@ public sealed class GradeAnalyticsController(
|
||||
SystemRoles.CollegeAdmin + "," +
|
||||
SystemRoles.Leader + "," +
|
||||
SystemRoles.Teacher;
|
||||
private const string ScheduleManagers =
|
||||
SystemRoles.SuperAdmin + "," + SystemRoles.AcademicAdmin;
|
||||
|
||||
[HttpGet("refresh-schedule")]
|
||||
[Authorize(Roles = ScheduleManagers)]
|
||||
public async Task<ActionResult> GetRefreshSchedule(CancellationToken cancellationToken)
|
||||
{
|
||||
var setting = await db.CourseGradeStatisticsRefreshSettings.AsNoTracking()
|
||||
.SingleOrDefaultAsync(
|
||||
x => x.Key == CourseGradeStatisticsRefreshSetting.DefaultKey,
|
||||
cancellationToken);
|
||||
var defaults = new CourseGradeStatisticsRefreshSetting();
|
||||
var enabled = setting?.IsEnabled ?? defaults.IsEnabled;
|
||||
var intervalSeconds = setting?.IntervalSeconds ?? defaults.IntervalSeconds;
|
||||
var batchSize = setting?.BatchSize ?? defaults.BatchSize;
|
||||
var lastRunAt = setting?.LastRunAt;
|
||||
return Ok(new
|
||||
{
|
||||
IsEnabled = enabled,
|
||||
IntervalSeconds = intervalSeconds,
|
||||
BatchSize = batchSize,
|
||||
LastRunAt = lastRunAt,
|
||||
NextRunAt = enabled && lastRunAt.HasValue
|
||||
? lastRunAt.Value.AddSeconds(intervalSeconds)
|
||||
: null as DateTime?
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPut("refresh-schedule")]
|
||||
[Authorize(Roles = ScheduleManagers)]
|
||||
public async Task<ActionResult> SaveRefreshSchedule(
|
||||
SaveGradeStatisticsRefreshScheduleRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.IntervalSeconds is < 10 or > 86400)
|
||||
return BadRequest(new ProblemDetails
|
||||
{
|
||||
Title = "刷新间隔应在 10 秒到 24 小时之间。",
|
||||
Status = StatusCodes.Status400BadRequest
|
||||
});
|
||||
if (request.BatchSize is < 1 or > 5000)
|
||||
return BadRequest(new ProblemDetails
|
||||
{
|
||||
Title = "单次刷新批量应在 1 到 5000 之间。",
|
||||
Status = StatusCodes.Status400BadRequest
|
||||
});
|
||||
|
||||
var setting = await db.CourseGradeStatisticsRefreshSettings
|
||||
.SingleOrDefaultAsync(
|
||||
x => x.Key == CourseGradeStatisticsRefreshSetting.DefaultKey,
|
||||
cancellationToken);
|
||||
if (setting is null)
|
||||
{
|
||||
setting = new CourseGradeStatisticsRefreshSetting();
|
||||
db.CourseGradeStatisticsRefreshSettings.Add(setting);
|
||||
}
|
||||
|
||||
setting.IsEnabled = request.IsEnabled;
|
||||
setting.IntervalSeconds = request.IntervalSeconds;
|
||||
setting.BatchSize = request.BatchSize;
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return NoContent();
|
||||
}
|
||||
|
||||
[HttpGet("teaching-classes")]
|
||||
public async Task<ActionResult> GetTeachingClasses(
|
||||
@@ -526,3 +589,8 @@ public sealed class GradeAnalyticsController(
|
||||
IReadOnlyList<HistoricalComparison> History,
|
||||
ComparisonDelta? UniversityDelta);
|
||||
}
|
||||
|
||||
public sealed record SaveGradeStatisticsRefreshScheduleRequest(
|
||||
bool IsEnabled,
|
||||
int IntervalSeconds,
|
||||
int BatchSize);
|
||||
@@ -508,7 +508,6 @@ public sealed class GradesController(
|
||||
}
|
||||
targetItem.SourceType = GradeItemSourceType.ExperimentSummary;
|
||||
targetItem.SourceSnapshotAt = DateTime.UtcNow;
|
||||
QueueCourseStatisticsRefresh(sheet.Id);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return Ok(new
|
||||
{
|
||||
@@ -668,7 +667,6 @@ public sealed class GradesController(
|
||||
return ConflictProblem("只有审核通过的成绩单可以发布。");
|
||||
sheet.Status = GradeSheetStatus.Published;
|
||||
sheet.PublishedAt = DateTime.UtcNow;
|
||||
QueueCourseStatisticsRefresh(sheet.Id);
|
||||
|
||||
// The grade sheet roster is authoritative at publication time. This also
|
||||
// covers students added through approved roster corrections.
|
||||
@@ -918,7 +916,6 @@ public sealed class GradesController(
|
||||
if (errors.Count > 0)
|
||||
return ImportValidationProblem(errors);
|
||||
|
||||
QueueCourseStatisticsRefresh(sheet.Id);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return Ok(new { updated, total = rows.Count });
|
||||
}
|
||||
@@ -1161,7 +1158,6 @@ public sealed class GradesController(
|
||||
{
|
||||
try
|
||||
{
|
||||
QueueCourseStatisticsRefresh(id);
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return created ? Created(string.Empty, new { id }) : NoContent();
|
||||
}
|
||||
@@ -1171,14 +1167,6 @@ public sealed class GradesController(
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueCourseStatisticsRefresh(Guid gradeSheetId)
|
||||
{
|
||||
var job = new CourseGradeStatisticsRefreshJob { GradeSheetId = gradeSheetId };
|
||||
db.CourseGradeStatisticsRefreshJobs.Add(job);
|
||||
db.BackgroundJobOutboxMessages.Add(BackgroundJobOutboxMessage.Create(
|
||||
BackgroundJobKind.CourseGradeStatisticsRefresh, job.Id));
|
||||
}
|
||||
|
||||
private ActionResult ConflictProblem(string detail) =>
|
||||
Conflict(new ProblemDetails
|
||||
{
|
||||
|
||||
Loaded 3 of 17 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user