修复实验成绩管理:
管理列表改为服务端分页,支持 10/20/50 条切换。 增加学期、成绩状态、开课学院以及项目/课程/教学班/教师关键词筛选。 数据范围分级:校级管理员可查看全校,院级管理员仅限本学院,教师仅限本人任课项目。 修复任课教师提交审核时报“无权限”:工作流查询现在会加载任课教师关系。 增加权限、学院隔离、分页和筛选回归测试。
This commit is contained in:
@@ -11,6 +11,160 @@ namespace Jiaowu.Api.Tests;
|
||||
|
||||
public sealed class ExperimentGradesControllerTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task AssignedTeacher_CanSubmitExperimentGradeSheet()
|
||||
{
|
||||
await using var fixture = await ExperimentGradeFixture.CreateAsync();
|
||||
var project = new ExperimentProject
|
||||
{
|
||||
TeachingTaskId = fixture.Task.Id,
|
||||
Code = "LAB-TEACHER",
|
||||
Name = "教师提交实验",
|
||||
ArrangementMode = ExperimentArrangementMode.Centralized,
|
||||
StartDate = fixture.Term.StartDate,
|
||||
EndDate = fixture.Term.EndDate,
|
||||
Status = ExperimentProjectStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow
|
||||
};
|
||||
fixture.Db.ExperimentProjects.Add(project);
|
||||
await fixture.Db.SaveChangesAsync();
|
||||
|
||||
var admin = fixture.ExperimentGrades(fixture.AdminScope);
|
||||
await admin.CreateSheet(
|
||||
new ExperimentGradeSheetRequest(
|
||||
project.Id,
|
||||
1,
|
||||
60,
|
||||
[new ExperimentGradeItemRequest(
|
||||
"操作",
|
||||
ExperimentGradeItemKind.Operation,
|
||||
100)]),
|
||||
CancellationToken.None);
|
||||
var sheet = await fixture.Db.ExperimentGradeSheets
|
||||
.Include(x => x.Items)
|
||||
.Include(x => x.Records)
|
||||
.ThenInclude(x => x.ItemScores)
|
||||
.SingleAsync();
|
||||
var record = Assert.Single(sheet.Records);
|
||||
var item = Assert.Single(sheet.Items);
|
||||
|
||||
var teacher = fixture.ExperimentGrades(fixture.TeacherScope);
|
||||
Assert.IsType<NoContentResult>(await teacher.UpdateRecords(
|
||||
sheet.Id,
|
||||
new ExperimentGradeRecordsRequest(
|
||||
[
|
||||
new ExperimentGradeRecordRequest(
|
||||
record.Id,
|
||||
ExperimentParticipationStatus.Completed,
|
||||
false,
|
||||
1,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
[new ExperimentGradeItemScoreRequest(item.Id, 85, null)])
|
||||
]),
|
||||
CancellationToken.None));
|
||||
fixture.Db.ChangeTracker.Clear();
|
||||
Assert.IsType<NoContentResult>(await teacher.Submit(
|
||||
sheet.Id,
|
||||
CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ManagementList_IsPagedFilteredAndCollegeScoped()
|
||||
{
|
||||
await using var fixture = await ExperimentGradeFixture.CreateAsync();
|
||||
var collegeId = await fixture.Db.Courses
|
||||
.Where(x => x.Id == fixture.Task.CourseId)
|
||||
.Select(x => x.CollegeId)
|
||||
.SingleAsync();
|
||||
for (var index = 1; index <= 12; index++)
|
||||
{
|
||||
fixture.Db.ExperimentProjects.Add(new ExperimentProject
|
||||
{
|
||||
TeachingTaskId = fixture.Task.Id,
|
||||
Code = $"LAB-{index:00}",
|
||||
Name = $"分页实验 {index:00}",
|
||||
ArrangementMode = ExperimentArrangementMode.Centralized,
|
||||
StartDate = fixture.Term.StartDate,
|
||||
EndDate = fixture.Term.EndDate,
|
||||
Status = ExperimentProjectStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
var otherCollege = new College { Code = "OTHER", Name = "其他学院" };
|
||||
var otherCourse = new Course
|
||||
{
|
||||
CollegeId = otherCollege.Id,
|
||||
Code = "OTHER-LAB",
|
||||
Name = "其他学院实验",
|
||||
Credits = 1,
|
||||
TotalHours = 16,
|
||||
PracticeHours = 16,
|
||||
Nature = CourseNature.Practice,
|
||||
AssessmentMethod = AssessmentMethod.Assessment
|
||||
};
|
||||
var otherTask = new TeachingTask
|
||||
{
|
||||
AcademicTermId = fixture.Term.Id,
|
||||
CourseId = otherCourse.Id,
|
||||
TaskNumber = "OTHER-LAB-01",
|
||||
Name = "其他学院实验班",
|
||||
Capacity = 20,
|
||||
Status = TeachingTaskStatus.Published
|
||||
};
|
||||
var otherProject = new ExperimentProject
|
||||
{
|
||||
TeachingTaskId = otherTask.Id,
|
||||
Code = "FOREIGN-LAB",
|
||||
Name = "不可见实验",
|
||||
ArrangementMode = ExperimentArrangementMode.Centralized,
|
||||
StartDate = fixture.Term.StartDate,
|
||||
EndDate = fixture.Term.EndDate,
|
||||
Status = ExperimentProjectStatus.Published,
|
||||
PublishedAt = DateTime.UtcNow
|
||||
};
|
||||
fixture.Db.AddRange(otherCollege, otherCourse, otherTask, otherProject);
|
||||
await fixture.Db.SaveChangesAsync();
|
||||
fixture.Db.ChangeTracker.Clear();
|
||||
|
||||
var collegeScope = new FixedScope(new CurrentUserScope(
|
||||
Guid.NewGuid(),
|
||||
"学院管理员",
|
||||
collegeId,
|
||||
DataScope.College,
|
||||
new HashSet<string>([SystemRoles.CollegeAdmin])));
|
||||
var controller = fixture.ExperimentGrades(collegeScope);
|
||||
var page = Assert.IsType<OkObjectResult>(await controller.GetManagement(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
1,
|
||||
10,
|
||||
CancellationToken.None));
|
||||
Assert.Equal(12, Property<int>(page.Value, "Total"));
|
||||
Assert.Equal(10, Property<System.Collections.IEnumerable>(
|
||||
page.Value,
|
||||
"Items").Cast<object>().Count());
|
||||
|
||||
var filtered = Assert.IsType<OkObjectResult>(await controller.GetManagement(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"LAB-12",
|
||||
1,
|
||||
10,
|
||||
CancellationToken.None));
|
||||
Assert.Equal(1, Property<int>(filtered.Value, "Total"));
|
||||
}
|
||||
|
||||
private static T Property<T>(object? value, string name) =>
|
||||
Assert.IsAssignableFrom<T>(
|
||||
value!.GetType().GetProperty(name)!.GetValue(value));
|
||||
|
||||
[Fact]
|
||||
public async Task IndependentExperimentGrade_CanPublishAndImportAsCourseSnapshot()
|
||||
{
|
||||
@@ -260,6 +414,7 @@ public sealed class ExperimentGradesControllerTests
|
||||
Student student,
|
||||
Classroom classroom,
|
||||
ICurrentUserDataScope adminScope,
|
||||
ICurrentUserDataScope teacherScope,
|
||||
ICurrentUserDataScope studentScope)
|
||||
{
|
||||
Connection = connection;
|
||||
@@ -269,6 +424,7 @@ public sealed class ExperimentGradesControllerTests
|
||||
Student = student;
|
||||
Classroom = classroom;
|
||||
AdminScope = adminScope;
|
||||
TeacherScope = teacherScope;
|
||||
StudentScope = studentScope;
|
||||
}
|
||||
|
||||
@@ -279,6 +435,7 @@ public sealed class ExperimentGradesControllerTests
|
||||
public Student Student { get; }
|
||||
public Classroom Classroom { get; }
|
||||
public ICurrentUserDataScope AdminScope { get; }
|
||||
public ICurrentUserDataScope TeacherScope { get; }
|
||||
public ICurrentUserDataScope StudentScope { get; }
|
||||
|
||||
public static async Task<ExperimentGradeFixture> CreateAsync()
|
||||
@@ -410,6 +567,7 @@ public sealed class ExperimentGradesControllerTests
|
||||
student,
|
||||
classroom,
|
||||
Scope(admin, SystemRoles.SuperAdmin, DataScope.All),
|
||||
Scope(teacherUser, SystemRoles.Teacher, DataScope.Self),
|
||||
Scope(studentUser, SystemRoles.Student, DataScope.Self));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user