From 376bc0d0a1128cbdb3675e84074057e342b21c2b Mon Sep 17 00:00:00 2001 From: biss Date: Sat, 25 Jul 2026 18:41:53 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E6=95=99=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/AttendanceController.cs | 16 +- .../Controllers/EvaluationsController.cs | 571 +++ .../Domain/Academic/EvaluationEntities.cs | 84 + .../Persistence/AppDbContext.cs | 41 + .../Persistence/DevelopmentSqliteMigrator.cs | 21 + ...60725103117_TeachingEvaluation.Designer.cs | 3968 +++++++++++++++++ .../20260725103117_TeachingEvaluation.cs | 22 + .../MySql/AppDbContextModelSnapshot.cs | 1987 +++++++-- web/src/components.d.ts | 1 + web/src/layouts/AdminLayout.vue | 4 + web/src/router/index.ts | 8 + web/src/views/EvaluationView.vue | 431 ++ 12 files changed, 6667 insertions(+), 487 deletions(-) create mode 100644 src/Jiaowu.Api/Controllers/EvaluationsController.cs create mode 100644 src/Jiaowu.Api/Domain/Academic/EvaluationEntities.cs create mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs create mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs create mode 100644 web/src/views/EvaluationView.vue diff --git a/src/Jiaowu.Api/Controllers/AttendanceController.cs b/src/Jiaowu.Api/Controllers/AttendanceController.cs index 69d4c33..1561816 100644 --- a/src/Jiaowu.Api/Controllers/AttendanceController.cs +++ b/src/Jiaowu.Api/Controllers/AttendanceController.cs @@ -13,7 +13,7 @@ using Microsoft.EntityFrameworkCore; namespace Jiaowu.Api.Controllers; [ApiController] -[Authorize(Roles = AttendanceRoles)] +[Authorize] [Route("api/attendance")] public sealed class AttendanceController( AppDbContext db, @@ -27,6 +27,7 @@ public sealed class AttendanceController( SystemRoles.Teacher; [HttpGet("my-tasks")] + [Authorize(Roles = AttendanceRoles)] public async Task GetMyTasks( Guid? academicTermId, CancellationToken cancellationToken) @@ -57,6 +58,7 @@ public sealed class AttendanceController( } [HttpGet("sheets")] + [Authorize(Roles = AttendanceRoles)] public async Task GetSheets( Guid teachingTaskId, CancellationToken cancellationToken) @@ -88,6 +90,7 @@ public sealed class AttendanceController( } [HttpPost("sheets")] + [Authorize(Roles = AttendanceRoles)] public async Task CreateSheet( AttendanceSheetRequest request, CancellationToken cancellationToken) @@ -123,6 +126,7 @@ public sealed class AttendanceController( } [HttpGet("sheets/{id:guid}")] + [Authorize(Roles = AttendanceRoles)] public async Task GetSheet(Guid id, CancellationToken cancellationToken) { var sheet = await db.AttendanceSheets.AsNoTracking() @@ -184,6 +188,7 @@ public sealed class AttendanceController( } [HttpPut("sheets/{id:guid}/records")] + [Authorize(Roles = AttendanceRoles)] public async Task UpdateRecords( Guid id, AttendanceRecordsRequest request, @@ -193,6 +198,7 @@ public sealed class AttendanceController( .Include(x => x.Records) .Include(x => x.TeachingTask) .ThenInclude(x => x!.Teachers) + .ThenInclude(x => x.Teacher) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); if (sheet is null) return NotFound(); if (!CanManageSheet(sheet)) return Forbid(); @@ -213,6 +219,7 @@ public sealed class AttendanceController( } [HttpPost("sheets/{id:guid}/import")] + [Authorize(Roles = AttendanceRoles)] public async Task Import( Guid id, IFormFile file, @@ -223,6 +230,7 @@ public sealed class AttendanceController( .ThenInclude(x => x.Student) .Include(x => x.TeachingTask) .ThenInclude(x => x!.Teachers) + .ThenInclude(x => x.Teacher) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); if (sheet is null) return NotFound(); if (!CanManageSheet(sheet)) return Forbid(); @@ -262,6 +270,7 @@ public sealed class AttendanceController( } [HttpGet("sheets/{id:guid}/export.xlsx")] + [Authorize(Roles = AttendanceRoles)] public async Task Export(Guid id, CancellationToken cancellationToken) { var sheetData = await db.AttendanceSheets.AsNoTracking() @@ -308,11 +317,13 @@ public sealed class AttendanceController( } [HttpPost("sheets/{id:guid}/submit")] + [Authorize(Roles = AttendanceRoles)] public async Task Submit(Guid id, CancellationToken cancellationToken) { var sheet = await db.AttendanceSheets .Include(x => x.TeachingTask) .ThenInclude(x => x!.Teachers) + .ThenInclude(x => x.Teacher) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); if (sheet is null) return NotFound(); if (!CanManageSheet(sheet)) return Forbid(); @@ -496,6 +507,7 @@ public sealed class AttendanceController( .Include(r => r.AttendanceSheet) .ThenInclude(s => s!.TeachingTask) .ThenInclude(t => t!.Teachers) + .ThenInclude(x => x.Teacher) .FirstOrDefaultAsync(r => r.AttendanceSheetId == attendanceSheetId && r.StudentId == studentId, @@ -538,11 +550,13 @@ public sealed class AttendanceController( } [HttpDelete("sheets/{id:guid}")] + [Authorize(Roles = AttendanceRoles)] public async Task Delete(Guid id, CancellationToken cancellationToken) { var sheet = await db.AttendanceSheets .Include(x => x.TeachingTask) .ThenInclude(x => x!.Teachers) + .ThenInclude(x => x.Teacher) .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); if (sheet is null) return NotFound(); if (!CanManageSheet(sheet)) return Forbid(); diff --git a/src/Jiaowu.Api/Controllers/EvaluationsController.cs b/src/Jiaowu.Api/Controllers/EvaluationsController.cs new file mode 100644 index 0000000..fea2860 --- /dev/null +++ b/src/Jiaowu.Api/Controllers/EvaluationsController.cs @@ -0,0 +1,571 @@ +using System.ComponentModel.DataAnnotations; +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; +using Microsoft.EntityFrameworkCore; + +namespace Jiaowu.Api.Controllers; + +[ApiController] +[Authorize] +[Route("api/evaluations")] +public sealed class EvaluationsController( + AppDbContext db, + ICurrentUserDataScope currentUserDataScope) : ControllerBase +{ + private const string AdminRoles = + SystemRoles.SuperAdmin + "," + + SystemRoles.AcademicAdmin; + + private const string StaffRoles = + SystemRoles.SuperAdmin + "," + + SystemRoles.AcademicAdmin + "," + + SystemRoles.CollegeAdmin + "," + + SystemRoles.Teacher + "," + + SystemRoles.Leader; + + // ── Admin: Manage setups ──────────────────────────────────────── + + [HttpGet("setups")] + [Authorize(Roles = AdminRoles)] + public async Task GetSetups(CancellationToken cancellationToken) + { + var setups = await db.EvaluationSetups.AsNoTracking() + .Include(x => x.Dimensions.OrderBy(d => d.SortOrder)) + .Include(x => x.AcademicTerm) + .OrderByDescending(x => x.AcademicTerm!.StartDate) + .Select(x => new + { + x.Id, x.Name, x.Status, + x.AcademicTermId, TermName = x.AcademicTerm!.Name, + x.StartsAt, x.EndsAt, + Dimensions = x.Dimensions.Select(d => new + { + d.Id, d.Name, d.MaxScore, d.SortOrder + }), + RecordCount = x.Records.Count, + x.CreatedAt + }) + .ToListAsync(cancellationToken); + return Ok(setups); + } + + [HttpPost("setups")] + [Authorize(Roles = AdminRoles)] + public async Task CreateSetup( + EvaluationSetupRequest request, + CancellationToken cancellationToken) + { + if (request.Dimensions.Count == 0) + return ValidationProblem("至少需要一个评分维度。"); + if (request.Dimensions.Sum(d => d.MaxScore) != 100) + return ValidationProblem("所有维度的满分之和必须等于 100。"); + + var termExists = await db.AcademicTerms.AnyAsync( + x => x.Id == request.AcademicTermId, cancellationToken); + if (!termExists) return ValidationProblem("所选学期不存在。"); + + var existing = await db.EvaluationSetups + .AnyAsync(x => x.AcademicTermId == request.AcademicTermId, cancellationToken); + if (existing) return ConflictProblem("该学期已有评教方案。"); + + var setup = new EvaluationSetup + { + AcademicTermId = request.AcademicTermId, + Name = request.Name.Trim(), + Status = EvaluationSetupStatus.Draft, + StartsAt = request.StartsAt, + EndsAt = request.EndsAt, + Dimensions = request.Dimensions.Select((d, i) => new EvaluationDimension + { + Name = d.Name.Trim(), + MaxScore = d.MaxScore, + SortOrder = i + }).ToList() + }; + db.EvaluationSetups.Add(setup); + await db.SaveChangesAsync(cancellationToken); + return Created(string.Empty, new { setup.Id }); + } + + [HttpPut("setups/{id:guid}")] + [Authorize(Roles = AdminRoles)] + public async Task UpdateSetup( + Guid id, + EvaluationSetupRequest request, + CancellationToken cancellationToken) + { + if (request.Dimensions.Count == 0) + return ValidationProblem("至少需要一个评分维度。"); + if (request.Dimensions.Sum(d => d.MaxScore) != 100) + return ValidationProblem("所有维度的满分之和必须等于 100。"); + + var setup = await db.EvaluationSetups + .Include(x => x.Dimensions) + .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (setup is null) return NotFound(); + if (setup.Status == EvaluationSetupStatus.Closed) + return ConflictProblem("已关闭的评教方案不能修改。"); + + setup.Name = request.Name.Trim(); + setup.StartsAt = request.StartsAt; + setup.EndsAt = request.EndsAt; + + db.EvaluationDimensions.RemoveRange(setup.Dimensions); + setup.Dimensions = request.Dimensions.Select((d, i) => new EvaluationDimension + { + EvaluationSetupId = setup.Id, + Name = d.Name.Trim(), + MaxScore = d.MaxScore, + SortOrder = i + }).ToList(); + + await db.SaveChangesAsync(cancellationToken); + return NoContent(); + } + + [HttpPost("setups/{id:guid}/open")] + [Authorize(Roles = AdminRoles)] + public async Task OpenSetup(Guid id, CancellationToken cancellationToken) + { + var setup = await db.EvaluationSetups.FindAsync([id], cancellationToken); + if (setup is null) return NotFound(); + if (setup.Status == EvaluationSetupStatus.Closed) + return ConflictProblem("已关闭的方案不能重新开启。"); + setup.Status = EvaluationSetupStatus.Open; + await db.SaveChangesAsync(cancellationToken); + return NoContent(); + } + + [HttpPost("setups/{id:guid}/close")] + [Authorize(Roles = AdminRoles)] + public async Task CloseSetup(Guid id, CancellationToken cancellationToken) + { + var setup = await db.EvaluationSetups.FindAsync([id], cancellationToken); + if (setup is null) return NotFound(); + setup.Status = EvaluationSetupStatus.Closed; + await db.SaveChangesAsync(cancellationToken); + return NoContent(); + } + + [HttpDelete("setups/{id:guid}")] + [Authorize(Roles = AdminRoles)] + public async Task DeleteSetup(Guid id, CancellationToken cancellationToken) + { + var setup = await db.EvaluationSetups + .Include(x => x.Records) + .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (setup is null) return NotFound(); + if (setup.Records.Count > 0) + return ConflictProblem("已有评教记录的方案不能删除。"); + db.EvaluationSetups.Remove(setup); + await db.SaveChangesAsync(cancellationToken); + return NoContent(); + } + + // ── Student: Evaluate courses ──────────────────────────────────── + + [HttpGet("my-tasks")] + [Authorize(Roles = SystemRoles.Student)] + public async Task GetMyTasks(CancellationToken cancellationToken) + { + var userId = currentUserDataScope.Current.UserId; + var studentId = await db.Students + .Where(s => s.UserId == userId) + .Select(s => (Guid?)s.Id) + .FirstOrDefaultAsync(cancellationToken); + if (!studentId.HasValue) + return ConflictProblem("当前账号未关联学生档案。"); + + // Find open evaluation setups + var openSetups = await db.EvaluationSetups.AsNoTracking() + .Where(x => x.Status == EvaluationSetupStatus.Open) + .Select(x => new { x.Id, x.AcademicTermId }) + .ToListAsync(cancellationToken); + + if (openSetups.Count == 0) + return Ok(new { Setups = Array.Empty(), Tasks = Array.Empty() }); + + // Find enrolled teaching tasks for the student in those terms + var termIds = openSetups.Select(x => x.AcademicTermId).ToHashSet(); + var enrollments = await db.CourseEnrollments.AsNoTracking() + .Where(e => + e.Status == CourseEnrollmentStatus.Enrolled && + e.StudentId == studentId.Value && + termIds.Contains(e.CourseSelectionOffering!.TeachingTask!.AcademicTermId)) + .Select(e => new + { + e.CourseSelectionOffering!.TeachingTaskId, + e.CourseSelectionOffering.TeachingTask!.AcademicTermId, + e.CourseSelectionOffering.TeachingTask.TaskNumber, + TaskName = e.CourseSelectionOffering.TeachingTask.Name, + CourseCode = e.CourseSelectionOffering.TeachingTask.Course!.Code, + CourseName = e.CourseSelectionOffering.TeachingTask.Course.Name, + TeacherNames = e.CourseSelectionOffering.TeachingTask.Teachers + .OrderByDescending(t => t.IsPrimary) + .Select(t => t.Teacher!.Name) + }) + .ToListAsync(cancellationToken); + + // Check which tasks have already been evaluated + var evaluatedIds = await db.EvaluationRecords.AsNoTracking() + .Where(r => r.StudentId == studentId.Value && + openSetups.Select(s => s.Id).Contains(r.EvaluationSetupId)) + .Select(r => r.TeachingTaskId) + .ToHashSetAsync(cancellationToken); + + var tasks = enrollments.Select(e => new + { + e.TeachingTaskId, + e.AcademicTermId, + e.TaskNumber, + e.TaskName, + e.CourseCode, + e.CourseName, + e.TeacherNames, + SetupId = openSetups.First(s => s.AcademicTermId == e.AcademicTermId).Id, + IsEvaluated = evaluatedIds.Contains(e.TeachingTaskId) + }).ToList(); + + return Ok(new { Tasks = tasks }); + } + + [HttpGet("tasks/{teachingTaskId:guid}/form")] + [Authorize(Roles = SystemRoles.Student)] + public async Task GetEvaluationForm( + Guid teachingTaskId, + CancellationToken cancellationToken) + { + var userId = currentUserDataScope.Current.UserId; + var studentId = await db.Students + .Where(s => s.UserId == userId) + .Select(s => (Guid?)s.Id) + .FirstOrDefaultAsync(cancellationToken); + if (!studentId.HasValue) + return ConflictProblem("当前账号未关联学生档案。"); + + var task = await db.TeachingTasks.AsNoTracking() + .Where(x => x.Id == teachingTaskId) + .Select(x => new + { + x.Id, x.TaskNumber, x.Name, + x.AcademicTermId, + CourseCode = x.Course!.Code, + CourseName = x.Course.Name, + TeacherNames = x.Teachers + .OrderByDescending(t => t.IsPrimary) + .Select(t => t.Teacher!.Name) + }) + .FirstOrDefaultAsync(cancellationToken); + if (task is null) return NotFound(); + + var setup = await db.EvaluationSetups.AsNoTracking() + .Include(x => x.Dimensions.OrderBy(d => d.SortOrder)) + .Where(x => x.AcademicTermId == task.AcademicTermId && + x.Status == EvaluationSetupStatus.Open) + .FirstOrDefaultAsync(cancellationToken); + if (setup is null) return ConflictProblem("当前学期没有开放的评教活动。"); + + // Check if already evaluated + var exists = await db.EvaluationRecords.AnyAsync( + x => x.EvaluationSetupId == setup.Id && + x.StudentId == studentId.Value && + x.TeachingTaskId == teachingTaskId, + cancellationToken); + if (exists) return ConflictProblem("您已经对该课程进行过评教。"); + + return Ok(new + { + SetupId = setup.Id, + setup.Name, + Dimensions = setup.Dimensions.Select(d => new + { + d.Id, d.Name, d.MaxScore, d.SortOrder + }), + Task = task + }); + } + + [HttpPost("tasks/{teachingTaskId:guid}/submit")] + [Authorize(Roles = SystemRoles.Student)] + public async Task SubmitEvaluation( + Guid teachingTaskId, + SubmitEvaluationRequest request, + CancellationToken cancellationToken) + { + var userId = currentUserDataScope.Current.UserId; + var studentId = await db.Students + .Where(s => s.UserId == userId) + .Select(s => (Guid?)s.Id) + .FirstOrDefaultAsync(cancellationToken); + if (!studentId.HasValue) + return ConflictProblem("当前账号未关联学生档案。"); + + var setup = await db.EvaluationSetups + .Include(x => x.Dimensions) + .FirstOrDefaultAsync(x => x.Id == request.SetupId, cancellationToken); + if (setup is null) return NotFound("评教方案不存在。"); + if (setup.Status != EvaluationSetupStatus.Open) + return ConflictProblem("该评教活动已结束。"); + + // Validate against dimensions + var dimMap = setup.Dimensions.ToDictionary(d => d.Id); + if (request.Scores.Count != dimMap.Count) + return ValidationProblem("评分维度数量不匹配。"); + + var totalScore = 0; + foreach (var score in request.Scores) + { + if (!dimMap.TryGetValue(score.DimensionId, out var dim)) + return ValidationProblem($"无效的评分维度。"); + if (score.Score < 0 || score.Score > dim.MaxScore) + return ValidationProblem($"「{dim.Name}」评分超出范围(0-{dim.MaxScore})。"); + totalScore += score.Score; + } + + // Check duplicate + var exists = await db.EvaluationRecords.AnyAsync( + x => x.EvaluationSetupId == setup.Id && + x.StudentId == studentId.Value && + x.TeachingTaskId == teachingTaskId, + cancellationToken); + if (exists) return ConflictProblem("您已经对该课程进行过评教。"); + + var record = new EvaluationRecord + { + EvaluationSetupId = setup.Id, + StudentId = studentId.Value, + TeachingTaskId = teachingTaskId, + SubmittedAt = DateTime.UtcNow, + Scores = request.Scores.Select(s => new EvaluationScore + { + EvaluationDimensionId = s.DimensionId, + Score = s.Score + }).ToList() + }; + db.EvaluationRecords.Add(record); + await db.SaveChangesAsync(cancellationToken); + + return Ok(new { record.Id, TotalScore = totalScore }); + } + + // ── Teacher / Staff: View results ───────────────────────────────── + + [HttpGet("my-results")] + [Authorize(Roles = SystemRoles.Teacher)] + public async Task GetMyResults(CancellationToken cancellationToken) + { + var userId = currentUserDataScope.Current.UserId; + var teacherId = await db.Teachers + .Where(t => t.UserId == userId) + .Select(t => (Guid?)t.Id) + .FirstOrDefaultAsync(cancellationToken); + if (!teacherId.HasValue) + return ConflictProblem("当前账号未关联教师档案。"); + + var taskIds = await db.TeachingTaskTeachers + .Where(x => x.TeacherId == teacherId.Value) + .Select(x => x.TeachingTaskId) + .ToListAsync(cancellationToken); + + if (taskIds.Count == 0) + return Ok(new { Tasks = Array.Empty() }); + + var setups = await db.EvaluationSetups.AsNoTracking() + .Where(x => x.Status == EvaluationSetupStatus.Closed) + .Include(x => x.Dimensions.OrderBy(d => d.SortOrder)) + .OrderByDescending(x => x.AcademicTerm!.StartDate) + .ToListAsync(cancellationToken); + + var result = new List(); + foreach (var setup in setups) + { + var setupTaskIds = await db.EvaluationRecords.AsNoTracking() + .Where(r => r.EvaluationSetupId == setup.Id && + taskIds.Contains(r.TeachingTaskId)) + .Select(r => r.TeachingTaskId) + .Distinct() + .ToListAsync(cancellationToken); + + foreach (var taskId in setupTaskIds) + { + var task = await db.TeachingTasks.AsNoTracking() + .Where(x => x.Id == taskId) + .Select(x => new + { + x.Id, x.TaskNumber, x.Name, + x.AcademicTermId, + CourseCode = x.Course!.Code, + CourseName = x.Course.Name + }) + .FirstOrDefaultAsync(cancellationToken); + if (task is null) continue; + + var records = await db.EvaluationRecords.AsNoTracking() + .Where(r => r.EvaluationSetupId == setup.Id && + r.TeachingTaskId == taskId) + .Include(r => r.Scores) + .ToListAsync(cancellationToken); + + if (records.Count == 0) continue; + + var dimensionResults = setup.Dimensions.Select(dim => + { + var scores = records + .SelectMany(r => r.Scores) + .Where(s => s.EvaluationDimensionId == dim.Id) + .Select(s => (double)s.Score) + .ToList(); + return new + { + DimensionId = dim.Id, + DimensionName = dim.Name, + MaxScore = dim.MaxScore, + AverageScore = scores.Count > 0 + ? Math.Round(scores.Average(), 1) : 0, + TotalScore = scores.Sum(), + Count = scores.Count + }; + }).ToList(); + + var totalAvg = records + .Select(r => (double)r.Scores.Sum(s => s.Score)) + .ToList(); + + result.Add(new + { + SetupId = setup.Id, + setup.Name, + Task = task, + StudentCount = records.Count, + AverageTotal = totalAvg.Count > 0 + ? Math.Round(totalAvg.Average(), 1) : 0, + Dimensions = dimensionResults + }); + } + } + + return Ok(new { Tasks = result }); + } + + [HttpGet("setups/{id:guid}/results")] + [Authorize(Roles = StaffRoles)] + public async Task GetSetupResults( + Guid id, + CancellationToken cancellationToken) + { + var setup = await db.EvaluationSetups.AsNoTracking() + .Include(x => x.Dimensions.OrderBy(d => d.SortOrder)) + .Include(x => x.AcademicTerm) + .FirstOrDefaultAsync(x => x.Id == id, cancellationToken); + if (setup is null) return NotFound(); + + var records = await db.EvaluationRecords.AsNoTracking() + .Where(r => r.EvaluationSetupId == id) + .Include(r => r.Scores) + .Include(r => r.TeachingTask) + .ThenInclude(t => t!.Course) + .ThenInclude(c => c!.College) + .ToListAsync(cancellationToken); + + // By teaching task + var byTask = records + .GroupBy(r => new + { + r.TeachingTaskId, + r.TeachingTask!.TaskNumber, + TaskName = r.TeachingTask.Name, + CourseName = r.TeachingTask.Course!.Name, + CollegeName = r.TeachingTask.Course.College!.Name, + TeacherNames = string.Join("、", + r.TeachingTask.Teachers + .OrderByDescending(t => t.IsPrimary) + .Select(t => t.Teacher!.Name)) + }) + .Select(g => + { + var dimAvgs = setup.Dimensions.Select(dim => + { + var scores = g + .SelectMany(r => r.Scores) + .Where(s => s.EvaluationDimensionId == dim.Id) + .Select(s => (double)s.Score) + .ToList(); + return new + { + DimensionName = dim.Name, + AverageScore = scores.Count > 0 + ? Math.Round(scores.Average(), 1) : 0, + Count = scores.Count + }; + }).ToList(); + + var totals = g.Select(r => (double)r.Scores.Sum(s => s.Score)).ToList(); + return new + { + g.Key.TeachingTaskId, + g.Key.TaskNumber, + g.Key.TaskName, + g.Key.CourseName, + g.Key.CollegeName, + g.Key.TeacherNames, + StudentCount = g.Count(), + AverageTotal = totals.Count > 0 + ? Math.Round(totals.Average(), 1) : 0, + Dimensions = dimAvgs + }; + }) + .OrderByDescending(x => x.AverageTotal) + .ToList(); + + return Ok(new + { + Setup = new + { + setup.Id, setup.Name, setup.Status, + TermName = setup.AcademicTerm!.Name, + setup.StartsAt, setup.EndsAt, + Dimensions = setup.Dimensions.Select(d => new + { + d.Id, d.Name, d.MaxScore, d.SortOrder + }) + }, + ByTask = byTask, + TotalRecords = records.Count, + DistinctTasks = byTask.Count, + DistinctStudents = records.Select(r => r.StudentId).Distinct().Count() + }); + } + + // ── Helpers ────────────────────────────────────────────────────── + + private ActionResult ConflictProblem(string detail) => + Conflict(new ProblemDetails + { + Title = "无法完成评教操作", + Detail = detail, + Status = StatusCodes.Status409Conflict + }); +} + +public sealed record EvaluationSetupRequest( + Guid AcademicTermId, + [MaxLength(120)] string Name, + DateTime? StartsAt, + DateTime? EndsAt, + IReadOnlyCollection Dimensions); + +public sealed record EvaluationDimensionRequest( + [MaxLength(60)] string Name, + int MaxScore); + +public sealed record SubmitEvaluationRequest( + Guid SetupId, + IReadOnlyCollection Scores); + +public sealed record EvaluationScoreRequest( + Guid DimensionId, + int Score); diff --git a/src/Jiaowu.Api/Domain/Academic/EvaluationEntities.cs b/src/Jiaowu.Api/Domain/Academic/EvaluationEntities.cs new file mode 100644 index 0000000..7c57e20 --- /dev/null +++ b/src/Jiaowu.Api/Domain/Academic/EvaluationEntities.cs @@ -0,0 +1,84 @@ +using Jiaowu.Api.Domain.Common; + +namespace Jiaowu.Api.Domain.Academic; + +/// +/// 评教方案 — 每个学期可以有一个评教方案,包含多个评分维度。 +/// +public sealed class EvaluationSetup : EntityBase +{ + public Guid AcademicTermId { get; set; } + public AcademicTerm AcademicTerm { get; set; } = null!; + + /// 方案名称,如"2026年秋季学期学生评教" + public string Name { get; set; } = string.Empty; + + /// 状态:Draft / Open / Closed + public EvaluationSetupStatus Status { get; set; } = EvaluationSetupStatus.Draft; + + public DateTime? StartsAt { get; set; } + public DateTime? EndsAt { get; set; } + + public ICollection Dimensions { get; set; } = new List(); + public ICollection Records { get; set; } = new List(); +} + +public enum EvaluationSetupStatus +{ + Draft = 0, + Open = 1, + Closed = 2 +} + +/// +/// 评分维度 — 例如"教学内容 30分"、"教学方法 30分"等。 +/// +public sealed class EvaluationDimension : EntityBase +{ + public Guid EvaluationSetupId { get; set; } + public EvaluationSetup EvaluationSetup { get; set; } = null!; + + /// 维度名称 + public string Name { get; set; } = string.Empty; + + /// 该维度满分 + public int MaxScore { get; set; } + + /// 排序 + public int SortOrder { get; set; } + + public ICollection Scores { get; set; } = new List(); +} + +/// +/// 学生评教记录 — 一个学生对一个教学班的评价。 +/// +public sealed class EvaluationRecord : EntityBase +{ + public Guid EvaluationSetupId { get; set; } + public EvaluationSetup EvaluationSetup { get; set; } = null!; + + public Guid StudentId { get; set; } + public Student Student { get; set; } = null!; + + public Guid TeachingTaskId { get; set; } + public TeachingTask TeachingTask { get; set; } = null!; + + public DateTime? SubmittedAt { get; set; } + + public ICollection Scores { get; set; } = new List(); +} + +/// +/// 评分明细 — 每个维度一条记录。 +/// +public sealed class EvaluationScore +{ + public Guid EvaluationRecordId { get; set; } + public EvaluationRecord EvaluationRecord { get; set; } = null!; + + public Guid EvaluationDimensionId { get; set; } + public EvaluationDimension EvaluationDimension { get; set; } = null!; + + public int Score { get; set; } +} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContext.cs b/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContext.cs index 21729fc..731a670 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContext.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContext.cs @@ -62,6 +62,10 @@ public sealed class AppDbContext(DbContextOptions options) public DbSet CourseSubstitutions => Set(); public DbSet WarningRules => Set(); public DbSet WarningRecords => Set(); + public DbSet EvaluationSetups => Set(); + public DbSet EvaluationDimensions => Set(); + public DbSet EvaluationRecords => Set(); + public DbSet EvaluationScores => Set(); public DbSet Notifications => Set(); public DbSet StudentStatusChanges => Set(); @@ -731,6 +735,43 @@ public sealed class AppDbContext(DbContextOptions options) .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); }); + // ── Evaluation ── + builder.Entity(entity => + { + entity.Property(x => x.Name).HasMaxLength(120); + entity.HasIndex(x => new { x.AcademicTermId, x.Status }); + entity.HasOne(x => x.AcademicTerm).WithMany() + .HasForeignKey(x => x.AcademicTermId).OnDelete(DeleteBehavior.Restrict); + }); + + builder.Entity(entity => + { + entity.Property(x => x.Name).HasMaxLength(60); + entity.HasIndex(x => new { x.EvaluationSetupId, x.SortOrder }); + entity.HasOne(x => x.EvaluationSetup).WithMany(x => x.Dimensions) + .HasForeignKey(x => x.EvaluationSetupId).OnDelete(DeleteBehavior.Cascade); + }); + + builder.Entity(entity => + { + entity.HasIndex(x => new { x.EvaluationSetupId, x.StudentId, x.TeachingTaskId }).IsUnique(); + entity.HasOne(x => x.EvaluationSetup).WithMany(x => x.Records) + .HasForeignKey(x => x.EvaluationSetupId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(x => x.Student).WithMany() + .HasForeignKey(x => x.StudentId).OnDelete(DeleteBehavior.Restrict); + entity.HasOne(x => x.TeachingTask).WithMany() + .HasForeignKey(x => x.TeachingTaskId).OnDelete(DeleteBehavior.Restrict); + }); + + builder.Entity(entity => + { + entity.HasKey(x => new { x.EvaluationRecordId, x.EvaluationDimensionId }); + entity.HasOne(x => x.EvaluationRecord).WithMany(x => x.Scores) + .HasForeignKey(x => x.EvaluationRecordId).OnDelete(DeleteBehavior.Cascade); + entity.HasOne(x => x.EvaluationDimension).WithMany(x => x.Scores) + .HasForeignKey(x => x.EvaluationDimensionId).OnDelete(DeleteBehavior.Restrict); + }); + builder.Entity(entity => { entity.Property(x => x.Reason).HasMaxLength(500); diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentSqliteMigrator.cs b/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentSqliteMigrator.cs index 2ca4e51..409ebe0 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentSqliteMigrator.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentSqliteMigrator.cs @@ -290,6 +290,14 @@ public sealed class DevelopmentSqliteMigrator( AcademicWarningsMigration, warningRulesExist ? [] : AcademicWarningStatements, cancellationToken); + + var evaluationSetupsExist = await db.Database + .SqlQueryRaw("SELECT COUNT(*) AS \"Value\" FROM sqlite_master WHERE type = 'table' AND name = 'EvaluationSetups'") + .AnyAsync(value => value > 0, cancellationToken); + await ApplyMigrationAsync( + TeachingEvaluationMigration, + evaluationSetupsExist ? [] : TeachingEvaluationStatements, + cancellationToken); } private async Task ApplyMigrationAsync( @@ -1630,4 +1638,17 @@ public sealed class DevelopmentSqliteMigrator( """ALTER TABLE "WarningRules" ADD COLUMN "CheckMinute" INTEGER NOT NULL DEFAULT 0;""", """ALTER TABLE "WarningRules" ADD COLUMN "LastCheckAt" TEXT NULL;""" ]; + + private const string TeachingEvaluationMigration = "TeachingEvaluation"; + + private static readonly string[] TeachingEvaluationStatements = + [ + """CREATE TABLE "EvaluationSetups" ("Id" TEXT NOT NULL, "CreatedAt" TEXT NOT NULL, "UpdatedAt" TEXT NOT NULL, "AcademicTermId" TEXT NOT NULL, "Name" TEXT NOT NULL, "Status" INTEGER NOT NULL, "StartsAt" TEXT NULL, "EndsAt" TEXT NULL, CONSTRAINT "PK_EvaluationSetups" PRIMARY KEY ("Id"), CONSTRAINT "FK_EvaluationSetups_AcademicTerms_AcademicTermId" FOREIGN KEY ("AcademicTermId") REFERENCES "AcademicTerms" ("Id") ON DELETE RESTRICT);""", + """CREATE INDEX "IX_EvaluationSetups_AcademicTermId_Status" ON "EvaluationSetups" ("AcademicTermId", "Status");""", + """CREATE TABLE "EvaluationDimensions" ("Id" TEXT NOT NULL, "CreatedAt" TEXT NOT NULL, "UpdatedAt" TEXT NOT NULL, "EvaluationSetupId" TEXT NOT NULL, "Name" TEXT NOT NULL, "MaxScore" INTEGER NOT NULL, "SortOrder" INTEGER NOT NULL, CONSTRAINT "PK_EvaluationDimensions" PRIMARY KEY ("Id"), CONSTRAINT "FK_EvaluationDimensions_EvaluationSetups_EvaluationSetupId" FOREIGN KEY ("EvaluationSetupId") REFERENCES "EvaluationSetups" ("Id") ON DELETE CASCADE);""", + """CREATE INDEX "IX_EvaluationDimensions_EvaluationSetupId_SortOrder" ON "EvaluationDimensions" ("EvaluationSetupId", "SortOrder");""", + """CREATE TABLE "EvaluationRecords" ("Id" TEXT NOT NULL, "CreatedAt" TEXT NOT NULL, "UpdatedAt" TEXT NOT NULL, "EvaluationSetupId" TEXT NOT NULL, "StudentId" TEXT NOT NULL, "TeachingTaskId" TEXT NOT NULL, "SubmittedAt" TEXT NULL, CONSTRAINT "PK_EvaluationRecords" PRIMARY KEY ("Id"), CONSTRAINT "FK_EvaluationRecords_EvaluationSetups_EvaluationSetupId" FOREIGN KEY ("EvaluationSetupId") REFERENCES "EvaluationSetups" ("Id") ON DELETE RESTRICT, CONSTRAINT "FK_EvaluationRecords_Students_StudentId" FOREIGN KEY ("StudentId") REFERENCES "Students" ("Id") ON DELETE RESTRICT, CONSTRAINT "FK_EvaluationRecords_TeachingTasks_TeachingTaskId" FOREIGN KEY ("TeachingTaskId") REFERENCES "TeachingTasks" ("Id") ON DELETE RESTRICT);""", + """CREATE UNIQUE INDEX "IX_EvaluationRecords_SetupId_StudentId_TaskId" ON "EvaluationRecords" ("EvaluationSetupId", "StudentId", "TeachingTaskId");""", + """CREATE TABLE "EvaluationScores" ("EvaluationRecordId" TEXT NOT NULL, "EvaluationDimensionId" TEXT NOT NULL, "Score" INTEGER NOT NULL, CONSTRAINT "PK_EvaluationScores" PRIMARY KEY ("EvaluationRecordId", "EvaluationDimensionId"), CONSTRAINT "FK_EvaluationScores_EvaluationRecords_EvaluationRecordId" FOREIGN KEY ("EvaluationRecordId") REFERENCES "EvaluationRecords" ("Id") ON DELETE CASCADE, CONSTRAINT "FK_EvaluationScores_EvaluationDimensions_EvaluationDimensionId" FOREIGN KEY ("EvaluationDimensionId") REFERENCES "EvaluationDimensions" ("Id") ON DELETE RESTRICT);""", + ]; } diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs new file mode 100644 index 0000000..35cd4bc --- /dev/null +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs @@ -0,0 +1,3968 @@ +// +using System; +using Jiaowu.Api.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql +{ + [DbContext(typeof(AppDbContext))] + [Migration("20260725103117_TeachingEvaluation")] + partial class TeachingEvaluation + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AcademicTerm", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicYear") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndDate") + .HasColumnType("TEXT"); + + b.Property("IsCurrent") + .HasColumnType("INTEGER"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Season") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("StartDate") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsCurrent"); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("AcademicTerms"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CounselorName") + .HasColumnType("TEXT"); + + b.Property("CounselorUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Grade") + .HasColumnType("INTEGER"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("MajorId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CounselorUserId"); + + b.HasIndex("MajorId"); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("AdministrativeClasses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => + { + b.Property("AttendanceSheetId") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("AppealReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("AppealReviewComment") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("AppealReviewedAt") + .HasColumnType("TEXT"); + + b.Property("AppealStatus") + .HasColumnType("INTEGER"); + + b.Property("AppealSubmittedAt") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("AttendanceSheetId", "StudentId"); + + b.HasIndex("AppealStatus"); + + b.HasIndex("StudentId"); + + b.ToTable("AttendanceRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttendanceDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId", "AttendanceDate"); + + b.ToTable("AttendanceSheets"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AutomaticScheduleJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ActiveSchedulePlanId") + .HasColumnType("TEXT"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CompletedTasks") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedEntries") + .HasColumnType("INTEGER"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("MessagesJson") + .HasColumnType("TEXT"); + + b.Property("ProcessedTasks") + .HasColumnType("INTEGER"); + + b.Property("RequestedByUserId") + .HasColumnType("TEXT"); + + b.Property("SchedulePlanId") + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TotalTasks") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ActiveSchedulePlanId") + .IsUnique(); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("SchedulePlanId", "CreatedAt"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("AutomaticScheduleJobs"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Building", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampusId") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Buildings"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Campus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Address") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Campuses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Classroom", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("BuildingId") + .HasColumnType("TEXT"); + + b.Property("Capacity") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Equipment") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RoomType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BuildingId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Classrooms"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.College", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CampusId") + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("ShortName") + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CampusId"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Colleges"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AssessmentMethod") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CollegeId") + .HasColumnType("TEXT"); + + b.Property("CourseCategoryId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Credits") + .HasPrecision(5, 2) + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("EnglishName") + .HasMaxLength(150) + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LectureHours") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Nature") + .HasColumnType("INTEGER"); + + b.Property("PracticeHours") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("TotalHours") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CourseCategoryId"); + + b.HasIndex("CollegeId", "Nature"); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseAdjustment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicantUserId") + .HasColumnType("TEXT"); + + b.Property("CancelDate") + .HasColumnType("TEXT"); + + b.Property("CancelWeek") + .HasColumnType("INTEGER"); + + b.Property("ClassroomId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("PeriodCount") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("StartPeriod") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("SubstituteTeacherId") + .HasColumnType("TEXT"); + + b.Property("TargetDate") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantUserId"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("SubstituteTeacherId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("TeachingTaskId", "Status"); + + b.ToTable("CourseAdjustments"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseCategory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("CourseCategories"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CourseSelectionOfferingId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EnrolledAt") + .HasColumnType("TEXT"); + + b.Property("EnrollmentType") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WithdrawnAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CourseSelectionOfferingId", "StudentId") + .IsUnique(); + + b.HasIndex("StudentId", "Status"); + + b.ToTable("CourseEnrollments"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseExemption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("CourseExemptions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Capacity") + .HasColumnType("INTEGER"); + + b.Property("CourseSelectionRoundId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsOpenToAll") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("CourseSelectionRoundId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("CourseSelectionOfferings"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionRound", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndsAt") + .HasColumnType("TEXT"); + + b.Property("MaxCredits") + .HasPrecision(6, 1) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("StartsAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WithdrawalEndsAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("CourseSelectionRounds"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSubstitution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("OriginalCourseId") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("SubstituteCourseId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OriginalCourseId"); + + b.HasIndex("SubstituteCourseId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "OriginalCourseId") + .IsUnique(); + + b.ToTable("CourseSubstitutions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumCourse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CourseId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurriculumModuleId") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RecommendedSemester") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("CurriculumModuleId", "CourseId") + .IsUnique(); + + b.ToTable("CurriculumCourses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumModule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurriculumPlanId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("RequiredCredits") + .HasPrecision(6, 2) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CurriculumPlanId", "Code") + .IsUnique(); + + b.ToTable("CurriculumModules"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("EffectiveGrade") + .HasColumnType("INTEGER"); + + b.Property("MajorId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TotalCredits") + .HasPrecision(6, 2) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Status", "EffectiveGrade"); + + b.HasIndex("MajorId", "EffectiveGrade", "Version") + .IsUnique(); + + b.ToTable("CurriculumPlans"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DeferredExam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("DeferredExams"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalculatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DegreeName") + .IsRequired() + .HasMaxLength(80) + .HasColumnType("TEXT"); + + b.Property("GraduationYear") + .HasColumnType("INTEGER"); + + b.Property("MinimumGradePoint") + .HasPrecision(3, 2) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GraduationYear", "Status"); + + b.ToTable("DegreeAwardBatches"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AverageGradePoint") + .HasPrecision(4, 2) + .HasColumnType("TEXT"); + + b.Property("CalculatedConclusion") + .HasColumnType("INTEGER"); + + b.Property("Conclusion") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DegreeAwardBatchId") + .HasColumnType("TEXT"); + + b.Property("ExceptionReason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("GraduationAuditResultId") + .HasColumnType("TEXT"); + + b.Property("IsOverridden") + .HasColumnType("INTEGER"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GraduationAuditResultId"); + + b.HasIndex("StudentId"); + + b.HasIndex("Conclusion", "IsOverridden"); + + b.HasIndex("DegreeAwardBatchId", "StudentId") + .IsUnique(); + + b.ToTable("DegreeAwardResults"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EvaluationSetupId") + .HasColumnType("TEXT"); + + b.Property("MaxScore") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EvaluationSetupId", "SortOrder"); + + b.ToTable("EvaluationDimensions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EvaluationSetupId") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StudentId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("EvaluationSetupId", "StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("EvaluationRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => + { + b.Property("EvaluationRecordId") + .HasColumnType("TEXT"); + + b.Property("EvaluationDimensionId") + .HasColumnType("TEXT"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.HasKey("EvaluationRecordId", "EvaluationDimensionId"); + + b.HasIndex("EvaluationDimensionId"); + + b.ToTable("EvaluationScores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndsAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("StartsAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("EvaluationSetups"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("ExamPlans"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ClassroomId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndsAt") + .HasColumnType("TEXT"); + + b.Property("ExamDate") + .HasColumnType("TEXT"); + + b.Property("ExamPlanId") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PeriodCount") + .HasColumnType("INTEGER"); + + b.Property("RequiredBuildingId") + .HasColumnType("TEXT"); + + b.Property("RequiredInvigilatorCount") + .HasColumnType("INTEGER"); + + b.Property("StartPeriod") + .HasColumnType("INTEGER"); + + b.Property("StartsAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("RequiredBuildingId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("ExamPlanId", "ExamDate"); + + b.HasIndex("ExamPlanId", "StartsAt"); + + b.ToTable("ExamSessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSessionInvigilator", b => + { + b.Property("ExamSessionId") + .HasColumnType("TEXT"); + + b.Property("TeacherId") + .HasColumnType("TEXT"); + + b.HasKey("ExamSessionId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("ExamSessionInvigilators"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GradeSheetId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Weight") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GradeSheetId", "SortOrder"); + + b.ToTable("GradeItems"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => + { + b.Property("GradeRecordId") + .HasColumnType("TEXT"); + + b.Property("GradeItemId") + .HasColumnType("TEXT"); + + b.Property("Score") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.HasKey("GradeRecordId", "GradeItemId"); + + b.HasIndex("GradeItemId"); + + b.ToTable("GradeItemScores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeModification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicantUserId") + .HasColumnType("TEXT"); + + b.Property("CollegeReviewedAt") + .HasColumnType("TEXT"); + + b.Property("CollegeReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurrentScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("FinalReviewedAt") + .HasColumnType("TEXT"); + + b.Property("FinalReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("GradeRecordId") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RequestedScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GradeRecordId"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("GradeModifications"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ExamStatus") + .HasColumnType("INTEGER"); + + b.Property("FinalScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("GradePoint") + .HasPrecision(3, 1) + .HasColumnType("TEXT"); + + b.Property("GradeSheetId") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("RegularScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("TotalScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GradeSheetId", "StudentId") + .IsUnique(); + + b.HasIndex("StudentId", "TotalScore"); + + b.ToTable("GradeRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeSheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("FinalWeight") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("RegularWeight") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("TeachingTaskId") + .IsUnique(); + + b.ToTable("GradeSheets"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationAuditBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalculatedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EnrollmentYear") + .HasColumnType("INTEGER"); + + b.Property("GraduationYear") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("GraduationYear", "EnrollmentYear"); + + b.ToTable("GraduationAuditBatches"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationAuditResult", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CalculatedConclusion") + .HasColumnType("INTEGER"); + + b.Property("Conclusion") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurriculumPlanId") + .HasColumnType("TEXT"); + + b.Property("EarnedCredits") + .HasPrecision(6, 2) + .HasColumnType("TEXT"); + + b.Property("FailedCourseCount") + .HasColumnType("INTEGER"); + + b.Property("GraduationAuditBatchId") + .HasColumnType("TEXT"); + + b.Property("IsOverridden") + .HasColumnType("INTEGER"); + + b.Property("MissingCourseNames") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("PassedRequiredCourseCount") + .HasColumnType("INTEGER"); + + b.Property("RequiredCourseCount") + .HasColumnType("INTEGER"); + + b.Property("RequiredCredits") + .HasPrecision(6, 2) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("StudentStatusSnapshot") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CurriculumPlanId"); + + b.HasIndex("StudentId"); + + b.HasIndex("Conclusion", "IsOverridden"); + + b.HasIndex("GraduationAuditBatchId", "StudentId") + .IsUnique(); + + b.ToTable("GraduationAuditResults"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceBatch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ClosedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GraduationYear") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GraduationYear", "Status"); + + b.ToTable("GraduationClearanceBatches"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GraduationClearanceBatchId") + .HasColumnType("TEXT"); + + b.Property("IsRequired") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("ResponsibleRole") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("ResponsibleUnit") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GraduationClearanceBatchId", "Code") + .IsUnique(); + + b.ToTable("GraduationClearanceItems"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CompletedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GraduationClearanceItemId") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GraduationClearanceItemId", "StudentId") + .IsUnique(); + + b.HasIndex("StudentId", "Status"); + + b.ToTable("GraduationClearanceRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("CollegeId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DegreeType") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("SchoolingYears") + .HasColumnType("INTEGER"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("CollegeId"); + + b.HasIndex("IsEnabled", "SortOrder"); + + b.ToTable("Majors"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("LinkUrl") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId", "IsRead"); + + b.ToTable("Notifications"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleEntry", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ClassroomId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndWeek") + .HasColumnType("INTEGER"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PeriodCount") + .HasColumnType("INTEGER"); + + b.Property("SchedulePlanId") + .HasColumnType("TEXT"); + + b.Property("StartPeriod") + .HasColumnType("INTEGER"); + + b.Property("StartWeek") + .HasColumnType("INTEGER"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WeekPattern") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("SchedulePlanId", "DayOfWeek", "StartPeriod"); + + b.ToTable("ScheduleEntries"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Version") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.HasIndex("AcademicTermId", "Version") + .IsUnique(); + + b.ToTable("SchedulePlans"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePublishJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("ActiveAcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CompletedAt") + .HasColumnType("TEXT"); + + b.Property("CompletedSteps") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurrentStep") + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("TEXT"); + + b.Property("RequestedByUserId") + .HasColumnType("TEXT"); + + b.Property("SchedulePlanId") + .HasColumnType("TEXT"); + + b.Property("StartedAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TotalSteps") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ActiveAcademicTermId") + .IsUnique(); + + b.HasIndex("RequestedByUserId"); + + b.HasIndex("SchedulePlanId", "CreatedAt"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("SchedulePublishJobs"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleTimeSlot", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndsAt") + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("PeriodNumber") + .HasColumnType("INTEGER"); + + b.Property("StartsAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "PeriodNumber") + .IsUnique(); + + b.ToTable("ScheduleTimeSlots"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AdministrativeClassId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DateOfBirth") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("EnrollmentDate") + .HasColumnType("TEXT"); + + b.Property("EnrollmentYear") + .HasColumnType("INTEGER"); + + b.Property("Gender") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentNumber") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EnrollmentYear"); + + b.HasIndex("StudentNumber") + .IsUnique(); + + b.HasIndex("UserId"); + + b.HasIndex("AdministrativeClassId", "Status"); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.StudentStatusChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApprovedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("OriginalStatus") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("State") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TargetStatus") + .HasColumnType("INTEGER"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StudentId", "State"); + + b.ToTable("StudentStatusChanges"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CollegeId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Gender") + .HasColumnType("INTEGER"); + + b.Property("HireDate") + .HasColumnType("TEXT"); + + b.Property("IsExternal") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Phone") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TeacherNumber") + .IsRequired() + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("Title") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeacherNumber") + .IsUnique(); + + b.HasIndex("UserId"); + + b.HasIndex("CollegeId", "Status"); + + b.ToTable("Teachers"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeacherCourseApplication", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CourseId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Statement") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeacherId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("ReviewedByUserId"); + + b.HasIndex("TeacherId"); + + b.HasIndex("Status", "AcademicTermId"); + + b.HasIndex("AcademicTermId", "TeacherId", "CourseId") + .IsUnique(); + + b.ToTable("TeacherCourseApplications"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTask", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("Capacity") + .HasColumnType("INTEGER"); + + b.Property("CourseId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndWeek") + .HasColumnType("INTEGER"); + + b.Property("GenerationBatchCode") + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("PublishedAt") + .HasColumnType("TEXT"); + + b.Property("SchedulingMode") + .HasColumnType("INTEGER"); + + b.Property("StartWeek") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("TaskNumber") + .IsRequired() + .HasMaxLength(40) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("WeeklyHours") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("CourseId"); + + b.HasIndex("TaskNumber") + .IsUnique(); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("TeachingTasks"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskAllowedClassroom", b => + { + b.Property("TeachingTaskScheduleConstraintId") + .HasColumnType("TEXT"); + + b.Property("ClassroomId") + .HasColumnType("TEXT"); + + b.HasKey("TeachingTaskScheduleConstraintId", "ClassroomId"); + + b.HasIndex("ClassroomId"); + + b.ToTable("TeachingTaskAllowedClassrooms"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskClass", b => + { + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("AdministrativeClassId") + .HasColumnType("TEXT"); + + b.HasKey("TeachingTaskId", "AdministrativeClassId"); + + b.HasIndex("AdministrativeClassId"); + + b.ToTable("TeachingTaskClasses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskScheduleConstraint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AllowedDayOfWeeks") + .HasMaxLength(20) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EarliestPeriod") + .HasColumnType("INTEGER"); + + b.Property("LatestPeriod") + .HasColumnType("INTEGER"); + + b.Property("RequiredBuildingId") + .HasColumnType("TEXT"); + + b.Property("RequiredCampusId") + .HasColumnType("TEXT"); + + b.Property("RequiresClassroom") + .HasColumnType("INTEGER"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RequiredBuildingId"); + + b.HasIndex("RequiredCampusId"); + + b.HasIndex("TeachingTaskId") + .IsUnique(); + + b.ToTable("TeachingTaskScheduleConstraints"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskTeacher", b => + { + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("TeacherId") + .HasColumnType("TEXT"); + + b.Property("IsPrimary") + .HasColumnType("INTEGER"); + + b.HasKey("TeachingTaskId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("TeachingTaskTeachers"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("AcknowledgeComment") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("AcknowledgedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Detail") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("TriggerValue") + .HasPrecision(7, 2) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("StudentId", "AcademicTermId", "Type") + .IsUnique(); + + b.ToTable("WarningRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("AutoCheckEnabled") + .HasColumnType("INTEGER"); + + b.Property("CheckDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("CheckHour") + .HasColumnType("INTEGER"); + + b.Property("CheckMinute") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LastCheckAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("NotifyCounselor") + .HasColumnType("INTEGER"); + + b.Property("NotifyStudent") + .HasColumnType("INTEGER"); + + b.Property("Threshold") + .HasPrecision(7, 2) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Type") + .IsUnique(); + + b.ToTable("WarningRules"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("DataScope") + .HasColumnType("INTEGER"); + + b.Property("Description") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AccessFailedCount") + .HasColumnType("INTEGER"); + + b.Property("CollegeId") + .HasColumnType("TEXT"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("TEXT"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("EmailConfirmed") + .HasColumnType("INTEGER"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LastLoginAt") + .HasColumnType("TEXT"); + + b.Property("LockoutEnabled") + .HasColumnType("INTEGER"); + + b.Property("LockoutEnd") + .HasColumnType("TEXT"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.Property("PasswordHash") + .HasColumnType("TEXT"); + + b.Property("PhoneNumber") + .HasColumnType("TEXT"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("INTEGER"); + + b.Property("SecurityStamp") + .HasColumnType("TEXT"); + + b.Property("StaffNumber") + .HasMaxLength(30) + .HasColumnType("TEXT"); + + b.Property("TwoFactorEnabled") + .HasColumnType("INTEGER"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.HasIndex("StaffNumber"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.System.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IpAddress") + .HasMaxLength(64) + .HasColumnType("TEXT"); + + b.Property("Method") + .IsRequired() + .HasMaxLength(10) + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("StatusCode") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("UserName") + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.ToTable("AuditLogs"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("ClaimType") + .HasColumnType("TEXT"); + + b.Property("ClaimValue") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("ProviderKey") + .HasColumnType("TEXT"); + + b.Property("ProviderDisplayName") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("RoleId") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("TEXT"); + + b.Property("LoginProvider") + .HasColumnType("TEXT"); + + b.Property("Name") + .HasColumnType("TEXT"); + + b.Property("Value") + .HasColumnType("TEXT"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + 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"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AttendanceSheet", "AttendanceSheet") + .WithMany("Records") + .HasForeignKey("AttendanceSheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceSheet"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", 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.AutomaticScheduleJob", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.SchedulePlan", "SchedulePlan") + .WithMany() + .HasForeignKey("SchedulePlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SchedulePlan"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Building", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Campus", "Campus") + .WithMany() + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Classroom", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Building", "Building") + .WithMany() + .HasForeignKey("BuildingId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Building"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.College", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Campus", "Campus") + .WithMany() + .HasForeignKey("CampusId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("Campus"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Course", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.College", "College") + .WithMany() + .HasForeignKey("CollegeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.CourseCategory", "CourseCategory") + .WithMany() + .HasForeignKey("CourseCategoryId") + .OnDelete(DeleteBehavior.Restrict); + + b.Navigation("College"); + + b.Navigation("CourseCategory"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseAdjustment", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "SubstituteTeacher") + .WithMany() + .HasForeignKey("SubstituteTeacherId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Classroom"); + + b.Navigation("SubstituteTeacher"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseEnrollment", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", "CourseSelectionOffering") + .WithMany("Enrollments") + .HasForeignKey("CourseSelectionOfferingId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CourseSelectionOffering"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseExemption", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.CourseSelectionRound", "CourseSelectionRound") + .WithMany("Offerings") + .HasForeignKey("CourseSelectionRoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CourseSelectionRound"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionRound", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSubstitution", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "OriginalCourse") + .WithMany() + .HasForeignKey("OriginalCourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "SubstituteCourse") + .WithMany() + .HasForeignKey("SubstituteCourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OriginalCourse"); + + b.Navigation("Student"); + + b.Navigation("SubstituteCourse"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumCourse", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "Course") + .WithMany() + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.CurriculumModule", "CurriculumModule") + .WithMany("Courses") + .HasForeignKey("CurriculumModuleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Course"); + + b.Navigation("CurriculumModule"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumModule", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.CurriculumPlan", "CurriculumPlan") + .WithMany("Modules") + .HasForeignKey("CurriculumPlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("CurriculumPlan"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumPlan", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Major", "Major") + .WithMany() + .HasForeignKey("MajorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Major"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DeferredExam", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardResult", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.DegreeAwardBatch", "DegreeAwardBatch") + .WithMany("Results") + .HasForeignKey("DegreeAwardBatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.GraduationAuditResult", "GraduationAuditResult") + .WithMany() + .HasForeignKey("GraduationAuditResultId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("DegreeAwardBatch"); + + b.Navigation("GraduationAuditResult"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationSetup", "EvaluationSetup") + .WithMany("Dimensions") + .HasForeignKey("EvaluationSetupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EvaluationSetup"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationSetup", "EvaluationSetup") + .WithMany("Records") + .HasForeignKey("EvaluationSetupId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EvaluationSetup"); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationDimension", "EvaluationDimension") + .WithMany("Scores") + .HasForeignKey("EvaluationDimensionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationRecord", "EvaluationRecord") + .WithMany("Scores") + .HasForeignKey("EvaluationRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EvaluationDimension"); + + b.Navigation("EvaluationRecord"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSession", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.ExamPlan", "ExamPlan") + .WithMany("Sessions") + .HasForeignKey("ExamPlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Building", "RequiredBuilding") + .WithMany() + .HasForeignKey("RequiredBuildingId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Classroom"); + + b.Navigation("ExamPlan"); + + b.Navigation("RequiredBuilding"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSessionInvigilator", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.ExamSession", "ExamSession") + .WithMany("Invigilators") + .HasForeignKey("ExamSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "Teacher") + .WithMany() + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("ExamSession"); + + b.Navigation("Teacher"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeSheet", "GradeSheet") + .WithMany("Items") + .HasForeignKey("GradeSheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GradeSheet"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeItem", "GradeItem") + .WithMany("Scores") + .HasForeignKey("GradeItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "GradeRecord") + .WithMany("ItemScores") + .HasForeignKey("GradeRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GradeItem"); + + b.Navigation("GradeRecord"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeModification", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "GradeRecord") + .WithMany() + .HasForeignKey("GradeRecordId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GradeRecord"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeSheet", "GradeSheet") + .WithMany("Records") + .HasForeignKey("GradeSheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GradeSheet"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeSheet", 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.GraduationAuditResult", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.CurriculumPlan", "CurriculumPlan") + .WithMany() + .HasForeignKey("CurriculumPlanId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.GraduationAuditBatch", "GraduationAuditBatch") + .WithMany("Results") + .HasForeignKey("GraduationAuditBatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("CurriculumPlan"); + + b.Navigation("GraduationAuditBatch"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceItem", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GraduationClearanceBatch", "GraduationClearanceBatch") + .WithMany("Items") + .HasForeignKey("GraduationClearanceBatchId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GraduationClearanceBatch"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GraduationClearanceItem", "GraduationClearanceItem") + .WithMany("Records") + .HasForeignKey("GraduationClearanceItemId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GraduationClearanceItem"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Major", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.College", "College") + .WithMany() + .HasForeignKey("CollegeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("College"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleEntry", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.SchedulePlan", "SchedulePlan") + .WithMany("Entries") + .HasForeignKey("SchedulePlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Classroom"); + + b.Navigation("SchedulePlan"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePlan", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePublishJob", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("RequestedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.SchedulePlan", "SchedulePlan") + .WithMany() + .HasForeignKey("SchedulePlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("SchedulePlan"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleTimeSlot", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Student", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AdministrativeClass", "AdministrativeClass") + .WithMany("Students") + .HasForeignKey("AdministrativeClassId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("AdministrativeClass"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.StudentStatusChange", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Teacher", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.College", "College") + .WithMany() + .HasForeignKey("CollegeId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("College"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeacherCourseApplication", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "Course") + .WithMany() + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("ReviewedByUserId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "Teacher") + .WithMany() + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AcademicTerm"); + + b.Navigation("Course"); + + b.Navigation("Teacher"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTask", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "Course") + .WithMany() + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + + b.Navigation("Course"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskAllowedClassroom", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTaskScheduleConstraint", "TeachingTaskScheduleConstraint") + .WithMany("AllowedClassrooms") + .HasForeignKey("TeachingTaskScheduleConstraintId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Classroom"); + + b.Navigation("TeachingTaskScheduleConstraint"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskClass", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AdministrativeClass", "AdministrativeClass") + .WithMany() + .HasForeignKey("AdministrativeClassId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany("Classes") + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AdministrativeClass"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskScheduleConstraint", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Building", "RequiredBuilding") + .WithMany() + .HasForeignKey("RequiredBuildingId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Jiaowu.Api.Domain.Academic.Campus", "RequiredCampus") + .WithMany() + .HasForeignKey("RequiredCampusId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("RequiredBuilding"); + + b.Navigation("RequiredCampus"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskTeacher", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "Teacher") + .WithMany() + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany("Teachers") + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Teacher"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRule", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AdministrativeClass", b => + { + b.Navigation("Students"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", b => + { + b.Navigation("Records"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionRound", b => + { + b.Navigation("Offerings"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumModule", b => + { + b.Navigation("Courses"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumPlan", b => + { + b.Navigation("Modules"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardBatch", b => + { + b.Navigation("Results"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.Navigation("Dimensions"); + + b.Navigation("Records"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => + { + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSession", b => + { + b.Navigation("Invigilators"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => + { + b.Navigation("ItemScores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeSheet", b => + { + b.Navigation("Items"); + + b.Navigation("Records"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationAuditBatch", b => + { + b.Navigation("Results"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceBatch", b => + { + b.Navigation("Items"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GraduationClearanceItem", b => + { + b.Navigation("Records"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePlan", b => + { + b.Navigation("Entries"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTask", b => + { + b.Navigation("Classes"); + + b.Navigation("Teachers"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskScheduleConstraint", b => + { + b.Navigation("AllowedClassrooms"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs new file mode 100644 index 0000000..0d4eba8 --- /dev/null +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql +{ + /// + public partial class TeachingEvaluation : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs index ff80c8c..01455aa 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs @@ -15,53 +15,51 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 64); + modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AcademicTerm", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicYear") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EndDate") - .HasColumnType("date"); + .HasColumnType("TEXT"); b.Property("IsCurrent") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("Season") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StartDate") - .HasColumnType("date"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -79,41 +77,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CounselorName") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("CounselorUserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Grade") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("MajorId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -129,54 +127,135 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("AdministrativeClasses"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => + { + b.Property("AttendanceSheetId") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("AppealReason") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("AppealReviewComment") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("AppealReviewedAt") + .HasColumnType("TEXT"); + + b.Property("AppealStatus") + .HasColumnType("INTEGER"); + + b.Property("AppealSubmittedAt") + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.HasKey("AttendanceSheetId", "StudentId"); + + b.HasIndex("AppealStatus"); + + b.HasIndex("StudentId"); + + b.ToTable("AttendanceRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AttendanceDate") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId", "AttendanceDate"); + + b.ToTable("AttendanceSheets"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AutomaticScheduleJob", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ActiveSchedulePlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CompletedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CompletedTasks") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CreatedEntries") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("varchar(2000)"); + .HasColumnType("TEXT"); b.Property("MessagesJson") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ProcessedTasks") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("RequestedByUserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("SchedulePlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("StartedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TotalTasks") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -196,32 +275,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CampusId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -239,32 +318,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Address") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -280,42 +359,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("BuildingId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Capacity") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Equipment") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("RoomType") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -333,35 +412,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CampusId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("ShortName") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -379,62 +458,62 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AssessmentMethod") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CollegeId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CourseCategoryId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Credits") .HasPrecision(5, 2) - .HasColumnType("decimal(5,2)"); + .HasColumnType("TEXT"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); + .HasColumnType("TEXT"); b.Property("EnglishName") .HasMaxLength(150) - .HasColumnType("varchar(150)"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("LectureHours") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("Nature") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("PracticeHours") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TotalHours") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -450,33 +529,114 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("Courses"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseAdjustment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicantUserId") + .HasColumnType("TEXT"); + + b.Property("CancelDate") + .HasColumnType("TEXT"); + + b.Property("CancelWeek") + .HasColumnType("INTEGER"); + + b.Property("ClassroomId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("PeriodCount") + .HasColumnType("INTEGER"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("StartPeriod") + .HasColumnType("INTEGER"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("SubstituteTeacherId") + .HasColumnType("TEXT"); + + b.Property("TargetDate") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("ApplicantUserId"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("SubstituteTeacherId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("TeachingTaskId", "Status"); + + b.ToTable("CourseAdjustments"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseCategory", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -492,28 +652,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CourseSelectionOfferingId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EnrolledAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); + + b.Property("EnrollmentType") + .HasColumnType("INTEGER"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("WithdrawnAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -525,33 +688,84 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("CourseEnrollments"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseExemption", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("CourseExemptions"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Capacity") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CourseSelectionRoundId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IsOpenToAll") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -567,41 +781,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EndsAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("MaxCredits") .HasPrecision(6, 1) - .HasColumnType("decimal(6,1)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("StartsAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("WithdrawalEndsAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -610,33 +824,89 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("CourseSelectionRounds"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSubstitution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("OriginalCourseId") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("SubstituteCourseId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("OriginalCourseId"); + + b.HasIndex("SubstituteCourseId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "OriginalCourseId") + .IsUnique(); + + b.ToTable("CourseSubstitutions"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumCourse", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CourseId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CurriculumModuleId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("RecommendedSemester") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -652,33 +922,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CurriculumPlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("decimal(6,2)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -692,43 +962,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); + .HasColumnType("TEXT"); b.Property("EffectiveGrade") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("MajorId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TotalCredits") .HasPrecision(6, 2) - .HasColumnType("decimal(6,2)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -740,47 +1010,98 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("CurriculumPlans"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DeferredExam", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("ReviewedAt") + .HasColumnType("TEXT"); + + b.Property("ReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("Status", "CreatedAt"); + + b.HasIndex("StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("DeferredExams"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardBatch", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CalculatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DegreeName") .IsRequired() .HasMaxLength(80) - .HasColumnType("varchar(80)"); + .HasColumnType("TEXT"); b.Property("GraduationYear") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("MinimumGradePoint") .HasPrecision(3, 2) - .HasColumnType("decimal(3,2)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -793,47 +1114,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AverageGradePoint") .HasPrecision(4, 2) - .HasColumnType("decimal(4,2)"); + .HasColumnType("TEXT"); b.Property("CalculatedConclusion") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Conclusion") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DegreeAwardBatchId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ExceptionReason") .IsRequired() .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("GraduationAuditResultId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("IsOverridden") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("ReviewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -849,35 +1170,158 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("DegreeAwardResults"); }); - modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); - - b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); + + b.Property("EvaluationSetupId") + .HasColumnType("TEXT"); + + b.Property("MaxScore") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("EvaluationSetupId", "SortOrder"); + + b.ToTable("EvaluationDimensions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EvaluationSetupId") + .HasColumnType("TEXT"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("TeachingTaskId") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("StudentId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("EvaluationSetupId", "StudentId", "TeachingTaskId") + .IsUnique(); + + b.ToTable("EvaluationRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => + { + b.Property("EvaluationRecordId") + .HasColumnType("TEXT"); + + b.Property("EvaluationDimensionId") + .HasColumnType("TEXT"); + + b.Property("Score") + .HasColumnType("INTEGER"); + + b.HasKey("EvaluationRecordId", "EvaluationDimensionId"); + + b.HasIndex("EvaluationDimensionId"); + + b.ToTable("EvaluationScores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("EndsAt") + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); + + b.Property("StartsAt") + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("EvaluationSetups"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -890,39 +1334,58 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); - b.Property("ClassroomId") - .HasColumnType("char(36)"); + b.Property("ClassroomId") + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EndsAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); + + b.Property("ExamDate") + .HasColumnType("TEXT"); b.Property("ExamPlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); + + b.Property("PeriodCount") + .HasColumnType("INTEGER"); + + b.Property("RequiredBuildingId") + .HasColumnType("TEXT"); + + b.Property("RequiredInvigilatorCount") + .HasColumnType("INTEGER"); + + b.Property("StartPeriod") + .HasColumnType("INTEGER"); b.Property("StartsAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); b.HasIndex("ClassroomId"); + b.HasIndex("RequiredBuildingId"); + b.HasIndex("TeachingTaskId"); + b.HasIndex("ExamPlanId", "ExamDate"); + b.HasIndex("ExamPlanId", "StartsAt"); b.ToTable("ExamSessions"); @@ -931,10 +1394,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSessionInvigilator", b => { b.Property("ExamSessionId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("TeacherId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("ExamSessionId", "TeacherId"); @@ -943,50 +1406,161 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("ExamSessionInvigilators"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("GradeSheetId") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(60) + .HasColumnType("TEXT"); + + b.Property("SortOrder") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("Weight") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GradeSheetId", "SortOrder"); + + b.ToTable("GradeItems"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => + { + b.Property("GradeRecordId") + .HasColumnType("TEXT"); + + b.Property("GradeItemId") + .HasColumnType("TEXT"); + + b.Property("Score") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.HasKey("GradeRecordId", "GradeItemId"); + + b.HasIndex("GradeItemId"); + + b.ToTable("GradeItemScores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeModification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("ApplicantUserId") + .HasColumnType("TEXT"); + + b.Property("CollegeReviewedAt") + .HasColumnType("TEXT"); + + b.Property("CollegeReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("CurrentScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("FinalReviewedAt") + .HasColumnType("TEXT"); + + b.Property("FinalReviewedByUserId") + .HasColumnType("TEXT"); + + b.Property("GradeRecordId") + .HasColumnType("TEXT"); + + b.Property("Reason") + .IsRequired() + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("RequestedScore") + .HasPrecision(5, 1) + .HasColumnType("TEXT"); + + b.Property("ReviewComment") + .HasMaxLength(500) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("SubmittedAt") + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("GradeRecordId"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("GradeModifications"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("ExamStatus") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("FinalScore") .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("GradePoint") .HasPrecision(3, 1) - .HasColumnType("decimal(3,1)"); + .HasColumnType("TEXT"); b.Property("GradeSheetId") - .HasColumnType("char(36)"); - - b.Property("MidtermScore") - .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(300) - .HasColumnType("varchar(300)"); + .HasColumnType("TEXT"); b.Property("RegularScore") .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("TotalScore") .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1002,44 +1576,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("FinalWeight") .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); - - b.Property("MidtermWeight") - .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("RegularWeight") .HasPrecision(5, 1) - .HasColumnType("decimal(5,1)"); + .HasColumnType("TEXT"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("ReviewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SubmittedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1055,37 +1625,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CalculatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EnrollmentYear") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("GraduationYear") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1100,63 +1670,63 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CalculatedConclusion") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Conclusion") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CurriculumPlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("EarnedCredits") .HasPrecision(6, 2) - .HasColumnType("decimal(6,2)"); + .HasColumnType("TEXT"); b.Property("FailedCourseCount") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("GraduationAuditBatchId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("IsOverridden") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("MissingCourseNames") .IsRequired() .HasMaxLength(2000) - .HasColumnType("varchar(2000)"); + .HasColumnType("TEXT"); b.Property("PassedRequiredCourseCount") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("RequiredCourseCount") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("decimal(6,2)"); + .HasColumnType("TEXT"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("ReviewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("StudentStatusSnapshot") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1176,31 +1746,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ClosedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("GraduationYear") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1213,42 +1783,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("GraduationClearanceBatchId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("IsRequired") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("ResponsibleRole") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("ResponsibleUnit") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1262,32 +1832,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CompletedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CompletedByUserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("GraduationClearanceItemId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1303,39 +1873,39 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("CollegeId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DegreeType") .IsRequired() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("SchoolingYears") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SortOrder") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1349,48 +1919,89 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("Majors"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("Content") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("IsRead") + .HasColumnType("INTEGER"); + + b.Property("LinkUrl") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("TEXT"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("CreatedAt"); + + b.HasIndex("UserId", "IsRead"); + + b.ToTable("Notifications"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleEntry", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ClassroomId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DayOfWeek") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("EndWeek") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PeriodCount") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SchedulePlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("StartPeriod") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StartWeek") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("WeekPattern") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -1407,36 +2018,36 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1452,48 +2063,48 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ActiveAcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CompletedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CompletedSteps") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CurrentStep") .HasMaxLength(200) - .HasColumnType("varchar(200)"); + .HasColumnType("TEXT"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("varchar(2000)"); + .HasColumnType("TEXT"); b.Property("RequestedByUserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("SchedulePlanId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("StartedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TotalSteps") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1513,33 +2124,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EndsAt") - .HasColumnType("time"); + .HasColumnType("TEXT"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("PeriodNumber") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StartsAt") - .HasColumnType("time"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1553,56 +2164,56 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AdministrativeClassId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DateOfBirth") - .HasColumnType("date"); + .HasColumnType("TEXT"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("EnrollmentDate") - .HasColumnType("date"); + .HasColumnType("TEXT"); b.Property("EnrollmentYear") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Gender") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("varchar(50)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StudentNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1622,46 +2233,46 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ApprovedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("OriginalStatus") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Reason") .IsRequired() .HasMaxLength(1000) - .HasColumnType("varchar(1000)"); + .HasColumnType("TEXT"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("ReviewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("State") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StudentId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("SubmittedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("TargetStatus") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Type") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1674,57 +2285,57 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CollegeId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("Gender") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("HireDate") - .HasColumnType("date"); + .HasColumnType("TEXT"); b.Property("IsExternal") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("varchar(50)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TeacherNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("Title") .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1742,42 +2353,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CourseId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("ReviewedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("ReviewedByUserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Statement") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("SubmittedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("TeacherId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1799,58 +2410,58 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AcademicTermId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("Capacity") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CourseId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EndWeek") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("GenerationBatchCode") .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("varchar(120)"); + .HasColumnType("TEXT"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("varchar(500)"); + .HasColumnType("TEXT"); b.Property("PublishedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("SchedulingMode") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("StartWeek") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Status") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("TaskNumber") .IsRequired() .HasMaxLength(40) - .HasColumnType("varchar(40)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("WeeklyHours") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.HasKey("Id"); @@ -1867,10 +2478,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskAllowedClassroom", b => { b.Property("TeachingTaskScheduleConstraintId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ClassroomId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("TeachingTaskScheduleConstraintId", "ClassroomId"); @@ -1882,10 +2493,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskClass", b => { b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AdministrativeClassId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("TeachingTaskId", "AdministrativeClassId"); @@ -1898,35 +2509,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AllowedDayOfWeeks") .HasMaxLength(20) - .HasColumnType("varchar(20)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("EarliestPeriod") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("LatestPeriod") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("RequiredBuildingId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("RequiredCampusId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("RequiresClassroom") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1943,13 +2554,13 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskTeacher", b => { b.Property("TeachingTaskId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("TeacherId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("IsPrimary") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.HasKey("TeachingTaskId", "TeacherId"); @@ -1958,30 +2569,143 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("TeachingTaskTeachers"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("AcknowledgeComment") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("AcknowledgedAt") + .HasColumnType("TEXT"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Detail") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("TEXT"); + + b.Property("Status") + .HasColumnType("INTEGER"); + + b.Property("StudentId") + .HasColumnType("TEXT"); + + b.Property("TriggerValue") + .HasPrecision(7, 2) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Status"); + + b.HasIndex("StudentId", "AcademicTermId", "Type") + .IsUnique(); + + b.ToTable("WarningRecords"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AcademicTermId") + .HasColumnType("TEXT"); + + b.Property("AutoCheckEnabled") + .HasColumnType("INTEGER"); + + b.Property("CheckDayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("CheckHour") + .HasColumnType("INTEGER"); + + b.Property("CheckMinute") + .HasColumnType("INTEGER"); + + b.Property("CreatedAt") + .HasColumnType("TEXT"); + + b.Property("Description") + .HasMaxLength(300) + .HasColumnType("TEXT"); + + b.Property("IsEnabled") + .HasColumnType("INTEGER"); + + b.Property("LastCheckAt") + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(100) + .HasColumnType("TEXT"); + + b.Property("NotifyCounselor") + .HasColumnType("INTEGER"); + + b.Property("NotifyStudent") + .HasColumnType("INTEGER"); + + b.Property("Threshold") + .HasPrecision(7, 2) + .HasColumnType("TEXT"); + + b.Property("Type") + .HasColumnType("INTEGER"); + + b.Property("UpdatedAt") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Type") + .IsUnique(); + + b.ToTable("WarningRules"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Identity.ApplicationRole", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("DataScope") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("Description") .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.Property("Name") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.Property("NormalizedName") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -1996,75 +2720,75 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("AccessFailedCount") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("CollegeId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("DisplayName") .IsRequired() .HasMaxLength(50) - .HasColumnType("varchar(50)"); + .HasColumnType("TEXT"); b.Property("Email") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.Property("EmailConfirmed") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("IsEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("LastLoginAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("LockoutEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("LockoutEnd") - .HasColumnType("datetime"); + .HasColumnType("TEXT"); b.Property("NormalizedEmail") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.Property("NormalizedUserName") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.Property("PasswordHash") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("PhoneNumber") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("PhoneNumberConfirmed") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("SecurityStamp") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("StaffNumber") .HasMaxLength(30) - .HasColumnType("varchar(30)"); + .HasColumnType("TEXT"); b.Property("TwoFactorEnabled") - .HasColumnType("tinyint(1)"); + .HasColumnType("INTEGER"); b.Property("UserName") .HasMaxLength(256) - .HasColumnType("varchar(256)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -2084,37 +2808,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("CreatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("IpAddress") .HasMaxLength(64) - .HasColumnType("varchar(64)"); + .HasColumnType("TEXT"); b.Property("Method") .IsRequired() .HasMaxLength(10) - .HasColumnType("varchar(10)"); + .HasColumnType("TEXT"); b.Property("Path") .IsRequired() .HasMaxLength(300) - .HasColumnType("varchar(300)"); + .HasColumnType("TEXT"); b.Property("StatusCode") - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("UpdatedAt") - .HasColumnType("datetime(6)"); + .HasColumnType("TEXT"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("UserName") .HasMaxLength(100) - .HasColumnType("varchar(100)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -2127,16 +2851,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ClaimType") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ClaimValue") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("RoleId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -2149,16 +2873,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("int"); + .HasColumnType("INTEGER"); b.Property("ClaimType") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("ClaimValue") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("Id"); @@ -2170,16 +2894,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("varchar(255)"); + .HasColumnType("TEXT"); b.Property("ProviderKey") - .HasColumnType("varchar(255)"); + .HasColumnType("TEXT"); b.Property("ProviderDisplayName") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("LoginProvider", "ProviderKey"); @@ -2191,10 +2915,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("RoleId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.HasKey("UserId", "RoleId"); @@ -2206,16 +2930,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId") - .HasColumnType("char(36)"); + .HasColumnType("TEXT"); b.Property("LoginProvider") - .HasColumnType("varchar(255)"); + .HasColumnType("TEXT"); b.Property("Name") - .HasColumnType("varchar(255)"); + .HasColumnType("TEXT"); b.Property("Value") - .HasColumnType("longtext"); + .HasColumnType("TEXT"); b.HasKey("UserId", "LoginProvider", "Name"); @@ -2240,6 +2964,36 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Major"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AttendanceSheet", "AttendanceSheet") + .WithMany("Records") + .HasForeignKey("AttendanceSheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AttendanceSheet"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", 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.AutomaticScheduleJob", b => { b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationUser", null) @@ -2306,6 +3060,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("CourseCategory"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseAdjustment", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "SubstituteTeacher") + .WithMany() + .HasForeignKey("SubstituteTeacherId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Classroom"); + + b.Navigation("SubstituteTeacher"); + + b.Navigation("TeachingTask"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseEnrollment", b => { b.HasOne("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", "CourseSelectionOffering") @@ -2325,6 +3104,25 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Student"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseExemption", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => { b.HasOne("Jiaowu.Api.Domain.Academic.CourseSelectionRound", "CourseSelectionRound") @@ -2355,6 +3153,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("AcademicTerm"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSubstitution", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "OriginalCourse") + .WithMany() + .HasForeignKey("OriginalCourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Course", "SubstituteCourse") + .WithMany() + .HasForeignKey("SubstituteCourseId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("OriginalCourse"); + + b.Navigation("Student"); + + b.Navigation("SubstituteCourse"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CurriculumCourse", b => { b.HasOne("Jiaowu.Api.Domain.Academic.Course", "Course") @@ -2396,6 +3221,25 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Major"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DeferredExam", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.DegreeAwardResult", b => { b.HasOne("Jiaowu.Api.Domain.Academic.DegreeAwardBatch", "DegreeAwardBatch") @@ -2423,6 +3267,74 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Student"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationSetup", "EvaluationSetup") + .WithMany("Dimensions") + .HasForeignKey("EvaluationSetupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EvaluationSetup"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationSetup", "EvaluationSetup") + .WithMany("Records") + .HasForeignKey("EvaluationSetupId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") + .WithMany() + .HasForeignKey("TeachingTaskId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("EvaluationSetup"); + + b.Navigation("Student"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationDimension", "EvaluationDimension") + .WithMany("Scores") + .HasForeignKey("EvaluationDimensionId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.EvaluationRecord", "EvaluationRecord") + .WithMany("Scores") + .HasForeignKey("EvaluationRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("EvaluationDimension"); + + b.Navigation("EvaluationRecord"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => { b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") @@ -2439,8 +3351,7 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") .WithMany() .HasForeignKey("ClassroomId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); + .OnDelete(DeleteBehavior.SetNull); b.HasOne("Jiaowu.Api.Domain.Academic.ExamPlan", "ExamPlan") .WithMany("Sessions") @@ -2448,6 +3359,11 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("Jiaowu.Api.Domain.Academic.Building", "RequiredBuilding") + .WithMany() + .HasForeignKey("RequiredBuildingId") + .OnDelete(DeleteBehavior.SetNull); + b.HasOne("Jiaowu.Api.Domain.Academic.TeachingTask", "TeachingTask") .WithMany() .HasForeignKey("TeachingTaskId") @@ -2458,6 +3374,8 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("ExamPlan"); + b.Navigation("RequiredBuilding"); + b.Navigation("TeachingTask"); }); @@ -2480,6 +3398,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Teacher"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeSheet", "GradeSheet") + .WithMany("Items") + .HasForeignKey("GradeSheetId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GradeSheet"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeItem", "GradeItem") + .WithMany("Scores") + .HasForeignKey("GradeItemId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "GradeRecord") + .WithMany("ItemScores") + .HasForeignKey("GradeRecordId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("GradeItem"); + + b.Navigation("GradeRecord"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeModification", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "GradeRecord") + .WithMany() + .HasForeignKey("GradeRecordId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("GradeRecord"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => { b.HasOne("Jiaowu.Api.Domain.Academic.GradeSheet", "GradeSheet") @@ -2817,6 +3776,28 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("TeachingTask"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRecord", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.WarningRule", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.AcademicTerm", "AcademicTerm") + .WithMany() + .HasForeignKey("AcademicTermId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AcademicTerm"); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Jiaowu.Api.Domain.Identity.ApplicationRole", null) @@ -2873,6 +3854,11 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Students"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceSheet", b => + { + b.Navigation("Records"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.CourseSelectionOffering", b => { b.Navigation("Enrollments"); @@ -2898,6 +3884,23 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Results"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationDimension", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationRecord", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationSetup", b => + { + b.Navigation("Dimensions"); + + b.Navigation("Records"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamPlan", b => { b.Navigation("Sessions"); @@ -2908,8 +3911,20 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Invigilators"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItem", b => + { + b.Navigation("Scores"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeRecord", b => + { + b.Navigation("ItemScores"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeSheet", b => { + b.Navigation("Items"); + b.Navigation("Records"); }); diff --git a/web/src/components.d.ts b/web/src/components.d.ts index 71827e5..2540142 100644 --- a/web/src/components.d.ts +++ b/web/src/components.d.ts @@ -40,6 +40,7 @@ declare module 'vue' { ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] ElSegmented: typeof import('element-plus/es')['ElSegmented'] ElSelect: typeof import('element-plus/es')['ElSelect'] + ElSlider: typeof import('element-plus/es')['ElSlider'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] diff --git a/web/src/layouts/AdminLayout.vue b/web/src/layouts/AdminLayout.vue index 3c65f8c..96f7b49 100644 --- a/web/src/layouts/AdminLayout.vue +++ b/web/src/layouts/AdminLayout.vue @@ -132,6 +132,10 @@ const navigationGroups = computed(() => [ ...whenVisible(isStudent.value, { path: '/free-classrooms', label: '空闲教室' }), ...whenVisible(isTeacher.value || isTeachingAdmin.value || hasAnyRole(['Counselor']), { path: '/teacher-attendance', label: '教学点名' }), ...whenVisible(isStudent.value, { path: '/my-attendance', label: '我的考勤' }), + { + path: '/evaluations', + label: isStudent.value ? '学生评教' : isTeacher.value ? '评教结果' : '教学评价', + }, ...whenVisible(isTeacher.value, { path: '/teacher-roster', label: '选课名单' }), ...whenVisible(!isStudent.value, { path: '/class-timetable', diff --git a/web/src/router/index.ts b/web/src/router/index.ts index 28d2617..24848cf 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -260,6 +260,14 @@ const router = createRouter({ component: () => import('../views/UsersView.vue'), meta: { roles: ['SuperAdmin'] }, }, + { + path: 'evaluations', + name: 'evaluations', + component: () => import('../views/EvaluationView.vue'), + meta: { + roles: ['SuperAdmin', 'AcademicAdmin', 'CollegeAdmin', 'Teacher', 'Student', 'Leader'], + }, + }, { path: 'statistics', name: 'statistics', diff --git a/web/src/views/EvaluationView.vue b/web/src/views/EvaluationView.vue new file mode 100644 index 0000000..06d1429 --- /dev/null +++ b/web/src/views/EvaluationView.vue @@ -0,0 +1,431 @@ + + + + +