多角色账号按 All > College > Class > Self 自动取最高权限。
学院管理员限制在所属学院。 辅导员通过稳定账号 ID 绑定行政班,避免重名串班。 教师只能访问本人档案、授课课程和所授课学生。 学生只能访问本人档案及所在班级课程。 教师/学生角色会自动校验并绑定工号或学号档案。 超级管理员可在用户页面调整角色、学院、工号/学号,并预览生效后的数据范围。
This commit is contained in:
@@ -72,6 +72,14 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.MajorId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
builder.Entity<AdministrativeClass>(entity =>
|
||||
{
|
||||
entity.HasIndex(x => x.CounselorUserId);
|
||||
entity.HasOne(x => x.CounselorUser)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.CounselorUserId)
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
});
|
||||
|
||||
builder.Entity<Building>()
|
||||
.HasOne(x => x.Campus)
|
||||
|
||||
@@ -419,6 +419,85 @@ public sealed class DatabaseInitializer(
|
||||
});
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
await SeedDevelopmentUsersAsync(computerCollege.Id);
|
||||
}
|
||||
|
||||
private async Task SeedDevelopmentUsersAsync(Guid collegeId)
|
||||
{
|
||||
var definitions = new[]
|
||||
{
|
||||
new DevelopmentUser(
|
||||
"academic", "校级教务员", "Academic@123456",
|
||||
null, null, SystemRoles.AcademicAdmin),
|
||||
new DevelopmentUser(
|
||||
"college", "计算机学院教务员", "College@123456",
|
||||
"A2026001", collegeId, SystemRoles.CollegeAdmin),
|
||||
new DevelopmentUser(
|
||||
"counselor", "陈老师", "Counselor@123456",
|
||||
"C2026001", collegeId, SystemRoles.Counselor),
|
||||
new DevelopmentUser(
|
||||
"teacher", "陈明远", "Teacher@123456",
|
||||
"T2026001", collegeId, SystemRoles.Teacher),
|
||||
new DevelopmentUser(
|
||||
"student", "周启航", "Student@123456",
|
||||
"202601001", collegeId, SystemRoles.Student),
|
||||
new DevelopmentUser(
|
||||
"leader", "教学分管领导", "Leader@123456",
|
||||
null, null, SystemRoles.Leader)
|
||||
};
|
||||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
var user = await userManager.FindByNameAsync(definition.UserName);
|
||||
if (user is null)
|
||||
{
|
||||
user = new ApplicationUser
|
||||
{
|
||||
UserName = definition.UserName,
|
||||
DisplayName = definition.DisplayName,
|
||||
StaffNumber = definition.StaffNumber,
|
||||
CollegeId = definition.CollegeId,
|
||||
LockoutEnabled = true,
|
||||
IsEnabled = true
|
||||
};
|
||||
EnsureSucceeded(
|
||||
await userManager.CreateAsync(user, definition.Password),
|
||||
$"创建开发账号 {definition.UserName}");
|
||||
}
|
||||
|
||||
if (!await userManager.IsInRoleAsync(user, definition.Role))
|
||||
{
|
||||
EnsureSucceeded(
|
||||
await userManager.AddToRoleAsync(user, definition.Role),
|
||||
$"授予开发账号 {definition.UserName} 角色");
|
||||
}
|
||||
|
||||
if (definition.Role == SystemRoles.Teacher)
|
||||
{
|
||||
var teacher = await db.Teachers.SingleAsync(
|
||||
x => x.TeacherNumber == definition.StaffNumber);
|
||||
if (!teacher.UserId.HasValue) teacher.UserId = user.Id;
|
||||
}
|
||||
if (definition.Role == SystemRoles.Student)
|
||||
{
|
||||
var student = await db.Students.SingleAsync(
|
||||
x => x.StudentNumber == definition.StaffNumber);
|
||||
if (!student.UserId.HasValue) student.UserId = user.Id;
|
||||
}
|
||||
if (definition.Role == SystemRoles.Counselor)
|
||||
{
|
||||
var classes = await db.AdministrativeClasses
|
||||
.Where(x =>
|
||||
x.CounselorUserId == null &&
|
||||
x.CounselorName == definition.DisplayName)
|
||||
.ToListAsync();
|
||||
foreach (var administrativeClass in classes)
|
||||
administrativeClass.CounselorUserId = user.Id;
|
||||
}
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static void EnsureSucceeded(IdentityResult result, string action)
|
||||
@@ -431,4 +510,12 @@ public sealed class DatabaseInitializer(
|
||||
throw new InvalidOperationException(
|
||||
$"{action}失败:{string.Join(";", result.Errors.Select(x => x.Description))}");
|
||||
}
|
||||
|
||||
private sealed record DevelopmentUser(
|
||||
string UserName,
|
||||
string DisplayName,
|
||||
string Password,
|
||||
string? StaffNumber,
|
||||
Guid? CollegeId,
|
||||
string Role);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
private const string CurriculumPlansMigration = "20260724_02_curriculum_plans";
|
||||
private const string TeachingTasksMigration = "20260724_03_teaching_tasks";
|
||||
private const string SchedulesMigration = "20260724_04_schedules";
|
||||
private const string ClassCounselorMigration = "20260724_05_class_counselor";
|
||||
|
||||
public async Task MigrateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -43,6 +44,20 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
SchedulesMigration,
|
||||
SchedulesStatements,
|
||||
cancellationToken);
|
||||
var counselorColumnExists = await db.Database
|
||||
.SqlQueryRaw<int>(
|
||||
"""
|
||||
SELECT COUNT(*) AS "Value"
|
||||
FROM pragma_table_info('AdministrativeClasses')
|
||||
WHERE name = 'CounselorUserId'
|
||||
""")
|
||||
.AnyAsync(value => value > 0, cancellationToken);
|
||||
await ApplyMigrationAsync(
|
||||
ClassCounselorMigration,
|
||||
counselorColumnExists
|
||||
? ClassCounselorStatements.Skip(1)
|
||||
: ClassCounselorStatements,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async Task ApplyMigrationAsync(
|
||||
@@ -399,4 +414,16 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
ON "ScheduleEntries" ("ClassroomId");
|
||||
"""
|
||||
];
|
||||
|
||||
private static readonly string[] ClassCounselorStatements =
|
||||
[
|
||||
"""
|
||||
ALTER TABLE "AdministrativeClasses"
|
||||
ADD COLUMN "CounselorUserId" TEXT NULL;
|
||||
""",
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS "IX_AdministrativeClasses_CounselorUserId"
|
||||
ON "AdministrativeClasses" ("CounselorUserId");
|
||||
"""
|
||||
];
|
||||
}
|
||||
|
||||
+1483
File diff suppressed because it is too large
Load Diff
+50
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ClassCounselorAssignment : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<Guid>(
|
||||
name: "CounselorUserId",
|
||||
table: "AdministrativeClasses",
|
||||
type: "char(36)",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_AdministrativeClasses_CounselorUserId",
|
||||
table: "AdministrativeClasses",
|
||||
column: "CounselorUserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_AdministrativeClasses_AspNetUsers_CounselorUserId",
|
||||
table: "AdministrativeClasses",
|
||||
column: "CounselorUserId",
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.SetNull);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_AdministrativeClasses_AspNetUsers_CounselorUserId",
|
||||
table: "AdministrativeClasses");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_AdministrativeClasses_CounselorUserId",
|
||||
table: "AdministrativeClasses");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CounselorUserId",
|
||||
table: "AdministrativeClasses");
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -89,6 +89,9 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
b.Property<string>("CounselorName")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<Guid?>("CounselorUserId")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
@@ -117,6 +120,8 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
b.HasIndex("Code")
|
||||
.IsUnique();
|
||||
|
||||
b.HasIndex("CounselorUserId");
|
||||
|
||||
b.HasIndex("MajorId");
|
||||
|
||||
b.HasIndex("IsEnabled", "SortOrder");
|
||||
@@ -1154,12 +1159,19 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b =>
|
||||
{
|
||||
b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", "CounselorUser")
|
||||
.WithMany()
|
||||
.HasForeignKey("CounselorUserId")
|
||||
.OnDelete(DeleteBehavior.SetNull);
|
||||
|
||||
b.HasOne("Jiaowu.Api.Domain.Academic.Major", "Major")
|
||||
.WithMany()
|
||||
.HasForeignKey("MajorId")
|
||||
.OnDelete(DeleteBehavior.Restrict)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("CounselorUser");
|
||||
|
||||
b.Navigation("Major");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user