多角色账号按 All > College > Class > Self 自动取最高权限。

学院管理员限制在所属学院。
辅导员通过稳定账号 ID 绑定行政班,避免重名串班。
教师只能访问本人档案、授课课程和所授课学生。
学生只能访问本人档案及所在班级课程。
教师/学生角色会自动校验并绑定工号或学号档案。
超级管理员可在用户页面调整角色、学院、工号/学号,并预览生效后的数据范围。
This commit is contained in:
2026-07-24 14:54:30 +08:00 Unverified
parent 0b463fa4f2
commit bcc4d33bd5
26 changed files with 2333 additions and 88 deletions
+32 -13
View File
@@ -1,8 +1,8 @@
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
using Jiaowu.Api.Contracts;
using Jiaowu.Api.Domain.Academic;
using Jiaowu.Api.Domain.Identity;
using Jiaowu.Api.Infrastructure.Auth;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -13,7 +13,9 @@ namespace Jiaowu.Api.Controllers;
[ApiController]
[Authorize]
[Route("api/courses")]
public sealed class CoursesController(AppDbContext db) : ControllerBase
public sealed class CoursesController(
AppDbContext db,
ICurrentUserDataScope currentUserDataScope) : ControllerBase
{
private const string WriteRoles =
SystemRoles.SuperAdmin + "," +
@@ -32,10 +34,7 @@ public sealed class CoursesController(AppDbContext db) : ControllerBase
{
page = Math.Max(1, page);
pageSize = Math.Clamp(pageSize, 10, 100);
var source = db.Courses.AsNoTracking().AsQueryable();
var scopedCollegeId = GetScopedCollegeId();
if (scopedCollegeId.HasValue)
source = source.Where(x => x.CollegeId == scopedCollegeId.Value);
var source = ScopedCourses().AsNoTracking();
if (collegeId.HasValue)
source = source.Where(x => x.CollegeId == collegeId.Value);
if (nature.HasValue)
@@ -162,17 +161,37 @@ public sealed class CoursesController(AppDbContext db) : ControllerBase
return null;
}
private Guid? GetScopedCollegeId()
private IQueryable<Course> ScopedCourses()
{
if (!User.IsInRole(SystemRoles.CollegeAdmin))
return null;
return Guid.TryParse(User.FindFirstValue("college_id"), out var collegeId)
? collegeId
: Guid.Empty;
var scope = currentUserDataScope.Current;
var source = db.Courses.AsQueryable();
if (scope.Scope == DataScope.All) return source;
if (scope.Scope == DataScope.College)
return source.Where(x => x.CollegeId == scope.RestrictedCollegeId);
if (scope.Scope == DataScope.Class)
{
return source.Where(course => db.TeachingTasks.Any(task =>
task.CourseId == course.Id &&
task.Classes.Any(item =>
item.AdministrativeClass!.CounselorUserId == scope.UserId)));
}
var userId = scope.UserId;
var isTeacher = scope.IsInRole(SystemRoles.Teacher);
var isStudent = scope.IsInRole(SystemRoles.Student);
return source.Where(course =>
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))));
}
private bool CanAccessCollege(Guid collegeId) =>
!GetScopedCollegeId().HasValue || GetScopedCollegeId() == collegeId;
currentUserDataScope.Current.CanAccessCollege(collegeId);
private async Task<ActionResult> SaveAsync(
Guid id,