已修改完成,同时解决了“考生名单都是 0 人”的根因。
考生范围统一为“行政班关联学生 + 已选课学生”,并自动去重;考场分配、人数显示、冲突检测、学生考试安排均使用同一口径。[TeachingTaskRosterQuery.cs (line 40)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/Teaching/TeachingTaskRosterQuery.cs:40) 新增考场签名单 Excel 导出接口。[ExamsController.cs (line 568)](E:/jiaowu/src/Jiaowu.Api/Controllers/ExamsController.cs:568) 考试计划页面新增“导出考场签名单”按钮。[ExamsView.vue (line 255)](E:/jiaowu/web/src/views/ExamsView.vue:255) 工作簿包含“考场汇总”以及每个考试场次独立的 A4 签到表,字段包括座位号、学号、姓名、行政班、考生签名和备注。[ExamSignInWorkbookExporter.cs (line 12)](E:/jiaowu/src/Jiaowu.Api/Infrastructure/Exams/ExamSignInWorkbookExporter.cs:12) 未分配考场或监考教师时,也能导出并显示“待分配”。
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Jiaowu.Api.Domain.Academic;
|
||||
using Jiaowu.Api.Infrastructure.Persistence;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Teaching;
|
||||
|
||||
@@ -15,4 +16,107 @@ public static class TeachingTaskRosterQuery
|
||||
enrollment.StudentId == student.Id &&
|
||||
enrollment.Status == CourseEnrollmentStatus.Enrolled &&
|
||||
enrollment.CourseSelectionOffering!.TeachingTaskId == teachingTaskId));
|
||||
|
||||
public static IQueryable<Guid> TaskIdsForStudent(AppDbContext db, Guid studentId)
|
||||
{
|
||||
var classTaskIds = db.Students
|
||||
.Where(student =>
|
||||
student.Id == studentId &&
|
||||
student.Status == StudentStatus.Active)
|
||||
.SelectMany(student => db.TeachingTaskClasses
|
||||
.Where(assignment =>
|
||||
assignment.AdministrativeClassId ==
|
||||
student.AdministrativeClassId)
|
||||
.Select(assignment => assignment.TeachingTaskId));
|
||||
var enrolledTaskIds = db.CourseEnrollments
|
||||
.Where(enrollment =>
|
||||
enrollment.StudentId == studentId &&
|
||||
enrollment.Status == CourseEnrollmentStatus.Enrolled)
|
||||
.Select(enrollment =>
|
||||
enrollment.CourseSelectionOffering!.TeachingTaskId);
|
||||
return classTaskIds.Concat(enrolledTaskIds).Distinct();
|
||||
}
|
||||
|
||||
public static async Task<IReadOnlyList<TeachingTaskRosterEntry>> LoadForTasksAsync(
|
||||
AppDbContext db,
|
||||
IEnumerable<Guid> teachingTaskIds,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var taskIds = teachingTaskIds.Distinct().ToArray();
|
||||
if (taskIds.Length == 0) return [];
|
||||
|
||||
var assignments = await db.TeachingTaskClasses.AsNoTracking()
|
||||
.WhereIn(taskIds, x => x.TeachingTaskId)
|
||||
.Select(x => new
|
||||
{
|
||||
x.TeachingTaskId,
|
||||
x.AdministrativeClassId
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var classIds = assignments
|
||||
.Select(x => x.AdministrativeClassId)
|
||||
.Distinct()
|
||||
.ToArray();
|
||||
var classStudents = classIds.Length == 0
|
||||
? []
|
||||
: await db.Students.AsNoTracking()
|
||||
.Where(x => x.Status == StudentStatus.Active)
|
||||
.WhereIn(classIds, x => x.AdministrativeClassId)
|
||||
.Select(x => new RosterStudent(
|
||||
x.Id,
|
||||
x.StudentNumber,
|
||||
x.Name,
|
||||
x.AdministrativeClassId,
|
||||
x.AdministrativeClass!.Name))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var enrolledStudents = await db.CourseEnrollments.AsNoTracking()
|
||||
.Where(x => x.Status == CourseEnrollmentStatus.Enrolled)
|
||||
.WhereIn(taskIds, x => x.CourseSelectionOffering!.TeachingTaskId)
|
||||
.Select(x => new TeachingTaskRosterEntry(
|
||||
x.CourseSelectionOffering!.TeachingTaskId,
|
||||
x.StudentId,
|
||||
x.Student!.StudentNumber,
|
||||
x.Student.Name,
|
||||
x.Student.AdministrativeClass!.Name))
|
||||
.ToListAsync(cancellationToken);
|
||||
|
||||
var result = enrolledStudents.ToDictionary(
|
||||
x => (x.TeachingTaskId, x.StudentId));
|
||||
var studentsByClass = classStudents.ToLookup(x => x.AdministrativeClassId);
|
||||
foreach (var assignment in assignments)
|
||||
{
|
||||
foreach (var student in studentsByClass[assignment.AdministrativeClassId])
|
||||
{
|
||||
result.TryAdd(
|
||||
(assignment.TeachingTaskId, student.StudentId),
|
||||
new TeachingTaskRosterEntry(
|
||||
assignment.TeachingTaskId,
|
||||
student.StudentId,
|
||||
student.StudentNumber,
|
||||
student.Name,
|
||||
student.ClassName));
|
||||
}
|
||||
}
|
||||
|
||||
return result.Values
|
||||
.OrderBy(x => x.TeachingTaskId)
|
||||
.ThenBy(x => x.StudentNumber)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private sealed record RosterStudent(
|
||||
Guid StudentId,
|
||||
string StudentNumber,
|
||||
string Name,
|
||||
Guid AdministrativeClassId,
|
||||
string ClassName);
|
||||
}
|
||||
|
||||
public sealed record TeachingTaskRosterEntry(
|
||||
Guid TeachingTaskId,
|
||||
Guid StudentId,
|
||||
string StudentNumber,
|
||||
string Name,
|
||||
string ClassName);
|
||||
|
||||
Reference in New Issue
Block a user