diff --git a/src/Jiaowu.Api/Controllers/CourseSelectionsController.cs b/src/Jiaowu.Api/Controllers/CourseSelectionsController.cs index fc09c89..952dd0f 100644 --- a/src/Jiaowu.Api/Controllers/CourseSelectionsController.cs +++ b/src/Jiaowu.Api/Controllers/CourseSelectionsController.cs @@ -85,15 +85,16 @@ public sealed class CourseSelectionsController( CancellationToken cancellationToken) { var source = ApplyEnrollmentFilters(ScopedEnrollments(), query); - var students = source.Select(x => new EnrollmentStudentIdentity( - x.StudentId, x.Student!.StudentNumber, x.Student.Name, - x.Student.AdministrativeClass!.Major!.Name, - x.Student.AdministrativeClass.Name, x.Student.AdministrativeClass.Grade)); - var total = await students.Distinct().CountAsync(cancellationToken); + var matchingStudentIds = source.Select(x => x.StudentId).Distinct(); + var total = await matchingStudentIds.CountAsync(cancellationToken); var page = Math.Max(1, query.Page); var pageSize = Math.Clamp(query.PageSize, 1, 100); - var pageStudents = await students.Distinct() + var pageStudents = await db.Students.AsNoTracking() + .Where(x => matchingStudentIds.Contains(x.Id)) .OrderBy(x => x.StudentNumber).Skip((page - 1) * pageSize).Take(pageSize) + .Select(x => new EnrollmentStudentIdentity( + x.Id, x.StudentNumber, x.Name, x.AdministrativeClass!.Major!.Name, + x.AdministrativeClass.Name, x.AdministrativeClass.Grade)) .ToListAsync(cancellationToken); var studentIds = pageStudents.Select(x => x.StudentId).ToArray(); var enrollments = await ProjectEnrollments(OrderEnrollments(source.Where(x => diff --git a/src/Jiaowu.Api/Infrastructure/Analytics/ClickHouseAnalyticsProjectionWorker.cs b/src/Jiaowu.Api/Infrastructure/Analytics/ClickHouseAnalyticsProjectionWorker.cs index b77a70b..4ac839f 100644 --- a/src/Jiaowu.Api/Infrastructure/Analytics/ClickHouseAnalyticsProjectionWorker.cs +++ b/src/Jiaowu.Api/Infrastructure/Analytics/ClickHouseAnalyticsProjectionWorker.cs @@ -1,3 +1,4 @@ +using Jiaowu.Api.Domain.Academic; using Jiaowu.Api.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; @@ -93,7 +94,11 @@ public sealed class ClickHouseAnalyticsProjectionWorker( .Select(x => new { x.AttendanceSheetId, x.StudentId, AttendanceDate = x.AttendanceSheet!.AttendanceDate, TeachingTaskId = x.AttendanceSheet.TeachingTaskId, AcademicTermId = x.AttendanceSheet.TeachingTask!.AcademicTermId, CollegeId = x.AttendanceSheet.TeachingTask.Course!.CollegeId, x.Status, x.CheckInAt, x.CheckedInMethod, x.AppealStatus, ProjectedAt = projectedAt }) .ToListAsync(cancellationToken); if (rows.Count == 0) return count; - await client.InsertAsync("attendanceRecords", rows, cancellationToken); + var analyticsRows = rows.Select(x => new AttendanceAnalyticsRow( + x.AttendanceSheetId, x.StudentId, DateOnly.FromDateTime(x.AttendanceDate), + x.TeachingTaskId, x.AcademicTermId, x.CollegeId, x.Status, x.CheckInAt, + x.CheckedInMethod, x.AppealStatus, x.ProjectedAt)).ToList(); + await client.InsertAsync("attendanceRecords", analyticsRows, cancellationToken); count += rows.Count; cursorDate = rows[^1].AttendanceDate; cursorSheetId = rows[^1].AttendanceSheetId; @@ -101,6 +106,19 @@ public sealed class ClickHouseAnalyticsProjectionWorker( } } + private sealed record AttendanceAnalyticsRow( + Guid AttendanceSheetId, + Guid StudentId, + DateOnly AttendanceDate, + Guid TeachingTaskId, + Guid AcademicTermId, + Guid CollegeId, + AttendanceStatus Status, + DateTime? CheckInAt, + AttendanceCheckInMethod? CheckedInMethod, + AttendanceAppealStatus AppealStatus, + DateTime ProjectedAt); + private async Task ProjectGradesAsync(AppDbContext db, DateTime projectedAt, CancellationToken cancellationToken) { var cursorAt = DateTime.MinValue;