实验预约

This commit is contained in:
2026-07-28 21:04:18 +08:00 Unverified
parent 865378dbdf
commit ec979f640d
12 changed files with 8750 additions and 0 deletions
@@ -44,6 +44,9 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
Set<SchedulePublishJob>();
public DbSet<ClassroomReservation> ClassroomReservations =>
Set<ClassroomReservation>();
public DbSet<ExperimentProject> ExperimentProjects => Set<ExperimentProject>();
public DbSet<ExperimentSession> ExperimentSessions => Set<ExperimentSession>();
public DbSet<ExperimentBooking> ExperimentBookings => Set<ExperimentBooking>();
public DbSet<CourseSelectionRound> CourseSelectionRounds =>
Set<CourseSelectionRound>();
public DbSet<CourseSelectionRoundGrade> CourseSelectionRoundGrades =>
@@ -547,6 +550,64 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
.OnDelete(DeleteBehavior.SetNull);
});
builder.Entity<ExperimentProject>(entity =>
{
entity.Property(x => x.Code).HasMaxLength(40);
entity.Property(x => x.Name).HasMaxLength(120);
entity.Property(x => x.Description).HasMaxLength(1000);
entity.Property(x => x.Requirements).HasMaxLength(1000);
entity.HasIndex(x => new { x.TeachingTaskId, x.Code }).IsUnique();
entity.HasIndex(x => new { x.Status, x.StartDate, x.EndDate });
entity.HasOne(x => x.TeachingTask).WithMany()
.HasForeignKey(x => x.TeachingTaskId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<ExperimentSession>(entity =>
{
entity.Property(x => x.Notes).HasMaxLength(500);
entity.HasIndex(x => new
{
x.ExperimentProjectId,
x.SessionDate,
x.StartPeriod
});
entity.HasIndex(x => new
{
x.ClassroomId,
x.SessionDate,
x.Status,
x.StartPeriod
});
entity.HasOne(x => x.ExperimentProject).WithMany(x => x.Sessions)
.HasForeignKey(x => x.ExperimentProjectId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(x => x.Classroom).WithMany()
.HasForeignKey(x => x.ClassroomId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<ExperimentBooking>(entity =>
{
entity.HasIndex(x => new { x.ExperimentProjectId, x.StudentId })
.IsUnique();
entity.HasIndex(x => new
{
x.ExperimentSessionId,
x.Status,
x.BookedAt
});
entity.HasOne(x => x.ExperimentProject).WithMany(x => x.Bookings)
.HasForeignKey(x => x.ExperimentProjectId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(x => x.ExperimentSession).WithMany(x => x.Bookings)
.HasForeignKey(x => x.ExperimentSessionId)
.OnDelete(DeleteBehavior.Restrict);
entity.HasOne(x => x.Student).WithMany()
.HasForeignKey(x => x.StudentId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<CourseSelectionRound>(entity =>
{
entity.Property(x => x.Name).HasMaxLength(120);
@@ -68,6 +68,10 @@ public sealed class DevelopmentSqliteMigrator(
"20260727_36_exam_room_mixing";
private const string AttendanceCheckInAuditMigration =
"20260728_37_attendance_check_in_audit";
private const string ExamPublishJobsMigration =
"20260728_38_exam_publish_jobs";
private const string ExperimentManagementMigration =
"20260728_39_experiment_management";
public async Task MigrateAsync(CancellationToken cancellationToken = default)
{
@@ -513,6 +517,32 @@ public sealed class DevelopmentSqliteMigrator(
ExamRoomMixingMigration,
examRoomsExist ? [] : ExamRoomMixingStatements,
cancellationToken);
var examPublishJobsExist = await db.Database
.SqlQueryRaw<int>(
"""
SELECT COUNT(*) AS "Value"
FROM sqlite_master
WHERE type = 'table' AND name = 'ExamPublishJobs'
""")
.AnyAsync(value => value > 0, cancellationToken);
await ApplyMigrationAsync(
ExamPublishJobsMigration,
examPublishJobsExist ? [] : ExamPublishJobStatements,
cancellationToken);
var experimentProjectsExist = await db.Database
.SqlQueryRaw<int>(
"""
SELECT COUNT(*) AS "Value"
FROM sqlite_master
WHERE type = 'table' AND name = 'ExperimentProjects'
""")
.AnyAsync(value => value > 0, cancellationToken);
await ApplyMigrationAsync(
ExperimentManagementMigration,
experimentProjectsExist ? [] : ExperimentManagementStatements,
cancellationToken);
}
private async Task ApplyMigrationAsync(
@@ -2351,4 +2381,134 @@ public sealed class DevelopmentSqliteMigrator(
ON "ExamRoomInvigilators" ("TeacherId");
"""
];
private static readonly string[] ExamPublishJobStatements =
[
"""
CREATE TABLE "ExamPublishJobs" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ExamPublishJobs" PRIMARY KEY,
"Kind" INTEGER NOT NULL,
"PlanId" TEXT NOT NULL,
"ActivePlanId" TEXT NULL,
"RequestedByUserId" TEXT NULL,
"Status" INTEGER NOT NULL,
"CurrentStep" TEXT NULL,
"ErrorMessage" TEXT NULL,
"StartedAt" TEXT NULL,
"CompletedAt" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL
);
"""
];
private static readonly string[] ExperimentManagementStatements =
[
"""
CREATE TABLE "ExperimentProjects" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ExperimentProjects" PRIMARY KEY,
"TeachingTaskId" TEXT NOT NULL,
"Code" TEXT NOT NULL,
"Name" TEXT NOT NULL,
"ArrangementMode" INTEGER NOT NULL,
"Description" TEXT NULL,
"Requirements" TEXT NULL,
"StartDate" TEXT NOT NULL,
"EndDate" TEXT NOT NULL,
"Status" INTEGER NOT NULL,
"PublishedAt" TEXT NULL,
"ClosedAt" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_ExperimentProjects_TeachingTasks_TeachingTaskId"
FOREIGN KEY ("TeachingTaskId") REFERENCES "TeachingTasks" ("Id")
ON DELETE RESTRICT
);
""",
"""
CREATE UNIQUE INDEX "IX_ExperimentProjects_TeachingTaskId_Code"
ON "ExperimentProjects" ("TeachingTaskId", "Code");
""",
"""
CREATE INDEX "IX_ExperimentProjects_Status_StartDate_EndDate"
ON "ExperimentProjects" ("Status", "StartDate", "EndDate");
""",
"""
CREATE TABLE "ExperimentSessions" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ExperimentSessions" PRIMARY KEY,
"ExperimentProjectId" TEXT NOT NULL,
"ClassroomId" TEXT NOT NULL,
"SessionDate" TEXT NOT NULL,
"StartPeriod" INTEGER NOT NULL,
"PeriodCount" INTEGER NOT NULL,
"Capacity" INTEGER NOT NULL,
"ReservedCount" INTEGER NOT NULL,
"Notes" TEXT NULL,
"Status" INTEGER NOT NULL,
"CancelledAt" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_ExperimentSessions_ExperimentProjects_ExperimentProjectId"
FOREIGN KEY ("ExperimentProjectId")
REFERENCES "ExperimentProjects" ("Id")
ON DELETE CASCADE,
CONSTRAINT "FK_ExperimentSessions_Classrooms_ClassroomId"
FOREIGN KEY ("ClassroomId") REFERENCES "Classrooms" ("Id")
ON DELETE RESTRICT
);
""",
"""
CREATE INDEX "IX_ExperimentSessions_ExperimentProjectId_SessionDate_StartPeriod"
ON "ExperimentSessions" (
"ExperimentProjectId",
"SessionDate",
"StartPeriod"
);
""",
"""
CREATE INDEX "IX_ExperimentSessions_ClassroomId_SessionDate_Status_StartPeriod"
ON "ExperimentSessions" (
"ClassroomId",
"SessionDate",
"Status",
"StartPeriod"
);
""",
"""
CREATE TABLE "ExperimentBookings" (
"Id" TEXT NOT NULL CONSTRAINT "PK_ExperimentBookings" PRIMARY KEY,
"ExperimentProjectId" TEXT NOT NULL,
"ExperimentSessionId" TEXT NOT NULL,
"StudentId" TEXT NOT NULL,
"Status" INTEGER NOT NULL,
"BookedAt" TEXT NOT NULL,
"CancelledAt" TEXT NULL,
"CreatedAt" TEXT NOT NULL,
"UpdatedAt" TEXT NOT NULL,
CONSTRAINT "FK_ExperimentBookings_ExperimentProjects_ExperimentProjectId"
FOREIGN KEY ("ExperimentProjectId")
REFERENCES "ExperimentProjects" ("Id")
ON DELETE CASCADE,
CONSTRAINT "FK_ExperimentBookings_ExperimentSessions_ExperimentSessionId"
FOREIGN KEY ("ExperimentSessionId")
REFERENCES "ExperimentSessions" ("Id")
ON DELETE RESTRICT,
CONSTRAINT "FK_ExperimentBookings_Students_StudentId"
FOREIGN KEY ("StudentId") REFERENCES "Students" ("Id")
ON DELETE RESTRICT
);
""",
"""
CREATE UNIQUE INDEX "IX_ExperimentBookings_ExperimentProjectId_StudentId"
ON "ExperimentBookings" ("ExperimentProjectId", "StudentId");
""",
"""
CREATE INDEX "IX_ExperimentBookings_ExperimentSessionId_Status_BookedAt"
ON "ExperimentBookings" (
"ExperimentSessionId",
"Status",
"BookedAt"
);
"""
];
}
@@ -0,0 +1,170 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
{
/// <inheritdoc />
public partial class ExperimentManagement : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "ExperimentProjects",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false),
TeachingTaskId = table.Column<Guid>(type: "char(36)", nullable: false),
Code = table.Column<string>(type: "varchar(40)", maxLength: 40, nullable: false),
Name = table.Column<string>(type: "varchar(120)", maxLength: 120, nullable: false),
ArrangementMode = table.Column<int>(type: "int", nullable: false),
Description = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: true),
Requirements = table.Column<string>(type: "varchar(1000)", maxLength: 1000, nullable: true),
StartDate = table.Column<DateTime>(type: "date", nullable: false),
EndDate = table.Column<DateTime>(type: "date", nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
PublishedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
ClosedAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ExperimentProjects", x => x.Id);
table.ForeignKey(
name: "FK_ExperimentProjects_TeachingTasks_TeachingTaskId",
column: x => x.TeachingTaskId,
principalTable: "TeachingTasks",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
})
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateTable(
name: "ExperimentSessions",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false),
ExperimentProjectId = table.Column<Guid>(type: "char(36)", nullable: false),
ClassroomId = table.Column<Guid>(type: "char(36)", nullable: false),
SessionDate = table.Column<DateTime>(type: "date", nullable: false),
StartPeriod = table.Column<int>(type: "int", nullable: false),
PeriodCount = table.Column<int>(type: "int", nullable: false),
Capacity = table.Column<int>(type: "int", nullable: false),
ReservedCount = table.Column<int>(type: "int", nullable: false),
Notes = table.Column<string>(type: "varchar(500)", maxLength: 500, nullable: true),
Status = table.Column<int>(type: "int", nullable: false),
CancelledAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ExperimentSessions", x => x.Id);
table.ForeignKey(
name: "FK_ExperimentSessions_Classrooms_ClassroomId",
column: x => x.ClassroomId,
principalTable: "Classrooms",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ExperimentSessions_ExperimentProjects_ExperimentProjectId",
column: x => x.ExperimentProjectId,
principalTable: "ExperimentProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
})
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateTable(
name: "ExperimentBookings",
columns: table => new
{
Id = table.Column<Guid>(type: "char(36)", nullable: false),
ExperimentProjectId = table.Column<Guid>(type: "char(36)", nullable: false),
ExperimentSessionId = table.Column<Guid>(type: "char(36)", nullable: false),
StudentId = table.Column<Guid>(type: "char(36)", nullable: false),
Status = table.Column<int>(type: "int", nullable: false),
BookedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
CancelledAt = table.Column<DateTime>(type: "datetime(6)", nullable: true),
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ExperimentBookings", x => x.Id);
table.ForeignKey(
name: "FK_ExperimentBookings_ExperimentProjects_ExperimentProjectId",
column: x => x.ExperimentProjectId,
principalTable: "ExperimentProjects",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_ExperimentBookings_ExperimentSessions_ExperimentSessionId",
column: x => x.ExperimentSessionId,
principalTable: "ExperimentSessions",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
table.ForeignKey(
name: "FK_ExperimentBookings_Students_StudentId",
column: x => x.StudentId,
principalTable: "Students",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
})
.Annotation("MySQL:Charset", "utf8mb4");
migrationBuilder.CreateIndex(
name: "IX_ExperimentBookings_ExperimentProjectId_StudentId",
table: "ExperimentBookings",
columns: new[] { "ExperimentProjectId", "StudentId" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ExperimentBookings_ExperimentSessionId_Status_BookedAt",
table: "ExperimentBookings",
columns: new[] { "ExperimentSessionId", "Status", "BookedAt" });
migrationBuilder.CreateIndex(
name: "IX_ExperimentBookings_StudentId",
table: "ExperimentBookings",
column: "StudentId");
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_Status_StartDate_EndDate",
table: "ExperimentProjects",
columns: new[] { "Status", "StartDate", "EndDate" });
migrationBuilder.CreateIndex(
name: "IX_ExperimentProjects_TeachingTaskId_Code",
table: "ExperimentProjects",
columns: new[] { "TeachingTaskId", "Code" },
unique: true);
migrationBuilder.CreateIndex(
name: "IX_ExperimentSessions_ClassroomId_SessionDate_Status_StartPeriod",
table: "ExperimentSessions",
columns: new[] { "ClassroomId", "SessionDate", "Status", "StartPeriod" });
migrationBuilder.CreateIndex(
name: "IX_ExperimentSessions_ExperimentProjectId_SessionDate_StartPeri~",
table: "ExperimentSessions",
columns: new[] { "ExperimentProjectId", "SessionDate", "StartPeriod" });
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "ExperimentBookings");
migrationBuilder.DropTable(
name: "ExperimentSessions");
migrationBuilder.DropTable(
name: "ExperimentProjects");
}
}
}
@@ -2001,6 +2001,161 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.ToTable("ExamSignInExportJobs");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentBooking", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime>("BookedAt")
.HasColumnType("datetime(6)");
b.Property<DateTime?>("CancelledAt")
.HasColumnType("datetime(6)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("ExperimentProjectId")
.HasColumnType("char(36)");
b.Property<Guid>("ExperimentSessionId")
.HasColumnType("char(36)");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid>("StudentId")
.HasColumnType("char(36)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("StudentId");
b.HasIndex("ExperimentProjectId", "StudentId")
.IsUnique();
b.HasIndex("ExperimentSessionId", "Status", "BookedAt");
b.ToTable("ExperimentBookings");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentProject", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<int>("ArrangementMode")
.HasColumnType("int");
b.Property<DateTime?>("ClosedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Code")
.IsRequired()
.HasMaxLength(40)
.HasColumnType("varchar(40)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Description")
.HasMaxLength(1000)
.HasColumnType("varchar(1000)");
b.Property<DateTime>("EndDate")
.HasColumnType("date");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(120)
.HasColumnType("varchar(120)");
b.Property<DateTime?>("PublishedAt")
.HasColumnType("datetime(6)");
b.Property<string>("Requirements")
.HasMaxLength(1000)
.HasColumnType("varchar(1000)");
b.Property<DateTime>("StartDate")
.HasColumnType("date");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid>("TeachingTaskId")
.HasColumnType("char(36)");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("TeachingTaskId", "Code")
.IsUnique();
b.HasIndex("Status", "StartDate", "EndDate");
b.ToTable("ExperimentProjects");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentSession", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<DateTime?>("CancelledAt")
.HasColumnType("datetime(6)");
b.Property<int>("Capacity")
.HasColumnType("int");
b.Property<Guid>("ClassroomId")
.HasColumnType("char(36)");
b.Property<DateTime>("CreatedAt")
.HasColumnType("datetime(6)");
b.Property<Guid>("ExperimentProjectId")
.HasColumnType("char(36)");
b.Property<string>("Notes")
.HasMaxLength(500)
.HasColumnType("varchar(500)");
b.Property<int>("PeriodCount")
.HasColumnType("int");
b.Property<int>("ReservedCount")
.HasColumnType("int");
b.Property<DateTime>("SessionDate")
.HasColumnType("date");
b.Property<int>("StartPeriod")
.HasColumnType("int");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<DateTime>("UpdatedAt")
.HasColumnType("datetime(6)");
b.HasKey("Id");
b.HasIndex("ExperimentProjectId", "SessionDate", "StartPeriod");
b.HasIndex("ClassroomId", "SessionDate", "Status", "StartPeriod");
b.ToTable("ExperimentSessions");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b =>
{
b.Property<Guid>("Id")
@@ -4630,6 +4785,63 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.Navigation("Teacher");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentBooking", b =>
{
b.HasOne("Jiaowu.Api.Domain.Academic.ExperimentProject", "ExperimentProject")
.WithMany("Bookings")
.HasForeignKey("ExperimentProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Jiaowu.Api.Domain.Academic.ExperimentSession", "ExperimentSession")
.WithMany("Bookings")
.HasForeignKey("ExperimentSessionId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student")
.WithMany()
.HasForeignKey("StudentId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("ExperimentProject");
b.Navigation("ExperimentSession");
b.Navigation("Student");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentProject", b =>
{
b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask")
.WithMany()
.HasForeignKey("TeachingTaskId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.Navigation("TeachingTask");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentSession", b =>
{
b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom")
.WithMany()
.HasForeignKey("ClassroomId")
.OnDelete(DeleteBehavior.Restrict)
.IsRequired();
b.HasOne("Jiaowu.Api.Domain.Academic.ExperimentProject", "ExperimentProject")
.WithMany("Sessions")
.HasForeignKey("ExperimentProjectId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Classroom");
b.Navigation("ExperimentProject");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b =>
{
b.HasOne("Jiaowu.Api.Domain.Academic.GradeSheet", "GradeSheet")
@@ -5338,6 +5550,18 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
b.Navigation("SeatAssignments");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentProject", b =>
{
b.Navigation("Bookings");
b.Navigation("Sessions");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExperimentSession", b =>
{
b.Navigation("Bookings");
});
modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b =>
{
b.Navigation("Scores");