From a576cc815da76bb4d5e05b470ff3e36e84f94534 Mon Sep 17 00:00:00 2001 From: biss Date: Sat, 25 Jul 2026 20:21:42 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=9F=E4=BA=A7=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 79 +- scripts/smoke-test.ps1 | 831 --------- scripts/start-visual-test.ps1 | 13 - .../Controllers/ApprovalsController.cs | 3 +- .../Persistence/AppDbContextFactory.cs | 20 + .../Persistence/DatabaseInitializer.cs | 565 +----- .../Persistence/DatabaseOptions.cs | 2 + .../Persistence/DevelopmentDemoDataSeeder.cs | 620 ------- ...60725051000_FlexibleGradesAndAttendance.cs | 184 -- ...260725100000_ExamSchedulingOptimization.cs | 158 -- .../20260725103117_TeachingEvaluation.cs | 22 - ...17_ProductionSchemaCompletion.Designer.cs} | 1588 ++++++++++------- ...260725120917_ProductionSchemaCompletion.cs | 1173 ++++++++++++ .../MySql/20260725150000_RetakeEnrollment.cs | 29 - .../MySql/20260725160000_CourseAdjustments.cs | 125 -- .../MySql/20260725170000_AttendanceAppeal.cs | 66 - .../MySql/20260725180000_ApprovalRequests.cs | 117 -- .../MySql/20260725190000_AcademicWarnings.cs | 46 - .../MySql/20260725200000_WarningAutoCheck.cs | 27 - .../MySql/AppDbContextModelSnapshot.cs | 1584 +++++++++------- src/Jiaowu.Api/Jiaowu.Api.csproj | 20 +- src/Jiaowu.Api/Jiaowu.Api.http | 14 - src/Jiaowu.Api/Program.cs | 82 +- src/Jiaowu.Api/appsettings.Development.json | 11 +- src/Jiaowu.Api/appsettings.json | 12 +- .../Jiaowu.Api.Tests/GradeCalculatorTests.cs | 15 +- tests/Jiaowu.Api.Tests/MySqlMigrationTests.cs | 36 + .../TimetableExcelExporterTests.cs | 3 +- web/src/style.css | 1 - web/src/views/LoginView.vue | 8 +- 30 files changed, 3325 insertions(+), 4129 deletions(-) delete mode 100644 scripts/smoke-test.ps1 delete mode 100644 scripts/start-visual-test.ps1 create mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/AppDbContextFactory.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentDemoDataSeeder.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725051000_FlexibleGradesAndAttendance.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725100000_ExamSchedulingOptimization.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs rename src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/{20260725103117_TeachingEvaluation.Designer.cs => 20260725120917_ProductionSchemaCompletion.Designer.cs} (70%) create mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725150000_RetakeEnrollment.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725160000_CourseAdjustments.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725170000_AttendanceAppeal.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725180000_ApprovalRequests.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725190000_AcademicWarnings.cs delete mode 100644 src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725200000_WarningAutoCheck.cs delete mode 100644 src/Jiaowu.Api/Jiaowu.Api.http create mode 100644 tests/Jiaowu.Api.Tests/MySqlMigrationTests.cs diff --git a/README.md b/README.md index 37859c4..897e953 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,14 @@ ## 本地开发:热更新模式 -本地开发固定使用 SQLite。首次启动会自动创建 `src/Jiaowu.Api/data/jiaowu-dev.sqlite` 并写入演示组织数据。 - -Development 环境还会按业务编码幂等补齐一套大规模测试数据:16 个常见学院或教学单位、52 个常见本科专业、每个专业 2 个 2026 级行政班、每班 35 名学生、每学院 8 名教师,以及覆盖公共必修、公共选修、专业必修、专业选修和实践教学的 150 余门课程。测试教师同时生成当前学期已审核通过的授课资格;重复启动不会重复累加数据,也不会覆盖已有记录。 - -只执行数据库迁移和测试数据补齐、不启动 Web 服务时,可以运行: - -```powershell -dotnet run --project src/Jiaowu.Api -- --seed-only -``` +本地开发固定使用 SQLite。首次启动会自动创建空的 +`src/Jiaowu.Api/data/jiaowu-dev.sqlite`,只初始化系统角色和课程分类,不再写入 +演示组织、人员、课程、业务记录或内置测试账号。 ```powershell +$env:SeedAdmin__UserName = 'admin' +$env:SeedAdmin__Password = '请替换为本机开发密码' +$env:SeedAdmin__DisplayName = '系统管理员' dotnet run --project src/Jiaowu.Api ``` @@ -34,17 +31,8 @@ npm install npm run dev ``` -访问 `http://localhost:5173`。本地开发会自动创建以下分级权限账号: - -| 数据范围 | 账号 | 密码 | -| --- | --- | --- | -| 超级管理员 | `admin` | `Admin@123456` | -| 校级教务 | `academic` | `Academic@123456` | -| 学院教务 | `college` | `College@123456` | -| 所带班级 | `counselor` | `Counselor@123456` | -| 教师本人 | `teacher` | `Teacher@123456` | -| 学生本人 | `student` | `Student@123456` | -| 领导查看 | `leader` | `Leader@123456` | +访问 `http://localhost:5173`。首次创建管理员后可以清除三个 +`SeedAdmin__*` 环境变量;后续账号和基础数据均通过管理界面维护。 ## 本地开发:单服务模式 @@ -57,36 +45,73 @@ dotnet run --project src/Jiaowu.Api 访问 `http://localhost:5255`。`/api` 和静态页面由同一个 ASP.NET Core 服务提供,`/base-data` 等前端路由刷新时也会回退到 `index.html`。 -## MySQL 部署 +## MySQL 8.4 生产部署 非 Development 环境只允许使用 MySQL。`dotnet publish` 会自动执行 `npm ci` 和 `npm run build`,并将 Vue 静态文件放入发布目录的 `wwwroot`: ```powershell $env:ASPNETCORE_ENVIRONMENT = 'Production' $env:Database__Provider = 'MySql' -$env:ConnectionStrings__MySql = 'Server=127.0.0.1;Port=3306;Database=jiaowu;User=YOUR_USER;Password=YOUR_PASSWORD;' +$env:ConnectionStrings__MySql = 'Server=db.example.edu.cn;Port=3306;Database=jiaowu;User=YOUR_USER;Password=YOUR_PASSWORD;SslMode=VerifyFull;SslCa=C:\certs\mysql-ca.pem;' $env:Jwt__Key = '至少32字节的随机生产密钥' +$env:AllowedHosts = 'jiaowu.example.edu.cn' dotnet publish src/Jiaowu.Api -c Release -o .artifacts/publish -.artifacts/publish/Jiaowu.Api.exe ``` 如需在特殊流水线中跳过自动前端构建,可传入 `-p:BuildFrontendOnPublish=false`。 -生产环境不会创建默认管理员。首次部署前可以临时配置 `SeedAdmin__UserName`、`SeedAdmin__Password` 和 `SeedAdmin__DisplayName`,账号创建后移除这些配置。 +数据库应明确使用 `utf8mb4`;MySQL 8.4 的默认排序规则为 +`utf8mb4_0900_ai_ci`。新建数据库时可执行: + +```sql +CREATE DATABASE `jiaowu` + CHARACTER SET utf8mb4 + COLLATE utf8mb4_0900_ai_ci; +``` + +发布前使用具备 DDL 权限的迁移账号单独执行迁移: + +```powershell +& '.artifacts\publish\Jiaowu.Api.exe' --migrate-only +``` + +迁移成功后,改用仅具备应用所需 DML 权限的运行账号并启动服务: + +```powershell +& '.artifacts\publish\Jiaowu.Api.exe' +``` + +`Database:ApplyMigrationsOnStartup` 默认关闭。普通启动会检查待执行迁移并在架构落后时 +直接失败,避免多实例同时执行 DDL。只有明确接受启动期 DDL 风险的单实例部署才应将 +`Database__ApplyMigrationsOnStartup` 设为 `true`。 + +生产环境不会创建默认管理员。首次部署可以临时配置 +`SeedAdmin__UserName`、`SeedAdmin__Password` 和 `SeedAdmin__DisplayName`, +账号创建后立即移除这些配置。 生产数据库使用 MySQL 专用 EF Core 迁移。部署前先恢复仓库工具并检查迁移: ```powershell dotnet tool restore -dotnet ef migrations list --project src/Jiaowu.Api --startup-project src/Jiaowu.Api +dotnet ef migrations list --no-connect --project src/Jiaowu.Api --startup-project src/Jiaowu.Api ``` -应用启动时会自动执行尚未应用的 MySQL 迁移。SQLite 只用于本地开发:新库通过 `EnsureCreated` 建立,已有开发库通过轻量、版本化的本地升级脚本补齐结构,不需要手动删除数据文件。SQLite 文件不能用于生产。 +不要使用当前提供程序生成的 `dotnet ef migrations script --idempotent` 作为 MySQL +部署脚本;其条件块不是 MySQL 8.4 可直接执行的语法。应使用上述 +`--migrate-only` 入口,或在明确知道目标迁移状态时生成非幂等脚本并先做备份。 +MySQL 的 DDL 会隐式提交,迁移不能依赖外层事务整体回滚。 + +SQLite 只用于本地开发:新库通过 `EnsureCreated` 建立,已有开发库通过轻量、版本化的 +本地升级脚本补齐结构,不需要手动删除数据文件。SQLite 文件不能用于生产。 + +服务探针: + +- `/health/live`:只检查进程存活。 +- `/health`、`/health/ready`:实际检查数据库连接,失败时返回 HTTP 503。 ## 验证 ```powershell dotnet test Jiaowu.slnx npm --prefix web run build -pwsh.exe -NoLogo -NoProfile -NonInteractive -File scripts/smoke-test.ps1 ``` diff --git a/scripts/smoke-test.ps1 b/scripts/smoke-test.ps1 deleted file mode 100644 index 2e16e70..0000000 --- a/scripts/smoke-test.ps1 +++ /dev/null @@ -1,831 +0,0 @@ -param( - [string] $ApiExecutablePath, - [string] $ApiContentRoot -) - -$ErrorActionPreference = 'Stop' - -$workspaceRoot = Split-Path -Parent $PSScriptRoot -$apiExecutable = if ([string]::IsNullOrWhiteSpace($ApiExecutablePath)) { - Join-Path $workspaceRoot 'src/Jiaowu.Api/bin/Debug/net10.0/Jiaowu.Api.exe' -} -else { - [System.IO.Path]::GetFullPath($ApiExecutablePath, $workspaceRoot) -} -$apiWorkingDirectory = if ([string]::IsNullOrWhiteSpace($ApiContentRoot)) { - Join-Path $workspaceRoot 'src/Jiaowu.Api' -} -else { - [System.IO.Path]::GetFullPath($ApiContentRoot, $workspaceRoot) -} -$stdoutPath = Join-Path $env:TEMP 'jiaowu-api-smoke.out.log' -$stderrPath = Join-Path $env:TEMP 'jiaowu-api-smoke.err.log' -$courseExcelToken = [guid]::NewGuid().ToString('N') -$courseTemplatePath = Join-Path $env:TEMP "jiaowu-course-template-$courseExcelToken.xlsx" -$courseExportPath = Join-Path $env:TEMP "jiaowu-course-export-$courseExcelToken.xlsx" -$env:ASPNETCORE_ENVIRONMENT = 'Development' -$env:ASPNETCORE_URLS = 'http://localhost:5255' -$startParameters = @{ - FilePath = $apiExecutable - WorkingDirectory = $apiWorkingDirectory - WindowStyle = 'Hidden' - RedirectStandardOutput = $stdoutPath - RedirectStandardError = $stderrPath - PassThru = $true -} -$process = Start-Process @startParameters - -try { - $healthy = $false - for ($attempt = 0; $attempt -lt 30; $attempt++) { - Start-Sleep -Milliseconds 500 - try { - $health = Invoke-RestMethod -Uri 'http://localhost:5255/health' -TimeoutSec 2 - $healthy = $true - break - } - catch { - # The API may still be starting. - } - } - - if (-not $healthy) { - Get-Content -LiteralPath $stdoutPath -ErrorAction SilentlyContinue - Get-Content -LiteralPath $stderrPath -ErrorAction SilentlyContinue - throw 'API did not become healthy.' - } - - $loginBody = @{ - userName = 'admin' - password = 'Admin@123456' - } | ConvertTo-Json - $loginParameters = @{ - Method = 'Post' - Uri = 'http://localhost:5255/api/auth/login' - ContentType = 'application/json' - Body = $loginBody - } - $login = Invoke-RestMethod @loginParameters - $headers = @{ Authorization = "Bearer $($login.token)" } - $dashboard = Invoke-RestMethod -Uri 'http://localhost:5255/api/dashboard' -Headers $headers - $campuses = Invoke-RestMethod -Uri 'http://localhost:5255/api/base-data/campuses' -Headers $headers - $classes = Invoke-RestMethod -Uri 'http://localhost:5255/api/base-data/classes' -Headers $headers - $counselors = Invoke-RestMethod -Uri 'http://localhost:5255/api/base-data/counselors' -Headers $headers - $assignedClassCount = @($classes | Where-Object { - $null -ne $_.counselorUserId -and - @($counselors).id -contains $_.counselorUserId - }).Count - if ($assignedClassCount -lt 1) { - throw 'No administrative class is linked to a counselor account.' - } - $teachers = Invoke-RestMethod -Uri 'http://localhost:5255/api/personnel/teachers?page=1&pageSize=10' -Headers $headers - $students = Invoke-RestMethod -Uri 'http://localhost:5255/api/personnel/students?page=1&pageSize=10' -Headers $headers - $courses = Invoke-RestMethod -Uri 'http://localhost:5255/api/courses?page=1&pageSize=10' -Headers $headers - $courseCategories = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/base-data/course-categories' ` - -Headers $headers - if (@($courseCategories).Count -lt 10 -or - @($courses.items | Where-Object { $null -eq $_.courseCategoryId }).Count -gt 0) { - throw 'Course categories were not seeded or assigned to all courses.' - } - Invoke-WebRequest ` - -Uri 'http://localhost:5255/api/courses/template' ` - -Headers $headers ` - -OutFile $courseTemplatePath | - Out-Null - Invoke-WebRequest ` - -Uri 'http://localhost:5255/api/courses/export' ` - -Headers $headers ` - -OutFile $courseExportPath | - Out-Null - if ((Get-Item -LiteralPath $courseTemplatePath).Length -lt 1 -or - (Get-Item -LiteralPath $courseExportPath).Length -lt 1) { - throw 'Course Excel template or export is empty.' - } - $courseImport = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/courses/import' ` - -Headers $headers ` - -Form @{ file = Get-Item -LiteralPath $courseExportPath } - if ($courseImport.created -ne 0 -or - $courseImport.updated -ne $courses.total -or - $courseImport.total -ne $courses.total) { - throw 'Course Excel round-trip did not update the expected records.' - } - $curriculumPlans = Invoke-RestMethod -Uri 'http://localhost:5255/api/curriculum-plans?page=1&pageSize=10' -Headers $headers - if ($curriculumPlans.total -gt 0) { - $curriculumDetail = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/curriculum-plans/$($curriculumPlans.items[0].id)" ` - -Headers $headers - } - $teachingTasks = Invoke-RestMethod -Uri 'http://localhost:5255/api/teaching-tasks?page=1&pageSize=10' -Headers $headers - $schedulePlans = Invoke-RestMethod -Uri 'http://localhost:5255/api/schedules/plans' -Headers $headers - if (@($schedulePlans).Count -gt 0) { - $scheduleDetail = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/schedules/plans/$($schedulePlans[0].id)" ` - -Headers $headers - } - $selectionRounds = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/course-selections/rounds' ` - -Headers $headers - if (@($selectionRounds).Count -lt 1) { - throw 'Development course-selection round was not seeded.' - } - $activeSelectionRound = @($selectionRounds) | - Where-Object { $_.isAvailableNow } | - Select-Object -First 1 - if ($null -eq $activeSelectionRound) { - throw 'No course-selection round is open for the smoke test.' - } - $selectionOfferings = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/course-selections/rounds/$($activeSelectionRound.id)/offerings" ` - -Headers $headers - if (@($selectionOfferings).Count -lt 1) { - throw 'Development course-selection offering was not seeded.' - } - $managedUsers = Invoke-RestMethod -Uri 'http://localhost:5255/api/users' -Headers $headers - $teacherAccount = @($managedUsers) | - Where-Object { $_.userName -eq 'teacher' } | - Select-Object -First 1 - if ($null -eq $teacherAccount) { - throw 'Development teacher account was not seeded.' - } - $teacherAccessBody = @{ - staffNumber = $teacherAccount.staffNumber - collegeId = $teacherAccount.collegeId - roles = @('Teacher') - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/users/$($teacherAccount.id)/roles" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $teacherAccessBody - - $scopeScenarios = @( - @{ - UserName = 'college'; Password = 'College@123456'; Scope = 'College' - Teachers = 2; Students = 3; Courses = 3 - }, - @{ - UserName = 'counselor'; Password = 'Counselor@123456'; Scope = 'Class' - Teachers = 1; Students = 3; Courses = 1 - }, - @{ - UserName = 'teacher'; Password = 'Teacher@123456'; Scope = 'Self' - Teachers = 1; Students = 3; Courses = 1 - }, - @{ - UserName = 'student'; Password = 'Student@123456'; Scope = 'Self' - Teachers = 0; Students = 1; Courses = 1 - } - ) - $scopeChecks = foreach ($scenario in $scopeScenarios) { - $scenarioLoginBody = @{ - userName = $scenario.UserName - password = $scenario.Password - } | ConvertTo-Json - $scenarioLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $scenarioLoginBody - $scenarioHeaders = @{ Authorization = "Bearer $($scenarioLogin.token)" } - $scenarioTeachers = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/personnel/teachers?page=1&pageSize=10' ` - -Headers $scenarioHeaders - $scenarioStudents = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/personnel/students?page=1&pageSize=10' ` - -Headers $scenarioHeaders - $scenarioCourses = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/courses?page=1&pageSize=10' ` - -Headers $scenarioHeaders - if ($scenarioLogin.user.effectiveDataScope -ne $scenario.Scope -or - $scenarioTeachers.total -ne $scenario.Teachers -or - $scenarioStudents.total -ne $scenario.Students -or - $scenarioCourses.total -ne $scenario.Courses) { - throw ("Data-scope check failed for {0}: scope={1}, teachers={2}, students={3}, courses={4}." -f - $scenario.UserName, - $scenarioLogin.user.effectiveDataScope, - $scenarioTeachers.total, - $scenarioStudents.total, - $scenarioCourses.total) - } - "$($scenario.UserName):$($scenario.Scope)" - } - - $studentLoginBody = @{ - userName = 'student' - password = 'Student@123456' - } | ConvertTo-Json - $studentLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $studentLoginBody - $studentHeaders = @{ Authorization = "Bearer $($studentLogin.token)" } - $studentOptions = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/course-selections/student/options?roundId=$($activeSelectionRound.id)" ` - -Headers $studentHeaders - $studentOffering = @($studentOptions.offerings) | Select-Object -First 1 - if ($null -eq $studentOffering) { - throw 'Student has no eligible course-selection offering.' - } - if ($studentOffering.enrollmentStatus -ne 'Enrolled') { - $enrollmentBody = @{ offeringId = $studentOffering.id } | ConvertTo-Json - Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/course-selections/student/enrollments' ` - -Headers $studentHeaders ` - -ContentType 'application/json' ` - -Body $enrollmentBody | - Out-Null - } - $studentEnrollments = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/course-selections/student/enrollments?academicTermId=$($activeSelectionRound.academicTermId)" ` - -Headers $studentHeaders - $activeEnrollments = @($studentEnrollments) | - Where-Object { $_.status -eq 'Enrolled' } - if ($activeEnrollments.Count -lt 1) { - throw 'Student course enrollment was not persisted.' - } - $selectionRoster = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/course-selections/offerings/$($studentOffering.id)/roster" ` - -Headers $headers - if ($selectionRoster.enrolledCount -lt 1) { - throw 'Course-selection roster did not include the selected student.' - } - - $gradeTasks = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/grades/sheets' ` - -Headers $headers - $gradeTask = @($gradeTasks) | - Where-Object { $null -ne $_.sheet } | - Select-Object -First 1 - if ($null -eq $gradeTask) { - throw 'Development grade sheet was not seeded.' - } - $gradeDetail = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/grades/sheets/$($gradeTask.sheet.id)" ` - -Headers $headers - if (@($gradeDetail.sheet.records).Count -lt 1) { - throw 'Development grade sheet has no student records.' - } - - $teacherLoginBody = @{ - userName = 'teacher' - password = 'Teacher@123456' - } | ConvertTo-Json - $teacherLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $teacherLoginBody - $teacherHeaders = @{ Authorization = "Bearer $($teacherLogin.token)" } - $teacherGradeTasks = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/grades/sheets' ` - -Headers $teacherHeaders - $teacherGradeTask = @($teacherGradeTasks) | - Where-Object { $_.id -eq $gradeTask.id } | - Select-Object -First 1 - if ($null -eq $teacherGradeTask) { - throw 'Assigned teacher cannot access the grade sheet.' - } - if ($teacherGradeTask.sheet.status -eq 'Draft' -or - $teacherGradeTask.sheet.status -eq 'Returned') { - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/grades/sheets/$($teacherGradeTask.sheet.id)/submit" ` - -Headers $teacherHeaders | - Out-Null - } - - $collegeLoginBody = @{ - userName = 'college' - password = 'College@123456' - } | ConvertTo-Json - $collegeLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $collegeLoginBody - $collegeHeaders = @{ Authorization = "Bearer $($collegeLogin.token)" } - $collegeGradeTasks = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/grades/sheets' ` - -Headers $collegeHeaders - $collegeGradeTask = @($collegeGradeTasks) | - Where-Object { $_.id -eq $gradeTask.id } | - Select-Object -First 1 - if ($collegeGradeTask.sheet.status -eq 'Submitted') { - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/grades/sheets/$($collegeGradeTask.sheet.id)/approve" ` - -Headers $collegeHeaders | - Out-Null - } - - $adminGradeTasks = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/grades/sheets' ` - -Headers $headers - $adminGradeTask = @($adminGradeTasks) | - Where-Object { $_.id -eq $gradeTask.id } | - Select-Object -First 1 - if ($adminGradeTask.sheet.status -eq 'Approved') { - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/grades/sheets/$($adminGradeTask.sheet.id)/publish" ` - -Headers $headers | - Out-Null - } - $studentTranscript = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/grades/student/transcript' ` - -Headers $studentHeaders - if (@($studentTranscript.records).Count -lt 1) { - throw 'Published grade is not visible in the student transcript.' - } - $examPlans = Invoke-RestMethod -Uri 'http://localhost:5255/api/exams/plans' -Headers $headers - if (@($examPlans).Count -lt 1) { throw 'Development exam plan was not seeded.' } - $examDetail = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/exams/plans/$($examPlans[0].id)" ` - -Headers $headers - if (@($examDetail.sessions).Count -lt 1) { throw 'Exam plan has no sessions.' } - $examRoster = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/exams/sessions/$($examDetail.sessions[0].id)/roster" ` - -Headers $headers - $studentExams = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/exams/my-schedule' ` - -Headers $studentHeaders - $teacherExams = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/exams/my-schedule' ` - -Headers $teacherHeaders - if (@($studentExams).Count -lt 1 -or @($teacherExams).Count -lt 1) { - throw 'Personal exam schedule is not visible to student or invigilator.' - } - - $statusOptions = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/student-status-changes/options' ` - -Headers $studentHeaders - $statusChanges = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/student-status-changes' ` - -Headers $studentHeaders - $openStatusChange = @($statusChanges) | - Where-Object { $_.state -in @('Submitted', 'CounselorApproved', 'CollegeApproved') } | - Select-Object -First 1 - if ($null -eq $openStatusChange) { - $statusChangeType = if ($statusOptions.status -eq 'Active') { - 'Suspension' - } - elseif ($statusOptions.status -eq 'Suspended') { - 'Resumption' - } - else { - throw "Smoke-test student status '$($statusOptions.status)' cannot start a reversible change." - } - $statusChangeBody = @{ - type = $statusChangeType - reason = '端到端验证学籍异动三级审核流程。' - } | ConvertTo-Json - $createdStatusChange = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/student-status-changes' ` - -Headers $studentHeaders ` - -ContentType 'application/json' ` - -Body $statusChangeBody - $statusChanges = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/student-status-changes' ` - -Headers $studentHeaders - $openStatusChange = @($statusChanges) | - Where-Object { $_.id -eq $createdStatusChange.id } | - Select-Object -First 1 - } - $expectedStudentStatus = $openStatusChange.targetStatus - $approvalBody = @{ approved = $true; comment = '端到端审核通过。' } | ConvertTo-Json - - if ($openStatusChange.state -eq 'Submitted') { - $prematureCollegeReview = Invoke-WebRequest ` - -Method Post ` - -Uri "http://localhost:5255/api/student-status-changes/$($openStatusChange.id)/review" ` - -Headers $collegeHeaders ` - -ContentType 'application/json' ` - -Body $approvalBody ` - -SkipHttpErrorCheck - if ($prematureCollegeReview.StatusCode -ne 409) { - throw 'College reviewer was able to bypass the counselor stage.' - } - $counselorLoginBody = @{ - userName = 'counselor' - password = 'Counselor@123456' - } | ConvertTo-Json - $counselorLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $counselorLoginBody - $counselorHeaders = @{ Authorization = "Bearer $($counselorLogin.token)" } - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/student-status-changes/$($openStatusChange.id)/review" ` - -Headers $counselorHeaders ` - -ContentType 'application/json' ` - -Body $approvalBody | - Out-Null - $openStatusChange.state = 'CounselorApproved' - } - if ($openStatusChange.state -eq 'CounselorApproved') { - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/student-status-changes/$($openStatusChange.id)/review" ` - -Headers $collegeHeaders ` - -ContentType 'application/json' ` - -Body $approvalBody | - Out-Null - $openStatusChange.state = 'CollegeApproved' - } - if ($openStatusChange.state -eq 'CollegeApproved') { - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/student-status-changes/$($openStatusChange.id)/review" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $approvalBody | - Out-Null - } - $completedStatusChanges = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/student-status-changes' ` - -Headers $studentHeaders - $completedStatusChange = @($completedStatusChanges) | - Where-Object { $_.id -eq $openStatusChange.id } | - Select-Object -First 1 - $updatedStatusOptions = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/student-status-changes/options' ` - -Headers $studentHeaders - if ($completedStatusChange.state -ne 'Approved' -or - $updatedStatusOptions.status -ne $expectedStudentStatus) { - throw 'Student status change was not finalized or did not update the student record.' - } - - $graduationBatches = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/graduation-audits/batches' ` - -Headers $headers - $graduationBatch = @($graduationBatches) | - Where-Object { $_.name -eq '2030届端到端毕业资格审核' } | - Select-Object -First 1 - if ($null -eq $graduationBatch) { - $graduationBatchBody = @{ - name = '2030届端到端毕业资格审核' - graduationYear = 2030 - enrollmentYear = 2026 - notes = '用于验证培养方案、成绩快照、人工复核与结果发布。' - } | ConvertTo-Json - $createdGraduationBatch = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/graduation-audits/batches' ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $graduationBatchBody - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($createdGraduationBatch.id)/calculate" ` - -Headers $headers | - Out-Null - $graduationBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($createdGraduationBatch.id)" ` - -Headers $headers - $studentGraduationResult = @($graduationBatch.results) | - Where-Object { $_.studentNumber -eq '202601001' } | - Select-Object -First 1 - if ($null -eq $studentGraduationResult) { - throw 'Calculated graduation batch does not contain the smoke-test student.' - } - $overrideDecisionBody = @{ - conclusion = 'Eligible' - comment = '端到端验证人工复核调整。' - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/graduation-audits/results/$($studentGraduationResult.id)" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $overrideDecisionBody | - Out-Null - $restoreDecisionBody = @{ - conclusion = 'Ineligible' - comment = '恢复规则结论后发布测试结果。' - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/graduation-audits/results/$($studentGraduationResult.id)" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $restoreDecisionBody | - Out-Null - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($graduationBatch.id)/publish" ` - -Headers $headers | - Out-Null - } - else { - $graduationBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($graduationBatch.id)" ` - -Headers $headers - } - $graduationBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($graduationBatch.id)" ` - -Headers $headers - $studentGraduationResult = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/graduation-audits/my-result' ` - -Headers $studentHeaders - if ($graduationBatch.status -ne 'Published' -or - $studentGraduationResult.conclusion -ne 'Ineligible') { - throw 'Published graduation result is not visible to the student.' - } - - $graduationBatches = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/graduation-audits/batches' ` - -Headers $headers - $degreeSourceBatch = @($graduationBatches) | - Where-Object { $_.name -eq '2030届学位授予资格演示' } | - Select-Object -First 1 - if ($null -eq $degreeSourceBatch) { - $degreeSourceBody = @{ - name = '2030届学位授予资格演示' - graduationYear = 2030 - enrollmentYear = 2026 - notes = '为未关联登录账号的演示学生生成学位授予前置资格。' - } | ConvertTo-Json - $createdDegreeSource = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/graduation-audits/batches' ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $degreeSourceBody - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($createdDegreeSource.id)/calculate" ` - -Headers $headers | - Out-Null - $degreeSourceDetail = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($createdDegreeSource.id)" ` - -Headers $headers - $degreeSourceResult = @($degreeSourceDetail.results) | - Where-Object { $_.studentNumber -eq '202601002' } | - Select-Object -First 1 - $degreeSourceDecision = @{ - conclusion = 'Eligible' - comment = '演示学生经校级专项复核符合毕业条件。' - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/graduation-audits/results/$($degreeSourceResult.id)" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $degreeSourceDecision | - Out-Null - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-audits/batches/$($createdDegreeSource.id)/publish" ` - -Headers $headers | - Out-Null - } - - $degreeBatches = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/degree-awards/batches' ` - -Headers $headers - $degreeBatch = @($degreeBatches) | - Where-Object { $_.name -eq '2030届端到端学位授予审核' } | - Select-Object -First 1 - if ($null -eq $degreeBatch) { - $degreeBatchBody = @{ - name = '2030届端到端学位授予审核' - graduationYear = 2030 - degreeName = '工学学士' - minimumGradePoint = 0 - notes = '验证毕业资格来源、绩点快照、人工复核与发布锁定。' - } | ConvertTo-Json - $createdDegreeBatch = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/degree-awards/batches' ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $degreeBatchBody - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/degree-awards/batches/$($createdDegreeBatch.id)/calculate" ` - -Headers $headers | - Out-Null - $degreeBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/degree-awards/batches/$($createdDegreeBatch.id)" ` - -Headers $headers - $degreeResult = @($degreeBatch.results) | Select-Object -First 1 - if ($null -eq $degreeResult -or $degreeResult.conclusion -ne 'Granted') { - throw 'Degree-award calculation did not include the eligible graduated student.' - } - $degreeRejectBody = @{ - conclusion = 'NotGranted' - comment = '端到端验证学位结论人工调整。' - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/degree-awards/results/$($degreeResult.id)" ` - -Headers $collegeHeaders ` - -ContentType 'application/json' ` - -Body $degreeRejectBody | - Out-Null - $degreeRestoreBody = @{ - conclusion = 'Granted' - comment = '恢复规则结论并完成授予发布。' - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/degree-awards/results/$($degreeResult.id)" ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $degreeRestoreBody | - Out-Null - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/degree-awards/batches/$($degreeBatch.id)/publish" ` - -Headers $headers | - Out-Null - } - $degreeBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/degree-awards/batches/$($degreeBatch.id)" ` - -Headers $headers - $collegeDegreeBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/degree-awards/batches/$($degreeBatch.id)" ` - -Headers $collegeHeaders - if ($degreeBatch.status -ne 'Published' -or - @($degreeBatch.results).Count -lt 1 -or - @($collegeDegreeBatch.results).Count -lt 1) { - throw 'Published degree-award results are not visible in the authorized college scope.' - } - - $clearanceBatches = Invoke-RestMethod ` - -Uri 'http://localhost:5255/api/graduation-clearance/batches' ` - -Headers $headers - $clearanceBatch = @($clearanceBatches) | - Where-Object { $_.name -eq '2030届端到端毕业离校手续' } | - Select-Object -First 1 - if ($null -eq $clearanceBatch) { - $clearanceBatchBody = @{ - name = '2030届端到端毕业离校手续' - graduationYear = 2030 - notes = '验证校级、学院、辅导员分工办理与批次关闭。' - items = @( - @{ - code = 'FINANCE' - name = '财务费用结清' - responsibleUnit = '财务处' - responsibleRole = 'AcademicAdmin' - isRequired = $true - }, - @{ - code = 'COLLEGE' - name = '学院材料归档' - responsibleUnit = '计算机学院' - responsibleRole = 'CollegeAdmin' - isRequired = $true - }, - @{ - code = 'DORM' - name = '宿舍退宿确认' - responsibleUnit = '学生工作办公室' - responsibleRole = 'Counselor' - isRequired = $true - } - ) - } | ConvertTo-Json -Depth 5 - $createdClearanceBatch = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/graduation-clearance/batches' ` - -Headers $headers ` - -ContentType 'application/json' ` - -Body $clearanceBatchBody - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-clearance/batches/$($createdClearanceBatch.id)/generate" ` - -Headers $headers | - Out-Null - $clearanceBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-clearance/batches/$($createdClearanceBatch.id)" ` - -Headers $headers - if (@($clearanceBatch.records).Count -ne 3) { - throw 'Graduation-clearance records were not generated for each configured item.' - } - $academicClearanceRecord = @($clearanceBatch.records) | - Where-Object { $_.responsibleRole -eq 'AcademicAdmin' } | - Select-Object -First 1 - $wrongRoleResponse = Invoke-WebRequest ` - -Method Put ` - -Uri "http://localhost:5255/api/graduation-clearance/records/$($academicClearanceRecord.id)" ` - -Headers $collegeHeaders ` - -ContentType 'application/json' ` - -Body (@{ status = 'Completed'; notes = '越权测试。' } | ConvertTo-Json) ` - -SkipHttpErrorCheck - if ($wrongRoleResponse.StatusCode -ne 403) { - throw 'College role was able to complete an academic-level clearance item.' - } - $counselorLoginBody = @{ - userName = 'counselor' - password = 'Counselor@123456' - } | ConvertTo-Json - $counselorLogin = Invoke-RestMethod ` - -Method Post ` - -Uri 'http://localhost:5255/api/auth/login' ` - -ContentType 'application/json' ` - -Body $counselorLoginBody - $counselorHeaders = @{ Authorization = "Bearer $($counselorLogin.token)" } - foreach ($record in @($clearanceBatch.records)) { - $recordHeaders = switch ($record.responsibleRole) { - 'CollegeAdmin' { $collegeHeaders } - 'Counselor' { $counselorHeaders } - default { $headers } - } - $recordBody = @{ - status = 'Completed' - notes = "$($record.responsibleUnit)端到端办理完成。" - } | ConvertTo-Json - Invoke-RestMethod ` - -Method Put ` - -Uri "http://localhost:5255/api/graduation-clearance/records/$($record.id)" ` - -Headers $recordHeaders ` - -ContentType 'application/json' ` - -Body $recordBody | - Out-Null - } - Invoke-RestMethod ` - -Method Post ` - -Uri "http://localhost:5255/api/graduation-clearance/batches/$($clearanceBatch.id)/close" ` - -Headers $headers | - Out-Null - } - $clearanceBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-clearance/batches/$($clearanceBatch.id)" ` - -Headers $headers - $collegeClearanceBatch = Invoke-RestMethod ` - -Uri "http://localhost:5255/api/graduation-clearance/batches/$($clearanceBatch.id)" ` - -Headers $collegeHeaders - if ($clearanceBatch.status -ne 'Closed' -or - @($clearanceBatch.records | Where-Object { $_.status -eq 'Pending' }).Count -ne 0 -or - @($collegeClearanceBatch.records).Count -lt 1) { - throw 'Graduation-clearance batch was not closed or is missing from college scope.' - } - - $frontend = Invoke-WebRequest -Uri 'http://localhost:5255/' -TimeoutSec 5 - $spaFallback = Invoke-WebRequest -Uri 'http://localhost:5255/base-data' -TimeoutSec 5 - $unknownApiParameters = @{ - Uri = 'http://localhost:5255/api/does-not-exist' - SkipHttpErrorCheck = $true - TimeoutSec = 5 - } - $unknownApi = Invoke-WebRequest @unknownApiParameters - - [pscustomobject]@{ - Health = $health.status - Database = $health.database - User = $login.user.displayName - Term = $dashboard.currentTerm.name - Campuses = @($campuses).Count - CounselorAssignments = $assignedClassCount - Teachers = $teachers.total - Students = $students.total - Courses = $courses.total - CourseCategories = @($courseCategories).Count - CourseExcelRows = $courseImport.total - Plans = $curriculumPlans.total - PlanModules = if ($null -ne $curriculumDetail) { @($curriculumDetail.modules).Count } else { 0 } - TeachingTasks = $teachingTasks.total - Schedules = @($schedulePlans).Count - ScheduleEntries = if ($null -ne $scheduleDetail) { @($scheduleDetail.entries).Count } else { 0 } - SelectionRounds = @($selectionRounds).Count - SelectionOfferings = @($selectionOfferings).Count - StudentEnrollments = $activeEnrollments.Count - RosterStudents = $selectionRoster.enrolledCount - GradeSheets = @($gradeTasks).Count - GradeRecords = @($gradeDetail.sheet.records).Count - TranscriptRecords = @($studentTranscript.records).Count - ExamSessions = @($examDetail.sessions).Count - ExamRoster = @($examRoster.students).Count - StatusChanges = @($completedStatusChanges).Count - StudentStatus = $updatedStatusOptions.status - GraduationResults = @($graduationBatch.results).Count - GraduationConclusion = $studentGraduationResult.conclusion - DegreeResults = @($degreeBatch.results).Count - DegreeConclusion = $degreeBatch.results[0].conclusion - ClearanceRecords = @($clearanceBatch.records).Count - ClearanceStatus = $clearanceBatch.status - AccessUpdate = $true - ScopeChecks = $scopeChecks -join ', ' - StaticIndex = $frontend.Content.Contains('明序教务管理系统') - SpaFallback = $spaFallback.StatusCode - ApiNotFound = $unknownApi.StatusCode - } | Format-List -} -finally { - if (-not $process.HasExited) { - Stop-Process -Id $process.Id -Force - } - Remove-Item -LiteralPath $courseTemplatePath -Force -ErrorAction SilentlyContinue - Remove-Item -LiteralPath $courseExportPath -Force -ErrorAction SilentlyContinue -} diff --git a/scripts/start-visual-test.ps1 b/scripts/start-visual-test.ps1 deleted file mode 100644 index 41cf091..0000000 --- a/scripts/start-visual-test.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -$ErrorActionPreference = 'Stop' -$env:ASPNETCORE_ENVIRONMENT = 'Development' -$env:ASPNETCORE_URLS = 'http://localhost:5256' - -$workspaceRoot = Split-Path -Parent $PSScriptRoot -$publishRoot = Join-Path $workspaceRoot '.artifacts/publish' -$process = Start-Process ` - -FilePath (Join-Path $publishRoot 'Jiaowu.Api.exe') ` - -WorkingDirectory $publishRoot ` - -WindowStyle Hidden ` - -PassThru - -$process.Id diff --git a/src/Jiaowu.Api/Controllers/ApprovalsController.cs b/src/Jiaowu.Api/Controllers/ApprovalsController.cs index a33e2b7..dc7897f 100644 --- a/src/Jiaowu.Api/Controllers/ApprovalsController.cs +++ b/src/Jiaowu.Api/Controllers/ApprovalsController.cs @@ -295,7 +295,8 @@ public sealed class ApprovalsController(AppDbContext db, ICurrentUserDataScope s { origRecord.TotalScore = passedRecord.TotalScore; origRecord.GradePoint = passedRecord.GradePoint; - origRecord.Notes = $"课程替代:{cs.SubstituteCourse.Code} {cs.SubstituteCourse.Name}"; + origRecord.Notes = + $"课程替代:{cs.SubstituteCourse!.Code} {cs.SubstituteCourse.Name}"; } } } diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContextFactory.cs b/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContextFactory.cs new file mode 100644 index 0000000..3c3a423 --- /dev/null +++ b/src/Jiaowu.Api/Infrastructure/Persistence/AppDbContextFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; + +namespace Jiaowu.Api.Infrastructure.Persistence; + +public sealed class AppDbContextFactory : IDesignTimeDbContextFactory +{ + public AppDbContext CreateDbContext(string[] args) + { + var connectionString = + Environment.GetEnvironmentVariable("ConnectionStrings__MySql") + ?? "Server=localhost;Port=3306;Database=jiaowu;User=__design_time__;Password=__not_used__;"; + + var options = new DbContextOptionsBuilder() + .UseMySQL(connectionString) + .Options; + + return new AppDbContext(options); + } +} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseInitializer.cs b/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseInitializer.cs index 0ca5380..abf12a6 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseInitializer.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseInitializer.cs @@ -10,31 +10,49 @@ public sealed class DatabaseInitializer( RoleManager roleManager, UserManager userManager, DevelopmentSqliteMigrator sqliteMigrator, - DevelopmentDemoDataSeeder developmentDemoDataSeeder, + DatabaseOptions databaseOptions, IConfiguration configuration, IHostEnvironment environment, ILogger logger) { - public async Task InitializeAsync() + public async Task InitializeAsync(bool migrateOnly = false) { if (environment.IsDevelopment()) { + if (migrateOnly) + { + throw new InvalidOperationException( + "--migrate-only 仅用于非 Development 环境的 MySQL 数据库。"); + } + await db.Database.EnsureCreatedAsync(); await sqliteMigrator.MigrateAsync(); } - else + else if (migrateOnly || databaseOptions.ApplyMigrationsOnStartup) { await db.Database.MigrateAsync(); } + else + { + var pendingMigrations = (await db.Database.GetPendingMigrationsAsync()).ToArray(); + if (pendingMigrations.Length > 0) + { + throw new InvalidOperationException( + $"数据库还有 {pendingMigrations.Length} 个待执行迁移。请在发布前运行 " + + "'Jiaowu.Api --migrate-only',或显式配置 " + + "Database:ApplyMigrationsOnStartup=true。"); + } + } + + if (migrateOnly) + { + logger.LogInformation("数据库迁移已完成。"); + return; + } await SeedRolesAsync(); await SeedAdministratorAsync(); await SeedCourseCategoriesAsync(); - - if (environment.IsDevelopment()) - { - await SeedDevelopmentDataAsync(); - } } private async Task SeedRolesAsync() @@ -131,7 +149,11 @@ public sealed class DatabaseInitializer( .ToHashSet(StringComparer.OrdinalIgnoreCase); foreach (var (code, name, sortOrder) in defaults) { - if (existingCodes.Contains(code)) continue; + if (existingCodes.Contains(code)) + { + continue; + } + db.CourseCategories.Add(new CourseCategory { Code = code, @@ -159,523 +181,6 @@ public sealed class DatabaseInitializer( await db.SaveChangesAsync(); } - private async Task SeedDevelopmentDataAsync() - { - if (!await db.Campuses.AnyAsync()) - { - var campus = new Campus - { - Code = "MAIN", - Name = "主校区", - Address = "大学路 1 号" - }; - var college = new College - { - Code = "CS", - Name = "计算机学院", - ShortName = "计算机学院", - CampusId = campus.Id - }; - var major = new Major - { - Code = "080901", - Name = "计算机科学与技术", - CollegeId = college.Id, - DegreeType = "工学学士", - SchoolingYears = 4 - }; - var building = new Building - { - Code = "J1", - Name = "第一教学楼", - CampusId = campus.Id - }; - - db.AddRange( - campus, - college, - major, - new AdministrativeClass - { - Code = "CS2026-01", - Name = "计科 2026-1 班", - MajorId = major.Id, - Grade = 2026, - CounselorName = "陈老师" - }, - building, - new Classroom - { - Code = "J1-201", - Name = "J1-201", - BuildingId = building.Id, - Capacity = 60, - RoomType = "多媒体教室", - Equipment = "投影、扩声、录播" - }, - new AcademicTerm - { - Code = "2026-2027-1", - Name = "2026—2027 学年第一学期", - AcademicYear = "2026-2027", - Season = TermSeason.Autumn, - StartDate = new DateOnly(2026, 9, 7), - EndDate = new DateOnly(2027, 1, 17), - IsCurrent = true - }); - - await db.SaveChangesAsync(); - } - - var computerCollege = await db.Colleges.SingleAsync(x => x.Code == "CS"); - var computerClass = await db.AdministrativeClasses - .SingleAsync(x => x.Code == "CS2026-01"); - - if (!await db.Teachers.AnyAsync()) - { - db.Teachers.AddRange( - new Teacher - { - TeacherNumber = "T2026001", - Name = "陈明远", - Gender = Gender.Male, - CollegeId = computerCollege.Id, - Title = "副教授", - Status = TeacherStatus.Active, - HireDate = new DateOnly(2018, 7, 1), - Email = "chenmy@example.edu.cn" - }, - new Teacher - { - TeacherNumber = "T2026002", - Name = "林书雅", - Gender = Gender.Female, - CollegeId = computerCollege.Id, - Title = "讲师", - Status = TeacherStatus.Active, - HireDate = new DateOnly(2022, 9, 1), - Email = "linsy@example.edu.cn" - }); - } - - if (!await db.Students.AnyAsync()) - { - db.Students.AddRange( - new Student - { - StudentNumber = "202601001", - Name = "周启航", - Gender = Gender.Male, - AdministrativeClassId = computerClass.Id, - EnrollmentYear = 2026, - EnrollmentDate = new DateOnly(2026, 9, 7), - Status = StudentStatus.Active - }, - new Student - { - StudentNumber = "202601002", - Name = "许知夏", - Gender = Gender.Female, - AdministrativeClassId = computerClass.Id, - EnrollmentYear = 2026, - EnrollmentDate = new DateOnly(2026, 9, 7), - Status = StudentStatus.Active - }, - new Student - { - StudentNumber = "202601003", - Name = "方嘉树", - Gender = Gender.Male, - AdministrativeClassId = computerClass.Id, - EnrollmentYear = 2026, - EnrollmentDate = new DateOnly(2026, 9, 7), - Status = StudentStatus.Active - }); - } - - var courseCategories = await db.CourseCategories - .ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase); - - if (!await db.Courses.AnyAsync()) - { - db.Courses.AddRange( - new Course - { - Code = "CS101", - Name = "程序设计基础", - EnglishName = "Fundamentals of Programming", - CollegeId = computerCollege.Id, - CourseCategoryId = courseCategories["BASIC"].Id, - Credits = 4, - TotalHours = 64, - LectureHours = 40, - PracticeHours = 24, - Nature = CourseNature.MajorRequired, - AssessmentMethod = AssessmentMethod.Examination, - Description = "面向一年级学生的程序设计入门课程。" - }, - new Course - { - Code = "CS201", - Name = "数据结构", - EnglishName = "Data Structures", - CollegeId = computerCollege.Id, - CourseCategoryId = courseCategories["BASIC"].Id, - Credits = 3.5m, - TotalHours = 56, - LectureHours = 40, - PracticeHours = 16, - Nature = CourseNature.MajorRequired, - AssessmentMethod = AssessmentMethod.Examination - }, - new Course - { - Code = "CS305", - Name = "软件工程实践", - EnglishName = "Software Engineering Practice", - CollegeId = computerCollege.Id, - CourseCategoryId = courseCategories["PRACTICE"].Id, - Credits = 2, - TotalHours = 48, - LectureHours = 8, - PracticeHours = 40, - Nature = CourseNature.Practice, - AssessmentMethod = AssessmentMethod.Assessment - }); - } - await db.SaveChangesAsync(); - - if (!await db.CurriculumPlans.AnyAsync()) - { - var seededCourses = await db.Courses - .Where(x => x.Code == "CS101" || x.Code == "CS201" || x.Code == "CS305") - .ToDictionaryAsync(x => x.Code); - if (seededCourses.Count == 3) - { - db.CurriculumPlans.Add(new CurriculumPlan - { - MajorId = (await db.Majors.SingleAsync(x => x.Code == "080901")).Id, - Name = "计算机科学与技术专业培养方案", - Version = "2026版", - EffectiveGrade = 2026, - TotalCredits = 9.5m, - Status = CurriculumPlanStatus.Published, - PublishedAt = DateTime.UtcNow, - Description = "用于本地开发的精简培养方案示例。", - Modules = - [ - new CurriculumModule - { - Code = "BASIC", - Name = "专业基础课程", - RequiredCredits = 7.5m, - SortOrder = 10, - Courses = - [ - new CurriculumCourse - { - CourseId = seededCourses["CS101"].Id, - RecommendedSemester = 1, - Type = CurriculumCourseType.Required - }, - new CurriculumCourse - { - CourseId = seededCourses["CS201"].Id, - RecommendedSemester = 3, - Type = CurriculumCourseType.Required - } - ] - }, - new CurriculumModule - { - Code = "PRACTICE", - Name = "实践教学", - RequiredCredits = 2m, - SortOrder = 20, - Courses = - [ - new CurriculumCourse - { - CourseId = seededCourses["CS305"].Id, - RecommendedSemester = 5, - Type = CurriculumCourseType.Required - } - ] - } - ] - }); - await db.SaveChangesAsync(); - } - } - - if (!await db.TeachingTasks.AnyAsync()) - { - var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent); - var course = await db.Courses.SingleAsync(x => x.Code == "CS101"); - var teacher = await db.Teachers.SingleAsync(x => x.TeacherNumber == "T2026001"); - var administrativeClass = await db.AdministrativeClasses - .SingleAsync(x => x.Code == "CS2026-01"); - db.TeachingTasks.Add(new TeachingTask - { - TaskNumber = "2026-1-CS101-01", - Name = "程序设计基础教学班 01", - AcademicTermId = term.Id, - CourseId = course.Id, - Capacity = 60, - StartWeek = 1, - EndWeek = 16, - WeeklyHours = 4, - Status = TeachingTaskStatus.Published, - PublishedAt = DateTime.UtcNow, - Teachers = - [ - new TeachingTaskTeacher - { - TeacherId = teacher.Id, - IsPrimary = true - } - ], - Classes = - [ - new TeachingTaskClass - { - AdministrativeClassId = administrativeClass.Id - } - ] - }); - await db.SaveChangesAsync(); - } - - if (!await db.SchedulePlans.AnyAsync()) - { - var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent); - var task = await db.TeachingTasks.SingleAsync(x => x.TaskNumber == "2026-1-CS101-01"); - var classroom = await db.Classrooms.SingleAsync(x => x.Code == "J1-201"); - db.SchedulePlans.Add(new SchedulePlan - { - AcademicTermId = term.Id, - Name = "2026—2027 学年第一学期正式课表", - Version = "V1", - Status = SchedulePlanStatus.Published, - PublishedAt = DateTime.UtcNow, - Entries = - [ - new ScheduleEntry - { - TeachingTaskId = task.Id, - ClassroomId = classroom.Id, - DayOfWeek = 1, - StartPeriod = 1, - PeriodCount = 2, - StartWeek = 1, - EndWeek = 16, - WeekPattern = WeekPattern.All - } - ] - }); - await db.SaveChangesAsync(); - } - - await SeedDevelopmentUsersAsync(computerCollege.Id); - await SeedDevelopmentCourseSelectionAsync(); - await SeedDevelopmentGradesAsync(); - await SeedDevelopmentExamsAsync(); - await developmentDemoDataSeeder.SeedAsync(); - } - - private async Task SeedDevelopmentCourseSelectionAsync() - { - if (!await db.CourseSelectionRounds.AnyAsync()) - { - var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent); - var task = await db.TeachingTasks.SingleAsync( - x => x.TaskNumber == "2026-1-CS101-01"); - var now = DateTime.UtcNow; - db.CourseSelectionRounds.Add(new CourseSelectionRound - { - AcademicTermId = term.Id, - Name = "2026—2027 学年第一学期第一轮选课", - StartsAt = now.AddDays(-2), - EndsAt = now.AddDays(14), - WithdrawalEndsAt = now.AddDays(21), - MaxCredits = 30, - Status = CourseSelectionRoundStatus.Open, - Notes = "本地开发演示轮次,可用于验证选课、退课和教学班名单。", - Offerings = - [ - new CourseSelectionOffering - { - TeachingTaskId = task.Id, - Capacity = 60, - IsOpenToAll = false, - Notes = "面向计科 2026-1 班开放。" - } - ] - }); - await db.SaveChangesAsync(); - } - - var seededTask = await db.TeachingTasks.SingleAsync( - x => x.TaskNumber == "2026-1-CS101-01"); - var offering = await db.CourseSelectionOfferings.FirstOrDefaultAsync( - x => x.TeachingTaskId == seededTask.Id); - var student = await db.Students.SingleAsync(x => - x.StudentNumber == "202601001"); - if (offering is not null && - !await db.CourseEnrollments.AnyAsync(x => - x.CourseSelectionOfferingId == offering.Id && - x.StudentId == student.Id)) - { - db.CourseEnrollments.Add(new CourseEnrollment - { - CourseSelectionOfferingId = offering.Id, - StudentId = student.Id - }); - await db.SaveChangesAsync(); - } - } - - private async Task SeedDevelopmentGradesAsync() - { - if (await db.GradeSheets.AnyAsync()) return; - var task = await db.TeachingTasks.SingleAsync( - x => x.TaskNumber == "2026-1-CS101-01"); - var student = await db.Students.SingleAsync( - x => x.StudentNumber == "202601001"); - db.GradeSheets.Add(new GradeSheet - { - TeachingTaskId = task.Id, - RegularWeight = 30, - FinalWeight = 70, - Status = GradeSheetStatus.Draft, - Records = - [ - new GradeRecord - { - StudentId = student.Id, - RegularScore = 88, - FinalScore = 92, - TotalScore = 90.8m, - GradePoint = 4.0m - } - ] - }); - await db.SaveChangesAsync(); - } - - private async Task SeedDevelopmentExamsAsync() - { - if (await db.ExamPlans.AnyAsync()) return; - var term = await db.AcademicTerms.SingleAsync(x => x.IsCurrent); - var task = await db.TeachingTasks.SingleAsync(x => x.TaskNumber == "2026-1-CS101-01"); - var room = await db.Classrooms.SingleAsync(x => x.Code == "J1-201"); - var teacher = await db.Teachers.SingleAsync(x => x.TeacherNumber == "T2026001"); - db.ExamPlans.Add(new ExamPlan - { - AcademicTermId = term.Id, - Name = "2026—2027 学年第一学期期末考试", - Status = ExamPlanStatus.Published, - PublishedAt = DateTime.UtcNow, - Notes = "本地开发演示考试计划。", - Sessions = - [ - new ExamSession - { - TeachingTaskId = task.Id, - ClassroomId = room.Id, - ExamDate = new DateOnly(2027, 1, 8), - StartPeriod = 1, - PeriodCount = 2, - StartsAt = new DateTime(2027, 1, 8, 9, 0, 0, DateTimeKind.Utc), - EndsAt = new DateTime(2027, 1, 8, 11, 0, 0, DateTimeKind.Utc), - RequiredInvigilatorCount = 2, - Invigilators = - [ - new ExamSessionInvigilator { TeacherId = teacher.Id } - ] - } - ] - }); - await db.SaveChangesAsync(); - } - - private async Task SeedDevelopmentUsersAsync(Guid collegeId) - { - var definitions = new[] - { - new DevelopmentUser( - "academic", "校级教务员", "Academic@123456", - null, null, SystemRoles.AcademicAdmin), - new DevelopmentUser( - "college", "计算机学院教务员", "College@123456", - "A2026001", collegeId, SystemRoles.CollegeAdmin), - new DevelopmentUser( - "counselor", "陈老师", "Counselor@123456", - "C2026001", collegeId, SystemRoles.Counselor), - new DevelopmentUser( - "teacher", "陈明远", "Teacher@123456", - "T2026001", collegeId, SystemRoles.Teacher), - new DevelopmentUser( - "student", "周启航", "Student@123456", - "202601001", collegeId, SystemRoles.Student), - new DevelopmentUser( - "leader", "教学分管领导", "Leader@123456", - null, null, SystemRoles.Leader) - }; - - foreach (var definition in definitions) - { - var user = await userManager.FindByNameAsync(definition.UserName); - if (user is null) - { - user = new ApplicationUser - { - UserName = definition.UserName, - DisplayName = definition.DisplayName, - StaffNumber = definition.StaffNumber, - CollegeId = definition.CollegeId, - LockoutEnabled = true, - IsEnabled = true - }; - EnsureSucceeded( - await userManager.CreateAsync(user, definition.Password), - $"创建开发账号 {definition.UserName}"); - } - - if (!await userManager.IsInRoleAsync(user, definition.Role)) - { - EnsureSucceeded( - await userManager.AddToRoleAsync(user, definition.Role), - $"授予开发账号 {definition.UserName} 角色"); - } - - if (definition.Role == SystemRoles.Teacher) - { - var teacher = await db.Teachers.SingleAsync( - x => x.TeacherNumber == definition.StaffNumber); - if (!teacher.UserId.HasValue) teacher.UserId = user.Id; - } - if (definition.Role == SystemRoles.Student) - { - var student = await db.Students.SingleAsync( - x => x.StudentNumber == definition.StaffNumber); - if (!student.UserId.HasValue) student.UserId = user.Id; - } - if (definition.Role == SystemRoles.Counselor) - { - var classes = await db.AdministrativeClasses - .Where(x => - x.CounselorUserId == null && - x.CounselorName == definition.DisplayName) - .ToListAsync(); - foreach (var administrativeClass in classes) - administrativeClass.CounselorUserId = user.Id; - } - } - - await db.SaveChangesAsync(); - } - private static void EnsureSucceeded(IdentityResult result, string action) { if (result.Succeeded) @@ -686,12 +191,4 @@ public sealed class DatabaseInitializer( throw new InvalidOperationException( $"{action}失败:{string.Join(";", result.Errors.Select(x => x.Description))}"); } - - private sealed record DevelopmentUser( - string UserName, - string DisplayName, - string Password, - string? StaffNumber, - Guid? CollegeId, - string Role); } diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseOptions.cs b/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseOptions.cs index 7eb7c8d..3107098 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseOptions.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/DatabaseOptions.cs @@ -4,4 +4,6 @@ public sealed class DatabaseOptions { public const string SectionName = "Database"; public string Provider { get; set; } = "MySql"; + public bool ApplyMigrationsOnStartup { get; set; } + public int CommandTimeoutSeconds { get; set; } = 30; } diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentDemoDataSeeder.cs b/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentDemoDataSeeder.cs deleted file mode 100644 index 27bb948..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/DevelopmentDemoDataSeeder.cs +++ /dev/null @@ -1,620 +0,0 @@ -using Jiaowu.Api.Domain.Academic; -using Microsoft.EntityFrameworkCore; - -namespace Jiaowu.Api.Infrastructure.Persistence; - -public sealed class DevelopmentDemoDataSeeder( - AppDbContext db, - ILogger logger) -{ - private const int Grade = 2026; - private const int ClassesPerMajor = 2; - private const int StudentsPerClass = 35; - private const int TeachersPerCollege = 8; - - public async Task SeedAsync(CancellationToken cancellationToken = default) - { - var campus = await db.Campuses - .OrderBy(x => x.Code == "MAIN" ? 0 : 1) - .ThenBy(x => x.Code) - .FirstOrDefaultAsync(cancellationToken); - if (campus is null) - { - campus = new Campus - { - Code = "MAIN", - Name = "主校区", - Address = "大学路 1 号" - }; - db.Campuses.Add(campus); - await db.SaveChangesAsync(cancellationToken); - } - - await SeedCollegesAsync(campus.Id, cancellationToken); - await SeedMajorsAsync(cancellationToken); - await SeedClassesAsync(cancellationToken); - await SeedTeachersAsync(cancellationToken); - await SeedStudentsAsync(cancellationToken); - await SeedCoursesAsync(cancellationToken); - await SeedTeacherCourseApplicationsAsync(cancellationToken); - await LogSummaryAsync(cancellationToken); - } - - private async Task SeedCollegesAsync(Guid campusId, CancellationToken cancellationToken) - { - var existingCodes = (await db.Colleges - .Select(x => x.Code) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = CollegeDefinitions - .Where(x => !existingCodes.Contains(x.Code)) - .Select((x, index) => new College - { - Code = x.Code, - Name = x.Name, - ShortName = x.ShortName, - CampusId = campusId, - SortOrder = (index + 1) * 10 - }) - .ToList(); - if (additions.Count == 0) return; - db.Colleges.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedMajorsAsync(CancellationToken cancellationToken) - { - var colleges = await db.Colleges - .ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken); - var existingCodes = (await db.Majors - .Select(x => x.Code) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = new List(); - foreach (var collegeDefinition in CollegeDefinitions) - { - if (!colleges.TryGetValue(collegeDefinition.Code, out var college)) continue; - for (var index = 0; index < collegeDefinition.Majors.Count; index++) - { - var definition = collegeDefinition.Majors[index]; - if (existingCodes.Contains(definition.Code)) continue; - additions.Add(new Major - { - Code = definition.Code, - Name = definition.Name, - CollegeId = college.Id, - DegreeType = definition.DegreeType, - SchoolingYears = definition.SchoolingYears, - SortOrder = (index + 1) * 10 - }); - } - } - if (additions.Count == 0) return; - db.Majors.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedClassesAsync(CancellationToken cancellationToken) - { - var targetMajorCodes = CollegeDefinitions - .SelectMany(x => x.Majors) - .Select(x => x.Code) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var majors = await db.Majors - .Where(x => targetMajorCodes.Contains(x.Code)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var existingClasses = await db.AdministrativeClasses - .Where(x => x.Grade == Grade && targetMajorCodes.Contains(x.Major!.Code)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var existingCodes = (await db.AdministrativeClasses - .Select(x => x.Code) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = new List(); - foreach (var major in majors) - { - var currentCount = existingClasses.Count(x => x.MajorId == major.Id); - for (var section = currentCount + 1; section <= ClassesPerMajor; section++) - { - var code = CreateAvailableCode( - $"{major.Code}-{Grade}-{section:D2}", - existingCodes); - additions.Add(new AdministrativeClass - { - Code = code, - Name = $"{major.Name}{Grade}级{section}班", - MajorId = major.Id, - Grade = Grade, - CounselorName = $"{CounselorSurnames[(section + major.Code.Length) % CounselorSurnames.Length]}老师", - SortOrder = section * 10 - }); - } - } - if (additions.Count == 0) return; - db.AdministrativeClasses.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedTeachersAsync(CancellationToken cancellationToken) - { - var collegeCodes = CollegeDefinitions - .Select(x => x.Code) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var colleges = await db.Colleges - .Where(x => collegeCodes.Contains(x.Code)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var existingTeachers = await db.Teachers - .Where(x => colleges.Select(c => c.Id).Contains(x.CollegeId)) - .ToListAsync(cancellationToken); - var existingNumbers = (await db.Teachers - .Select(x => x.TeacherNumber) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = new List(); - for (var collegeIndex = 0; collegeIndex < colleges.Count; collegeIndex++) - { - var college = colleges[collegeIndex]; - var currentCount = existingTeachers.Count(x => x.CollegeId == college.Id); - for (var position = currentCount + 1; position <= TeachersPerCollege; position++) - { - var teacherNumber = CreateAvailableCode( - $"T26{collegeIndex + 1:D2}{position:D3}", - existingNumbers); - additions.Add(new Teacher - { - TeacherNumber = teacherNumber, - Name = BuildPersonName(collegeIndex, position), - Gender = position % 2 == 0 ? Gender.Female : Gender.Male, - CollegeId = college.Id, - Title = TeacherTitles[(position - 1) % TeacherTitles.Length], - Status = TeacherStatus.Active, - HireDate = new DateOnly(2012 + position, 7, 1), - Email = $"{teacherNumber.ToLowerInvariant()}@example.edu.cn", - Notes = "Development 环境批量测试教师。" - }); - } - } - if (additions.Count == 0) return; - db.Teachers.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedStudentsAsync(CancellationToken cancellationToken) - { - var targetMajorCodes = CollegeDefinitions - .SelectMany(x => x.Majors) - .Select(x => x.Code) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var classes = await db.AdministrativeClasses - .Where(x => x.Grade == Grade && targetMajorCodes.Contains(x.Major!.Code)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var classIds = classes.Select(x => x.Id).ToHashSet(); - var existingStudents = await db.Students - .Where(x => classIds.Contains(x.AdministrativeClassId)) - .ToListAsync(cancellationToken); - var existingNumbers = (await db.Students - .Select(x => x.StudentNumber) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = new List(); - for (var classIndex = 0; classIndex < classes.Count; classIndex++) - { - var administrativeClass = classes[classIndex]; - var existingCount = existingStudents.Count(x => - x.AdministrativeClassId == administrativeClass.Id); - var candidate = 1; - while (existingCount + additions.Count(x => - x.AdministrativeClassId == administrativeClass.Id) < StudentsPerClass) - { - var studentNumber = $"{Grade}{classIndex + 1:D3}{candidate:D3}"; - candidate++; - if (!existingNumbers.Add(studentNumber)) continue; - additions.Add(new Student - { - StudentNumber = studentNumber, - Name = BuildPersonName(classIndex, candidate + 5), - Gender = candidate % 2 == 0 ? Gender.Male : Gender.Female, - AdministrativeClassId = administrativeClass.Id, - EnrollmentYear = Grade, - EnrollmentDate = new DateOnly(Grade, 9, 7), - DateOfBirth = new DateOnly(2007 + candidate % 2, candidate % 12 + 1, - candidate % 27 + 1), - Status = StudentStatus.Active, - Email = $"{studentNumber}@student.example.edu.cn", - Notes = "Development 环境批量测试学生。" - }); - } - } - if (additions.Count == 0) return; - db.Students.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedCoursesAsync(CancellationToken cancellationToken) - { - var colleges = await db.Colleges - .ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken); - var categories = await db.CourseCategories - .ToDictionaryAsync(x => x.Code, StringComparer.OrdinalIgnoreCase, cancellationToken); - var existingCodes = (await db.Courses - .Select(x => x.Code) - .ToListAsync(cancellationToken)) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var additions = new List(); - - foreach (var definition in PublicCourseDefinitions) - { - if (existingCodes.Contains(definition.Code) || - !colleges.TryGetValue(definition.CollegeCode, out var college) || - !categories.TryGetValue(definition.CategoryCode, out var category)) - continue; - additions.Add(new Course - { - Code = definition.Code, - Name = definition.Name, - CollegeId = college.Id, - CourseCategoryId = category.Id, - Credits = definition.Credits, - TotalHours = definition.TotalHours, - LectureHours = definition.LectureHours, - PracticeHours = definition.TotalHours - definition.LectureHours, - Nature = definition.Nature, - AssessmentMethod = definition.AssessmentMethod, - Description = "Development 环境公共课程测试数据。" - }); - } - - foreach (var collegeDefinition in CollegeDefinitions) - { - if (!colleges.TryGetValue(collegeDefinition.Code, out var college)) continue; - for (var index = 0; index < collegeDefinition.ProfessionalCourses.Count; index++) - { - var code = $"{collegeDefinition.Code}D{index + 1:D2}"; - if (existingCodes.Contains(code)) continue; - var nature = index switch - { - < 4 => CourseNature.MajorRequired, - < 6 => CourseNature.MajorElective, - _ => CourseNature.Practice - }; - var categoryCode = nature == CourseNature.Practice ? "PRACTICE" : "MAJOR"; - additions.Add(new Course - { - Code = code, - Name = collegeDefinition.ProfessionalCourses[index], - CollegeId = college.Id, - CourseCategoryId = categories[categoryCode].Id, - Credits = nature switch - { - CourseNature.MajorRequired => index % 2 == 0 ? 3m : 3.5m, - _ => 2m - }, - TotalHours = nature switch - { - CourseNature.MajorRequired => index % 2 == 0 ? 48 : 56, - CourseNature.MajorElective => 32, - _ => 48 - }, - LectureHours = nature == CourseNature.Practice ? 8 : - nature == CourseNature.MajorElective ? 24 : - index % 2 == 0 ? 40 : 48, - PracticeHours = nature == CourseNature.Practice ? 40 : - nature == CourseNature.MajorElective ? 8 : 8, - Nature = nature, - AssessmentMethod = nature == CourseNature.Practice - ? AssessmentMethod.Assessment - : AssessmentMethod.Examination, - Description = $"由{collegeDefinition.Name}开设的专业课程测试数据。" - }); - } - } - if (additions.Count == 0) return; - db.Courses.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task SeedTeacherCourseApplicationsAsync(CancellationToken cancellationToken) - { - var term = await db.AcademicTerms - .OrderByDescending(x => x.IsCurrent) - .ThenByDescending(x => x.StartDate) - .FirstOrDefaultAsync(cancellationToken); - if (term is null) return; - - var collegeCodes = CollegeDefinitions - .Select(x => x.Code) - .ToHashSet(StringComparer.OrdinalIgnoreCase); - var colleges = await db.Colleges - .Where(x => collegeCodes.Contains(x.Code)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var collegeIds = colleges.Select(x => x.Id).ToHashSet(); - var teachers = await db.Teachers - .Where(x => collegeIds.Contains(x.CollegeId) && x.Status == TeacherStatus.Active) - .OrderBy(x => x.TeacherNumber) - .ToListAsync(cancellationToken); - var courses = await db.Courses - .Where(x => collegeIds.Contains(x.CollegeId)) - .OrderBy(x => x.Code) - .ToListAsync(cancellationToken); - var publicCourses = courses - .Where(x => x.Nature is CourseNature.GeneralRequired or CourseNature.GeneralElective) - .ToList(); - if (publicCourses.Count == 0) return; - - var existing = (await db.TeacherCourseApplications - .Where(x => x.AcademicTermId == term.Id) - .Select(x => new { x.TeacherId, x.CourseId }) - .ToListAsync(cancellationToken)) - .Select(x => (x.TeacherId, x.CourseId)) - .ToHashSet(); - var additions = new List(); - for (var teacherIndex = 0; teacherIndex < teachers.Count; teacherIndex++) - { - var teacher = teachers[teacherIndex]; - var professionalCourses = courses - .Where(x => x.CollegeId == teacher.CollegeId && - x.Nature is CourseNature.MajorRequired or - CourseNature.MajorElective or CourseNature.Practice) - .Take(4); - var selectedPublicCourses = Enumerable.Range(0, 2) - .Select(offset => publicCourses[(teacherIndex + offset) % publicCourses.Count]); - foreach (var course in professionalCourses - .Concat(selectedPublicCourses) - .DistinctBy(x => x.Id)) - { - if (!existing.Add((teacher.Id, course.Id))) continue; - additions.Add(new TeacherCourseApplication - { - AcademicTermId = term.Id, - TeacherId = teacher.Id, - CourseId = course.Id, - Status = TeacherCourseApplicationStatus.Approved, - Statement = "Development 环境批量生成的授课意向。", - ReviewComment = "测试数据自动审核通过。", - SubmittedAt = DateTime.UtcNow.AddDays(-7), - ReviewedAt = DateTime.UtcNow.AddDays(-6) - }); - } - } - if (additions.Count == 0) return; - db.TeacherCourseApplications.AddRange(additions); - await db.SaveChangesAsync(cancellationToken); - } - - private async Task LogSummaryAsync(CancellationToken cancellationToken) - { - var courseCounts = await db.Courses - .GroupBy(x => x.Nature) - .Select(x => new { Nature = x.Key, Count = x.Count() }) - .ToDictionaryAsync(x => x.Nature, x => x.Count, cancellationToken); - logger.LogInformation( - "Development 测试数据已就绪:学院/教学单位 {CollegeCount},专业 {MajorCount}," + - "行政班 {ClassCount},教师 {TeacherCount},学生 {StudentCount},课程 {CourseCount};" + - "公共必修 {GeneralRequiredCount},公共选修 {GeneralElectiveCount}," + - "专业必修 {MajorRequiredCount},专业选修 {MajorElectiveCount},实践课程 {PracticeCount};" + - "已审核授课资格 {ApplicationCount}。", - await db.Colleges.CountAsync(cancellationToken), - await db.Majors.CountAsync(cancellationToken), - await db.AdministrativeClasses.CountAsync(cancellationToken), - await db.Teachers.CountAsync(cancellationToken), - await db.Students.CountAsync(cancellationToken), - await db.Courses.CountAsync(cancellationToken), - courseCounts.GetValueOrDefault(CourseNature.GeneralRequired), - courseCounts.GetValueOrDefault(CourseNature.GeneralElective), - courseCounts.GetValueOrDefault(CourseNature.MajorRequired), - courseCounts.GetValueOrDefault(CourseNature.MajorElective), - courseCounts.GetValueOrDefault(CourseNature.Practice), - await db.TeacherCourseApplications - .CountAsync(x => x.Status == TeacherCourseApplicationStatus.Approved, - cancellationToken)); - } - - private static string CreateAvailableCode(string preferredCode, ISet existingCodes) - { - var candidate = preferredCode; - var suffix = 1; - while (!existingCodes.Add(candidate)) - { - candidate = $"{preferredCode}-{suffix++}"; - } - return candidate; - } - - private static string BuildPersonName(int groupIndex, int position) - { - var surname = Surnames[(groupIndex + position) % Surnames.Length]; - var givenName = GivenNames[ - (groupIndex * 7 + position * 3) % GivenNames.Length]; - return surname + givenName; - } - - private static readonly string[] Surnames = - ["王", "李", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周", "徐", "孙", "马", "朱", "胡", "郭", "何", "高", "林", "罗"]; - - private static readonly string[] GivenNames = - ["明远", "知夏", "嘉树", "雨桐", "思源", "若溪", "景行", "书雅", "子涵", "浩然", "清越", "语晨", "承宇", "欣怡", "博文", "婉宁", "俊逸", "安然", "泽楷", "可心"]; - - private static readonly string[] CounselorSurnames = - ["陈", "林", "周", "王", "李", "张", "刘", "赵"]; - - private static readonly string[] TeacherTitles = - ["教授", "副教授", "讲师", "讲师", "副教授", "实验师", "讲师", "教授"]; - - private static readonly CollegeSeed[] CollegeDefinitions = - [ - new("CS", "计算机学院", "计算机学院", - [ - new("080901", "计算机科学与技术", "工学学士"), - new("080902", "软件工程", "工学学士"), - new("080903", "网络工程", "工学学士"), - new("080910T", "数据科学与大数据技术", "工学学士"), - new("080717T", "人工智能", "工学学士") - ], - ["程序设计基础", "离散数学", "数据结构", "计算机组成原理", "操作系统", "数据库系统原理", "软件工程课程设计", "人工智能项目实践"]), - new("EIA", "电子信息与自动化学院", "电子信息学院", - [ - new("080701", "电子信息工程", "工学学士"), - new("080703", "通信工程", "工学学士"), - new("080801", "自动化", "工学学士"), - new("080803T", "机器人工程", "工学学士") - ], - ["电路分析", "模拟电子技术", "数字电子技术", "信号与系统", "通信原理", "嵌入式系统", "电子系统设计", "综合电子实训"]), - new("ME", "机械与车辆工程学院", "机械学院", - [ - new("080202", "机械设计制造及其自动化", "工学学士"), - new("080207", "车辆工程", "工学学士"), - new("080205", "工业设计", "工学学士"), - new("080213T", "智能制造工程", "工学学士") - ], - ["工程制图", "理论力学", "材料力学", "机械原理", "机械设计", "智能制造技术", "机械创新设计", "工程训练"]), - new("EE", "电气工程学院", "电气学院", - [ - new("080601", "电气工程及其自动化", "工学学士"), - new("080604T", "电气工程与智能控制", "工学学士"), - new("080605T", "电机电器智能化", "工学学士") - ], - ["电路原理", "电机学", "电力电子技术", "自动控制原理", "电力系统分析", "继电保护", "电气控制实训", "电力系统综合设计"]), - new("CIVIL", "土木建筑工程学院", "土建学院", - [ - new("081001", "土木工程", "工学学士"), - new("082801", "建筑学", "建筑学学士", 5), - new("120103", "工程管理", "管理学学士"), - new("081006T", "道路桥梁与渡河工程", "工学学士") - ], - ["工程制图与识图", "工程力学", "结构力学", "混凝土结构", "土力学与地基基础", "工程项目管理", "建筑设计基础", "工程测量实习"]), - new("ECON", "经济与管理学院", "经管学院", - [ - new("120201K", "工商管理", "管理学学士"), - new("120203K", "会计学", "管理学学士"), - new("020301K", "金融学", "经济学学士"), - new("020401", "国际经济与贸易", "经济学学士"), - new("120202", "市场营销", "管理学学士") - ], - ["微观经济学", "宏观经济学", "管理学原理", "会计学原理", "统计学", "财务管理", "企业经营沙盘", "商务数据分析实践"]), - new("FOREIGN", "外国语学院", "外国语学院", - [ - new("050201", "英语", "文学学士"), - new("050207", "日语", "文学学士"), - new("050262", "商务英语", "文学学士") - ], - ["综合英语", "英语听力", "英语口语", "英语写作", "翻译理论与实践", "跨文化交际", "商务英语实训", "口译实践"]), - new("MATH", "数学与统计学院", "数统学院", - [ - new("070101", "数学与应用数学", "理学学士"), - new("071201", "统计学", "理学学士"), - new("020102", "经济统计学", "经济学学士") - ], - ["数学分析", "高等代数", "解析几何", "常微分方程", "实变函数", "数值分析", "数学建模", "统计软件实践"]), - new("PHYSICS", "物理与光电工程学院", "物电学院", - [ - new("070201", "物理学", "理学学士"), - new("070202", "应用物理学", "理学学士"), - new("080705", "光电信息科学与工程", "工学学士") - ], - ["力学", "热学", "电磁学", "光学", "量子力学", "固体物理", "近代物理实验", "光电技术综合实验"]), - new("CHEM", "化学与环境工程学院", "化环学院", - [ - new("070301", "化学", "理学学士"), - new("070302", "应用化学", "理学学士"), - new("081301", "化学工程与工艺", "工学学士"), - new("082502", "环境工程", "工学学士") - ], - ["无机化学", "有机化学", "分析化学", "物理化学", "化工原理", "仪器分析", "基础化学实验", "化工设计实践"]), - new("HUMANITIES", "人文与法学院", "人文法学院", - [ - new("050101", "汉语言文学", "文学学士"), - new("030101K", "法学", "法学学士"), - new("120402", "行政管理", "管理学学士") - ], - ["中国古代文学", "中国现当代文学", "现代汉语", "古代汉语", "文学概论", "行政管理学", "新闻写作实训", "社会调查实践"]), - new("EDU", "教育科学学院", "教育学院", - [ - new("040101", "教育学", "教育学学士"), - new("040107", "小学教育", "教育学学士"), - new("040106", "学前教育", "教育学学士") - ], - ["教育学原理", "普通心理学", "教育心理学", "课程与教学论", "教育研究方法", "班级管理", "微格教学", "教育见习"]), - new("ART", "艺术设计学院", "艺术学院", - [ - new("130502", "视觉传达设计", "艺术学学士"), - new("130503", "环境设计", "艺术学学士"), - new("130202", "音乐学", "艺术学学士") - ], - ["设计素描", "色彩基础", "构成基础", "艺术概论", "数字媒体设计", "品牌视觉设计", "专业采风", "毕业创作实践"]), - new("PE", "体育学院", "体育学院", - [ - new("040201", "体育教育", "教育学学士"), - new("040203", "社会体育指导与管理", "教育学学士") - ], - ["运动解剖学", "运动生理学", "学校体育学", "体育心理学", "运动训练学", "体育社会学", "田径专项训练", "球类专项训练"]), - new("LIFE", "生命科学与食品工程学院", "生食学院", - [ - new("071001", "生物科学", "理学学士"), - new("071002", "生物技术", "理学学士"), - new("082701", "食品科学与工程", "工学学士") - ], - ["普通生物学", "生物化学", "细胞生物学", "遗传学", "微生物学", "食品化学", "分子生物学实验", "生物工程综合实践"]), - new("MARXISM", "马克思主义学院", "马克思主义学院", [], []) - ]; - - private static readonly PublicCourseSeed[] PublicCourseDefinitions = - [ - new("PUB001", "思想道德与法治", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB002", "中国近现代史纲要", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB003", "马克思主义基本原理", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB004", "毛泽东思想和中国特色社会主义理论体系概论", "MARXISM", "MORAL", 5, 80, 64, CourseNature.GeneralRequired), - new("PUB005", "习近平新时代中国特色社会主义思想概论", "MARXISM", "MORAL", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB006", "形势与政策", "MARXISM", "MORAL", 2, 32, 32, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB007", "大学英语 I", "FOREIGN", "ENGLISH", 4, 64, 48, CourseNature.GeneralRequired), - new("PUB008", "大学英语 II", "FOREIGN", "ENGLISH", 4, 64, 48, CourseNature.GeneralRequired), - new("PUB009", "高等数学 A(上)", "MATH", "BASIC", 5, 80, 80, CourseNature.GeneralRequired), - new("PUB010", "高等数学 A(下)", "MATH", "BASIC", 5, 80, 80, CourseNature.GeneralRequired), - new("PUB011", "线性代数", "MATH", "BASIC", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB012", "概率论与数理统计", "MATH", "BASIC", 3, 48, 48, CourseNature.GeneralRequired), - new("PUB013", "大学计算机基础", "CS", "BASIC", 2, 32, 16, CourseNature.GeneralRequired), - new("PUB014", "Python 程序设计", "CS", "BASIC", 3, 48, 24, CourseNature.GeneralRequired), - new("PUB015", "大学体育 I", "PE", "SPORTS", 1, 32, 4, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB016", "大学体育 II", "PE", "SPORTS", 1, 32, 4, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB017", "军事理论", "HUMANITIES", "MILITARY", 2, 36, 32, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB018", "大学生心理健康教育", "EDU", "BASIC", 2, 32, 24, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB019", "大学生职业发展与就业指导", "EDU", "BASIC", 2, 32, 24, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB020", "创新创业基础", "ECON", "INNOVATION", 2, 32, 20, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB021", "劳动教育", "EDU", "LABOR", 1, 32, 8, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB022", "国家安全教育", "HUMANITIES", "MORAL", 1, 16, 16, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB023", "文献检索与学术规范", "HUMANITIES", "BASIC", 1, 16, 12, CourseNature.GeneralRequired, AssessmentMethod.Assessment), - new("PUB024", "艺术鉴赏", "ART", "AESTHETIC", 2, 32, 24, CourseNature.GeneralElective, AssessmentMethod.Assessment), - new("PUB025", "中国传统文化", "HUMANITIES", "AESTHETIC", 2, 32, 32, CourseNature.GeneralElective, AssessmentMethod.Assessment), - new("PUB026", "生态文明导论", "LIFE", "BASIC", 2, 32, 24, CourseNature.GeneralElective, AssessmentMethod.Assessment), - new("PUB027", "人工智能导论", "CS", "INNOVATION", 2, 32, 20, CourseNature.GeneralElective, AssessmentMethod.Assessment), - new("PUB028", "经济学通识", "ECON", "BASIC", 2, 32, 32, CourseNature.GeneralElective, AssessmentMethod.Assessment) - ]; - - private sealed record CollegeSeed( - string Code, - string Name, - string ShortName, - IReadOnlyList Majors, - IReadOnlyList ProfessionalCourses); - - private sealed record MajorSeed( - string Code, - string Name, - string DegreeType, - int SchoolingYears = 4); - - private sealed record PublicCourseSeed( - string Code, - string Name, - string CollegeCode, - string CategoryCode, - decimal Credits, - int TotalHours, - int LectureHours, - CourseNature Nature, - AssessmentMethod AssessmentMethod = AssessmentMethod.Examination); -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725051000_FlexibleGradesAndAttendance.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725051000_FlexibleGradesAndAttendance.cs deleted file mode 100644 index 22b3f45..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725051000_FlexibleGradesAndAttendance.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - /// - public partial class FlexibleGradesAndAttendance : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "GradeItems", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false), - GradeSheetId = table.Column(type: "char(36)", nullable: false), - Name = table.Column(type: "varchar(60)", maxLength: 60, nullable: false), - Weight = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), - SortOrder = table.Column(type: "int", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_GradeItems", x => x.Id); - table.ForeignKey( - name: "FK_GradeItems_GradeSheets_GradeSheetId", - column: x => x.GradeSheetId, - principalTable: "GradeSheets", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "GradeItemScores", - columns: table => new - { - GradeRecordId = table.Column(type: "char(36)", nullable: false), - GradeItemId = table.Column(type: "char(36)", nullable: false), - Score = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_GradeItemScores", x => new { x.GradeRecordId, x.GradeItemId }); - table.ForeignKey( - name: "FK_GradeItemScores_GradeItems_GradeItemId", - column: x => x.GradeItemId, - principalTable: "GradeItems", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_GradeItemScores_GradeRecords_GradeRecordId", - column: x => x.GradeRecordId, - principalTable: "GradeRecords", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_GradeItems_GradeSheetId_SortOrder", - table: "GradeItems", - columns: new[] { "GradeSheetId", "SortOrder" }); - - migrationBuilder.CreateIndex( - name: "IX_GradeItemScores_GradeRecordId_GradeItemId", - table: "GradeItemScores", - columns: new[] { "GradeRecordId", "GradeItemId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_GradeItemScores_GradeItemId", - table: "GradeItemScores", - column: "GradeItemId"); - - migrationBuilder.DropColumn( - name: "MidtermWeight", - table: "GradeSheets"); - - migrationBuilder.DropColumn( - name: "MidtermScore", - table: "GradeRecords"); - - migrationBuilder.CreateTable( - name: "AttendanceSheets", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false), - TeachingTaskId = table.Column(type: "char(36)", nullable: false), - Name = table.Column(type: "varchar(120)", maxLength: 120, nullable: false), - AttendanceDate = table.Column(type: "datetime(6)", nullable: false), - Status = table.Column(type: "int", nullable: false), - Notes = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - SubmittedAt = table.Column(type: "datetime(6)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AttendanceSheets", x => x.Id); - table.ForeignKey( - name: "FK_AttendanceSheets_TeachingTasks_TeachingTaskId", - column: x => x.TeachingTaskId, - principalTable: "TeachingTasks", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "AttendanceRecords", - columns: table => new - { - AttendanceSheetId = table.Column(type: "char(36)", nullable: false), - StudentId = table.Column(type: "char(36)", nullable: false), - Status = table.Column(type: "int", nullable: false), - Notes = table.Column(type: "varchar(300)", maxLength: 300, nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_AttendanceRecords", x => new { x.AttendanceSheetId, x.StudentId }); - table.ForeignKey( - name: "FK_AttendanceRecords_AttendanceSheets_AttendanceSheetId", - column: x => x.AttendanceSheetId, - principalTable: "AttendanceSheets", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_AttendanceRecords_Students_StudentId", - column: x => x.StudentId, - principalTable: "Students", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_AttendanceSheets_TeachingTaskId_AttendanceDate", - table: "AttendanceSheets", - columns: new[] { "TeachingTaskId", "AttendanceDate" }); - - migrationBuilder.CreateIndex( - name: "IX_AttendanceRecords_AttendanceSheetId_StudentId", - table: "AttendanceRecords", - columns: new[] { "AttendanceSheetId", "StudentId" }, - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_AttendanceRecords_StudentId", - table: "AttendanceRecords", - column: "StudentId"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "AttendanceRecords"); - migrationBuilder.DropTable(name: "AttendanceSheets"); - migrationBuilder.DropTable(name: "GradeItemScores"); - migrationBuilder.DropTable(name: "GradeItems"); - - migrationBuilder.AddColumn( - name: "MidtermWeight", - table: "GradeSheets", - type: "decimal(5,1)", - precision: 5, - scale: 1, - nullable: false, - defaultValue: 0m); - - migrationBuilder.AddColumn( - name: "MidtermScore", - table: "GradeRecords", - type: "decimal(5,1)", - precision: 5, - scale: 1, - nullable: true); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725100000_ExamSchedulingOptimization.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725100000_ExamSchedulingOptimization.cs deleted file mode 100644 index 714daee..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725100000_ExamSchedulingOptimization.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - /// - public partial class ExamSchedulingOptimization : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - // Drop old FK/index on ClassroomId - migrationBuilder.DropForeignKey( - name: "FK_ExamSessions_Classrooms_ClassroomId", - table: "ExamSessions"); - - migrationBuilder.DropIndex( - name: "IX_ExamSessions_ClassroomId", - table: "ExamSessions"); - - // Make ClassroomId nullable - migrationBuilder.AlterColumn( - name: "ClassroomId", - table: "ExamSessions", - type: "char(36)", - nullable: true, - oldClrType: typeof(Guid), - oldType: "char(36)"); - - // Add new columns - migrationBuilder.AddColumn( - name: "ExamDate", - table: "ExamSessions", - type: "date", - nullable: false, - defaultValue: new DateOnly(2027, 1, 1)); - - migrationBuilder.AddColumn( - name: "StartPeriod", - table: "ExamSessions", - type: "int", - nullable: false, - defaultValue: 1); - - migrationBuilder.AddColumn( - name: "PeriodCount", - table: "ExamSessions", - type: "int", - nullable: false, - defaultValue: 2); - - migrationBuilder.AddColumn( - name: "RequiredBuildingId", - table: "ExamSessions", - type: "char(36)", - nullable: true); - - migrationBuilder.AddColumn( - name: "RequiredInvigilatorCount", - table: "ExamSessions", - type: "int", - nullable: false, - defaultValue: 2); - - // Re-add FK/index on ClassroomId (nullable, SetNull) - migrationBuilder.CreateIndex( - name: "IX_ExamSessions_ClassroomId", - table: "ExamSessions", - column: "ClassroomId"); - - migrationBuilder.AddForeignKey( - name: "FK_ExamSessions_Classrooms_ClassroomId", - table: "ExamSessions", - column: "ClassroomId", - principalTable: "Classrooms", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - - // New indices - migrationBuilder.CreateIndex( - name: "IX_ExamSessions_ExamPlanId_ExamDate", - table: "ExamSessions", - columns: new[] { "ExamPlanId", "ExamDate" }); - - migrationBuilder.CreateIndex( - name: "IX_ExamSessions_RequiredBuildingId", - table: "ExamSessions", - column: "RequiredBuildingId"); - - migrationBuilder.AddForeignKey( - name: "FK_ExamSessions_Buildings_RequiredBuildingId", - table: "ExamSessions", - column: "RequiredBuildingId", - principalTable: "Buildings", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - // Remove new FK/index - migrationBuilder.DropForeignKey( - name: "FK_ExamSessions_Buildings_RequiredBuildingId", - table: "ExamSessions"); - - migrationBuilder.DropForeignKey( - name: "FK_ExamSessions_Classrooms_ClassroomId", - table: "ExamSessions"); - - migrationBuilder.DropIndex( - name: "IX_ExamSessions_ClassroomId", - table: "ExamSessions"); - - migrationBuilder.DropIndex( - name: "IX_ExamSessions_ExamPlanId_ExamDate", - table: "ExamSessions"); - - migrationBuilder.DropIndex( - name: "IX_ExamSessions_RequiredBuildingId", - table: "ExamSessions"); - - // Drop new columns - migrationBuilder.DropColumn(name: "RequiredInvigilatorCount", table: "ExamSessions"); - migrationBuilder.DropColumn(name: "RequiredBuildingId", table: "ExamSessions"); - migrationBuilder.DropColumn(name: "PeriodCount", table: "ExamSessions"); - migrationBuilder.DropColumn(name: "StartPeriod", table: "ExamSessions"); - migrationBuilder.DropColumn(name: "ExamDate", table: "ExamSessions"); - - // Restore ClassroomId to non-nullable - migrationBuilder.AlterColumn( - name: "ClassroomId", - table: "ExamSessions", - type: "char(36)", - nullable: false, - defaultValue: Guid.Empty, - oldClrType: typeof(Guid), - oldType: "char(36)", - oldNullable: true); - - // Restore original FK/index - migrationBuilder.CreateIndex( - name: "IX_ExamSessions_ClassroomId", - table: "ExamSessions", - column: "ClassroomId"); - - migrationBuilder.AddForeignKey( - name: "FK_ExamSessions_Classrooms_ClassroomId", - table: "ExamSessions", - column: "ClassroomId", - principalTable: "Classrooms", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs deleted file mode 100644 index 0d4eba8..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.cs +++ /dev/null @@ -1,22 +0,0 @@ -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/20260725103117_TeachingEvaluation.Designer.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.Designer.cs similarity index 70% rename from src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs rename to src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.Designer.cs index 35cd4bc..38c3a21 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725103117_TeachingEvaluation.Designer.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.Designer.cs @@ -11,58 +11,60 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { [DbContext(typeof(AppDbContext))] - [Migration("20260725103117_TeachingEvaluation")] - partial class TeachingEvaluation + [Migration("20260725120917_ProductionSchemaCompletion")] + partial class ProductionSchemaCompletion { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AcademicTerm", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicYear") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("IsCurrent") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Season") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -80,41 +82,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CounselorName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("CounselorUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Grade") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("MajorId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -133,34 +135,34 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => { b.Property("AttendanceSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AppealReason") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("AppealReviewComment") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("AppealReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("AppealStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AppealSubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Notes") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("AttendanceSheetId", "StudentId"); @@ -175,34 +177,34 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AttendanceDate") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -215,50 +217,50 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ActiveSchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedEntries") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("MessagesJson") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ProcessedTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequestedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -278,32 +280,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -321,32 +323,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Address") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -362,42 +364,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("BuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Equipment") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("RoomType") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -415,35 +417,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("ShortName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -461,62 +463,62 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AssessmentMethod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseCategoryId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Credits") .HasPrecision(5, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,2)"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("EnglishName") .HasMaxLength(150) - .HasColumnType("TEXT"); + .HasColumnType("varchar(150)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LectureHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Nature") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PracticeHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -536,67 +538,67 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApplicantUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CancelDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("CancelWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SubstituteTeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TargetDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -617,29 +619,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -655,31 +657,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseSelectionOfferingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrolledAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrollmentType") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WithdrawnAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -695,40 +697,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -746,29 +748,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CourseSelectionRoundId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsOpenToAll") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -784,41 +786,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("MaxCredits") .HasPrecision(6, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,1)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WithdrawalEndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -831,43 +833,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("OriginalCourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SubstituteCourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -887,29 +889,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumModuleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("RecommendedSemester") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -925,33 +927,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -965,43 +967,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("EffectiveGrade") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("MajorId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.HasKey("Id"); @@ -1017,40 +1019,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1068,43 +1070,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeName") .IsRequired() .HasMaxLength(80) - .HasColumnType("TEXT"); + .HasColumnType("varchar(80)"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("MinimumGradePoint") .HasPrecision(3, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(3,2)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1117,47 +1119,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AverageGradePoint") .HasPrecision(4, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(4,2)"); b.Property("CalculatedConclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Conclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeAwardBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ExceptionReason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("GraduationAuditResultId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsOverridden") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1177,27 +1179,27 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EvaluationSetupId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("MaxScore") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(60) - .HasColumnType("TEXT"); + .HasColumnType("varchar(60)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1210,25 +1212,25 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EvaluationSetupId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1245,13 +1247,13 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => { b.Property("EvaluationRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("EvaluationDimensionId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Score") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("EvaluationRecordId", "EvaluationDimensionId"); @@ -1264,30 +1266,30 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1300,31 +1302,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1337,47 +1339,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ExamDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("ExamPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredBuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiredInvigilatorCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1397,10 +1399,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSessionInvigilator", b => { b.Property("ExamSessionId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("ExamSessionId", "TeacherId"); @@ -1413,28 +1415,28 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GradeSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(60) - .HasColumnType("TEXT"); + .HasColumnType("varchar(60)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Weight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.HasKey("Id"); @@ -1446,14 +1448,14 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => { b.Property("GradeRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("GradeItemId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Score") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.HasKey("GradeRecordId", "GradeItemId"); @@ -1466,54 +1468,54 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApplicantUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CollegeReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CollegeReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurrentScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("FinalReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("FinalReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("GradeRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("RequestedScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1528,42 +1530,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ExamStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("FinalScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("GradePoint") .HasPrecision(3, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(3,1)"); b.Property("GradeSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("RegularScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TotalScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1579,40 +1581,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("FinalWeight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("RegularWeight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1628,37 +1630,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrollmentYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1673,63 +1675,63 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedConclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Conclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("EarnedCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("FailedCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GraduationAuditBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsOverridden") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("MissingCourseNames") .IsRequired() .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("PassedRequiredCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentStatusSnapshot") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1749,31 +1751,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClosedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1786,42 +1788,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationClearanceBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsRequired") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("ResponsibleRole") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("ResponsibleUnit") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1835,32 +1837,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationClearanceItemId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1876,39 +1878,39 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeType") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SchoolingYears") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1922,37 +1924,234 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("Majors"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamAutoJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedSessions") + .HasColumnType("int"); + + b.Property("EnrolledStudents") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("MakeupExamPlanId") + .HasColumnType("char(36)"); + + b.Property("MessagesJson") + .HasColumnType("longtext"); + + b.Property("ProcessedCourses") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalCourses") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("MakeupExamPlanId", "CreatedAt"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("MakeupExamAutoJobs"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamEnrollment", b => + { + b.Property("MakeupExamSessionId") + .HasColumnType("char(36)"); + + b.Property("StudentId") + .HasColumnType("char(36)"); + + b.Property("MakeupScore") + .HasPrecision(5, 1) + .HasColumnType("decimal(5,1)"); + + b.Property("Reason") + .HasColumnType("int"); + + b.Property("SourceDeferredExamId") + .HasColumnType("char(36)"); + + b.Property("SourceGradeRecordId") + .HasColumnType("char(36)"); + + b.HasKey("MakeupExamSessionId", "StudentId"); + + b.HasIndex("SourceDeferredExamId"); + + b.HasIndex("SourceGradeRecordId"); + + b.HasIndex("StudentId", "MakeupExamSessionId"); + + b.ToTable("MakeupExamEnrollments"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AcademicTermId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PublishedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("MakeupExamPlans"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ClassroomId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndsAt") + .HasColumnType("datetime(6)"); + + b.Property("ExamDate") + .HasColumnType("date"); + + b.Property("MakeupExamPlanId") + .HasColumnType("char(36)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PeriodCount") + .HasColumnType("int"); + + b.Property("RequiredBuildingId") + .HasColumnType("char(36)"); + + b.Property("RequiredInvigilatorCount") + .HasColumnType("int"); + + b.Property("StartPeriod") + .HasColumnType("int"); + + b.Property("StartsAt") + .HasColumnType("datetime(6)"); + + b.Property("TeachingTaskId") + .HasColumnType("char(36)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("RequiredBuildingId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("MakeupExamPlanId", "ExamDate"); + + b.HasIndex("MakeupExamPlanId", "StartsAt"); + + b.ToTable("MakeupExamSessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSessionInvigilator", b => + { + b.Property("MakeupExamSessionId") + .HasColumnType("char(36)"); + + b.Property("TeacherId") + .HasColumnType("char(36)"); + + b.HasKey("MakeupExamSessionId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("MakeupExamSessionInvigilators"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Notification", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Content") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsRead") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LinkUrl") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("Title") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT"); + .HasColumnType("varchar(200)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -1967,44 +2166,44 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("EndWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WeekPattern") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -2021,36 +2220,36 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.HasKey("Id"); @@ -2066,48 +2265,48 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ActiveAcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedSteps") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurrentStep") .HasMaxLength(200) - .HasColumnType("TEXT"); + .HasColumnType("varchar(200)"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("RequestedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalSteps") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2127,33 +2326,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("time"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("PeriodNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("time"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2167,56 +2366,56 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AdministrativeClassId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DateOfBirth") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("EnrollmentDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("EnrollmentYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Gender") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2236,46 +2435,46 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApprovedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("OriginalStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Reason") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("State") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TargetStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2288,57 +2487,57 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Gender") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("HireDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("IsExternal") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TeacherNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Title") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2356,42 +2555,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Statement") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2413,58 +2612,58 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GenerationBatchCode") .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SchedulingMode") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TaskNumber") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WeeklyHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -2481,10 +2680,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskAllowedClassroom", b => { b.Property("TeachingTaskScheduleConstraintId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("TeachingTaskScheduleConstraintId", "ClassroomId"); @@ -2496,10 +2695,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskClass", b => { b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AdministrativeClassId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("TeachingTaskId", "AdministrativeClassId"); @@ -2512,35 +2711,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AllowedDayOfWeeks") .HasMaxLength(20) - .HasColumnType("TEXT"); + .HasColumnType("varchar(20)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EarliestPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("LatestPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredBuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiredCampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiresClassroom") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2557,13 +2756,13 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskTeacher", b => { b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsPrimary") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.HasKey("TeachingTaskId", "TeacherId"); @@ -2576,41 +2775,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcknowledgeComment") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("AcknowledgedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Detail") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TriggerValue") .HasPrecision(7, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(7,2)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2626,56 +2825,56 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AutoCheckEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("CheckDayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CheckHour") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CheckMinute") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Description") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LastCheckAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("NotifyCounselor") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("NotifyStudent") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Threshold") .HasPrecision(7, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(7,2)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2689,26 +2888,26 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("DataScope") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Description") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Name") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("NormalizedName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.HasKey("Id"); @@ -2723,75 +2922,75 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DisplayName") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Email") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LastLoginAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LockoutEnd") - .HasColumnType("TEXT"); + .HasColumnType("datetime"); b.Property("NormalizedEmail") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("NormalizedUserName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("PasswordHash") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("PhoneNumber") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("SecurityStamp") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("StaffNumber") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("UserName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.HasKey("Id"); @@ -2811,37 +3010,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IpAddress") .HasMaxLength(64) - .HasColumnType("TEXT"); + .HasColumnType("varchar(64)"); b.Property("Method") .IsRequired() .HasMaxLength(10) - .HasColumnType("TEXT"); + .HasColumnType("varchar(10)"); b.Property("Path") .IsRequired() .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("StatusCode") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UserName") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.HasKey("Id"); @@ -2854,16 +3053,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClaimType") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ClaimValue") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("RoleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2876,16 +3075,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClaimType") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ClaimValue") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2897,16 +3096,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderKey") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("LoginProvider", "ProviderKey"); @@ -2918,10 +3117,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RoleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("UserId", "RoleId"); @@ -2933,16 +3132,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("LoginProvider") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("Name") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("Value") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("UserId", "LoginProvider", "Name"); @@ -3539,6 +3738,113 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("College"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamAutoJob", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamPlan", "MakeupExamPlan") + .WithMany() + .HasForeignKey("MakeupExamPlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MakeupExamPlan"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamEnrollment", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamSession", "MakeupExamSession") + .WithMany("Enrollments") + .HasForeignKey("MakeupExamSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.DeferredExam", "SourceDeferredExam") + .WithMany() + .HasForeignKey("SourceDeferredExamId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "SourceGradeRecord") + .WithMany() + .HasForeignKey("SourceGradeRecordId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("MakeupExamSession"); + + b.Navigation("SourceDeferredExam"); + + b.Navigation("SourceGradeRecord"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", 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.MakeupExamSession", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamPlan", "MakeupExamPlan") + .WithMany("Sessions") + .HasForeignKey("MakeupExamPlanId") + .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("MakeupExamPlan"); + + b.Navigation("RequiredBuilding"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSessionInvigilator", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamSession", "MakeupExamSession") + .WithMany("Invigilators") + .HasForeignKey("MakeupExamSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "Teacher") + .WithMany() + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("MakeupExamSession"); + + b.Navigation("Teacher"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleEntry", b => { b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") @@ -3946,6 +4252,18 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Records"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", b => + { + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSession", b => + { + b.Navigation("Enrollments"); + + b.Navigation("Invigilators"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePlan", b => { b.Navigation("Entries"); diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.cs new file mode 100644 index 0000000..0158c84 --- /dev/null +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725120917_ProductionSchemaCompletion.cs @@ -0,0 +1,1173 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql +{ + /// + public partial class ProductionSchemaCompletion : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ExamSessions_Classrooms_ClassroomId", + table: "ExamSessions"); + + migrationBuilder.AlterColumn( + name: "ClassroomId", + table: "ExamSessions", + type: "char(36)", + nullable: true, + oldClrType: typeof(Guid), + oldType: "char(36)"); + + migrationBuilder.AddColumn( + name: "ExamDate", + table: "ExamSessions", + type: "date", + nullable: true); + + migrationBuilder.AddColumn( + name: "PeriodCount", + table: "ExamSessions", + type: "int", + nullable: false, + defaultValue: 2); + + migrationBuilder.AddColumn( + name: "RequiredBuildingId", + table: "ExamSessions", + type: "char(36)", + nullable: true); + + migrationBuilder.AddColumn( + name: "RequiredInvigilatorCount", + table: "ExamSessions", + type: "int", + nullable: false, + defaultValue: 2); + + migrationBuilder.AddColumn( + name: "StartPeriod", + table: "ExamSessions", + type: "int", + nullable: false, + defaultValue: 1); + + migrationBuilder.AddColumn( + name: "EnrollmentType", + table: "CourseEnrollments", + type: "int", + nullable: false, + defaultValue: 1); + + migrationBuilder.CreateTable( + name: "AttendanceSheets", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "varchar(120)", maxLength: 120, nullable: false), + AttendanceDate = table.Column(type: "datetime(6)", nullable: false), + Status = table.Column(type: "int", nullable: false), + Notes = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + SubmittedAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AttendanceSheets", x => x.Id); + table.ForeignKey( + name: "FK_AttendanceSheets_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CourseAdjustments", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + Type = table.Column(type: "int", nullable: false), + Status = table.Column(type: "int", nullable: false), + ApplicantUserId = table.Column(type: "char(36)", nullable: false), + TargetDate = table.Column(type: "date", nullable: true), + DayOfWeek = table.Column(type: "int", nullable: true), + StartPeriod = table.Column(type: "int", nullable: true), + PeriodCount = table.Column(type: "int", nullable: true), + ClassroomId = table.Column(type: "char(36)", nullable: true), + SubstituteTeacherId = table.Column(type: "char(36)", nullable: true), + CancelWeek = table.Column(type: "int", nullable: true), + CancelDate = table.Column(type: "date", nullable: true), + Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), + ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + SubmittedAt = table.Column(type: "datetime(6)", nullable: true), + ReviewedAt = table.Column(type: "datetime(6)", nullable: true), + ReviewedByUserId = table.Column(type: "char(36)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CourseAdjustments", x => x.Id); + table.ForeignKey( + name: "FK_CourseAdjustments_Classrooms_ClassroomId", + column: x => x.ClassroomId, + principalTable: "Classrooms", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_CourseAdjustments_Teachers_SubstituteTeacherId", + column: x => x.SubstituteTeacherId, + principalTable: "Teachers", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_CourseAdjustments_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CourseExemptions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), + Status = table.Column(type: "int", nullable: false), + ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + SubmittedAt = table.Column(type: "datetime(6)", nullable: false), + ReviewedAt = table.Column(type: "datetime(6)", nullable: true), + ReviewedByUserId = table.Column(type: "char(36)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CourseExemptions", x => x.Id); + table.ForeignKey( + name: "FK_CourseExemptions_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CourseExemptions_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "CourseSubstitutions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + OriginalCourseId = table.Column(type: "char(36)", nullable: false), + SubstituteCourseId = table.Column(type: "char(36)", nullable: false), + Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), + Status = table.Column(type: "int", nullable: false), + ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + SubmittedAt = table.Column(type: "datetime(6)", nullable: false), + ReviewedAt = table.Column(type: "datetime(6)", nullable: true), + ReviewedByUserId = table.Column(type: "char(36)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CourseSubstitutions", x => x.Id); + table.ForeignKey( + name: "FK_CourseSubstitutions_Courses_OriginalCourseId", + column: x => x.OriginalCourseId, + principalTable: "Courses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CourseSubstitutions_Courses_SubstituteCourseId", + column: x => x.SubstituteCourseId, + principalTable: "Courses", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_CourseSubstitutions_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "DeferredExams", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), + Status = table.Column(type: "int", nullable: false), + ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + SubmittedAt = table.Column(type: "datetime(6)", nullable: false), + ReviewedAt = table.Column(type: "datetime(6)", nullable: true), + ReviewedByUserId = table.Column(type: "char(36)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_DeferredExams", x => x.Id); + table.ForeignKey( + name: "FK_DeferredExams_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_DeferredExams_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "EvaluationSetups", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + AcademicTermId = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "varchar(120)", maxLength: 120, nullable: false), + Status = table.Column(type: "int", nullable: false), + StartsAt = table.Column(type: "datetime(6)", nullable: true), + EndsAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EvaluationSetups", x => x.Id); + table.ForeignKey( + name: "FK_EvaluationSetups_AcademicTerms_AcademicTermId", + column: x => x.AcademicTermId, + principalTable: "AcademicTerms", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "GradeItems", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + GradeSheetId = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "varchar(60)", maxLength: 60, nullable: false), + Weight = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), + SortOrder = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GradeItems", x => x.Id); + table.ForeignKey( + name: "FK_GradeItems_GradeSheets_GradeSheetId", + column: x => x.GradeSheetId, + principalTable: "GradeSheets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "GradeModifications", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + GradeRecordId = table.Column(type: "char(36)", nullable: false), + CurrentScore = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), + RequestedScore = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), + Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), + Status = table.Column(type: "int", nullable: false), + ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + ApplicantUserId = table.Column(type: "char(36)", nullable: false), + SubmittedAt = table.Column(type: "datetime(6)", nullable: false), + CollegeReviewedAt = table.Column(type: "datetime(6)", nullable: true), + CollegeReviewedByUserId = table.Column(type: "char(36)", nullable: true), + FinalReviewedAt = table.Column(type: "datetime(6)", nullable: true), + FinalReviewedByUserId = table.Column(type: "char(36)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GradeModifications", x => x.Id); + table.ForeignKey( + name: "FK_GradeModifications_GradeRecords_GradeRecordId", + column: x => x.GradeRecordId, + principalTable: "GradeRecords", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MakeupExamPlans", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + AcademicTermId = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "varchar(120)", maxLength: 120, nullable: false), + Status = table.Column(type: "int", nullable: false), + Notes = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + PublishedAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MakeupExamPlans", x => x.Id); + table.ForeignKey( + name: "FK_MakeupExamPlans_AcademicTerms_AcademicTermId", + column: x => x.AcademicTermId, + principalTable: "AcademicTerms", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Notifications", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + UserId = table.Column(type: "char(36)", nullable: false), + Title = table.Column(type: "varchar(200)", maxLength: 200, nullable: false), + Content = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false), + IsRead = table.Column(type: "tinyint(1)", nullable: false), + LinkUrl = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Notifications", x => x.Id); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WarningRecords", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + Type = table.Column(type: "int", nullable: false), + Status = table.Column(type: "int", nullable: false), + TriggerValue = table.Column(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false), + Detail = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false), + AcknowledgedAt = table.Column(type: "datetime(6)", nullable: true), + AcknowledgeComment = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), + AcademicTermId = table.Column(type: "char(36)", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WarningRecords", x => x.Id); + table.ForeignKey( + name: "FK_WarningRecords_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "WarningRules", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + Type = table.Column(type: "int", nullable: false), + Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false), + Threshold = table.Column(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false), + IsEnabled = table.Column(type: "tinyint(1)", nullable: false), + NotifyStudent = table.Column(type: "tinyint(1)", nullable: false), + NotifyCounselor = table.Column(type: "tinyint(1)", nullable: false), + AcademicTermId = table.Column(type: "char(36)", nullable: false), + Description = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), + AutoCheckEnabled = table.Column(type: "tinyint(1)", nullable: false), + CheckDayOfWeek = table.Column(type: "int", nullable: true), + CheckHour = table.Column(type: "int", nullable: false), + CheckMinute = table.Column(type: "int", nullable: false), + LastCheckAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_WarningRules", x => x.Id); + table.ForeignKey( + name: "FK_WarningRules_AcademicTerms_AcademicTermId", + column: x => x.AcademicTermId, + principalTable: "AcademicTerms", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "AttendanceRecords", + columns: table => new + { + AttendanceSheetId = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + Status = table.Column(type: "int", nullable: false), + Notes = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), + AppealStatus = table.Column(type: "int", nullable: false), + AppealReason = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + AppealSubmittedAt = table.Column(type: "datetime(6)", nullable: true), + AppealReviewComment = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), + AppealReviewedAt = table.Column(type: "datetime(6)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AttendanceRecords", x => new { x.AttendanceSheetId, x.StudentId }); + table.ForeignKey( + name: "FK_AttendanceRecords_AttendanceSheets_AttendanceSheetId", + column: x => x.AttendanceSheetId, + principalTable: "AttendanceSheets", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AttendanceRecords_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "EvaluationDimensions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + EvaluationSetupId = table.Column(type: "char(36)", nullable: false), + Name = table.Column(type: "varchar(60)", maxLength: 60, nullable: false), + MaxScore = table.Column(type: "int", nullable: false), + SortOrder = table.Column(type: "int", nullable: false), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EvaluationDimensions", x => x.Id); + table.ForeignKey( + name: "FK_EvaluationDimensions_EvaluationSetups_EvaluationSetupId", + column: x => x.EvaluationSetupId, + principalTable: "EvaluationSetups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "EvaluationRecords", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + EvaluationSetupId = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + SubmittedAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EvaluationRecords", x => x.Id); + table.ForeignKey( + name: "FK_EvaluationRecords_EvaluationSetups_EvaluationSetupId", + column: x => x.EvaluationSetupId, + principalTable: "EvaluationSetups", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EvaluationRecords_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EvaluationRecords_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "GradeItemScores", + columns: table => new + { + GradeRecordId = table.Column(type: "char(36)", nullable: false), + GradeItemId = table.Column(type: "char(36)", nullable: false), + Score = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_GradeItemScores", x => new { x.GradeRecordId, x.GradeItemId }); + table.ForeignKey( + name: "FK_GradeItemScores_GradeItems_GradeItemId", + column: x => x.GradeItemId, + principalTable: "GradeItems", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_GradeItemScores_GradeRecords_GradeRecordId", + column: x => x.GradeRecordId, + principalTable: "GradeRecords", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MakeupExamAutoJobs", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + MakeupExamPlanId = table.Column(type: "char(36)", nullable: false), + Status = table.Column(type: "int", nullable: false), + TotalCourses = table.Column(type: "int", nullable: false), + ProcessedCourses = table.Column(type: "int", nullable: false), + CreatedSessions = table.Column(type: "int", nullable: false), + EnrolledStudents = table.Column(type: "int", nullable: false), + MessagesJson = table.Column(type: "longtext", nullable: true), + ErrorMessage = table.Column(type: "varchar(2000)", maxLength: 2000, nullable: true), + StartedAt = table.Column(type: "datetime(6)", nullable: true), + CompletedAt = table.Column(type: "datetime(6)", nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MakeupExamAutoJobs", x => x.Id); + table.ForeignKey( + name: "FK_MakeupExamAutoJobs_MakeupExamPlans_MakeupExamPlanId", + column: x => x.MakeupExamPlanId, + principalTable: "MakeupExamPlans", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MakeupExamSessions", + columns: table => new + { + Id = table.Column(type: "char(36)", nullable: false), + MakeupExamPlanId = table.Column(type: "char(36)", nullable: false), + TeachingTaskId = table.Column(type: "char(36)", nullable: false), + ClassroomId = table.Column(type: "char(36)", nullable: true), + ExamDate = table.Column(type: "date", nullable: false), + StartPeriod = table.Column(type: "int", nullable: false), + PeriodCount = table.Column(type: "int", nullable: false), + StartsAt = table.Column(type: "datetime(6)", nullable: false), + EndsAt = table.Column(type: "datetime(6)", nullable: false), + RequiredBuildingId = table.Column(type: "char(36)", nullable: true), + RequiredInvigilatorCount = table.Column(type: "int", nullable: false), + Notes = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), + CreatedAt = table.Column(type: "datetime(6)", nullable: false), + UpdatedAt = table.Column(type: "datetime(6)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MakeupExamSessions", x => x.Id); + table.ForeignKey( + name: "FK_MakeupExamSessions_Buildings_RequiredBuildingId", + column: x => x.RequiredBuildingId, + principalTable: "Buildings", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_MakeupExamSessions_Classrooms_ClassroomId", + column: x => x.ClassroomId, + principalTable: "Classrooms", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_MakeupExamSessions_MakeupExamPlans_MakeupExamPlanId", + column: x => x.MakeupExamPlanId, + principalTable: "MakeupExamPlans", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MakeupExamSessions_TeachingTasks_TeachingTaskId", + column: x => x.TeachingTaskId, + principalTable: "TeachingTasks", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "EvaluationScores", + columns: table => new + { + EvaluationRecordId = table.Column(type: "char(36)", nullable: false), + EvaluationDimensionId = table.Column(type: "char(36)", nullable: false), + Score = table.Column(type: "int", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_EvaluationScores", x => new { x.EvaluationRecordId, x.EvaluationDimensionId }); + table.ForeignKey( + name: "FK_EvaluationScores_EvaluationDimensions_EvaluationDimensionId", + column: x => x.EvaluationDimensionId, + principalTable: "EvaluationDimensions", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_EvaluationScores_EvaluationRecords_EvaluationRecordId", + column: x => x.EvaluationRecordId, + principalTable: "EvaluationRecords", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MakeupExamEnrollments", + columns: table => new + { + MakeupExamSessionId = table.Column(type: "char(36)", nullable: false), + StudentId = table.Column(type: "char(36)", nullable: false), + Reason = table.Column(type: "int", nullable: false), + SourceGradeRecordId = table.Column(type: "char(36)", nullable: true), + SourceDeferredExamId = table.Column(type: "char(36)", nullable: true), + MakeupScore = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MakeupExamEnrollments", x => new { x.MakeupExamSessionId, x.StudentId }); + table.ForeignKey( + name: "FK_MakeupExamEnrollments_DeferredExams_SourceDeferredExamId", + column: x => x.SourceDeferredExamId, + principalTable: "DeferredExams", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_MakeupExamEnrollments_GradeRecords_SourceGradeRecordId", + column: x => x.SourceGradeRecordId, + principalTable: "GradeRecords", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_MakeupExamEnrollments_MakeupExamSessions_MakeupExamSessionId", + column: x => x.MakeupExamSessionId, + principalTable: "MakeupExamSessions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MakeupExamEnrollments_Students_StudentId", + column: x => x.StudentId, + principalTable: "Students", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "MakeupExamSessionInvigilators", + columns: table => new + { + MakeupExamSessionId = table.Column(type: "char(36)", nullable: false), + TeacherId = table.Column(type: "char(36)", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_MakeupExamSessionInvigilators", x => new { x.MakeupExamSessionId, x.TeacherId }); + table.ForeignKey( + name: "FK_MakeupExamSessionInvigilators_MakeupExamSessions_MakeupExamS~", + column: x => x.MakeupExamSessionId, + principalTable: "MakeupExamSessions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_MakeupExamSessionInvigilators_Teachers_TeacherId", + column: x => x.TeacherId, + principalTable: "Teachers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }) + .Annotation("MySQL:Charset", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_ExamSessions_ExamPlanId_ExamDate", + table: "ExamSessions", + columns: new[] { "ExamPlanId", "ExamDate" }); + + migrationBuilder.CreateIndex( + name: "IX_ExamSessions_RequiredBuildingId", + table: "ExamSessions", + column: "RequiredBuildingId"); + + migrationBuilder.CreateIndex( + name: "IX_AttendanceRecords_AppealStatus", + table: "AttendanceRecords", + column: "AppealStatus"); + + migrationBuilder.CreateIndex( + name: "IX_AttendanceRecords_StudentId", + table: "AttendanceRecords", + column: "StudentId"); + + migrationBuilder.CreateIndex( + name: "IX_AttendanceSheets_TeachingTaskId_AttendanceDate", + table: "AttendanceSheets", + columns: new[] { "TeachingTaskId", "AttendanceDate" }); + + migrationBuilder.CreateIndex( + name: "IX_CourseAdjustments_ApplicantUserId", + table: "CourseAdjustments", + column: "ApplicantUserId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseAdjustments_ClassroomId", + table: "CourseAdjustments", + column: "ClassroomId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseAdjustments_Status_CreatedAt", + table: "CourseAdjustments", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_CourseAdjustments_SubstituteTeacherId", + table: "CourseAdjustments", + column: "SubstituteTeacherId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseAdjustments_TeachingTaskId_Status", + table: "CourseAdjustments", + columns: new[] { "TeachingTaskId", "Status" }); + + migrationBuilder.CreateIndex( + name: "IX_CourseExemptions_Status_CreatedAt", + table: "CourseExemptions", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_CourseExemptions_StudentId_TeachingTaskId", + table: "CourseExemptions", + columns: new[] { "StudentId", "TeachingTaskId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_CourseExemptions_TeachingTaskId", + table: "CourseExemptions", + column: "TeachingTaskId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseSubstitutions_OriginalCourseId", + table: "CourseSubstitutions", + column: "OriginalCourseId"); + + migrationBuilder.CreateIndex( + name: "IX_CourseSubstitutions_Status_CreatedAt", + table: "CourseSubstitutions", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_CourseSubstitutions_StudentId_OriginalCourseId", + table: "CourseSubstitutions", + columns: new[] { "StudentId", "OriginalCourseId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_CourseSubstitutions_SubstituteCourseId", + table: "CourseSubstitutions", + column: "SubstituteCourseId"); + + migrationBuilder.CreateIndex( + name: "IX_DeferredExams_Status_CreatedAt", + table: "DeferredExams", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_DeferredExams_StudentId_TeachingTaskId", + table: "DeferredExams", + columns: new[] { "StudentId", "TeachingTaskId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_DeferredExams_TeachingTaskId", + table: "DeferredExams", + column: "TeachingTaskId"); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationDimensions_EvaluationSetupId_SortOrder", + table: "EvaluationDimensions", + columns: new[] { "EvaluationSetupId", "SortOrder" }); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationRecords_EvaluationSetupId_StudentId_TeachingTaskId", + table: "EvaluationRecords", + columns: new[] { "EvaluationSetupId", "StudentId", "TeachingTaskId" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationRecords_StudentId", + table: "EvaluationRecords", + column: "StudentId"); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationRecords_TeachingTaskId", + table: "EvaluationRecords", + column: "TeachingTaskId"); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationScores_EvaluationDimensionId", + table: "EvaluationScores", + column: "EvaluationDimensionId"); + + migrationBuilder.CreateIndex( + name: "IX_EvaluationSetups_AcademicTermId_Status", + table: "EvaluationSetups", + columns: new[] { "AcademicTermId", "Status" }); + + migrationBuilder.CreateIndex( + name: "IX_GradeItems_GradeSheetId_SortOrder", + table: "GradeItems", + columns: new[] { "GradeSheetId", "SortOrder" }); + + migrationBuilder.CreateIndex( + name: "IX_GradeItemScores_GradeItemId", + table: "GradeItemScores", + column: "GradeItemId"); + + migrationBuilder.CreateIndex( + name: "IX_GradeModifications_GradeRecordId", + table: "GradeModifications", + column: "GradeRecordId"); + + migrationBuilder.CreateIndex( + name: "IX_GradeModifications_Status_CreatedAt", + table: "GradeModifications", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamAutoJobs_MakeupExamPlanId_CreatedAt", + table: "MakeupExamAutoJobs", + columns: new[] { "MakeupExamPlanId", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamAutoJobs_Status_CreatedAt", + table: "MakeupExamAutoJobs", + columns: new[] { "Status", "CreatedAt" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamEnrollments_SourceDeferredExamId", + table: "MakeupExamEnrollments", + column: "SourceDeferredExamId"); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamEnrollments_SourceGradeRecordId", + table: "MakeupExamEnrollments", + column: "SourceGradeRecordId"); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamEnrollments_StudentId_MakeupExamSessionId", + table: "MakeupExamEnrollments", + columns: new[] { "StudentId", "MakeupExamSessionId" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamPlans_AcademicTermId_Status", + table: "MakeupExamPlans", + columns: new[] { "AcademicTermId", "Status" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessionInvigilators_TeacherId", + table: "MakeupExamSessionInvigilators", + column: "TeacherId"); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessions_ClassroomId", + table: "MakeupExamSessions", + column: "ClassroomId"); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessions_MakeupExamPlanId_ExamDate", + table: "MakeupExamSessions", + columns: new[] { "MakeupExamPlanId", "ExamDate" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessions_MakeupExamPlanId_StartsAt", + table: "MakeupExamSessions", + columns: new[] { "MakeupExamPlanId", "StartsAt" }); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessions_RequiredBuildingId", + table: "MakeupExamSessions", + column: "RequiredBuildingId"); + + migrationBuilder.CreateIndex( + name: "IX_MakeupExamSessions_TeachingTaskId", + table: "MakeupExamSessions", + column: "TeachingTaskId"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_CreatedAt", + table: "Notifications", + column: "CreatedAt"); + + migrationBuilder.CreateIndex( + name: "IX_Notifications_UserId_IsRead", + table: "Notifications", + columns: new[] { "UserId", "IsRead" }); + + migrationBuilder.CreateIndex( + name: "IX_WarningRecords_Status", + table: "WarningRecords", + column: "Status"); + + migrationBuilder.CreateIndex( + name: "IX_WarningRecords_StudentId_AcademicTermId_Type", + table: "WarningRecords", + columns: new[] { "StudentId", "AcademicTermId", "Type" }, + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_WarningRules_AcademicTermId_Type", + table: "WarningRules", + columns: new[] { "AcademicTermId", "Type" }, + unique: true); + + migrationBuilder.Sql( + """ + UPDATE `ExamSessions` + SET `ExamDate` = DATE(`StartsAt`) + WHERE `ExamDate` IS NULL; + """); + + migrationBuilder.AlterColumn( + name: "ExamDate", + table: "ExamSessions", + type: "date", + nullable: false, + oldClrType: typeof(DateOnly), + oldType: "date", + oldNullable: true); + + migrationBuilder.Sql( + """ + INSERT INTO `GradeItems` + (`Id`, `GradeSheetId`, `Name`, `Weight`, `SortOrder`, `CreatedAt`, `UpdatedAt`) + SELECT UUID(), gs.`Id`, '期中成绩', gs.`MidtermWeight`, 10, + UTC_TIMESTAMP(6), UTC_TIMESTAMP(6) + FROM `GradeSheets` AS gs + WHERE gs.`MidtermWeight` <> 0 + OR EXISTS ( + SELECT 1 + FROM `GradeRecords` AS gr + WHERE gr.`GradeSheetId` = gs.`Id` + AND gr.`MidtermScore` IS NOT NULL + ); + + INSERT INTO `GradeItemScores` (`GradeRecordId`, `GradeItemId`, `Score`) + SELECT gr.`Id`, gi.`Id`, gr.`MidtermScore` + FROM `GradeRecords` AS gr + INNER JOIN `GradeItems` AS gi + ON gi.`GradeSheetId` = gr.`GradeSheetId` + AND gi.`Name` = '期中成绩' + WHERE gr.`MidtermScore` IS NOT NULL; + """); + + migrationBuilder.DropColumn( + name: "MidtermWeight", + table: "GradeSheets"); + + migrationBuilder.DropColumn( + name: "MidtermScore", + table: "GradeRecords"); + + migrationBuilder.AddForeignKey( + name: "FK_ExamSessions_Buildings_RequiredBuildingId", + table: "ExamSessions", + column: "RequiredBuildingId", + principalTable: "Buildings", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + + migrationBuilder.AddForeignKey( + name: "FK_ExamSessions_Classrooms_ClassroomId", + table: "ExamSessions", + column: "ClassroomId", + principalTable: "Classrooms", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_ExamSessions_Buildings_RequiredBuildingId", + table: "ExamSessions"); + + migrationBuilder.DropForeignKey( + name: "FK_ExamSessions_Classrooms_ClassroomId", + table: "ExamSessions"); + + migrationBuilder.DropTable( + name: "AttendanceRecords"); + + migrationBuilder.DropTable( + name: "CourseAdjustments"); + + migrationBuilder.DropTable( + name: "CourseExemptions"); + + migrationBuilder.DropTable( + name: "CourseSubstitutions"); + + migrationBuilder.DropTable( + name: "EvaluationScores"); + + migrationBuilder.DropTable( + name: "GradeItemScores"); + + migrationBuilder.DropTable( + name: "GradeModifications"); + + migrationBuilder.DropTable( + name: "MakeupExamAutoJobs"); + + migrationBuilder.DropTable( + name: "MakeupExamEnrollments"); + + migrationBuilder.DropTable( + name: "MakeupExamSessionInvigilators"); + + migrationBuilder.DropTable( + name: "Notifications"); + + migrationBuilder.DropTable( + name: "WarningRecords"); + + migrationBuilder.DropTable( + name: "WarningRules"); + + migrationBuilder.DropTable( + name: "AttendanceSheets"); + + migrationBuilder.DropTable( + name: "EvaluationDimensions"); + + migrationBuilder.DropTable( + name: "EvaluationRecords"); + + migrationBuilder.DropTable( + name: "GradeItems"); + + migrationBuilder.DropTable( + name: "DeferredExams"); + + migrationBuilder.DropTable( + name: "MakeupExamSessions"); + + migrationBuilder.DropTable( + name: "EvaluationSetups"); + + migrationBuilder.DropTable( + name: "MakeupExamPlans"); + + migrationBuilder.DropIndex( + name: "IX_ExamSessions_ExamPlanId_ExamDate", + table: "ExamSessions"); + + migrationBuilder.DropIndex( + name: "IX_ExamSessions_RequiredBuildingId", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "ExamDate", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "PeriodCount", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "RequiredBuildingId", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "RequiredInvigilatorCount", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "StartPeriod", + table: "ExamSessions"); + + migrationBuilder.DropColumn( + name: "EnrollmentType", + table: "CourseEnrollments"); + + migrationBuilder.AddColumn( + name: "MidtermWeight", + table: "GradeSheets", + type: "decimal(5,1)", + precision: 5, + scale: 1, + nullable: false, + defaultValue: 0m); + + migrationBuilder.AddColumn( + name: "MidtermScore", + table: "GradeRecords", + type: "decimal(5,1)", + precision: 5, + scale: 1, + nullable: true); + + migrationBuilder.AlterColumn( + name: "ClassroomId", + table: "ExamSessions", + type: "char(36)", + nullable: false, + defaultValue: new Guid("00000000-0000-0000-0000-000000000000"), + oldClrType: typeof(Guid), + oldType: "char(36)", + oldNullable: true); + + migrationBuilder.AddForeignKey( + name: "FK_ExamSessions_Classrooms_ClassroomId", + table: "ExamSessions", + column: "ClassroomId", + principalTable: "Classrooms", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725150000_RetakeEnrollment.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725150000_RetakeEnrollment.cs deleted file mode 100644 index 0f2e1ec..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725150000_RetakeEnrollment.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - /// - public partial class RetakeEnrollment : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "EnrollmentType", - table: "CourseEnrollments", - type: "int", - nullable: false, - defaultValue: 1); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "EnrollmentType", - table: "CourseEnrollments"); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725160000_CourseAdjustments.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725160000_CourseAdjustments.cs deleted file mode 100644 index 34a1e26..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725160000_CourseAdjustments.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - /// - public partial class CourseAdjustments : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "CourseAdjustments", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false), - TeachingTaskId = table.Column(type: "char(36)", nullable: false), - Type = table.Column(type: "int", nullable: false), - Status = table.Column(type: "int", nullable: false), - ApplicantUserId = table.Column(type: "char(36)", nullable: false), - TargetDate = table.Column(type: "date", nullable: true), - DayOfWeek = table.Column(type: "int", nullable: true), - StartPeriod = table.Column(type: "int", nullable: true), - PeriodCount = table.Column(type: "int", nullable: true), - ClassroomId = table.Column(type: "char(36)", nullable: true), - SubstituteTeacherId = table.Column(type: "char(36)", nullable: true), - CancelWeek = table.Column(type: "int", nullable: true), - CancelDate = table.Column(type: "date", nullable: true), - Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), - ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - SubmittedAt = table.Column(type: "datetime(6)", nullable: true), - ReviewedAt = table.Column(type: "datetime(6)", nullable: true), - ReviewedByUserId = table.Column(type: "char(36)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_CourseAdjustments", x => x.Id); - table.ForeignKey( - name: "FK_CourseAdjustments_Classrooms_ClassroomId", - column: x => x.ClassroomId, - principalTable: "Classrooms", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "FK_CourseAdjustments_Teachers_SubstituteTeacherId", - column: x => x.SubstituteTeacherId, - principalTable: "Teachers", - principalColumn: "Id", - onDelete: ReferentialAction.SetNull); - table.ForeignKey( - name: "FK_CourseAdjustments_TeachingTasks_TeachingTaskId", - column: x => x.TeachingTaskId, - principalTable: "TeachingTasks", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateTable( - name: "Notifications", - columns: table => new - { - Id = table.Column(type: "char(36)", nullable: false), - UserId = table.Column(type: "char(36)", nullable: false), - Title = table.Column(type: "varchar(200)", maxLength: 200, nullable: false), - Content = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false), - IsRead = table.Column(type: "tinyint(1)", nullable: false), - LinkUrl = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Notifications", x => x.Id); - }) - .Annotation("MySQL:Charset", "utf8mb4"); - - migrationBuilder.CreateIndex( - name: "IX_CourseAdjustments_ApplicantUserId", - table: "CourseAdjustments", - column: "ApplicantUserId"); - - migrationBuilder.CreateIndex( - name: "IX_CourseAdjustments_ClassroomId", - table: "CourseAdjustments", - column: "ClassroomId"); - - migrationBuilder.CreateIndex( - name: "IX_CourseAdjustments_Status_CreatedAt", - table: "CourseAdjustments", - columns: new[] { "Status", "CreatedAt" }); - - migrationBuilder.CreateIndex( - name: "IX_CourseAdjustments_SubstituteTeacherId", - table: "CourseAdjustments", - column: "SubstituteTeacherId"); - - migrationBuilder.CreateIndex( - name: "IX_CourseAdjustments_TeachingTaskId_Status", - table: "CourseAdjustments", - columns: new[] { "TeachingTaskId", "Status" }); - - migrationBuilder.CreateIndex( - name: "IX_Notifications_CreatedAt", - table: "Notifications", - column: "CreatedAt"); - - migrationBuilder.CreateIndex( - name: "IX_Notifications_UserId_IsRead", - table: "Notifications", - columns: new[] { "UserId", "IsRead" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable(name: "Notifications"); - migrationBuilder.DropTable(name: "CourseAdjustments"); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725170000_AttendanceAppeal.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725170000_AttendanceAppeal.cs deleted file mode 100644 index c7f7a35..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725170000_AttendanceAppeal.cs +++ /dev/null @@ -1,66 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - /// - public partial class AttendanceAppeal : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "AppealStatus", - table: "AttendanceRecords", - type: "int", - nullable: false, - defaultValue: 0); - - migrationBuilder.AddColumn( - name: "AppealReason", - table: "AttendanceRecords", - type: "varchar(500)", - maxLength: 500, - nullable: true); - - migrationBuilder.AddColumn( - name: "AppealSubmittedAt", - table: "AttendanceRecords", - type: "datetime(6)", - nullable: true); - - migrationBuilder.AddColumn( - name: "AppealReviewComment", - table: "AttendanceRecords", - type: "varchar(300)", - maxLength: 300, - nullable: true); - - migrationBuilder.AddColumn( - name: "AppealReviewedAt", - table: "AttendanceRecords", - type: "datetime(6)", - nullable: true); - - migrationBuilder.CreateIndex( - name: "IX_AttendanceRecords_AppealStatus", - table: "AttendanceRecords", - column: "AppealStatus"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_AttendanceRecords_AppealStatus", - table: "AttendanceRecords"); - - migrationBuilder.DropColumn(name: "AppealReviewedAt", table: "AttendanceRecords"); - migrationBuilder.DropColumn(name: "AppealReviewComment", table: "AttendanceRecords"); - migrationBuilder.DropColumn(name: "AppealSubmittedAt", table: "AttendanceRecords"); - migrationBuilder.DropColumn(name: "AppealReason", table: "AttendanceRecords"); - migrationBuilder.DropColumn(name: "AppealStatus", table: "AttendanceRecords"); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725180000_ApprovalRequests.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725180000_ApprovalRequests.cs deleted file mode 100644 index 752d2c7..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725180000_ApprovalRequests.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - public partial class ApprovalRequests : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - // CourseExemption - migrationBuilder.CreateTable("CourseExemptions", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - StudentId = table.Column(type: "char(36)", nullable: false), - TeachingTaskId = table.Column(type: "char(36)", nullable: false), - Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), - Status = table.Column(type: "int", nullable: false), - ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - SubmittedAt = table.Column(type: "datetime(6)", nullable: false), - ReviewedAt = table.Column(type: "datetime(6)", nullable: true), - ReviewedByUserId = table.Column(type: "char(36)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: table => - { - table.PrimaryKey("PK_CourseExemptions", x => x.Id); - table.ForeignKey("FK_CourseExemptions_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict); - table.ForeignKey("FK_CourseExemptions_TeachingTasks", x => x.TeachingTaskId, "TeachingTasks", "Id", onDelete: ReferentialAction.Restrict); - }); - migrationBuilder.CreateIndex("IX_CourseExemptions_Status_CreatedAt", "CourseExemptions", new[] { "Status", "CreatedAt" }); - migrationBuilder.CreateIndex("IX_CourseExemptions_StudentId_TeachingTaskId", "CourseExemptions", new[] { "StudentId", "TeachingTaskId" }, unique: true); - - // DeferredExam - migrationBuilder.CreateTable("DeferredExams", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - StudentId = table.Column(type: "char(36)", nullable: false), - TeachingTaskId = table.Column(type: "char(36)", nullable: false), - Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), - Status = table.Column(type: "int", nullable: false), - ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - SubmittedAt = table.Column(type: "datetime(6)", nullable: false), - ReviewedAt = table.Column(type: "datetime(6)", nullable: true), - ReviewedByUserId = table.Column(type: "char(36)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: table => - { - table.PrimaryKey("PK_DeferredExams", x => x.Id); - table.ForeignKey("FK_DeferredExams_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict); - table.ForeignKey("FK_DeferredExams_TeachingTasks", x => x.TeachingTaskId, "TeachingTasks", "Id", onDelete: ReferentialAction.Restrict); - }); - migrationBuilder.CreateIndex("IX_DeferredExams_Status_CreatedAt", "DeferredExams", new[] { "Status", "CreatedAt" }); - migrationBuilder.CreateIndex("IX_DeferredExams_StudentId_TeachingTaskId", "DeferredExams", new[] { "StudentId", "TeachingTaskId" }, unique: true); - - // GradeModification - migrationBuilder.CreateTable("GradeModifications", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - GradeRecordId = table.Column(type: "char(36)", nullable: false), - CurrentScore = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), - RequestedScore = table.Column(type: "decimal(5,1)", precision: 5, scale: 1, nullable: false), - Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), - Status = table.Column(type: "int", nullable: false), - ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - ApplicantUserId = table.Column(type: "char(36)", nullable: false), - SubmittedAt = table.Column(type: "datetime(6)", nullable: false), - CollegeReviewedAt = table.Column(type: "datetime(6)", nullable: true), - CollegeReviewedByUserId = table.Column(type: "char(36)", nullable: true), - FinalReviewedAt = table.Column(type: "datetime(6)", nullable: true), - FinalReviewedByUserId = table.Column(type: "char(36)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: table => - { - table.PrimaryKey("PK_GradeModifications", x => x.Id); - table.ForeignKey("FK_GradeModifications_GradeRecords", x => x.GradeRecordId, "GradeRecords", "Id", onDelete: ReferentialAction.Restrict); - }); - migrationBuilder.CreateIndex("IX_GradeModifications_Status_CreatedAt", "GradeModifications", new[] { "Status", "CreatedAt" }); - - // CourseSubstitution - migrationBuilder.CreateTable("CourseSubstitutions", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - StudentId = table.Column(type: "char(36)", nullable: false), - OriginalCourseId = table.Column(type: "char(36)", nullable: false), - SubstituteCourseId = table.Column(type: "char(36)", nullable: false), - Reason = table.Column(type: "varchar(500)", maxLength: 500, nullable: false), - Status = table.Column(type: "int", nullable: false), - ReviewComment = table.Column(type: "varchar(500)", maxLength: 500, nullable: true), - SubmittedAt = table.Column(type: "datetime(6)", nullable: false), - ReviewedAt = table.Column(type: "datetime(6)", nullable: true), - ReviewedByUserId = table.Column(type: "char(36)", nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: table => - { - table.PrimaryKey("PK_CourseSubstitutions", x => x.Id); - table.ForeignKey("FK_CourseSubstitutions_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict); - table.ForeignKey("FK_CourseSubstitutions_OriginalCourse", x => x.OriginalCourseId, "Courses", "Id", onDelete: ReferentialAction.Restrict); - table.ForeignKey("FK_CourseSubstitutions_SubstituteCourse", x => x.SubstituteCourseId, "Courses", "Id", onDelete: ReferentialAction.Restrict); - }); - migrationBuilder.CreateIndex("IX_CourseSubstitutions_Status_CreatedAt", "CourseSubstitutions", new[] { "Status", "CreatedAt" }); - migrationBuilder.CreateIndex("IX_CourseSubstitutions_StudentId_OriginalCourseId", "CourseSubstitutions", new[] { "StudentId", "OriginalCourseId" }, unique: true); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable("CourseSubstitutions"); - migrationBuilder.DropTable("GradeModifications"); - migrationBuilder.DropTable("DeferredExams"); - migrationBuilder.DropTable("CourseExemptions"); - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725190000_AcademicWarnings.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725190000_AcademicWarnings.cs deleted file mode 100644 index 704b289..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725190000_AcademicWarnings.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - public partial class AcademicWarnings : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable("WarningRules", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - Type = table.Column(type: "int", nullable: false), - Name = table.Column(type: "varchar(100)", maxLength: 100, nullable: false), - Threshold = table.Column(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false), - IsEnabled = table.Column(type: "tinyint(1)", nullable: false), - NotifyStudent = table.Column(type: "tinyint(1)", nullable: false), - NotifyCounselor = table.Column(type: "tinyint(1)", nullable: false), - AcademicTermId = table.Column(type: "char(36)", nullable: false), - Description = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: t => { t.PrimaryKey("PK_WarningRules", x => x.Id); t.ForeignKey("FK_WarningRules_AcademicTerms", x => x.AcademicTermId, "AcademicTerms", "Id", onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateIndex("IX_WarningRules_AcademicTermId_Type", "WarningRules", new[] { "AcademicTermId", "Type" }, unique: true); - - migrationBuilder.CreateTable("WarningRecords", table => new - { - Id = table.Column(type: "char(36)", nullable: false), - StudentId = table.Column(type: "char(36)", nullable: false), - Type = table.Column(type: "int", nullable: false), - Status = table.Column(type: "int", nullable: false), - TriggerValue = table.Column(type: "decimal(7,2)", precision: 7, scale: 2, nullable: false), - Detail = table.Column(type: "varchar(1000)", maxLength: 1000, nullable: false), - AcknowledgedAt = table.Column(type: "datetime(6)", nullable: true), - AcknowledgeComment = table.Column(type: "varchar(300)", maxLength: 300, nullable: true), - AcademicTermId = table.Column(type: "char(36)", nullable: false), - CreatedAt = table.Column(type: "datetime(6)", nullable: false), - UpdatedAt = table.Column(type: "datetime(6)", nullable: false) - }, constraints: t => { t.PrimaryKey("PK_WarningRecords", x => x.Id); t.ForeignKey("FK_WarningRecords_Students", x => x.StudentId, "Students", "Id", onDelete: ReferentialAction.Restrict); }); - migrationBuilder.CreateIndex("IX_WarningRecords_StudentId_AcademicTermId_Type", "WarningRecords", new[] { "StudentId", "AcademicTermId", "Type" }, unique: true); - migrationBuilder.CreateIndex("IX_WarningRecords_Status", "WarningRecords", "Status"); - } - protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable("WarningRecords"); migrationBuilder.DropTable("WarningRules"); } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725200000_WarningAutoCheck.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725200000_WarningAutoCheck.cs deleted file mode 100644 index 822584c..0000000 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/20260725200000_WarningAutoCheck.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable -namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql -{ - public partial class WarningAutoCheck : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable("WarningSchedules"); - migrationBuilder.AddColumn("AutoCheckEnabled", "WarningRules", type: "tinyint(1)", nullable: false, defaultValue: false); - migrationBuilder.AddColumn("CheckDayOfWeek", "WarningRules", type: "int", nullable: true); - migrationBuilder.AddColumn("CheckHour", "WarningRules", type: "int", nullable: false, defaultValue: 8); - migrationBuilder.AddColumn("CheckMinute", "WarningRules", type: "int", nullable: false, defaultValue: 0); - migrationBuilder.AddColumn("LastCheckAt", "WarningRules", type: "datetime(6)", nullable: true); - } - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn("LastCheckAt", "WarningRules"); - migrationBuilder.DropColumn("CheckMinute", "WarningRules"); - migrationBuilder.DropColumn("CheckHour", "WarningRules"); - migrationBuilder.DropColumn("CheckDayOfWeek", "WarningRules"); - migrationBuilder.DropColumn("AutoCheckEnabled", "WarningRules"); - // WarningSchedules table recreation omitted for brevity - } - } -} diff --git a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs index 01455aa..dd4b68f 100644 --- a/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs +++ b/src/Jiaowu.Api/Infrastructure/Persistence/Migrations/MySql/AppDbContextModelSnapshot.cs @@ -15,51 +15,53 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.10"); + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AcademicTerm", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicYear") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("IsCurrent") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Season") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -77,41 +79,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CounselorName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("CounselorUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Grade") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("MajorId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -130,34 +132,34 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.AttendanceRecord", b => { b.Property("AttendanceSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AppealReason") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("AppealReviewComment") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("AppealReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("AppealStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("AppealSubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Notes") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("AttendanceSheetId", "StudentId"); @@ -172,34 +174,34 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AttendanceDate") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -212,50 +214,50 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ActiveSchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedEntries") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("MessagesJson") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ProcessedTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequestedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalTasks") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -275,32 +277,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -318,32 +320,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Address") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -359,42 +361,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("BuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Equipment") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("RoomType") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -412,35 +414,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("ShortName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -458,62 +460,62 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AssessmentMethod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseCategoryId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Credits") .HasPrecision(5, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,2)"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("EnglishName") .HasMaxLength(150) - .HasColumnType("TEXT"); + .HasColumnType("varchar(150)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LectureHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Nature") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PracticeHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -533,67 +535,67 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApplicantUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CancelDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("CancelWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SubstituteTeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TargetDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -614,29 +616,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -652,31 +654,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseSelectionOfferingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrolledAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrollmentType") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WithdrawnAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -692,40 +694,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -743,29 +745,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CourseSelectionRoundId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsOpenToAll") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -781,41 +783,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("MaxCredits") .HasPrecision(6, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,1)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WithdrawalEndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -828,43 +830,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("OriginalCourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SubstituteCourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -884,29 +886,29 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumModuleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("RecommendedSemester") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -922,33 +924,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -962,43 +964,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Description") .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("EffectiveGrade") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("MajorId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.HasKey("Id"); @@ -1014,40 +1016,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1065,43 +1067,43 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeName") .IsRequired() .HasMaxLength(80) - .HasColumnType("TEXT"); + .HasColumnType("varchar(80)"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("MinimumGradePoint") .HasPrecision(3, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(3,2)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1114,47 +1116,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AverageGradePoint") .HasPrecision(4, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(4,2)"); b.Property("CalculatedConclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Conclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeAwardBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ExceptionReason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("GraduationAuditResultId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsOverridden") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1174,27 +1176,27 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EvaluationSetupId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("MaxScore") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(60) - .HasColumnType("TEXT"); + .HasColumnType("varchar(60)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1207,25 +1209,25 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EvaluationSetupId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1242,13 +1244,13 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.EvaluationScore", b => { b.Property("EvaluationRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("EvaluationDimensionId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Score") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("EvaluationRecordId", "EvaluationDimensionId"); @@ -1261,30 +1263,30 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1297,31 +1299,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1334,47 +1336,47 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ExamDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("ExamPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredBuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiredInvigilatorCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1394,10 +1396,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ExamSessionInvigilator", b => { b.Property("ExamSessionId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("ExamSessionId", "TeacherId"); @@ -1410,28 +1412,28 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GradeSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Name") .IsRequired() .HasMaxLength(60) - .HasColumnType("TEXT"); + .HasColumnType("varchar(60)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Weight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.HasKey("Id"); @@ -1443,14 +1445,14 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.GradeItemScore", b => { b.Property("GradeRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("GradeItemId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Score") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.HasKey("GradeRecordId", "GradeItemId"); @@ -1463,54 +1465,54 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApplicantUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CollegeReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CollegeReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurrentScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("FinalReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("FinalReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("GradeRecordId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Reason") .IsRequired() .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("RequestedScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1525,42 +1527,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ExamStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("FinalScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("GradePoint") .HasPrecision(3, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(3,1)"); b.Property("GradeSheetId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("RegularScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TotalScore") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1576,40 +1578,40 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("FinalWeight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("RegularWeight") .HasPrecision(5, 1) - .HasColumnType("TEXT"); + .HasColumnType("decimal(5,1)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1625,37 +1627,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EnrollmentYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1670,63 +1672,63 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CalculatedConclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Conclusion") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurriculumPlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("EarnedCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("FailedCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GraduationAuditBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsOverridden") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("MissingCourseNames") .IsRequired() .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("PassedRequiredCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredCourseCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredCredits") .HasPrecision(6, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(6,2)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StudentStatusSnapshot") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1746,31 +1748,31 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClosedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1783,42 +1785,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationClearanceBatchId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsRequired") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("ResponsibleRole") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("ResponsibleUnit") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1832,32 +1834,32 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("GraduationClearanceItemId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1873,39 +1875,39 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Code") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DegreeType") .IsRequired() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("SchoolingYears") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SortOrder") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -1919,37 +1921,234 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.ToTable("Majors"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamAutoJob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("CompletedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("CreatedSessions") + .HasColumnType("int"); + + b.Property("EnrolledStudents") + .HasColumnType("int"); + + b.Property("ErrorMessage") + .HasMaxLength(2000) + .HasColumnType("varchar(2000)"); + + b.Property("MakeupExamPlanId") + .HasColumnType("char(36)"); + + b.Property("MessagesJson") + .HasColumnType("longtext"); + + b.Property("ProcessedCourses") + .HasColumnType("int"); + + b.Property("StartedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("TotalCourses") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("MakeupExamPlanId", "CreatedAt"); + + b.HasIndex("Status", "CreatedAt"); + + b.ToTable("MakeupExamAutoJobs"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamEnrollment", b => + { + b.Property("MakeupExamSessionId") + .HasColumnType("char(36)"); + + b.Property("StudentId") + .HasColumnType("char(36)"); + + b.Property("MakeupScore") + .HasPrecision(5, 1) + .HasColumnType("decimal(5,1)"); + + b.Property("Reason") + .HasColumnType("int"); + + b.Property("SourceDeferredExamId") + .HasColumnType("char(36)"); + + b.Property("SourceGradeRecordId") + .HasColumnType("char(36)"); + + b.HasKey("MakeupExamSessionId", "StudentId"); + + b.HasIndex("SourceDeferredExamId"); + + b.HasIndex("SourceGradeRecordId"); + + b.HasIndex("StudentId", "MakeupExamSessionId"); + + b.ToTable("MakeupExamEnrollments"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AcademicTermId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(120) + .HasColumnType("varchar(120)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PublishedAt") + .HasColumnType("datetime(6)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("AcademicTermId", "Status"); + + b.ToTable("MakeupExamPlans"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSession", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ClassroomId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)"); + + b.Property("EndsAt") + .HasColumnType("datetime(6)"); + + b.Property("ExamDate") + .HasColumnType("date"); + + b.Property("MakeupExamPlanId") + .HasColumnType("char(36)"); + + b.Property("Notes") + .HasMaxLength(500) + .HasColumnType("varchar(500)"); + + b.Property("PeriodCount") + .HasColumnType("int"); + + b.Property("RequiredBuildingId") + .HasColumnType("char(36)"); + + b.Property("RequiredInvigilatorCount") + .HasColumnType("int"); + + b.Property("StartPeriod") + .HasColumnType("int"); + + b.Property("StartsAt") + .HasColumnType("datetime(6)"); + + b.Property("TeachingTaskId") + .HasColumnType("char(36)"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)"); + + b.HasKey("Id"); + + b.HasIndex("ClassroomId"); + + b.HasIndex("RequiredBuildingId"); + + b.HasIndex("TeachingTaskId"); + + b.HasIndex("MakeupExamPlanId", "ExamDate"); + + b.HasIndex("MakeupExamPlanId", "StartsAt"); + + b.ToTable("MakeupExamSessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSessionInvigilator", b => + { + b.Property("MakeupExamSessionId") + .HasColumnType("char(36)"); + + b.Property("TeacherId") + .HasColumnType("char(36)"); + + b.HasKey("MakeupExamSessionId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("MakeupExamSessionInvigilators"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.Notification", b => { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Content") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IsRead") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LinkUrl") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("Title") .IsRequired() .HasMaxLength(200) - .HasColumnType("TEXT"); + .HasColumnType("varchar(200)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -1964,44 +2163,44 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("EndWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PeriodCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WeekPattern") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -2018,36 +2217,36 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Version") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.HasKey("Id"); @@ -2063,48 +2262,48 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ActiveAcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CompletedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CompletedSteps") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CurrentStep") .HasMaxLength(200) - .HasColumnType("TEXT"); + .HasColumnType("varchar(200)"); b.Property("ErrorMessage") .HasMaxLength(2000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(2000)"); b.Property("RequestedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SchedulePlanId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("StartedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TotalSteps") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2124,33 +2323,33 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndsAt") - .HasColumnType("TEXT"); + .HasColumnType("time"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("PeriodNumber") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartsAt") - .HasColumnType("TEXT"); + .HasColumnType("time"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2164,56 +2363,56 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AdministrativeClassId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DateOfBirth") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("EnrollmentDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("EnrollmentYear") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Gender") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2233,46 +2432,46 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ApprovedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("OriginalStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Reason") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("State") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TargetStatus") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2285,57 +2484,57 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Email") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Gender") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("HireDate") - .HasColumnType("TEXT"); + .HasColumnType("date"); b.Property("IsExternal") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Name") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Phone") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TeacherNumber") .IsRequired() .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("Title") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2353,42 +2552,42 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewComment") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("ReviewedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("ReviewedByUserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Statement") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("SubmittedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2410,58 +2609,58 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("Capacity") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CourseId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EndWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("GenerationBatchCode") .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("Name") .IsRequired() .HasMaxLength(120) - .HasColumnType("TEXT"); + .HasColumnType("varchar(120)"); b.Property("Notes") .HasMaxLength(500) - .HasColumnType("TEXT"); + .HasColumnType("varchar(500)"); b.Property("PublishedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("SchedulingMode") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StartWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("TaskNumber") .IsRequired() .HasMaxLength(40) - .HasColumnType("TEXT"); + .HasColumnType("varchar(40)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("WeeklyHours") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.HasKey("Id"); @@ -2478,10 +2677,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskAllowedClassroom", b => { b.Property("TeachingTaskScheduleConstraintId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ClassroomId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("TeachingTaskScheduleConstraintId", "ClassroomId"); @@ -2493,10 +2692,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskClass", b => { b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AdministrativeClassId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("TeachingTaskId", "AdministrativeClassId"); @@ -2509,35 +2708,35 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AllowedDayOfWeeks") .HasMaxLength(20) - .HasColumnType("TEXT"); + .HasColumnType("varchar(20)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("EarliestPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("LatestPeriod") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("RequiredBuildingId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiredCampusId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RequiresClassroom") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2554,13 +2753,13 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Jiaowu.Api.Domain.Academic.TeachingTaskTeacher", b => { b.Property("TeachingTaskId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TeacherId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("IsPrimary") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.HasKey("TeachingTaskId", "TeacherId"); @@ -2573,41 +2772,41 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcknowledgeComment") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("AcknowledgedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Detail") .IsRequired() .HasMaxLength(1000) - .HasColumnType("TEXT"); + .HasColumnType("varchar(1000)"); b.Property("Status") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("StudentId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("TriggerValue") .HasPrecision(7, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(7,2)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2623,56 +2822,56 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AcademicTermId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AutoCheckEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("CheckDayOfWeek") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CheckHour") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CheckMinute") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Description") .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LastCheckAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("Name") .IsRequired() .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("NotifyCounselor") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("NotifyStudent") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("Threshold") .HasPrecision(7, 2) - .HasColumnType("TEXT"); + .HasColumnType("decimal(7,2)"); b.Property("Type") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.HasKey("Id"); @@ -2686,26 +2885,26 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("DataScope") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("Description") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.Property("Name") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("NormalizedName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.HasKey("Id"); @@ -2720,75 +2919,75 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("AccessFailedCount") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("CollegeId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("DisplayName") .IsRequired() .HasMaxLength(50) - .HasColumnType("TEXT"); + .HasColumnType("varchar(50)"); b.Property("Email") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("EmailConfirmed") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("IsEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LastLoginAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("LockoutEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("LockoutEnd") - .HasColumnType("TEXT"); + .HasColumnType("datetime"); b.Property("NormalizedEmail") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("NormalizedUserName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.Property("PasswordHash") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("PhoneNumber") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("PhoneNumberConfirmed") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("SecurityStamp") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("StaffNumber") .HasMaxLength(30) - .HasColumnType("TEXT"); + .HasColumnType("varchar(30)"); b.Property("TwoFactorEnabled") - .HasColumnType("INTEGER"); + .HasColumnType("tinyint(1)"); b.Property("UserName") .HasMaxLength(256) - .HasColumnType("TEXT"); + .HasColumnType("varchar(256)"); b.HasKey("Id"); @@ -2808,37 +3007,37 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("CreatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("IpAddress") .HasMaxLength(64) - .HasColumnType("TEXT"); + .HasColumnType("varchar(64)"); b.Property("Method") .IsRequired() .HasMaxLength(10) - .HasColumnType("TEXT"); + .HasColumnType("varchar(10)"); b.Property("Path") .IsRequired() .HasMaxLength(300) - .HasColumnType("TEXT"); + .HasColumnType("varchar(300)"); b.Property("StatusCode") - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("UpdatedAt") - .HasColumnType("TEXT"); + .HasColumnType("datetime(6)"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("UserName") .HasMaxLength(100) - .HasColumnType("TEXT"); + .HasColumnType("varchar(100)"); b.HasKey("Id"); @@ -2851,16 +3050,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClaimType") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ClaimValue") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("RoleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2873,16 +3072,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); + .HasColumnType("int"); b.Property("ClaimType") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("ClaimValue") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("Id"); @@ -2894,16 +3093,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderKey") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("ProviderDisplayName") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("LoginProvider", "ProviderKey"); @@ -2915,10 +3114,10 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("RoleId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.HasKey("UserId", "RoleId"); @@ -2930,16 +3129,16 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId") - .HasColumnType("TEXT"); + .HasColumnType("char(36)"); b.Property("LoginProvider") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("Name") - .HasColumnType("TEXT"); + .HasColumnType("varchar(255)"); b.Property("Value") - .HasColumnType("TEXT"); + .HasColumnType("longtext"); b.HasKey("UserId", "LoginProvider", "Name"); @@ -3536,6 +3735,113 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("College"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamAutoJob", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamPlan", "MakeupExamPlan") + .WithMany() + .HasForeignKey("MakeupExamPlanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("MakeupExamPlan"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamEnrollment", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamSession", "MakeupExamSession") + .WithMany("Enrollments") + .HasForeignKey("MakeupExamSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.DeferredExam", "SourceDeferredExam") + .WithMany() + .HasForeignKey("SourceDeferredExamId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.GradeRecord", "SourceGradeRecord") + .WithMany() + .HasForeignKey("SourceGradeRecordId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.Student", "Student") + .WithMany() + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("MakeupExamSession"); + + b.Navigation("SourceDeferredExam"); + + b.Navigation("SourceGradeRecord"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", 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.MakeupExamSession", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") + .WithMany() + .HasForeignKey("ClassroomId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamPlan", "MakeupExamPlan") + .WithMany("Sessions") + .HasForeignKey("MakeupExamPlanId") + .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("MakeupExamPlan"); + + b.Navigation("RequiredBuilding"); + + b.Navigation("TeachingTask"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSessionInvigilator", b => + { + b.HasOne("Jiaowu.Api.Domain.Academic.MakeupExamSession", "MakeupExamSession") + .WithMany("Invigilators") + .HasForeignKey("MakeupExamSessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Jiaowu.Api.Domain.Academic.Teacher", "Teacher") + .WithMany() + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("MakeupExamSession"); + + b.Navigation("Teacher"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.ScheduleEntry", b => { b.HasOne("Jiaowu.Api.Domain.Academic.Classroom", "Classroom") @@ -3943,6 +4249,18 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql b.Navigation("Records"); }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamPlan", b => + { + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.MakeupExamSession", b => + { + b.Navigation("Enrollments"); + + b.Navigation("Invigilators"); + }); + modelBuilder.Entity("Jiaowu.Api.Domain.Academic.SchedulePlan", b => { b.Navigation("Entries"); diff --git a/src/Jiaowu.Api/Jiaowu.Api.csproj b/src/Jiaowu.Api/Jiaowu.Api.csproj index 5578891..f14fe62 100644 --- a/src/Jiaowu.Api/Jiaowu.Api.csproj +++ b/src/Jiaowu.Api/Jiaowu.Api.csproj @@ -6,10 +6,13 @@ enable $([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/../../web')) true + call npm.cmd + npm + @@ -29,10 +32,19 @@ - - - + Condition="Exists('$(SpaRoot)/package.json')"> + + + diff --git a/src/Jiaowu.Api/Jiaowu.Api.http b/src/Jiaowu.Api/Jiaowu.Api.http deleted file mode 100644 index 5b1d627..0000000 --- a/src/Jiaowu.Api/Jiaowu.Api.http +++ /dev/null @@ -1,14 +0,0 @@ -@Host = http://localhost:5255 - -GET {{Host}}/health -Accept: application/json - -### - -POST {{Host}}/api/auth/login -Content-Type: application/json - -{ - "userName": "admin", - "password": "Admin@123456" -} diff --git a/src/Jiaowu.Api/Program.cs b/src/Jiaowu.Api/Program.cs index bcec165..c54179e 100644 --- a/src/Jiaowu.Api/Program.cs +++ b/src/Jiaowu.Api/Program.cs @@ -27,7 +27,22 @@ if (databaseOptions.Provider.Equals("SQLite", StringComparison.OrdinalIgnoreCase throw new InvalidOperationException("SQLite 仅允许在 Development 环境使用。生产环境请配置 MySql。"); } -builder.Services.AddDbContext(options => +var allowedHosts = builder.Configuration["AllowedHosts"]; +if (!builder.Environment.IsDevelopment() && + (string.IsNullOrWhiteSpace(allowedHosts) || allowedHosts == "*")) +{ + throw new InvalidOperationException( + "生产环境必须通过 AllowedHosts 配置明确的访问域名,不能使用通配符。"); +} + +if (databaseOptions.CommandTimeoutSeconds is < 5 or > 300) +{ + throw new InvalidOperationException( + "Database:CommandTimeoutSeconds 必须在 5 到 300 秒之间。"); +} + +builder.Services.AddSingleton(databaseOptions); +builder.Services.AddDbContextPool(options => { if (databaseOptions.Provider.Equals("SQLite", StringComparison.OrdinalIgnoreCase)) { @@ -59,7 +74,14 @@ builder.Services.AddDbContext(options => throw new InvalidOperationException( "缺少 MySQL 连接串。请通过 ConnectionStrings__MySql 环境变量配置。"); } - options.UseMySQL(connectionString); + options.UseMySQL(connectionString, mySqlOptions => + { + mySqlOptions.CommandTimeout(databaseOptions.CommandTimeoutSeconds); + mySqlOptions.EnableRetryOnFailure( + maxRetryCount: 5, + maxRetryDelay: TimeSpan.FromSeconds(10), + errorNumbersToAdd: null); + }); }); builder.Services @@ -79,9 +101,11 @@ builder.Services var jwtOptions = builder.Configuration.GetSection(JwtOptions.SectionName).Get() ?? throw new InvalidOperationException("缺少 Jwt 配置。"); -if (Encoding.UTF8.GetByteCount(jwtOptions.Key) < 32) +if (Encoding.UTF8.GetByteCount(jwtOptions.Key) < 32 || + jwtOptions.Key.Contains("REPLACE", StringComparison.OrdinalIgnoreCase)) { - throw new InvalidOperationException("Jwt:Key 至少需要 32 字节。"); + throw new InvalidOperationException( + "Jwt:Key 必须配置为至少 32 字节的随机生产密钥,不能使用示例值。"); } builder.Services.Configure( @@ -92,7 +116,6 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); @@ -147,13 +170,17 @@ builder.Services.AddCors(options => options.AddPolicy("Web", policy => { var origins = builder.Configuration.GetSection("Cors:Origins").Get() - ?? ["http://localhost:5173"]; - policy.WithOrigins(origins) - .AllowAnyHeader() - .AllowAnyMethod(); + ?? []; + if (origins.Length > 0) + { + policy.WithOrigins(origins) + .AllowAnyHeader() + .AllowAnyMethod(); + } }); }); +builder.Services.AddResponseCompression(options => options.EnableForHttps = true); builder.Services.AddProblemDetails(); builder.Services.AddExceptionHandler(options => { @@ -215,6 +242,7 @@ builder.Services.AddSwaggerGen(options => var app = builder.Build(); app.UseExceptionHandler(); +app.UseResponseCompression(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); @@ -246,13 +274,10 @@ app.UseAuthentication(); app.UseAuthorization(); app.UseMiddleware(); app.MapControllers(); -app.MapGet("/health", () => Results.Ok(new -{ - Status = "healthy", - Database = databaseOptions.Provider, - Environment = app.Environment.EnvironmentName, - Time = DateTimeOffset.UtcNow -})).AllowAnonymous(); +app.MapGet("/health/live", () => Results.Ok(new { Status = "healthy" })) + .AllowAnonymous(); +app.MapGet("/health", CheckDatabaseHealthAsync).AllowAnonymous(); +app.MapGet("/health/ready", CheckDatabaseHealthAsync).AllowAnonymous(); app.MapFallback(async context => { if (context.Request.Path.StartsWithSegments("/api") || @@ -280,14 +305,35 @@ app.MapFallback(async context => using (var scope = app.Services.CreateScope()) { await scope.ServiceProvider.GetRequiredService() - .InitializeAsync(); + .InitializeAsync( + args.Contains("--migrate-only", StringComparer.OrdinalIgnoreCase)); } -if (args.Contains("--seed-only", StringComparer.OrdinalIgnoreCase)) +if (args.Contains("--migrate-only", StringComparer.OrdinalIgnoreCase)) { return; } app.Run(); +static async Task CheckDatabaseHealthAsync( + AppDbContext db, + CancellationToken cancellationToken) +{ + try + { + return await db.Database.CanConnectAsync(cancellationToken) + ? Results.Ok(new { Status = "healthy" }) + : Results.Json( + new { Status = "unhealthy" }, + statusCode: StatusCodes.Status503ServiceUnavailable); + } + catch + { + return Results.Json( + new { Status = "unhealthy" }, + statusCode: StatusCodes.Status503ServiceUnavailable); + } +} + public partial class Program; diff --git a/src/Jiaowu.Api/appsettings.Development.json b/src/Jiaowu.Api/appsettings.Development.json index c28fb4c..f81bd58 100644 --- a/src/Jiaowu.Api/appsettings.Development.json +++ b/src/Jiaowu.Api/appsettings.Development.json @@ -6,12 +6,13 @@ "SQLite": "Data Source=data/jiaowu-dev.sqlite" }, "Jwt": { - "Key": "jiaowu-development-secret-key-change-before-production" + "Key": "jiaowu-development-secret-key-change-before-production", + "ExpireMinutes": 480 }, - "SeedAdmin": { - "UserName": "admin", - "Password": "Admin@123456", - "DisplayName": "系统管理员" + "Cors": { + "Origins": [ + "http://localhost:5173" + ] }, "Logging": { "LogLevel": { diff --git a/src/Jiaowu.Api/appsettings.json b/src/Jiaowu.Api/appsettings.json index f915a5d..f3d940a 100644 --- a/src/Jiaowu.Api/appsettings.json +++ b/src/Jiaowu.Api/appsettings.json @@ -1,6 +1,8 @@ { "Database": { - "Provider": "MySql" + "Provider": "MySql", + "ApplyMigrationsOnStartup": false, + "CommandTimeoutSeconds": 30 }, "ConnectionStrings": { "MySql": "" @@ -8,13 +10,11 @@ "Jwt": { "Issuer": "Jiaowu.Api", "Audience": "Jiaowu.Web", - "Key": "REPLACE_IN_PRODUCTION_WITH_A_LONG_RANDOM_SECRET", - "ExpireMinutes": 480 + "Key": "", + "ExpireMinutes": 60 }, "Cors": { - "Origins": [ - "http://localhost:5173" - ] + "Origins": [] }, "Logging": { "LogLevel": { diff --git a/tests/Jiaowu.Api.Tests/GradeCalculatorTests.cs b/tests/Jiaowu.Api.Tests/GradeCalculatorTests.cs index 6fb8347..5c0a242 100644 --- a/tests/Jiaowu.Api.Tests/GradeCalculatorTests.cs +++ b/tests/Jiaowu.Api.Tests/GradeCalculatorTests.cs @@ -18,7 +18,10 @@ public sealed class GradeCalculatorTests { Assert.Equal( expected, - GradeCalculator.AreWeightsValid(regular, midterm, final)); + GradeCalculator.AreWeightsValid( + regular, + final, + [new GradeItem { Name = "期中成绩", Weight = midterm }])); } [Fact] @@ -26,11 +29,11 @@ public sealed class GradeCalculatorTests { var total = GradeCalculator.CalculateTotal( 83, - null, 91, + [], 30, - 0, 70, + [], GradeExamStatus.Normal); Assert.Equal(88.6m, total); @@ -43,10 +46,10 @@ public sealed class GradeCalculatorTests Assert.Null(GradeCalculator.CalculateTotal( 83, null, - null, + [], 30, - 0, 70, + [], GradeExamStatus.Normal)); } @@ -56,6 +59,6 @@ public sealed class GradeCalculatorTests [InlineData(GradeExamStatus.Exempt)] public void Exceptional_exam_status_has_no_numeric_total(GradeExamStatus status) { - Assert.Null(GradeCalculator.CalculateTotal(90, null, 90, 30, 0, 70, status)); + Assert.Null(GradeCalculator.CalculateTotal(90, 90, [], 30, 70, [], status)); } } diff --git a/tests/Jiaowu.Api.Tests/MySqlMigrationTests.cs b/tests/Jiaowu.Api.Tests/MySqlMigrationTests.cs new file mode 100644 index 0000000..5ec7040 --- /dev/null +++ b/tests/Jiaowu.Api.Tests/MySqlMigrationTests.cs @@ -0,0 +1,36 @@ +using Jiaowu.Api.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jiaowu.Api.Tests; + +public sealed class MySqlMigrationTests +{ + private const string LatestMigration = + "20260725120917_ProductionSchemaCompletion"; + + [Fact] + public void Production_migration_is_discoverable_and_generates_mysql_sql() + { + var options = new DbContextOptionsBuilder() + .UseMySQL( + "Server=localhost;Database=jiaowu;User=__test__;Password=__not_used__;") + .Options; + using var db = new AppDbContext(options); + + Assert.Contains(LatestMigration, db.Database.GetMigrations()); + + var script = db.GetService().GenerateScript( + fromMigration: "20260725040310_SchedulePublishJobs", + toMigration: LatestMigration); + + Assert.Contains("CREATE TABLE `GradeItems`", script); + Assert.Contains("CREATE TABLE `EvaluationSetups`", script); + Assert.Contains("CREATE TABLE `WarningRules`", script); + Assert.Contains("SET `ExamDate` = DATE(`StartsAt`)", script); + Assert.Contains("DEFAULT 1", script); + Assert.DoesNotContain("0001-01-01", script); + Assert.DoesNotContain("0000-00-00", script); + } +} diff --git a/tests/Jiaowu.Api.Tests/TimetableExcelExporterTests.cs b/tests/Jiaowu.Api.Tests/TimetableExcelExporterTests.cs index 09ee378..c44d727 100644 --- a/tests/Jiaowu.Api.Tests/TimetableExcelExporterTests.cs +++ b/tests/Jiaowu.Api.Tests/TimetableExcelExporterTests.cs @@ -83,7 +83,8 @@ public sealed class TimetableExcelExporterTests ["李老师"], ["计算机科学 2601 班"], null) - ]); + ], + []); var bytes = TimetableExcelExporter.Create(timetable); diff --git a/web/src/style.css b/web/src/style.css index 18dafb7..a54cdce 100644 --- a/web/src/style.css +++ b/web/src/style.css @@ -1052,7 +1052,6 @@ button { cursor: pointer; } .public-timetable-link { display: block; margin: 14px 0 18px; color: #176b87; font-size: 13px; font-weight: 650; text-align: center; text-decoration: none; } .account-activation-link { display: block; margin: -8px 0 18px; color: #315b73; font-size: 13px; font-weight: 650; text-align: center; text-decoration: none; } .login-submit { width: 100%; margin-top: 6px; height: 46px; } -.dev-hint { margin-top: 24px; padding: 13px 15px; display: flex; justify-content: space-between; color: #767e8d; background: #f5f7fa; font-size: 11px; } @media (max-width: 1100px) { .metric-grid { grid-template-columns: repeat(2, 1fr); } diff --git a/web/src/views/LoginView.vue b/web/src/views/LoginView.vue index 763b856..8d70cf9 100644 --- a/web/src/views/LoginView.vue +++ b/web/src/views/LoginView.vue @@ -9,8 +9,8 @@ const router = useRouter() const auth = useAuthStore() const loading = ref(false) const form = reactive({ - userName: String(route.query.userName ?? 'admin'), - password: route.query.activated ? '' : 'Admin@123456', + userName: String(route.query.userName ?? ''), + password: '', }) async function submit() { @@ -84,10 +84,6 @@ async function submit() { 无需登录,查询班级课表 → -
- 本地开发账号 - admin / Admin@123456 -