From 3b1b1f3c889bcac81fa9497c6a16dbe1e3ff8020 Mon Sep 17 00:00:00 2001 From: biss Date: Mon, 17 Aug 2026 19:48:20 +0800 Subject: [PATCH] Include personally enrolled courses in student course listings --- .../Controllers/CoursesController.cs | 16 ++-- .../CoursesControllerTests.cs | 88 +++++++++++++++++++ 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/Jiaowu.Api/Controllers/CoursesController.cs b/src/Jiaowu.Api/Controllers/CoursesController.cs index b7c35ba..c891f81 100644 --- a/src/Jiaowu.Api/Controllers/CoursesController.cs +++ b/src/Jiaowu.Api/Controllers/CoursesController.cs @@ -340,11 +340,17 @@ public sealed class CoursesController( isTeacher && db.TeachingTasks.Any(task => task.CourseId == course.Id && task.Teachers.Any(item => item.Teacher!.UserId == userId)) || - isStudent && db.TeachingTasks.Any(task => - task.CourseId == course.Id && - task.Classes.Any(item => - item.AdministrativeClass!.Students.Any(student => - student.UserId == userId)))); + isStudent && ( + db.TeachingTasks.Any(task => + task.CourseId == course.Id && + task.Classes.Any(item => + item.AdministrativeClass!.Students.Any(student => + student.UserId == userId))) || + db.CourseEnrollments.Any(enrollment => + enrollment.Status == CourseEnrollmentStatus.Enrolled && + enrollment.Student!.UserId == userId && + enrollment.CourseSelectionOffering!.TeachingTask!.CourseId == + course.Id))); } private bool CanManage(Guid collegeId, CourseNature nature) diff --git a/tests/Jiaowu.Api.Tests/CoursesControllerTests.cs b/tests/Jiaowu.Api.Tests/CoursesControllerTests.cs index be195c7..b6a7697 100644 --- a/tests/Jiaowu.Api.Tests/CoursesControllerTests.cs +++ b/tests/Jiaowu.Api.Tests/CoursesControllerTests.cs @@ -101,6 +101,84 @@ public sealed class CoursesControllerTests Assert.Contains("OTHER-001", courses); } + [Fact] + public async Task Student_Get_IncludesCourseFromActivePersonalEnrollment() + { + await using var connection = new SqliteConnection("Data Source=:memory:"); + await connection.OpenAsync(); + var options = new DbContextOptionsBuilder() + .UseSqlite(connection) + .Options; + await using var db = new AppDbContext(options); + await db.Database.EnsureCreatedAsync(); + + var studentUserId = Guid.NewGuid(); + var college = new College { Code = "CS", Name = "计算机学院" }; + var major = new Major + { + Code = "CS01", Name = "软件工程", CollegeId = college.Id, DegreeType = "本科" + }; + var administrativeClass = new AdministrativeClass + { + Code = "SE2601", Name = "软件 2601", MajorId = major.Id, Grade = 2026 + }; + var student = new Student + { + StudentNumber = "20260001", Name = "测试学生", UserId = studentUserId, + AdministrativeClassId = administrativeClass.Id, + EnrollmentDate = new DateOnly(2026, 9, 1) + }; + var category = new CourseCategory { Code = "CORE", Name = "核心课程" }; + var selectedCourse = new Course + { + Code = "CS102", Name = "数据结构", CollegeId = college.Id, + CourseCategoryId = category.Id, Nature = CourseNature.MajorElective, + Credits = 3, TotalHours = 48, IsEnabled = true + }; + var term = new AcademicTerm + { + Code = "2026-1", Name = "2026-2027-1", AcademicYear = "2026-2027", + Season = TermSeason.Autumn, StartDate = new DateOnly(2026, 9, 1), + EndDate = new DateOnly(2027, 1, 31), IsCurrent = true + }; + var task = new TeachingTask + { + TaskNumber = "T-CS102", Name = "数据结构选课班", AcademicTermId = term.Id, + CourseId = selectedCourse.Id, Capacity = 50, Status = TeachingTaskStatus.Published + }; + var round = new CourseSelectionRound + { + AcademicTermId = term.Id, Name = "第一轮", StartsAt = DateTime.UtcNow, + EndsAt = DateTime.UtcNow.AddDays(1), WithdrawalEndsAt = DateTime.UtcNow.AddDays(2) + }; + var offering = new CourseSelectionOffering + { + CourseSelectionRoundId = round.Id, TeachingTaskId = task.Id, Capacity = 50 + }; + db.AddRange(college, major, administrativeClass, student, category, selectedCourse, + new ApplicationUser + { + Id = studentUserId, UserName = "20260001", DisplayName = "测试学生" + }, + term, task, round, offering, + new CourseEnrollment + { + CourseSelectionOfferingId = offering.Id, StudentId = student.Id, + Status = CourseEnrollmentStatus.Enrolled + }); + await db.SaveChangesAsync(); + + var controller = new CoursesController( + db, new StudentDataScope(studentUserId), NoOpAppCache.Instance); + + var result = await controller.Get(cancellationToken: CancellationToken.None); + + var ok = Assert.IsType(result.Result); + var page = Assert.IsType>(ok.Value); + Assert.Equal(1, page.Total); + Assert.Contains("CS102", JsonSerializer.Serialize(page.Items)); + } + private sealed class TestDataScope(Guid collegeId) : ICurrentUserDataScope { public CurrentUserScope Current { get; } = new( @@ -110,4 +188,14 @@ public sealed class CoursesControllerTests DataScope.College, new HashSet([SystemRoles.CollegeAdmin])); } + + private sealed class StudentDataScope(Guid userId) : ICurrentUserDataScope + { + public CurrentUserScope Current { get; } = new( + userId, + "测试学生", + null, + DataScope.Self, + new HashSet([SystemRoles.Student])); + } }