“教学班成绩分析”页面新增“定时刷新设置”,支持启停、刷新间隔、单次处理上限、查看上次/下次扫描时间。

配置保存在独立数据库表 CourseGradeStatisticsRefreshSettings,不是修改 appsettings.json。
后台每 10 秒读取配置,仅到期扫描;成绩录入、导入、审批时不再立即创建统计任务。
扫描发现过期数据后,仍通过持久任务、Outbox 和 RabbitMQ 执行;Redis统计缓存由处理任务统一刷新。
This commit is contained in:
2026-08-09 20:51:57 +08:00 Unverified
parent e8261714da
commit cd073885b5
17 changed files with 7661 additions and 46 deletions
@@ -0,0 +1,78 @@
using System.Collections;
using Jiaowu.Api.Controllers;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
namespace Jiaowu.Api.Tests;
public sealed class WarningRulePersistenceTests
{
[Fact]
public async Task SaveRules_PersistsAutoCheck_AndReturnsNumericType()
{
await using var connection = new SqliteConnection("Data Source=:memory:");
await connection.OpenAsync();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(connection)
.Options;
await using var db = new AppDbContext(options);
await db.Database.EnsureCreatedAsync();
var term = new AcademicTerm
{
Code = "WARN-TERM",
Name = "预警测试学期",
AcademicYear = "2026-2027",
Season = TermSeason.Autumn,
StartDate = new DateOnly(2026, 9, 1),
EndDate = new DateOnly(2027, 1, 15)
};
db.AcademicTerms.Add(term);
await db.SaveChangesAsync();
var controller = new WarningsController(db, new AllDataScope());
var saveResult = await controller.SaveRules(term.Id,
[
new WarningRuleDto(
WarningType.FailedCredits,
"不及格学分",
2m,
true,
true,
true,
null,
true,
1,
9,
30)
], CancellationToken.None);
Assert.IsType<NoContentResult>(saveResult);
db.ChangeTracker.Clear();
var persisted = Assert.Single(await db.WarningRules.AsNoTracking().ToListAsync());
Assert.True(persisted.AutoCheckEnabled);
Assert.Equal(1, persisted.CheckDayOfWeek);
Assert.Equal(9, persisted.CheckHour);
Assert.Equal(30, persisted.CheckMinute);
var getResult = await controller.GetRules(term.Id, CancellationToken.None);
var ok = Assert.IsType<OkObjectResult>(getResult);
var row = Assert.Single(Assert.IsAssignableFrom<IEnumerable>(ok.Value).Cast<object>());
var type = row.GetType().GetProperty("Type")?.GetValue(row);
Assert.Equal(1, Assert.IsType<int>(type));
}
private sealed class AllDataScope : ICurrentUserDataScope
{
public CurrentUserScope Current { get; } = new(
Guid.NewGuid(),
"测试管理员",
null,
DataScope.All,
new HashSet<string>([SystemRoles.SuperAdmin]));
}
}