主要变更:

新增统一缓存封装:[AppCache.cs (line 12)](/E:/jiaowu/src/Jiaowu.Api/Infrastructure/Caching/AppCache.cs:12)
接入 HybridCache 和可选 Redis:[Program.cs (line 153)](/E:/jiaowu/src/Jiaowu.Api/Program.cs:153)
缓存学生激活选项、基础数据、公开课表和课表选项。
个人课表、选课容量、成绩、权限、通知和任务状态保持实时查询。
基础数据、课程、教师、教学任务、作息、考试和课表发布后自动失效相关缓存。
新增 /health/cache,Redis 故障不影响 /health/ready。
Compose 增加 256MB、allkeys-lfu、无持久化的 redis:8.8-alpine 服务;该镜像标签已由 Docker 官方镜像仓库核对。
更新 [.env.example (line 1)](/E:/jiaowu/.env.example:1) 和 [README.md (line 190)](/E:/jiaowu/README.md:190) 部署说明。
This commit is contained in:
2026-07-26 14:42:27 +08:00 Unverified
parent 0970c7cd40
commit 77dfa8145c
28 changed files with 1091 additions and 208 deletions
+62 -24
View File
@@ -3,6 +3,7 @@ using System.Security.Claims;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Caching;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@@ -17,35 +18,51 @@ namespace Jiaowu.Api.Controllers;
public sealed class AuthController(
AppDbContext db,
UserManager<ApplicationUser> userManager,
ITokenService tokenService) : ControllerBase
ITokenService tokenService,
IAppCache cache) : ControllerBase
{
[AllowAnonymous]
[HttpGet("activation-options")]
public async Task<ActionResult> GetActivationOptions(CancellationToken cancellationToken)
{
var colleges = await db.Colleges.AsNoTracking()
.Where(x => x.IsEnabled)
.OrderBy(x => x.Code)
.Select(x => new { x.Id, x.Code, x.Name })
.ToListAsync(cancellationToken);
var majors = await db.Majors.AsNoTracking()
.Where(x => x.IsEnabled && x.College!.IsEnabled)
.OrderBy(x => x.Code)
.Select(x => new { x.Id, x.Code, x.Name, x.CollegeId })
.ToListAsync(cancellationToken);
var classes = await db.AdministrativeClasses.AsNoTracking()
.Where(x => x.IsEnabled && x.Major!.IsEnabled && x.Major.College!.IsEnabled)
.OrderByDescending(x => x.Grade)
.ThenBy(x => x.Code)
.Select(x => new { x.Id, x.Code, x.Name, x.Grade, x.MajorId })
.ToListAsync(cancellationToken);
return Ok(new
{
Colleges = colleges,
Majors = majors,
Classes = classes,
Grades = classes.Select(x => x.Grade).Distinct().OrderByDescending(x => x)
});
var result = await cache.GetOrCreateAsync(
AppCacheKeys.ActivationOptions,
async token =>
{
var colleges = await db.Colleges.AsNoTracking()
.Where(x => x.IsEnabled)
.OrderBy(x => x.Code)
.Select(x => new ActivationCollegeOption(x.Id, x.Code, x.Name))
.ToListAsync(token);
var majors = await db.Majors.AsNoTracking()
.Where(x => x.IsEnabled && x.College!.IsEnabled)
.OrderBy(x => x.Code)
.Select(x => new ActivationMajorOption(
x.Id, x.Code, x.Name, x.CollegeId))
.ToListAsync(token);
var classes = await db.AdministrativeClasses.AsNoTracking()
.Where(x =>
x.IsEnabled &&
x.Major!.IsEnabled &&
x.Major.College!.IsEnabled)
.OrderByDescending(x => x.Grade)
.ThenBy(x => x.Code)
.Select(x => new ActivationClassOption(
x.Id, x.Code, x.Name, x.Grade, x.MajorId))
.ToListAsync(token);
return new ActivationOptionsResponse(
colleges,
majors,
classes,
classes.Select(x => x.Grade)
.Distinct()
.OrderByDescending(x => x)
.ToList());
},
AppCacheProfile.ReferenceData,
[AppCacheTags.BaseData],
cancellationToken);
return Ok(result);
}
[AllowAnonymous]
@@ -219,3 +236,24 @@ public sealed record CurrentUserResponse(
IEnumerable<string> Roles,
Guid? CollegeId,
string EffectiveDataScope);
public sealed record ActivationOptionsResponse(
IReadOnlyList<ActivationCollegeOption> Colleges,
IReadOnlyList<ActivationMajorOption> Majors,
IReadOnlyList<ActivationClassOption> Classes,
IReadOnlyList<int> Grades);
public sealed record ActivationCollegeOption(Guid Id, string Code, string Name);
public sealed record ActivationMajorOption(
Guid Id,
string Code,
string Name,
Guid CollegeId);
public sealed record ActivationClassOption(
Guid Id,
string Code,
string Name,
int Grade,
Guid MajorId);