已完成“成绩管理与学业档案”模块:

教师批量录入平时、期中、期末成绩。
自动计算总评、绩点和及格状态。
支持缺考、缓考、免修。
教师提交 → 学院审核/退回 → 校级发布。
学生只能查看正式发布成绩。
自动统计平均分、完成度、及格率和加权 GPA。
SQLite 本地增量升级和 MySQL 正式迁移均已完成。
分级权限在前后端同时校验。
This commit is contained in:
2026-07-24 15:33:47 +08:00 Unverified
parent 151a22554f
commit af52e27a3a
18 changed files with 3686 additions and 22 deletions
@@ -33,6 +33,8 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
public DbSet<CourseSelectionOffering> CourseSelectionOfferings =>
Set<CourseSelectionOffering>();
public DbSet<CourseEnrollment> CourseEnrollments => Set<CourseEnrollment>();
public DbSet<GradeSheet> GradeSheets => Set<GradeSheet>();
public DbSet<GradeRecord> GradeRecords => Set<GradeRecord>();
public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
protected override void OnModelCreating(ModelBuilder builder)
@@ -317,6 +319,40 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<GradeSheet>(entity =>
{
entity.Property(x => x.RegularWeight).HasPrecision(5, 1);
entity.Property(x => x.MidtermWeight).HasPrecision(5, 1);
entity.Property(x => x.FinalWeight).HasPrecision(5, 1);
entity.Property(x => x.ReviewComment).HasMaxLength(500);
entity.HasIndex(x => x.TeachingTaskId).IsUnique();
entity.HasIndex(x => x.Status);
entity.HasOne(x => x.TeachingTask)
.WithMany()
.HasForeignKey(x => x.TeachingTaskId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<GradeRecord>(entity =>
{
entity.Property(x => x.RegularScore).HasPrecision(5, 1);
entity.Property(x => x.MidtermScore).HasPrecision(5, 1);
entity.Property(x => x.FinalScore).HasPrecision(5, 1);
entity.Property(x => x.TotalScore).HasPrecision(5, 1);
entity.Property(x => x.GradePoint).HasPrecision(3, 1);
entity.Property(x => x.Notes).HasMaxLength(300);
entity.HasIndex(x => new { x.GradeSheetId, x.StudentId }).IsUnique();
entity.HasIndex(x => new { x.StudentId, x.TotalScore });
entity.HasOne(x => x.GradeSheet)
.WithMany(x => x.Records)
.HasForeignKey(x => x.GradeSheetId)
.OnDelete(DeleteBehavior.Cascade);
entity.HasOne(x => x.Student)
.WithMany()
.HasForeignKey(x => x.StudentId)
.OnDelete(DeleteBehavior.Restrict);
});
builder.Entity<AuditLog>(entity =>
{
entity.Property(x => x.Method).HasMaxLength(10);