From 6a6528185092cbc5455ea739eb974e6c446435ce Mon Sep 17 00:00:00 2001 From: SpecialX <47072643+wangxiner55@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:43:33 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BD=9C=E4=B8=9A=E7=BB=93?= =?UTF-8?q?=E6=9E=84=EF=BC=9A=E4=BC=98=E5=8C=96=E5=AE=9E=E4=BD=93=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E3=80=81DTO=E6=98=A0=E5=B0=84=E5=92=8C=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E7=95=8C=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构AppMainStruct、AssignmentQuestion、Question等实体模型 - 更新相关DTO以匹配新的数据结构 - 优化前端页面布局和组件 - 添加全局信息和笔记功能相关代码 - 更新数据库迁移和程序配置 --- Entities/Contracts/AppMainStruct.cs | 136 +- Entities/Contracts/AssignmentQuestion.cs | 2 + Entities/Contracts/Global.cs | 24 + Entities/Contracts/Question.cs | 1 + Entities/Contracts/User.cs | 4 +- Entities/DTO/AssignmentQuestionDto.cs | 2 + Entities/DTO/GlobalDto.cs | 23 + Entities/DTO/QuestionDto.cs | 1 + Entities/DTO/SubjectTypeMetadataDto.cs | 14 + TechHelper.Client/Helper/Helper.cs | 31 + TechHelper.Client/Layout/MainLayout.razor | 132 +- .../Common/Exam/AssignmentInfoCard.razor | 22 +- .../Pages/Common/ExamGlobalInfoDialog.razor | 34 + .../Pages/Common/GlobalInfoCard.razor | 37 + .../Pages/Common/QuestionCardDialog.razor | 2 +- .../Pages/Common/SimpleCard.razor | 2 +- .../Pages/Exam/AssignmentQuestionEdit.razor | 47 + TechHelper.Client/Pages/Exam/ExamCreate.razor | 63 +- .../Pages/Exam/ExamView/ExamStructView.razor | 28 + .../Exam/QuestionCard/QuestionEdit.razor | 50 + .../Pages/Global/LoginInOut/LoginInOut.razor | 32 + .../MainStruct/ApplicationMainIconCard.razor | 11 + .../Pages/Global/MainStruct/SearchBar.razor | 8 + .../MainStruct/SnackErrorBoundary.razor | 35 + TechHelper.Client/Pages/Home.razor | 3 - .../Student/BaseInfoCard/HeadIconCard.razor | 11 + .../StudentSubmissionPreviewCard.razor | 28 + .../StudentSubmissionPreviewTableCard.razor | 39 + ...issionPreviewTableCard_MudCard_NoUSE.razor | 79 + .../BaseInfoCard/TotalErrorQuestionType.razor | 18 + .../Pages/Student/HomePage.razor | 114 +- .../Pages/Student/HomeworkCard.razor | 12 + .../Pages/Student/NotifyCard.razor | 12 + .../StudentSubmissionPreviewView.razor | 11 + TechHelper.Client/Program.cs | 1 + TechHelper.Client/Services/INoteService.cs | 14 + TechHelper.Client/Services/NoteService.cs | 151 ++ TechHelper.Client/_Imports.razor | 3 +- TechHelper.Client/wwwroot/index.html | 5 + TechHelper.Client/wwwroot/ref/Buou.png | Bin 0 -> 15396 bytes TechHelper.Client/wwwroot/ref/Caiq.png | Bin 0 -> 13303 bytes TechHelper.Client/wwwroot/ref/Hasq.png | Bin 0 -> 11531 bytes TechHelper.Client/wwwroot/ref/Keda.png | Bin 0 -> 10260 bytes .../Context/ApplicationContext.cs | 1 + .../Context/AutoMapperProFile.cs | 24 +- .../Controllers/NoteController.cs | 91 ++ ...50901072725_question_qt_update.Designer.cs | 1277 ++++++++++++++++ .../20250901072725_question_qt_update.cs | 93 ++ ...901080732_question_qt_update_2.Designer.cs | 1296 ++++++++++++++++ .../20250901080732_question_qt_update_2.cs | 89 ++ ...901083708_question_qt_update_3.Designer.cs | 1299 +++++++++++++++++ .../20250901083708_question_qt_update_3.cs | 82 ++ .../ApplicationContextModelSnapshot.cs | 35 +- TechHelper.Server/Program.cs | 36 +- .../Repository/GlobalRepository.cs | 17 + TechHelper.Server/Services/INoteService.cs | 13 + TechHelper.Server/Services/NoteService.cs | 107 ++ TechHelper.Server/TechHelper.Server.csproj | 1 + 58 files changed, 5459 insertions(+), 244 deletions(-) create mode 100644 Entities/Contracts/Global.cs create mode 100644 Entities/DTO/GlobalDto.cs create mode 100644 Entities/DTO/SubjectTypeMetadataDto.cs create mode 100644 TechHelper.Client/Helper/Helper.cs create mode 100644 TechHelper.Client/Pages/Common/ExamGlobalInfoDialog.razor create mode 100644 TechHelper.Client/Pages/Common/GlobalInfoCard.razor create mode 100644 TechHelper.Client/Pages/Global/LoginInOut/LoginInOut.razor create mode 100644 TechHelper.Client/Pages/Global/MainStruct/ApplicationMainIconCard.razor create mode 100644 TechHelper.Client/Pages/Global/MainStruct/SearchBar.razor create mode 100644 TechHelper.Client/Pages/Global/MainStruct/SnackErrorBoundary.razor create mode 100644 TechHelper.Client/Pages/Student/BaseInfoCard/HeadIconCard.razor create mode 100644 TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewCard.razor create mode 100644 TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard.razor create mode 100644 TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard_MudCard_NoUSE.razor create mode 100644 TechHelper.Client/Pages/Student/BaseInfoCard/TotalErrorQuestionType.razor create mode 100644 TechHelper.Client/Pages/Student/HomeworkCard.razor create mode 100644 TechHelper.Client/Pages/Student/NotifyCard.razor create mode 100644 TechHelper.Client/Pages/Student/StudentSubmissionPreviewView.razor create mode 100644 TechHelper.Client/Services/INoteService.cs create mode 100644 TechHelper.Client/Services/NoteService.cs create mode 100644 TechHelper.Client/wwwroot/ref/Buou.png create mode 100644 TechHelper.Client/wwwroot/ref/Caiq.png create mode 100644 TechHelper.Client/wwwroot/ref/Hasq.png create mode 100644 TechHelper.Client/wwwroot/ref/Keda.png create mode 100644 TechHelper.Server/Controllers/NoteController.cs create mode 100644 TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs create mode 100644 TechHelper.Server/Migrations/20250901072725_question_qt_update.cs create mode 100644 TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs create mode 100644 TechHelper.Server/Migrations/20250901080732_question_qt_update_2.cs create mode 100644 TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs create mode 100644 TechHelper.Server/Migrations/20250901083708_question_qt_update_3.cs create mode 100644 TechHelper.Server/Repository/GlobalRepository.cs create mode 100644 TechHelper.Server/Services/INoteService.cs create mode 100644 TechHelper.Server/Services/NoteService.cs diff --git a/Entities/Contracts/AppMainStruct.cs b/Entities/Contracts/AppMainStruct.cs index 65b1896..6039c28 100644 --- a/Entities/Contracts/AppMainStruct.cs +++ b/Entities/Contracts/AppMainStruct.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; @@ -34,9 +36,11 @@ namespace Entities.Contracts public enum DifficultyLevel : byte { + simple, easy, medium, - hard + hard, + veryHard } public enum QuestionType : byte @@ -55,47 +59,131 @@ namespace Entities.Contracts public enum SubjectAreaEnum : byte { - Unknown = 0, - Mathematics, // 数学 - Physics, // 物理 - Chemistry, // 化学 - Biology, // 生物 - History, // 历史 - Geography, // 地理 + [Display(Name = "未知", Description = "未知")] + Unknown = 0, + + [Display(Name = "数学", Description = "数")] + Mathematics, // 数学 + + [Display(Name = "物理", Description = "物")] + Physics, // 物理 + + [Display(Name = "化学", Description = "化")] + Chemistry, // 化学 + + [Display(Name = "生物", Description = "生")] + Biology, // 生物 + + [Display(Name = "历史", Description = "史")] + History, // 历史 + + [Display(Name = "地理", Description = "地")] + Geography, // 地理 + + [Display(Name = "语文", Description = "语")] Literature, // 语文/文学 - English, // 英语 - ComputerScience, // 计算机科学 + + [Display(Name = "英语", Description = "英")] + English, // 英语 + + [Display(Name = "计算机科学", Description = "计")] + ComputerScience // 计算机科学 } public enum AssignmentStructType : byte { + [Display(Name = "根节点", Description = "根")] Root, + [Display(Name = "单个问题", Description = "问")] Question, + [Display(Name = "问题组", Description = "组")] Group, + [Display(Name = "结构", Description = "结")] Struct, + [Display(Name = "子问题", Description = "子")] SubQuestion, + [Display(Name = "选项", Description = "选")] Option } - public enum ExamType : byte { - MidtermExam, // 期中 - FinalExam, // 期末 - MonthlyExam, // 月考 - WeeklyExam, // 周考 - DailyTest, // 平时测试 - AITest, // AI测试 - } + [Display(Name = "期中考试", Description = "中")] + MidtermExam, + [Display(Name = "期末考试", Description = "末")] + FinalExam, + + [Display(Name = "月考", Description = "月")] + MonthlyExam, + + [Display(Name = "周考", Description = "周")] + WeeklyExam, + + [Display(Name = "平时测试", Description = "平")] + DailyTest, + + [Display(Name = "AI测试", Description = "AI")] + AITest, + } public enum SubmissionStatus { - Pending, // 待提交/未开始 - Submitted, // 已提交 - Graded, // 已批改 - Resubmission, // 待重新提交 (如果允许) - Late, // 迟交 - Draft, // 草稿 + [Display(Name = "待提交/未开始", Description = "待")] + Pending, + + [Display(Name = "已提交", Description = "提")] + Submitted, + + [Display(Name = "已批改", Description = "批")] + Graded, + + [Display(Name = "待重新提交", Description = "重")] + Resubmission, + + [Display(Name = "迟交", Description = "迟")] + Late, + + [Display(Name = "草稿", Description = "草")] + Draft, + } + + public static class EnumExtensions + { + public static string GetDisplayName(this Enum enumValue) + { + var fieldInfo = enumValue.GetType().GetField(enumValue.ToString()); + + if (fieldInfo == null) + { + return enumValue.ToString(); + } + + var displayAttribute = fieldInfo.GetCustomAttribute(); + + if (displayAttribute != null) + { + return displayAttribute.Name; + } + + return enumValue.ToString(); + } + + public static string GetShortName(this Enum enumValue) + { + var memberInfo = enumValue.GetType().GetMember(enumValue.ToString()).FirstOrDefault(); + + if (memberInfo != null) + { + var displayAttribute = memberInfo.GetCustomAttribute(); + + if (displayAttribute != null && !string.IsNullOrEmpty(displayAttribute.Description)) + { + return displayAttribute.Description; + } + } + + return enumValue.ToString(); + } } } diff --git a/Entities/Contracts/AssignmentQuestion.cs b/Entities/Contracts/AssignmentQuestion.cs index cfbaf91..180deda 100644 --- a/Entities/Contracts/AssignmentQuestion.cs +++ b/Entities/Contracts/AssignmentQuestion.cs @@ -40,6 +40,8 @@ namespace Entities.Contracts [Column("group_state")] public AssignmentStructType StructType { get; set; } = AssignmentStructType.Question; + public QuestionType Type { get; set; } = QuestionType.Unknown; + [Column("created_at")] public DateTime CreatedAt { get; set; } diff --git a/Entities/Contracts/Global.cs b/Entities/Contracts/Global.cs new file mode 100644 index 0000000..03c5425 --- /dev/null +++ b/Entities/Contracts/Global.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Entities.Contracts +{ + [Table("global")] + public class Global + { + [Key] + [Column("id")] + public Guid Id { get; set; } = Guid.NewGuid(); + + public SubjectAreaEnum Area { get; set; } + + public string Info { get; set; } = string.Empty; + } + + +} diff --git a/Entities/Contracts/Question.cs b/Entities/Contracts/Question.cs index 7d91b6d..c824eeb 100644 --- a/Entities/Contracts/Question.cs +++ b/Entities/Contracts/Question.cs @@ -38,6 +38,7 @@ namespace Entities.Contracts [Column("subject_area")] public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown; + public string QType { get; set; } = string.Empty; [Column("options")] public string? Options { get; set; } diff --git a/Entities/Contracts/User.cs b/Entities/Contracts/User.cs index 4054383..87cfb3c 100644 --- a/Entities/Contracts/User.cs +++ b/Entities/Contracts/User.cs @@ -15,8 +15,8 @@ namespace Entities.Contracts public string? RefreshToken { get; set; } public DateTime? RefreshTokenExpiryTime { get; set; } public string? Address { get; set; } - public string? DisplayName { get; set; } - + public string? DisplayName { get; set; } + public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown; [Column("deleted")] public bool IsDeleted { get; set; } diff --git a/Entities/DTO/AssignmentQuestionDto.cs b/Entities/DTO/AssignmentQuestionDto.cs index c38774a..d76f076 100644 --- a/Entities/DTO/AssignmentQuestionDto.cs +++ b/Entities/DTO/AssignmentQuestionDto.cs @@ -17,6 +17,8 @@ namespace Entities.DTO public float Score { get; set; } = 0; public string Sequence { get; set; } = string.Empty; public bool BCorrect { get; set; } = true; + public QuestionType Type { get; set; } = QuestionType.Unknown; + public string QType { get; set; } = string.Empty; public Layout Layout { get; set; } = Layout.horizontal; public AssignmentStructType StructType { get; set; } = AssignmentStructType.Question; diff --git a/Entities/DTO/GlobalDto.cs b/Entities/DTO/GlobalDto.cs new file mode 100644 index 0000000..c018284 --- /dev/null +++ b/Entities/DTO/GlobalDto.cs @@ -0,0 +1,23 @@ +using Entities.Contracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Entities.DTO +{ + + public class GlobalDto + { + + public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown; + public string Data { get; set; } = string.Empty; + } + + public class QuestionDisplayTypeData + { + public string Color { get; set; } + public string DisplayName { get; set; } + } +} diff --git a/Entities/DTO/QuestionDto.cs b/Entities/DTO/QuestionDto.cs index a35ff20..fad6196 100644 --- a/Entities/DTO/QuestionDto.cs +++ b/Entities/DTO/QuestionDto.cs @@ -16,6 +16,7 @@ namespace Entities.DTO public string Title { get; set; } = string.Empty; public QuestionType Type { get; set; } = QuestionType.Unknown; + public string QType { get; set; } = string.Empty; public string? Answer { get; set; } = string.Empty; diff --git a/Entities/DTO/SubjectTypeMetadataDto.cs b/Entities/DTO/SubjectTypeMetadataDto.cs new file mode 100644 index 0000000..6847777 --- /dev/null +++ b/Entities/DTO/SubjectTypeMetadataDto.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using Entities.Contracts; + +namespace Entities.DTO +{ + public class SubjectTypeMetadataDto + { + public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown; + //public Dictionary Data = new Dictionary(); + + public string Data = string.Empty; + } +} diff --git a/TechHelper.Client/Helper/Helper.cs b/TechHelper.Client/Helper/Helper.cs new file mode 100644 index 0000000..f291e01 --- /dev/null +++ b/TechHelper.Client/Helper/Helper.cs @@ -0,0 +1,31 @@ +using MudBlazor; + +namespace TechHelper.Client.Helper +{ + public static class Helper + { + public static Color GetColorFromInt(int value) + { + var v = value % Enum.GetValues(typeof(Color)).Length; + switch (value) + { + case 1: + return MudBlazor.Color.Primary; + case 2: + return MudBlazor.Color.Secondary; + case 3: + return MudBlazor.Color.Success; + case 4: + return MudBlazor.Color.Info; + case 5: + return MudBlazor.Color.Warning; + case 6: + return MudBlazor.Color.Error; + case 7: + return MudBlazor.Color.Dark; + default: + return MudBlazor.Color.Default; + } + } + } +} diff --git a/TechHelper.Client/Layout/MainLayout.razor b/TechHelper.Client/Layout/MainLayout.razor index e3345db..b48d420 100644 --- a/TechHelper.Client/Layout/MainLayout.razor +++ b/TechHelper.Client/Layout/MainLayout.razor @@ -4,83 +4,67 @@ -@* - - - + + + + + + + + + application + + + + + + + + + + + + Dashboard + Exam + Billing + + Users + Security + - - - - - - - - - - - - - - + + About + + + + + Setting + - - - - @Body + + + + + + + @Body + + + + +@code { + ErrorBoundary? errorBoundary; + protected override void OnParametersSet() + { + errorBoundary?.Recover(); + } + bool _drawerOpen = true; - - - - - - - - - *@ - - - - - - - - - - - - - - -@* - - *@ - - - @Body - - - \ No newline at end of file + void DrawerToggle() + { + _drawerOpen = !_drawerOpen; + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor b/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor index f70f9f0..763cacd 100644 --- a/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor +++ b/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor @@ -4,7 +4,7 @@ - 期中测试BETA版本 + BETA版本 75 @@ -12,13 +12,13 @@ - TotalNumber: + 总数: 15 - TotalScore: + 总分: 15 @@ -26,8 +26,8 @@ - - 中位数: + + 中位: 15 @@ -67,17 +67,17 @@ 类型分布 课时分布 - 成绩趋势 - 分值区间 - Success - Warning - Error - Dark +@* 成绩趋势 + 分值区间 + Success + Warning + Error + Dark *@ @code { public double[] data = { 25, 77, 28, 5 }; diff --git a/TechHelper.Client/Pages/Common/ExamGlobalInfoDialog.razor b/TechHelper.Client/Pages/Common/ExamGlobalInfoDialog.razor new file mode 100644 index 0000000..4a2ed6d --- /dev/null +++ b/TechHelper.Client/Pages/Common/ExamGlobalInfoDialog.razor @@ -0,0 +1,34 @@ +@using Entities.DTO +@inject ISnackbar Snackbar + + + + + + 编辑属性 + + + + + + + Cancel + 确认 + + + +@code { + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } + + [Parameter] + public AssignmentDto Assignment { get; set; } = new AssignmentDto(); + + private void Cancel() => MudDialog.Cancel(); + + private void Confirm() + { + Snackbar.Add("属性已更新", Severity.Success); + MudDialog.Close(DialogResult.Ok(Assignment)); + } +} diff --git a/TechHelper.Client/Pages/Common/GlobalInfoCard.razor b/TechHelper.Client/Pages/Common/GlobalInfoCard.razor new file mode 100644 index 0000000..e1e8e1b --- /dev/null +++ b/TechHelper.Client/Pages/Common/GlobalInfoCard.razor @@ -0,0 +1,37 @@ +@using Entities.DTO +@using Entities.Contracts +@using Helper + + + + SCORE + NUMQUESTION + + + @foreach (SubjectAreaEnum item in Enum.GetValues(typeof(SubjectAreaEnum))) + { + var color = Helper.GetColorFromInt((int)item); + + + } + + DUETIME + EXAMTYPE + + + +@code { + [Parameter] + + public AssignmentDto AssignmentDto { get; set; } + [Parameter] + public string Style { get; set; } + [Parameter] + public string Height { get; set; } = "auto"; + + public void HandleQTSelectedValueChanged(SubjectAreaEnum subject) + { + AssignmentDto.SubjectArea = subject; + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/QuestionCardDialog.razor b/TechHelper.Client/Pages/Common/QuestionCardDialog.razor index ac77875..b373699 100644 --- a/TechHelper.Client/Pages/Common/QuestionCardDialog.razor +++ b/TechHelper.Client/Pages/Common/QuestionCardDialog.razor @@ -31,4 +31,4 @@ Snackbar.Add("属性已更新", Severity.Success); MudDialog.Close(DialogResult.Ok(Questions)); } -} \ No newline at end of file +} diff --git a/TechHelper.Client/Pages/Common/SimpleCard.razor b/TechHelper.Client/Pages/Common/SimpleCard.razor index b370d2b..f1ad056 100644 --- a/TechHelper.Client/Pages/Common/SimpleCard.razor +++ b/TechHelper.Client/Pages/Common/SimpleCard.razor @@ -1,4 +1,4 @@ - + @TitleContent @BodyContent @FooterContent diff --git a/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor b/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor index 86e5af7..0561315 100644 --- a/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor +++ b/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor @@ -1,5 +1,6 @@ @using Entities.DTO @using Entities.Contracts +@using Newtonsoft.Json @using TechHelper.Client.Exam @using TechHelper.Client.Pages.Exam.QuestionCard @@ -12,12 +13,30 @@ + @AssignmentStructType.Root @AssignmentStructType.Struct @AssignmentStructType.Group @AssignmentStructType.Question @AssignmentStructType.SubQuestion @AssignmentStructType.Option + + + + + @foreach (var item in QuestionTypes) + { + var qt = item; + @* Style = "@($"background - color:{ item.Value.Color} ")"*@ + + + @item.Value.DisplayName + + } + @if (AssignmentQuestion.Question != null) { @@ -30,6 +49,10 @@ [Parameter] public AssignmentQuestionDto AssignmentQuestion { get; set; } = new AssignmentQuestionDto(); public QuestionDto TempQuesdto; + Dictionary QuestionTypes = new Dictionary(); + + [Inject] + private ILocalStorageService LocalStorageService { get; set; } protected override void OnInitialized() { base.OnInitialized(); @@ -37,6 +60,30 @@ { TempQuesdto = AssignmentQuestion.Question; } + + var cs = LocalStorageService.GetItem("GlobalInfo"); + var GlobalInfo = JsonConvert.DeserializeObject>(cs); + if(GlobalInfo != null) + { + QuestionTypes = GlobalInfo; + } + } + + private void HandleQTSelectedValueChanged(string type) + { + AssignmentQuestion.QType = type; + if (AssignmentQuestion.ChildrenAssignmentQuestion.Count > 0 && AssignmentQuestion.StructType == AssignmentStructType.Group) + { + foreach (var item in AssignmentQuestion.ChildrenAssignmentQuestion) + { + item.QType = type; + if (item.Question != null) + { + item.Question.QType = type; + } + } + } + StateHasChanged(); } private void HandleSelectedValueChanged(AssignmentStructType type) diff --git a/TechHelper.Client/Pages/Exam/ExamCreate.razor b/TechHelper.Client/Pages/Exam/ExamCreate.razor index ea870a9..7dde53f 100644 --- a/TechHelper.Client/Pages/Exam/ExamCreate.razor +++ b/TechHelper.Client/Pages/Exam/ExamCreate.razor @@ -1,5 +1,7 @@ @page "/exam/create" @using AutoMapper +@using Entities.Contracts +@using Newtonsoft.Json @using TechHelper.Client.Pages.Common @using TechHelper.Client.Pages.Exam.ExamView @using TechHelper.Client.Services @@ -39,10 +41,12 @@ - 文本编辑器 - 载入 - 发布 - 指派 + 文本编辑器 + 载入 + 发布 + 指派 + Test + GlobalExamInfo @@ -84,11 +88,32 @@ private ExamParserConfig _examParserConfig { get; set; } = new ExamParserConfig(); private string EditorText = ""; + [Inject] + private ILocalStorageService LocalStorageService { get; set; } [Inject] public IMapper Mapper { get; set; } + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + + var response = await NoteService.GetNote((byte)SubjectAreaEnum.Literature); + + if (response.Status) + { + try + { + LocalStorageService.SetItem("GlobalInfo", response.Result); + } + catch (Exception ex) + { + + } + } + } + private async void OpenEditor() { var parameters = new DialogParameters { { x => x.TextEditor, _textEditor } }; @@ -169,13 +194,43 @@ [Inject] public IExamService examService { get; set; } + [Inject] + public INoteService NoteService { get; set; } + public async Task Publish() { var apiRespon = await examService.SaveParsedExam(ExamContent); Snackbar.Add(apiRespon.Message); } + public async Task OpenTest() + { + Dictionary Note = new Dictionary { { "Hello", (Color.Surface, "World") }, { "My", (Color.Surface, "App") }, }; + var json = JsonConvert.SerializeObject(Note); + var result = await NoteService.AddNote(new GlobalDto { SubjectArea = Entities.Contracts.SubjectAreaEnum.Physics, Data = json }); + + + Console.WriteLine(json); + var res = JsonConvert.DeserializeObject>(json); + + } + + private async void HandleGlobalInfo() + { + // _open = true; + // _edit = true; + // StateHasChanged(); + + var parameters = new DialogParameters { { x => x.Assignment, ExamContent } }; + + var dialog = await DialogService.ShowAsync("Exam_GlobalInfo", parameters); + var result = await dialog.Result; + if (!result.Canceled) + { + } + StateHasChanged(); + } } diff --git a/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor b/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor index d35d260..ea7e5aa 100644 --- a/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor +++ b/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor @@ -1,5 +1,6 @@ @using Entities.Contracts @using Entities.DTO +@using Newtonsoft.Json @using TechHelper.Client.Exam @using TechHelper.Client.Pages.Exam.QuestionCard @@ -22,6 +23,11 @@ @ExamStruct.StructType + @(ExamStruct.QType == string.Empty ? "" : QuestionTypes[ExamStruct.QType].DisplayName) + @if(ExamStruct.Question!=null) + { + + } @@ -75,6 +81,22 @@ [Parameter] public string Style { get; set; } = "background-color : #eeeeee"; + Dictionary QuestionTypes = new Dictionary(); + + [Inject] + private ILocalStorageService LocalStorageService { get; set; } + protected override void OnInitialized() + { + base.OnInitialized(); + + var cs = LocalStorageService.GetItem("GlobalInfo"); + var GlobalInfo = JsonConvert.DeserializeObject>(cs); + if (GlobalInfo != null) + { + QuestionTypes = GlobalInfo; + } + } + private async void HandleClick() { await ClickedStruct.InvokeAsync(ExamStruct); @@ -84,4 +106,10 @@ { await ClickedStruct.InvokeAsync(clickedChildExamStruct); } + + private void HandleSelected(int num) + { + ExamStruct.Question.DifficultyLevel = (DifficultyLevel)num; + } + } \ No newline at end of file diff --git a/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor index 0e96fce..3432676 100644 --- a/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor +++ b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor @@ -1,10 +1,29 @@ @using Entities.DTO +@using Entities.Contracts +@using Newtonsoft.Json @using TechHelper.Client.Exam @* @Question.Id *@ 问题属性 + + + + @foreach (var item in QuestionTypes) + { + var qt = item; + @* Style = "@($"background - color:{ item.Value.Color} ")"*@ + + + @item.Value.DisplayName + + } + + @@ -15,4 +34,35 @@ @code { [Parameter] public QuestionDto Question { get; set; } = new QuestionDto(); + public int diffi = 0; + Dictionary QuestionTypes = new Dictionary(); + + [Inject] + private ILocalStorageService LocalStorageService { get; set; } + protected override void OnInitialized() + { + base.OnInitialized(); + + var cs = LocalStorageService.GetItem("GlobalInfo"); + var GlobalInfo = JsonConvert.DeserializeObject>(cs); + if (GlobalInfo != null) + { + QuestionTypes = GlobalInfo; + } + } + private void HandleSelectedValueChanged(QuestionType type) + { + Question.Type = type; + } + + private void HandleSelected(int num) + { + Question.DifficultyLevel = (DifficultyLevel)num; + } + + private void HandleQTSelectedValueChanged(string type) + { + Question.QType = type; + StateHasChanged(); + } } diff --git a/TechHelper.Client/Pages/Global/LoginInOut/LoginInOut.razor b/TechHelper.Client/Pages/Global/LoginInOut/LoginInOut.razor new file mode 100644 index 0000000..0bcd1b2 --- /dev/null +++ b/TechHelper.Client/Pages/Global/LoginInOut/LoginInOut.razor @@ -0,0 +1,32 @@ +@using Microsoft.AspNetCore.Components.WebAssembly.Authentication +@inject NavigationManager Navigation +@inject IAuthenticationClientService AuthenticationClientService + + + + + Hello, @context.User.Identity.Name! + + LOGOUT + + + Login + + + +@code { + [CascadingParameter] + private Task authenticationStateTask { get; set; } + + private async Task Logout() + { + await AuthenticationClientService.LogoutAsync(); + Navigation.NavigateTo("/"); + } + + private void LoginIN() + { + Navigation.NavigateToLogin("/login"); + } + +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Global/MainStruct/ApplicationMainIconCard.razor b/TechHelper.Client/Pages/Global/MainStruct/ApplicationMainIconCard.razor new file mode 100644 index 0000000..154a6c5 --- /dev/null +++ b/TechHelper.Client/Pages/Global/MainStruct/ApplicationMainIconCard.razor @@ -0,0 +1,11 @@ + + + TechHelper + + +@code { + [Parameter] + public string Height { get; set; } = "30px"; + [Parameter] + public string Width { get; set; } = "100%"; +} diff --git a/TechHelper.Client/Pages/Global/MainStruct/SearchBar.razor b/TechHelper.Client/Pages/Global/MainStruct/SearchBar.razor new file mode 100644 index 0000000..e0096cc --- /dev/null +++ b/TechHelper.Client/Pages/Global/MainStruct/SearchBar.razor @@ -0,0 +1,8 @@ + + + + + +@code { + public string TextValue { get; set; } +} diff --git a/TechHelper.Client/Pages/Global/MainStruct/SnackErrorBoundary.razor b/TechHelper.Client/Pages/Global/MainStruct/SnackErrorBoundary.razor new file mode 100644 index 0000000..b6c459b --- /dev/null +++ b/TechHelper.Client/Pages/Global/MainStruct/SnackErrorBoundary.razor @@ -0,0 +1,35 @@ +@inherits ErrorBoundary +@inject ISnackbar Snackbar + +@if (CurrentException is null) +{ + @ChildContent +} +else if (ErrorContent is not null) +{ + @ErrorContent(CurrentException) +} +else +{ +
+ + 组件加载或执行时出现了问题。 + + 重试 + + +
+} + + +@code { + protected override async Task OnErrorAsync(Exception exception) + { + Snackbar.Add("操作失败,请重试或联系管理员。", Severity.Error); + + await base.OnErrorAsync(exception); + } + +} diff --git a/TechHelper.Client/Pages/Home.razor b/TechHelper.Client/Pages/Home.razor index 9ae147c..4d6753f 100644 --- a/TechHelper.Client/Pages/Home.razor +++ b/TechHelper.Client/Pages/Home.razor @@ -8,9 +8,6 @@ - - - @code { [CascadingParameter] private Task authenticationStateTask { get; set; } diff --git a/TechHelper.Client/Pages/Student/BaseInfoCard/HeadIconCard.razor b/TechHelper.Client/Pages/Student/BaseInfoCard/HeadIconCard.razor new file mode 100644 index 0000000..5989da3 --- /dev/null +++ b/TechHelper.Client/Pages/Student/BaseInfoCard/HeadIconCard.razor @@ -0,0 +1,11 @@ + + + TechHelper + + +@code { + [Parameter] + public string Height { get; set; } = "250px"; + [Parameter] + public string Width { get; set; } = "100%"; +} diff --git a/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewCard.razor b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewCard.razor new file mode 100644 index 0000000..adb9799 --- /dev/null +++ b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewCard.razor @@ -0,0 +1,28 @@ +@using static TechHelper.Client.Pages.Student.BaseInfoCard.StudentSubmissionPreviewTableCard + + +@if(StudentSubmission!=null) +{ + + @StudentSubmission.StudentName + @StudentSubmission.TotalProblems + @StudentSubmission.ErrorCount + @StudentSubmission.TimeSpent + @StudentSubmission.Score + +} +else +{ + + 名称 + 题目总数 + 错误总数 + 时间 + 得分 + +} +@code{ + + [Parameter] + public StudentSubmission StudentSubmission{ get; set; } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard.razor b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard.razor new file mode 100644 index 0000000..62f08da --- /dev/null +++ b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard.razor @@ -0,0 +1,39 @@ + + + + @foreach (var submission in _studentSubmissions) + { + + } + + + + +@code { + // 假设的学生提交数据模型 + public class StudentSubmission + { + public string StudentName { get; set; } + public int TotalProblems { get; set; } + public int ErrorCount { get; set; } + public TimeSpan TimeSpent { get; set; } + public int Score { get; set; } + } + + // 模拟数据列表 + private List _studentSubmissions = new(); + + protected override void OnInitialized() + { + // 模拟获取或初始化数据,实际应用中可能来自数据库或API + _studentSubmissions = new List + { + new() { StudentName = "张三", TotalProblems = 10, ErrorCount = 2, TimeSpent = TimeSpan.FromMinutes(25), Score = 80 }, + new() { StudentName = "李四", TotalProblems = 10, ErrorCount = 1, TimeSpent = TimeSpan.FromMinutes(20), Score = 90 }, + new() { StudentName = "王五", TotalProblems = 10, ErrorCount = 5, TimeSpent = TimeSpan.FromMinutes(30), Score = 50 }, + new() { StudentName = "赵六", TotalProblems = 10, ErrorCount = 3, TimeSpent = TimeSpan.FromMinutes(28), Score = 70 }, + new() { StudentName = "钱七", TotalProblems = 10, ErrorCount = 0, TimeSpent = TimeSpan.FromMinutes(18), Score = 100 } + // ... 可以添加更多模拟数据 + }; + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard_MudCard_NoUSE.razor b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard_MudCard_NoUSE.razor new file mode 100644 index 0000000..f2063f5 --- /dev/null +++ b/TechHelper.Client/Pages/Student/BaseInfoCard/StudentSubmissionPreviewTableCard_MudCard_NoUSE.razor @@ -0,0 +1,79 @@ +@using MudBlazor +@using System.Collections.Generic + + + + + + + + + + + +
+ Hover + Dense + Striped + Bordered +
+ +@code { + // Element类定义 + public class Element + { + public int Number { get; set; } + public string Sign { get; set; } + public string Name { get; set; } + public int Position { get; set; } + public decimal Molar { get; set; } + } + + // 示例数据 + private IEnumerable Elements = new List + { + new Element { Number = 1, Sign = "H", Name = "Hydrogen", Position = 1, Molar = 1.008m }, + new Element { Number = 2, Sign = "He", Name = "Helium", Position = 0, Molar = 4.0026m }, + new Element { Number = 3, Sign = "Li", Name = "Lithium", Position = 1, Molar = 6.94m }, + new Element { Number = 4, Sign = "Be", Name = "Beryllium", Position = 2, Molar = 9.0122m }, + new Element { Number = 5, Sign = "B", Name = "Boron", Position = 13, Molar = 10.81m } + }; + + private bool _hover; + private bool _dense; + private bool _striped; + private bool _bordered; + + // 行样式函数:Position为0的行显示为斜体 + private Func _rowStyleFunc => (x, i) => + { + if (x.Position == 0) + return "font-style:italic"; + + return ""; + }; + + // 单元格样式函数:根据元素编号设置背景色,根据摩尔质量设置字体粗细 + private Func _cellStyleFunc => x => + { + string style = ""; + + if (x.Number == 1) + style += "background-color:#8CED8C"; // 浅绿色 + + else if (x.Number == 2) + style += "background-color:#E5BDE5"; // 浅紫色 + + else if (x.Number == 3) + style += "background-color:#EACE5D"; // 浅黄色 + + else if (x.Number == 4) + style += "background-color:#F1F165"; // 浅黄色 + + if (x.Molar > 5) + style += ";font-weight:bold"; + + return style; + }; +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Student/BaseInfoCard/TotalErrorQuestionType.razor b/TechHelper.Client/Pages/Student/BaseInfoCard/TotalErrorQuestionType.razor new file mode 100644 index 0000000..ba92635 --- /dev/null +++ b/TechHelper.Client/Pages/Student/BaseInfoCard/TotalErrorQuestionType.razor @@ -0,0 +1,18 @@ + + + + +@code{ + private string[] _xAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" }; + private AxisChartOptions _axisChartOptions = new AxisChartOptions + { + }; + protected override void OnInitialized() + { + _axisChartOptions.MatchBoundsToSize = true; + } + private List _series = new List() + { + new ChartSeries() { Name = "United States", Data = new double[] { 40, 20, 25, 27, 46, 60, 48, 80, 15 } } + }; +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Student/HomePage.razor b/TechHelper.Client/Pages/Student/HomePage.razor index ec43a5a..d7df942 100644 --- a/TechHelper.Client/Pages/Student/HomePage.razor +++ b/TechHelper.Client/Pages/Student/HomePage.razor @@ -1,104 +1,26 @@ - +@using TechHelper.Client.Pages.Common.Exam; +@using TechHelper.Client.Pages.Student.BaseInfoCard; +@using TechHelper.Client.Pages.Common; + + + + + + + + + + + + + + - - - - - - - Total - @data.Sum().ToString() - - - - - - - - BodyContent - - - TitleContent - - - FooterContent - - - - - - BodyContent - - - TitleContent - - - FooterContent - - - - - - TitleContent - - - BodyContent - - - leran about this curson - - - - - - - - - - - Visits Summary: - - - - - - leran about this curson - - - - -@* - - Visits Summary: - - - - - - - - - - - - - - - - - - leran about this curson - - *@ - - - + - - @code { public double[] data = { 25, 77, 28, 5 }; public string[] labels = { "Oil", "Coal", "Gas", "Biomass" }; diff --git a/TechHelper.Client/Pages/Student/HomeworkCard.razor b/TechHelper.Client/Pages/Student/HomeworkCard.razor new file mode 100644 index 0000000..ee47c7e --- /dev/null +++ b/TechHelper.Client/Pages/Student/HomeworkCard.razor @@ -0,0 +1,12 @@ +@using TechHelper.Client.Pages.Common; + + + 作业 + + + 你暂时还没有任何作业 + + +@code { + +} diff --git a/TechHelper.Client/Pages/Student/NotifyCard.razor b/TechHelper.Client/Pages/Student/NotifyCard.razor new file mode 100644 index 0000000..f9deadf --- /dev/null +++ b/TechHelper.Client/Pages/Student/NotifyCard.razor @@ -0,0 +1,12 @@ +@using TechHelper.Client.Pages.Common; + + + 通知 + + + 暂时没有任何通知 + + +@code { + +} diff --git a/TechHelper.Client/Pages/Student/StudentSubmissionPreviewView.razor b/TechHelper.Client/Pages/Student/StudentSubmissionPreviewView.razor new file mode 100644 index 0000000..9b1c93e --- /dev/null +++ b/TechHelper.Client/Pages/Student/StudentSubmissionPreviewView.razor @@ -0,0 +1,11 @@ +@page "/studentSubmissionView" +@using TechHelper.Client.Pages.Student.BaseInfoCard + + + + + + + + + diff --git a/TechHelper.Client/Program.cs b/TechHelper.Client/Program.cs index 25a67a7..0a421e4 100644 --- a/TechHelper.Client/Program.cs +++ b/TechHelper.Client/Program.cs @@ -45,6 +45,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddHttpClient("WebApiClient", client => { diff --git a/TechHelper.Client/Services/INoteService.cs b/TechHelper.Client/Services/INoteService.cs new file mode 100644 index 0000000..729e29a --- /dev/null +++ b/TechHelper.Client/Services/INoteService.cs @@ -0,0 +1,14 @@ +using Entities.DTO; +using TechHelper.Services; + +namespace TechHelper.Client.Services +{ + public interface INoteService + { + public Task AddNote(GlobalDto dto); + public Task DeleteNote(byte id); + public Task GetAllNotes(); + public Task GetNote(byte id); + public Task UpdateNote(GlobalDto dto); + } +} diff --git a/TechHelper.Client/Services/NoteService.cs b/TechHelper.Client/Services/NoteService.cs new file mode 100644 index 0000000..a73e0d7 --- /dev/null +++ b/TechHelper.Client/Services/NoteService.cs @@ -0,0 +1,151 @@ +using Entities.DTO; +using System.Net.Http.Json; +using TechHelper.Client.AI; +using TechHelper.Services; + +namespace TechHelper.Client.Services +{ + public class NoteService : INoteService + { + private readonly HttpClient _client; + + public NoteService(HttpClient client) + { + _client = client; + } + + /// + /// 添加一个新笔记 + /// + /// 包含笔记数据的数据传输对象 + /// 操作结果 + public async Task AddNote(GlobalDto dto) + { + try + { + var response = await _client.PostAsJsonAsync("note", dto); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result ?? ApiResponse.Success("操作成功。"); + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + return ApiResponse.Error($"添加失败。状态码: {response.StatusCode}。详情: {errorContent}"); + } + } + catch (HttpRequestException ex) + { + return ApiResponse.Error($"网络请求错误: {ex.Message}"); + } + } + + /// + /// 根据 ID 删除一个笔记 + /// + /// 要删除的笔记的 ID + /// 操作结果 + public async Task DeleteNote(byte id) + { + try + { + var response = await _client.DeleteAsync($"note/{id}"); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result ?? ApiResponse.Success("删除成功。"); + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + return ApiResponse.Error($"删除失败。状态码: {response.StatusCode}。详情: {errorContent}"); + } + } + catch (HttpRequestException ex) + { + return ApiResponse.Error($"网络请求错误: {ex.Message}"); + } + } + + /// + /// 获取所有笔记 + /// + /// 包含所有笔记列表的操作结果 + public async Task GetAllNotes() + { + try + { + var response = await _client.GetAsync("note"); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result ?? ApiResponse.Success("获取成功。"); + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + return ApiResponse.Error($"获取失败。状态码: {response.StatusCode}。详情: {errorContent}"); + } + } + catch (HttpRequestException ex) + { + return ApiResponse.Error($"网络请求错误: {ex.Message}"); + } + } + + /// + /// 根据 ID 获取单个笔记 + /// + /// 要获取的笔记的 ID + /// 包含单个笔记数据的操作结果 + public async Task GetNote(byte id) + { + try + { + var response = await _client.GetAsync($"note/{id}"); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result ?? ApiResponse.Success("获取成功。"); + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + return ApiResponse.Error($"获取失败。状态码: {response.StatusCode}。详情: {errorContent}"); + } + } + catch (HttpRequestException ex) + { + return ApiResponse.Error($"网络请求错误: {ex.Message}"); + } + } + + public async Task UpdateNote(GlobalDto dto) + { + try + { + var response = await _client.PutAsJsonAsync("note", dto); + + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync(); + return result ?? ApiResponse.Success("更新成功。"); + } + else + { + var errorContent = await response.Content.ReadAsStringAsync(); + return ApiResponse.Error($"更新失败。状态码: {response.StatusCode}。详情: {errorContent}"); + } + } + catch (HttpRequestException ex) + { + return ApiResponse.Error($"网络请求错误: {ex.Message}"); + } + } + } +} diff --git a/TechHelper.Client/_Imports.razor b/TechHelper.Client/_Imports.razor index fca362f..d22148d 100644 --- a/TechHelper.Client/_Imports.razor +++ b/TechHelper.Client/_Imports.razor @@ -19,4 +19,5 @@ @using TechHelper.Client.HttpRepository @using TechHelper.Client.Pages.Author @using TechHelper.Client.Pages -@using Blazored.TextEditor \ No newline at end of file +@using Blazored.TextEditor +@using TechHelper.Client.Pages.Global.MainStruct \ No newline at end of file diff --git a/TechHelper.Client/wwwroot/index.html b/TechHelper.Client/wwwroot/index.html index 01e157f..9db845f 100644 --- a/TechHelper.Client/wwwroot/index.html +++ b/TechHelper.Client/wwwroot/index.html @@ -36,6 +36,11 @@ + + diff --git a/TechHelper.Client/wwwroot/ref/Buou.png b/TechHelper.Client/wwwroot/ref/Buou.png new file mode 100644 index 0000000000000000000000000000000000000000..36b8f3a7f418ee0c44289704a9964070868df96a GIT binary patch literal 15396 zcmV+wBkbyj{0$_g#B>hVD>98W5=fq+$gX4HVEo1t0~KQz@!wpaPJJ z4OBExKm#cPq(HBgqKXE(hz2SE=^_hN8D{5bpaPK2(R-!WE~0@7K)T36RfgF)8mIuI zbM#*6wToz=YXYQ;D7P}quCIX#K)U`KR%Vc*209fW%}w<~#x~VALl-pFH<3?qt`*T0 z4TRP}rvL;XjB9F`Q6-soGLo4HWhLX};VZz52QZ;$Q)$rUHK5kMd0bQ9iOLS%Q8WX> zUOH=HB?kzTRlt~`CfuaR_HI&unE+sva zn$-|R*hrJImv_cC)sI*ClD|t15H(q)0AcbH?3HcKc0`xH69G%GcF}58TBWZ6W$ex4 z+CgCg!nbn;oC#x_N)M8f0wibLcm-cY6fS57i;65WP*x?X6xLQbm9qBZ+CkxLtED|k zgc>3sDfOC{6d)=R5IsCfsp(lKbq#xX_yriwHcUk$XP-J&h3Qf&uE5wTMJ^4nHp-#B zqt;$+tFwCRT&lbqLtCl*7 zlnfwhVxFxPlQ$y=N&*~!L<7fG&#knN(|KF4=1NrgSTkv-(Dk^yEtE7#fEVk}`fBIc zr~m?wrr1Km9>YDm3uUw=iz2EohLSF!Jk>*D`z_)XH#x= zD(R8{M0MgpeB-`{hn^71LYXKv3|O-2E*Oi*VwOs=p(oeH8jNe|J5lL0ci@&h=O>mI zYo|fM+BK?3VX0yHMFw(LUYvqo!3qQJV&Drx5R%On@j*umhBalHFh_*g(V; zRr*e>07XK-#^$1h0!B7Sv|x<@1%QB9uh5<58ki0>BRx)(61-9eNX`)R0D?C_c}dL= zSty@HARHsVu|v=sb-sVwiQEa@usN* zM0McVUiGRUSbSs=YM?~G0Z3RJuK7y!2yMz%wqG8lXvVlEXB!phP87ONDq86N&1=IR z!}3_wB10tQm$KiKd$5QvVzibP;aMygU*Cw5@cQvfkD2#rqBZNbV371C!uNU~i!f`l=!+Ffg1Y8k&}AA5sN1d1*B0s;^0wzqVQehQSW}H4 zO1Q@p+0Gw;V_^S1WRL!PN`3E!DDw#2hQ-G74L#xv!4uZ`f7PX1o>LSs#UXb~y&Lx~DNyi)?xh9;tvn>3$muKz?X~HZEdY1k}HnW$+$jhYJ0nKB0EGD%J$LZ!XHtb0A)LP`(nC2&SO%)(&ka>e=a*CJtX1|%oaAkV+ z>Rr-pm+qQzHY&p?vi;IPQ3jh20E{}Lwk}Y+K{`e`M9GKLCGwm0nv`y{&8iRQkIU#2 zQb1DUX#l+A$2QfgZB*Zh@Ff5+a0)mF$Zdp?M_ZsFj*sosK()2@tG0{UQP|4r^0!$^ z54Kv#@KOT^UdtR}FU#|fx}NzV(`-|vlvJPFcA-?WJk2~nac{p!+xyGnm9^p8tCWt* zIM@zO06bxUfm2}4X%7{r=-y8G^~c&1Va# AdLIvrW`|Y5-A!J@=^VEC*q8d*wF~ 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 O0000Q6_r5 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

Bl@MGeo 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 literal 0 HcmV?d00001 diff --git a/TechHelper.Client/wwwroot/ref/Keda.png b/TechHelper.Client/wwwroot/ref/Keda.png new file mode 100644 index 0000000000000000000000000000000000000000..0e3c646c199d0a3caafd37ae45b66e62adc16fc7 GIT binary patch literal 10260 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 B Submissions { get; set; } public DbSet SubmissionDetails { get; set; } public DbSet QuestionContexts { get; set; } + public DbSet Globals { get; set; } protected override void OnModelCreating(ModelBuilder builder) { diff --git a/TechHelper.Server/Context/AutoMapperProFile.cs b/TechHelper.Server/Context/AutoMapperProFile.cs index 9cb9c2a..5a17b0f 100644 --- a/TechHelper.Server/Context/AutoMapperProFile.cs +++ b/TechHelper.Server/Context/AutoMapperProFile.cs @@ -2,6 +2,10 @@ using AutoMapper.Internal.Mappers; using Entities.Contracts; using Entities.DTO; +using Newtonsoft.Json; +using System.Net.Http.Json; +using System.Text.Json; +using System.Text.Json.Serialization; namespace TechHelper.Context { @@ -23,13 +27,13 @@ namespace TechHelper.Context public AutoMapperProFile() { CreateMap() - .ForMember(dest => dest.Id, opt => opt.Ignore()) - .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Name)) + .ForMember(dest => dest.Id, opt => opt.Ignore()) + .ForMember(dest => dest.UserName, opt => opt.MapFrom(src => src.Name)) .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.Email)) .ForMember(dest => dest.PhoneNumber, opt => opt.MapFrom(src => src.PhoneNumber)) - .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.HomeAddress)) - .ForMember(dest => dest.PasswordHash, opt => opt.Ignore()) - .ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore()); + .ForMember(dest => dest.Address, opt => opt.MapFrom(src => src.HomeAddress)) + .ForMember(dest => dest.PasswordHash, opt => opt.Ignore()) + .ForMember(dest => dest.EmailConfirmed, opt => opt.Ignore()); CreateMap() .ForMember(d => d.Number, o => o.MapFrom(src => src.Class)) @@ -44,7 +48,7 @@ namespace TechHelper.Context CreateMap().ReverseMap(); - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); @@ -52,7 +56,13 @@ namespace TechHelper.Context // Submission CreateMap().ReverseMap(); - CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + + CreateMap() + .ForMember(dest => dest.Info, opt => opt.MapFrom(src => JsonConvert.SerializeObject(src.Data))); + CreateMap() + .ForMember(dest => dest.Data, opt => opt.MapFrom(src => JsonConvert.DeserializeObject>(src.Info))); } } diff --git a/TechHelper.Server/Controllers/NoteController.cs b/TechHelper.Server/Controllers/NoteController.cs new file mode 100644 index 0000000..08658d9 --- /dev/null +++ b/TechHelper.Server/Controllers/NoteController.cs @@ -0,0 +1,91 @@ +using Entities.DTO; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using TechHelper.Services; + +namespace TechHelper.Server.Controllers +{ + [Route("api/note")] + [ApiController] + public class NoteController : ControllerBase + { + private readonly INoteService _noteService; + + // 通过依赖注入获取 NoteService + public NoteController(INoteService noteService) + { + _noteService = noteService; + } + + ///

+ /// 获取所有全局数据。 + /// GET: api/Note + /// + [HttpGet] + public async Task GetAll([FromQuery] QueryParameter query) + { + var response = await _noteService.GetAllAsync(query); + return Ok(response); + } + + /// + /// 根据 ID 获取单个全局数据。 + /// GET: api/Note/{id} + /// + [HttpGet("{id}")] + public async Task Get(byte id) + { + var response = await _noteService.GetAsync(id); + if (!response.Status) + { + return NotFound(response); + } + return Ok(response); + } + + /// + /// 添加新的全局数据。 + /// POST: api/Note + /// + [HttpPost] + public async Task Add([FromBody] GlobalDto model) + { + var response = await _noteService.AddAsync(model); + if (!response.Status) + { + return BadRequest(response); + } + return Ok(response); + } + + /// + /// 更新已存在的全局数据。 + /// PUT: api/Note + /// + [HttpPut] + public async Task Update([FromBody] GlobalDto model) + { + var response = await _noteService.UpdateAsync(model); + if (!response.Status) + { + return NotFound(response); + } + return Ok(response); + } + + /// + /// 根据 ID 删除全局数据。 + /// DELETE: api/Note/{id} + /// + [HttpDelete("{id}")] + public async Task Delete(byte id) + { + var response = await _noteService.DeleteAsync(id); + if (!response.Status) + { + return NotFound(response); + } + return Ok(response); + } + } +} diff --git a/TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs b/TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs new file mode 100644 index 0000000..c732281 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901072725_question_qt_update.Designer.cs @@ -0,0 +1,1277 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TechHelper.Context; + +#nullable disable + +namespace TechHelper.Server.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20250901072725_question_qt_update")] + partial class question_qt_update + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.16") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("DueDate") + .HasColumnType("datetime(6)") + .HasColumnName("due_date"); + + b.Property("ExamStructId") + .HasColumnType("char(36)") + .HasColumnName("exam_struct_id"); + + b.Property("ExamType") + .HasColumnType("tinyint unsigned"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("title"); + + b.Property("TotalQuestions") + .HasColumnType("tinyint unsigned") + .HasColumnName("total_points"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("ExamStructId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("assignments", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_name"); + + b.Property("FilePath") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_path"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)") + .HasColumnName("uploaded_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.ToTable("assignment_attachments"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id") + .HasColumnOrder(0); + + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(1); + + b.Property("AssignedAt") + .HasColumnType("datetime(6)") + .HasColumnName("assigned_at"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("AssignmentId", "ClassId"); + + b.HasIndex("ClassId"); + + b.ToTable("assignment_class", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("Index") + .HasColumnType("tinyint unsigned") + .HasColumnName("question_number"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("ParentAssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("parent_question_group_id"); + + b.Property("QuestionContextId") + .HasColumnType("char(36)") + .HasColumnName("description"); + + b.Property("QuestionId") + .HasColumnType("char(36)") + .HasColumnName("question_id"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("Sequence") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("sequence"); + + b.Property("StructType") + .HasColumnType("tinyint unsigned") + .HasColumnName("group_state"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("ParentAssignmentQuestionId"); + + b.HasIndex("QuestionContextId"); + + b.HasIndex("QuestionId"); + + b.ToTable("assignment_questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Description") + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned") + .HasColumnName("grade"); + + b.Property("HeadTeacherId") + .HasColumnType("char(36)") + .HasColumnName("head_teacher_id"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("Number") + .HasColumnType("tinyint unsigned") + .HasColumnName("class"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("HeadTeacherId"); + + b.ToTable("classes", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(0); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id") + .HasColumnOrder(1); + + b.Property("EnrollmentDate") + .HasColumnType("datetime(6)") + .HasColumnName("enrollment_date"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("ClassId", "StudentId"); + + b.HasIndex("StudentId"); + + b.ToTable("class_student", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id"); + + b.Property("TeacherId") + .HasColumnType("char(36)") + .HasColumnName("teacher_id"); + + b.Property("SubjectTaught") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_taught"); + + b.HasKey("ClassId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("class_teachers", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("key_point"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TextbookID") + .HasColumnType("char(36)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("TextbookID"); + + b.ToTable("lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("lesson_question"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Answer") + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("correct_answer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("DifficultyLevel") + .HasMaxLength(10) + .HasColumnType("tinyint unsigned") + .HasColumnName("difficulty_level"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("KeyPointId") + .HasColumnType("char(36)") + .HasColumnName("key_point"); + + b.Property("LessonId") + .HasColumnType("char(36)") + .HasColumnName("lesson"); + + b.Property("Options") + .HasColumnType("longtext") + .HasColumnName("options"); + + b.Property("QType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SubjectArea") + .HasMaxLength(100) + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("question_text"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("tinyint unsigned") + .HasColumnName("question_type"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("KeyPointId"); + + b.HasIndex("LessonId"); + + b.HasIndex("Title") + .HasAnnotation("MySql:IndexPrefixLength", new[] { 20 }); + + b.ToTable("questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("QuestionContexts"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("AttemptNumber") + .HasColumnType("tinyint unsigned") + .HasColumnName("attempt_number"); + + b.Property("ErrorQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("GradedAt") + .HasColumnType("datetime(6)") + .HasColumnName("graded_at"); + + b.Property("GraderId") + .HasColumnType("char(36)") + .HasColumnName("graded_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("OverallFeedback") + .HasColumnType("longtext") + .HasColumnName("overall_feedback"); + + b.Property("OverallGrade") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("overall_grade"); + + b.Property("Status") + .HasMaxLength(15) + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)") + .HasColumnName("submission_time"); + + b.Property("TotalQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("TotalScore") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("GraderId"); + + b.HasIndex("StudentId"); + + b.ToTable("submissions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("assignment_question_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsCorrect") + .HasColumnType("tinyint(1)") + .HasColumnName("is_correct"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("PointsAwarded") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("points_awarded"); + + b.Property("Status") + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentAnswer") + .HasColumnType("longtext") + .HasColumnName("student_answer"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionId") + .HasColumnType("char(36)") + .HasColumnName("submission_id"); + + b.Property("TeacherFeedback") + .HasColumnType("longtext") + .HasColumnName("teacher_feedback"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentQuestionId"); + + b.HasIndex("StudentId"); + + b.HasIndex("SubmissionId"); + + b.ToTable("submission_details", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned"); + + b.Property("Publisher") + .HasColumnType("tinyint unsigned"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("DisplayName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("RefreshToken") + .HasColumnType("longtext"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"), + Name = "Student", + NormalizedName = "STUDENT" + }, + new + { + Id = new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"), + Name = "Teacher", + NormalizedName = "TEACHER" + }, + new + { + Id = new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"), + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ExamStruct") + .WithOne() + .HasForeignKey("Entities.Contracts.Assignment", "ExamStructId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany("CreatedAssignments") + .HasForeignKey("UserId"); + + b.Navigation("Creator"); + + b.Navigation("ExamStruct"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentAttachments") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentClasses") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("AssignmentClasses") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Class"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany() + .HasForeignKey("AssignmentId"); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion") + .WithMany("ChildrenAssignmentQuestion") + .HasForeignKey("ParentAssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext") + .WithMany("Questions") + .HasForeignKey("QuestionContextId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Question", "Question") + .WithMany("AssignmentQuestions") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Assignment"); + + b.Navigation("ParentAssignmentQuestion"); + + b.Navigation("Question"); + + b.Navigation("QuestionContext"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.HasOne("Entities.Contracts.User", "HeadTeacher") + .WithMany() + .HasForeignKey("HeadTeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("HeadTeacher"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassStudents") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("EnrolledClassesLink") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassTeachers") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Teacher") + .WithMany("TaughtClassesLink") + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Teacher"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("KeyPoints") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.HasOne("Entities.Contracts.Textbook", "Textbook") + .WithMany("Lessons") + .HasForeignKey("TextbookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("LessonQuestions") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany("CreatedQuestions") + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Entities.Contracts.KeyPoint", "KeyPoint") + .WithMany("Questions") + .HasForeignKey("KeyPointId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("Questions") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Creator"); + + b.Navigation("KeyPoint"); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("Submissions") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Grader") + .WithMany("GradedSubmissions") + .HasForeignKey("GraderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionsAsStudent") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Grader"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.HasOne("Entities.Contracts.AssignmentQuestion", "AssignmentQuestion") + .WithMany("SubmissionDetails") + .HasForeignKey("AssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionDetails") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Submission", "Submission") + .WithMany("SubmissionDetails") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AssignmentQuestion"); + + b.Navigation("Student"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Navigation("AssignmentAttachments"); + + b.Navigation("AssignmentClasses"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Navigation("ChildrenAssignmentQuestion"); + + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Navigation("AssignmentClasses"); + + b.Navigation("ClassStudents"); + + b.Navigation("ClassTeachers"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Navigation("KeyPoints"); + + b.Navigation("LessonQuestions"); + + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Navigation("AssignmentQuestions"); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Navigation("CreatedAssignments"); + + b.Navigation("CreatedQuestions"); + + b.Navigation("EnrolledClassesLink"); + + b.Navigation("GradedSubmissions"); + + b.Navigation("SubmissionDetails"); + + b.Navigation("SubmissionsAsStudent"); + + b.Navigation("TaughtClassesLink"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TechHelper.Server/Migrations/20250901072725_question_qt_update.cs b/TechHelper.Server/Migrations/20250901072725_question_qt_update.cs new file mode 100644 index 0000000..fc93975 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901072725_question_qt_update.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace TechHelper.Server.Migrations +{ + /// + public partial class question_qt_update : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("0775702a-5db7-4747-94d0-4376fad2b58b")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("37f41430-0cb7-44e5-988b-976200bd602d")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70")); + + migrationBuilder.AddColumn( + name: "QType", + table: "questions", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.AddColumn( + name: "Type", + table: "assignment_questions", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"), null, "Teacher", "TEACHER" }, + { new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"), null, "Student", "STUDENT" }, + { new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"), null, "Administrator", "ADMINISTRATOR" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274")); + + migrationBuilder.DropColumn( + name: "QType", + table: "questions"); + + migrationBuilder.DropColumn( + name: "Type", + table: "assignment_questions"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"), null, "Teacher", "TEACHER" }, + { new Guid("37f41430-0cb7-44e5-988b-976200bd602d"), null, "Administrator", "ADMINISTRATOR" }, + { new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"), null, "Student", "STUDENT" } + }); + } + } +} diff --git a/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs b/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs new file mode 100644 index 0000000..769e685 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.Designer.cs @@ -0,0 +1,1296 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TechHelper.Context; + +#nullable disable + +namespace TechHelper.Server.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20250901080732_question_qt_update_2")] + partial class question_qt_update_2 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.16") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("DueDate") + .HasColumnType("datetime(6)") + .HasColumnName("due_date"); + + b.Property("ExamStructId") + .HasColumnType("char(36)") + .HasColumnName("exam_struct_id"); + + b.Property("ExamType") + .HasColumnType("tinyint unsigned"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("title"); + + b.Property("TotalQuestions") + .HasColumnType("tinyint unsigned") + .HasColumnName("total_points"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("ExamStructId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("assignments", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_name"); + + b.Property("FilePath") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_path"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)") + .HasColumnName("uploaded_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.ToTable("assignment_attachments"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id") + .HasColumnOrder(0); + + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(1); + + b.Property("AssignedAt") + .HasColumnType("datetime(6)") + .HasColumnName("assigned_at"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("AssignmentId", "ClassId"); + + b.HasIndex("ClassId"); + + b.ToTable("assignment_class", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("Index") + .HasColumnType("tinyint unsigned") + .HasColumnName("question_number"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("ParentAssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("parent_question_group_id"); + + b.Property("QuestionContextId") + .HasColumnType("char(36)") + .HasColumnName("description"); + + b.Property("QuestionId") + .HasColumnType("char(36)") + .HasColumnName("question_id"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("Sequence") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("sequence"); + + b.Property("StructType") + .HasColumnType("tinyint unsigned") + .HasColumnName("group_state"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("ParentAssignmentQuestionId"); + + b.HasIndex("QuestionContextId"); + + b.HasIndex("QuestionId"); + + b.ToTable("assignment_questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Description") + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned") + .HasColumnName("grade"); + + b.Property("HeadTeacherId") + .HasColumnType("char(36)") + .HasColumnName("head_teacher_id"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("Number") + .HasColumnType("tinyint unsigned") + .HasColumnName("class"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("HeadTeacherId"); + + b.ToTable("classes", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(0); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id") + .HasColumnOrder(1); + + b.Property("EnrollmentDate") + .HasColumnType("datetime(6)") + .HasColumnName("enrollment_date"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("ClassId", "StudentId"); + + b.HasIndex("StudentId"); + + b.ToTable("class_student", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id"); + + b.Property("TeacherId") + .HasColumnType("char(36)") + .HasColumnName("teacher_id"); + + b.Property("SubjectTaught") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_taught"); + + b.HasKey("ClassId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("class_teachers", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Global", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Area") + .HasColumnType("tinyint unsigned"); + + b.Property("Info") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("global"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("key_point"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TextbookID") + .HasColumnType("char(36)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("TextbookID"); + + b.ToTable("lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("lesson_question"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Answer") + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("correct_answer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("DifficultyLevel") + .HasMaxLength(10) + .HasColumnType("tinyint unsigned") + .HasColumnName("difficulty_level"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("KeyPointId") + .HasColumnType("char(36)") + .HasColumnName("key_point"); + + b.Property("LessonId") + .HasColumnType("char(36)") + .HasColumnName("lesson"); + + b.Property("Options") + .HasColumnType("longtext") + .HasColumnName("options"); + + b.Property("QType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SubjectArea") + .HasMaxLength(100) + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("question_text"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("tinyint unsigned") + .HasColumnName("question_type"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("KeyPointId"); + + b.HasIndex("LessonId"); + + b.HasIndex("Title") + .HasAnnotation("MySql:IndexPrefixLength", new[] { 20 }); + + b.ToTable("questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("QuestionContexts"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("AttemptNumber") + .HasColumnType("tinyint unsigned") + .HasColumnName("attempt_number"); + + b.Property("ErrorQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("GradedAt") + .HasColumnType("datetime(6)") + .HasColumnName("graded_at"); + + b.Property("GraderId") + .HasColumnType("char(36)") + .HasColumnName("graded_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("OverallFeedback") + .HasColumnType("longtext") + .HasColumnName("overall_feedback"); + + b.Property("OverallGrade") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("overall_grade"); + + b.Property("Status") + .HasMaxLength(15) + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)") + .HasColumnName("submission_time"); + + b.Property("TotalQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("TotalScore") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("GraderId"); + + b.HasIndex("StudentId"); + + b.ToTable("submissions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("assignment_question_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsCorrect") + .HasColumnType("tinyint(1)") + .HasColumnName("is_correct"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("PointsAwarded") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("points_awarded"); + + b.Property("Status") + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentAnswer") + .HasColumnType("longtext") + .HasColumnName("student_answer"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionId") + .HasColumnType("char(36)") + .HasColumnName("submission_id"); + + b.Property("TeacherFeedback") + .HasColumnType("longtext") + .HasColumnName("teacher_feedback"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentQuestionId"); + + b.HasIndex("StudentId"); + + b.HasIndex("SubmissionId"); + + b.ToTable("submission_details", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned"); + + b.Property("Publisher") + .HasColumnType("tinyint unsigned"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("DisplayName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("RefreshToken") + .HasColumnType("longtext"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"), + Name = "Student", + NormalizedName = "STUDENT" + }, + new + { + Id = new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"), + Name = "Teacher", + NormalizedName = "TEACHER" + }, + new + { + Id = new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"), + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ExamStruct") + .WithOne() + .HasForeignKey("Entities.Contracts.Assignment", "ExamStructId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany("CreatedAssignments") + .HasForeignKey("UserId"); + + b.Navigation("Creator"); + + b.Navigation("ExamStruct"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentAttachments") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentClasses") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("AssignmentClasses") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Class"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany() + .HasForeignKey("AssignmentId"); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion") + .WithMany("ChildrenAssignmentQuestion") + .HasForeignKey("ParentAssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext") + .WithMany("Questions") + .HasForeignKey("QuestionContextId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Question", "Question") + .WithMany("AssignmentQuestions") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Assignment"); + + b.Navigation("ParentAssignmentQuestion"); + + b.Navigation("Question"); + + b.Navigation("QuestionContext"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.HasOne("Entities.Contracts.User", "HeadTeacher") + .WithMany() + .HasForeignKey("HeadTeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("HeadTeacher"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassStudents") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("EnrolledClassesLink") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassTeachers") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Teacher") + .WithMany("TaughtClassesLink") + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Teacher"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("KeyPoints") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.HasOne("Entities.Contracts.Textbook", "Textbook") + .WithMany("Lessons") + .HasForeignKey("TextbookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("LessonQuestions") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany("CreatedQuestions") + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Entities.Contracts.KeyPoint", "KeyPoint") + .WithMany("Questions") + .HasForeignKey("KeyPointId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("Questions") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Creator"); + + b.Navigation("KeyPoint"); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("Submissions") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Grader") + .WithMany("GradedSubmissions") + .HasForeignKey("GraderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionsAsStudent") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Grader"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.HasOne("Entities.Contracts.AssignmentQuestion", "AssignmentQuestion") + .WithMany("SubmissionDetails") + .HasForeignKey("AssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionDetails") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Submission", "Submission") + .WithMany("SubmissionDetails") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AssignmentQuestion"); + + b.Navigation("Student"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Navigation("AssignmentAttachments"); + + b.Navigation("AssignmentClasses"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Navigation("ChildrenAssignmentQuestion"); + + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Navigation("AssignmentClasses"); + + b.Navigation("ClassStudents"); + + b.Navigation("ClassTeachers"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Navigation("KeyPoints"); + + b.Navigation("LessonQuestions"); + + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Navigation("AssignmentQuestions"); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Navigation("CreatedAssignments"); + + b.Navigation("CreatedQuestions"); + + b.Navigation("EnrolledClassesLink"); + + b.Navigation("GradedSubmissions"); + + b.Navigation("SubmissionDetails"); + + b.Navigation("SubmissionsAsStudent"); + + b.Navigation("TaughtClassesLink"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.cs b/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.cs new file mode 100644 index 0000000..2603b32 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901080732_question_qt_update_2.cs @@ -0,0 +1,89 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace TechHelper.Server.Migrations +{ + /// + public partial class question_qt_update_2 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274")); + + migrationBuilder.CreateTable( + name: "global", + columns: table => new + { + id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"), + Area = table.Column(type: "tinyint unsigned", nullable: false), + Info = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_global", x => x.id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"), null, "Teacher", "TEACHER" }, + { new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"), null, "Student", "STUDENT" }, + { new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"), null, "Administrator", "ADMINISTRATOR" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "global"); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5")); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("67de6514-79a5-4a9c-b54c-13cac296b0c6"), null, "Teacher", "TEACHER" }, + { new Guid("94f0d8d9-ffba-4e28-b578-8596363d42ae"), null, "Student", "STUDENT" }, + { new Guid("bf46ed67-2dc9-40f8-8717-37dd3572f274"), null, "Administrator", "ADMINISTRATOR" } + }); + } + } +} diff --git a/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs b/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs new file mode 100644 index 0000000..3135303 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.Designer.cs @@ -0,0 +1,1299 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using TechHelper.Context; + +#nullable disable + +namespace TechHelper.Server.Migrations +{ + [DbContext(typeof(ApplicationContext))] + [Migration("20250901083708_question_qt_update_3")] + partial class question_qt_update_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.16") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("DueDate") + .HasColumnType("datetime(6)") + .HasColumnName("due_date"); + + b.Property("ExamStructId") + .HasColumnType("char(36)") + .HasColumnName("exam_struct_id"); + + b.Property("ExamType") + .HasColumnType("tinyint unsigned"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("title"); + + b.Property("TotalQuestions") + .HasColumnType("tinyint unsigned") + .HasColumnName("total_points"); + + b.Property("UpdatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("ExamStructId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("assignments", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("FileName") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_name"); + + b.Property("FilePath") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)") + .HasColumnName("file_path"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("UploadedAt") + .HasColumnType("datetime(6)") + .HasColumnName("uploaded_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.ToTable("assignment_attachments"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id") + .HasColumnOrder(0); + + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(1); + + b.Property("AssignedAt") + .HasColumnType("datetime(6)") + .HasColumnName("assigned_at"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("AssignmentId", "ClassId"); + + b.HasIndex("ClassId"); + + b.ToTable("assignment_class", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)"); + + b.Property("CreatedAt") + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + b.Property("Index") + .HasColumnType("tinyint unsigned") + .HasColumnName("question_number"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("ParentAssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("parent_question_group_id"); + + b.Property("QuestionContextId") + .HasColumnType("char(36)") + .HasColumnName("description"); + + b.Property("QuestionId") + .HasColumnType("char(36)") + .HasColumnName("question_id"); + + b.Property("Score") + .HasColumnType("float") + .HasColumnName("score"); + + b.Property("Sequence") + .IsRequired() + .HasColumnType("longtext") + .HasColumnName("sequence"); + + b.Property("StructType") + .HasColumnType("tinyint unsigned") + .HasColumnName("group_state"); + + b.Property("Title") + .HasMaxLength(1024) + .HasColumnType("varchar(1024)") + .HasColumnName("title"); + + b.Property("Type") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("ParentAssignmentQuestionId"); + + b.HasIndex("QuestionContextId"); + + b.HasIndex("QuestionId"); + + b.ToTable("assignment_questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("Description") + .HasColumnType("longtext") + .HasColumnName("description"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned") + .HasColumnName("grade"); + + b.Property("HeadTeacherId") + .HasColumnType("char(36)") + .HasColumnName("head_teacher_id"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("Number") + .HasColumnType("tinyint unsigned") + .HasColumnName("class"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + MySqlPropertyBuilderExtensions.UseMySqlComputedColumn(b.Property("UpdatedAt")); + + b.HasKey("Id"); + + b.HasIndex("HeadTeacherId"); + + b.ToTable("classes", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id") + .HasColumnOrder(0); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id") + .HasColumnOrder(1); + + b.Property("EnrollmentDate") + .HasColumnType("datetime(6)") + .HasColumnName("enrollment_date"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.HasKey("ClassId", "StudentId"); + + b.HasIndex("StudentId"); + + b.ToTable("class_student", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.Property("ClassId") + .HasColumnType("char(36)") + .HasColumnName("class_id"); + + b.Property("TeacherId") + .HasColumnType("char(36)") + .HasColumnName("teacher_id"); + + b.Property("SubjectTaught") + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_taught"); + + b.HasKey("ClassId", "TeacherId"); + + b.HasIndex("TeacherId"); + + b.ToTable("class_teachers", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Global", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Area") + .HasColumnType("tinyint unsigned"); + + b.Property("Info") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("global"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Key") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("key_point"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("TextbookID") + .HasColumnType("char(36)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("TextbookID"); + + b.ToTable("lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("LessonID") + .HasColumnType("char(36)"); + + b.Property("Question") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.HasIndex("LessonID"); + + b.ToTable("lesson_question"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Answer") + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("correct_answer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("CreatorId") + .HasColumnType("char(36)") + .HasColumnName("created_by"); + + b.Property("DifficultyLevel") + .HasMaxLength(10) + .HasColumnType("tinyint unsigned") + .HasColumnName("difficulty_level"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("KeyPointId") + .HasColumnType("char(36)") + .HasColumnName("key_point"); + + b.Property("LessonId") + .HasColumnType("char(36)") + .HasColumnName("lesson"); + + b.Property("Options") + .HasColumnType("longtext") + .HasColumnName("options"); + + b.Property("QType") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SubjectArea") + .HasMaxLength(100) + .HasColumnType("tinyint unsigned") + .HasColumnName("subject_area"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(65535) + .HasColumnType("longtext") + .HasColumnName("question_text"); + + b.Property("Type") + .HasMaxLength(20) + .HasColumnType("tinyint unsigned") + .HasColumnName("question_type"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("CreatorId"); + + b.HasIndex("KeyPointId"); + + b.HasIndex("LessonId"); + + b.HasIndex("Title") + .HasAnnotation("MySql:IndexPrefixLength", new[] { 20 }); + + b.ToTable("questions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("QuestionContexts"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentId") + .HasColumnType("char(36)") + .HasColumnName("assignment_id"); + + b.Property("AttemptNumber") + .HasColumnType("tinyint unsigned") + .HasColumnName("attempt_number"); + + b.Property("ErrorQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("GradedAt") + .HasColumnType("datetime(6)") + .HasColumnName("graded_at"); + + b.Property("GraderId") + .HasColumnType("char(36)") + .HasColumnName("graded_by"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("OverallFeedback") + .HasColumnType("longtext") + .HasColumnName("overall_feedback"); + + b.Property("OverallGrade") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("overall_grade"); + + b.Property("Status") + .HasMaxLength(15) + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionTime") + .HasColumnType("datetime(6)") + .HasColumnName("submission_time"); + + b.Property("TotalQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("TotalScore") + .HasColumnType("tinyint unsigned"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentId"); + + b.HasIndex("GraderId"); + + b.HasIndex("StudentId"); + + b.ToTable("submissions", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("AssignmentQuestionId") + .HasColumnType("char(36)") + .HasColumnName("assignment_question_id"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("datetime(6)") + .HasColumnName("created_at"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("CreatedAt")); + + b.Property("IsCorrect") + .HasColumnType("tinyint(1)") + .HasColumnName("is_correct"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("tinyint(1)") + .HasDefaultValue(false) + .HasColumnName("deleted"); + + b.Property("PointsAwarded") + .HasPrecision(5, 2) + .HasColumnType("float") + .HasColumnName("points_awarded"); + + b.Property("Status") + .HasColumnType("int") + .HasColumnName("status"); + + b.Property("StudentAnswer") + .HasColumnType("longtext") + .HasColumnName("student_answer"); + + b.Property("StudentId") + .HasColumnType("char(36)") + .HasColumnName("student_id"); + + b.Property("SubmissionId") + .HasColumnType("char(36)") + .HasColumnName("submission_id"); + + b.Property("TeacherFeedback") + .HasColumnType("longtext") + .HasColumnName("teacher_feedback"); + + b.Property("UpdatedAt") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("datetime(6)") + .HasColumnName("updated_at"); + + b.HasKey("Id"); + + b.HasIndex("AssignmentQuestionId"); + + b.HasIndex("StudentId"); + + b.HasIndex("SubmissionId"); + + b.ToTable("submission_details", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("Grade") + .HasColumnType("tinyint unsigned"); + + b.Property("Publisher") + .HasColumnType("tinyint unsigned"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned"); + + b.Property("Title") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("AccessFailedCount") + .HasColumnType("int"); + + b.Property("Address") + .HasColumnType("longtext"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("DisplayName") + .HasColumnType("longtext"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + b.Property("LockoutEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("LockoutEnd") + .HasColumnType("datetime(6)"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("PasswordHash") + .HasColumnType("longtext"); + + b.Property("PhoneNumber") + .HasColumnType("longtext"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("tinyint(1)"); + + b.Property("RefreshToken") + .HasColumnType("longtext"); + + b.Property("RefreshTokenExpiryTime") + .HasColumnType("datetime(6)"); + + b.Property("SecurityStamp") + .HasColumnType("longtext"); + + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned"); + + b.Property("TwoFactorEnabled") + .HasColumnType("tinyint(1)"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("longtext"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("varchar(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + + b.HasData( + new + { + Id = new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"), + Name = "Student", + NormalizedName = "STUDENT" + }, + new + { + Id = new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"), + Name = "Teacher", + NormalizedName = "TEACHER" + }, + new + { + Id = new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"), + Name = "Administrator", + NormalizedName = "ADMINISTRATOR" + }); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("longtext"); + + b.Property("ClaimValue") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("ProviderKey") + .HasColumnType("varchar(255)"); + + b.Property("ProviderDisplayName") + .HasColumnType("longtext"); + + b.Property("UserId") + .HasColumnType("char(36)"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("RoleId") + .HasColumnType("char(36)"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("char(36)"); + + b.Property("LoginProvider") + .HasColumnType("varchar(255)"); + + b.Property("Name") + .HasColumnType("varchar(255)"); + + b.Property("Value") + .HasColumnType("longtext"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ExamStruct") + .WithOne() + .HasForeignKey("Entities.Contracts.Assignment", "ExamStructId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany("CreatedAssignments") + .HasForeignKey("UserId"); + + b.Navigation("Creator"); + + b.Navigation("ExamStruct"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentAttachment", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentAttachments") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentClass", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("AssignmentClasses") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("AssignmentClasses") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Class"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany() + .HasForeignKey("AssignmentId"); + + b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion") + .WithMany("ChildrenAssignmentQuestion") + .HasForeignKey("ParentAssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext") + .WithMany("Questions") + .HasForeignKey("QuestionContextId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Question", "Question") + .WithMany("AssignmentQuestions") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade); + + b.Navigation("Assignment"); + + b.Navigation("ParentAssignmentQuestion"); + + b.Navigation("Question"); + + b.Navigation("QuestionContext"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.HasOne("Entities.Contracts.User", "HeadTeacher") + .WithMany() + .HasForeignKey("HeadTeacherId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("HeadTeacher"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassStudent", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassStudents") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("EnrolledClassesLink") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.ClassTeacher", b => + { + b.HasOne("Entities.Contracts.Class", "Class") + .WithMany("ClassTeachers") + .HasForeignKey("ClassId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Teacher") + .WithMany("TaughtClassesLink") + .HasForeignKey("TeacherId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Class"); + + b.Navigation("Teacher"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("KeyPoints") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.HasOne("Entities.Contracts.Textbook", "Textbook") + .WithMany("Lessons") + .HasForeignKey("TextbookID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Textbook"); + }); + + modelBuilder.Entity("Entities.Contracts.LessonQuestion", b => + { + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("LessonQuestions") + .HasForeignKey("LessonID") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.HasOne("Entities.Contracts.User", "Creator") + .WithMany("CreatedQuestions") + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("Entities.Contracts.KeyPoint", "KeyPoint") + .WithMany("Questions") + .HasForeignKey("KeyPointId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.Lesson", "Lesson") + .WithMany("Questions") + .HasForeignKey("LessonId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Creator"); + + b.Navigation("KeyPoint"); + + b.Navigation("Lesson"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.HasOne("Entities.Contracts.Assignment", "Assignment") + .WithMany("Submissions") + .HasForeignKey("AssignmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Grader") + .WithMany("GradedSubmissions") + .HasForeignKey("GraderId") + .OnDelete(DeleteBehavior.SetNull); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionsAsStudent") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Assignment"); + + b.Navigation("Grader"); + + b.Navigation("Student"); + }); + + modelBuilder.Entity("Entities.Contracts.SubmissionDetail", b => + { + b.HasOne("Entities.Contracts.AssignmentQuestion", "AssignmentQuestion") + .WithMany("SubmissionDetails") + .HasForeignKey("AssignmentQuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", "Student") + .WithMany("SubmissionDetails") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.Submission", "Submission") + .WithMany("SubmissionDetails") + .HasForeignKey("SubmissionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("AssignmentQuestion"); + + b.Navigation("Student"); + + b.Navigation("Submission"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Entities.Contracts.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Entities.Contracts.Assignment", b => + { + b.Navigation("AssignmentAttachments"); + + b.Navigation("AssignmentClasses"); + + b.Navigation("Submissions"); + }); + + modelBuilder.Entity("Entities.Contracts.AssignmentQuestion", b => + { + b.Navigation("ChildrenAssignmentQuestion"); + + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Class", b => + { + b.Navigation("AssignmentClasses"); + + b.Navigation("ClassStudents"); + + b.Navigation("ClassTeachers"); + }); + + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Lesson", b => + { + b.Navigation("KeyPoints"); + + b.Navigation("LessonQuestions"); + + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Question", b => + { + b.Navigation("AssignmentQuestions"); + }); + + modelBuilder.Entity("Entities.Contracts.QuestionContext", b => + { + b.Navigation("Questions"); + }); + + modelBuilder.Entity("Entities.Contracts.Submission", b => + { + b.Navigation("SubmissionDetails"); + }); + + modelBuilder.Entity("Entities.Contracts.Textbook", b => + { + b.Navigation("Lessons"); + }); + + modelBuilder.Entity("Entities.Contracts.User", b => + { + b.Navigation("CreatedAssignments"); + + b.Navigation("CreatedQuestions"); + + b.Navigation("EnrolledClassesLink"); + + b.Navigation("GradedSubmissions"); + + b.Navigation("SubmissionDetails"); + + b.Navigation("SubmissionsAsStudent"); + + b.Navigation("TaughtClassesLink"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.cs b/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.cs new file mode 100644 index 0000000..cb7e822 --- /dev/null +++ b/TechHelper.Server/Migrations/20250901083708_question_qt_update_3.cs @@ -0,0 +1,82 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace TechHelper.Server.Migrations +{ + /// + public partial class question_qt_update_3 : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5")); + + migrationBuilder.AddColumn( + name: "SubjectArea", + table: "AspNetUsers", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"), null, "Teacher", "TEACHER" }, + { new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"), null, "Student", "STUDENT" }, + { new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"), null, "Administrator", "ADMINISTRATOR" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("2670f35a-df0c-4071-8879-80eb99d138a1")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db")); + + migrationBuilder.DropColumn( + name: "SubjectArea", + table: "AspNetUsers"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("49854839-b861-4d42-bdbe-96b1a66c25ef"), null, "Teacher", "TEACHER" }, + { new Guid("5c7a7971-2610-4bce-9e41-0caffd5a5558"), null, "Student", "STUDENT" }, + { new Guid("83ff7de8-edc9-47f8-8de8-22f892ca6bb5"), null, "Administrator", "ADMINISTRATOR" } + }); + } + } +} diff --git a/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs b/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs index d50daf4..4c2f81e 100644 --- a/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs +++ b/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs @@ -219,6 +219,9 @@ namespace TechHelper.Server.Migrations .HasColumnType("varchar(1024)") .HasColumnName("title"); + b.Property("Type") + .HasColumnType("tinyint unsigned"); + b.HasKey("Id"); b.HasIndex("AssignmentId"); @@ -332,6 +335,25 @@ namespace TechHelper.Server.Migrations b.ToTable("class_teachers", (string)null); }); + modelBuilder.Entity("Entities.Contracts.Global", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("char(36)") + .HasColumnName("id"); + + b.Property("Area") + .HasColumnType("tinyint unsigned"); + + b.Property("Info") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("Id"); + + b.ToTable("global"); + }); + modelBuilder.Entity("Entities.Contracts.KeyPoint", b => { b.Property("Id") @@ -445,6 +467,10 @@ namespace TechHelper.Server.Migrations .HasColumnType("longtext") .HasColumnName("options"); + b.Property("QType") + .IsRequired() + .HasColumnType("longtext"); + b.Property("SubjectArea") .HasMaxLength(100) .HasColumnType("tinyint unsigned") @@ -723,6 +749,9 @@ namespace TechHelper.Server.Migrations b.Property("SecurityStamp") .HasColumnType("longtext"); + b.Property("SubjectArea") + .HasColumnType("tinyint unsigned"); + b.Property("TwoFactorEnabled") .HasColumnType("tinyint(1)"); @@ -771,19 +800,19 @@ namespace TechHelper.Server.Migrations b.HasData( new { - Id = new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"), + Id = new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"), Name = "Student", NormalizedName = "STUDENT" }, new { - Id = new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"), + Id = new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"), Name = "Teacher", NormalizedName = "TEACHER" }, new { - Id = new Guid("37f41430-0cb7-44e5-988b-976200bd602d"), + Id = new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"), Name = "Administrator", NormalizedName = "ADMINISTRATOR" }); diff --git a/TechHelper.Server/Program.cs b/TechHelper.Server/Program.cs index 717c971..a489afd 100644 --- a/TechHelper.Server/Program.cs +++ b/TechHelper.Server/Program.cs @@ -17,9 +17,9 @@ using Microsoft.OpenApi.Models; var builder = WebApplication.CreateBuilder(args); -builder.Services.AddControllers(); // MVC ( API) +builder.Services.AddControllers(); // 添加 MVC 控制器服务 (用于 API) -// 2. ݿ (DbContext) +// 2. 数据库服务 (DbContext) builder.Services.AddDbContext(options => options.UseMySql( builder.Configuration.GetConnectionString("XSDB"), @@ -34,17 +34,18 @@ builder.Services.AddDbContext(options => .AddCustomRepository() .AddCustomRepository() .AddCustomRepository() -.AddCustomRepository(); +.AddCustomRepository() +.AddCustomRepository(); builder.Services.AddAutoMapper(typeof(AutoMapperProFile).Assembly); -// 3. ÷ (IOptions) +// 3. 配置服务 (IOptions) builder.Services.Configure(builder.Configuration.GetSection("ApiConfiguration")); builder.Services.Configure(builder.Configuration.GetSection("JWTSettings")); -// 4. ֤Ȩ (Identity, JWT, Զ Auth) -// ASP.NET Core Identity (Ĭϵ Cookie ֤Ȩ) +// 4. 认证和授权服务 (Identity, JWT, 自定义 Auth) +// 添加 ASP.NET Core Identity (包含默认的 Cookie 认证和授权服务) builder.Services.AddIdentity>(opt => { opt.User.AllowedUserNameCharacters = ""; @@ -60,25 +61,25 @@ builder.Services.Configure(Options => }); -// JWT Bearer ֤ +// 添加 JWT Bearer 认证方案 var jwtSettings = builder.Configuration.GetSection("JWTSettings"); builder.Services.AddAuthentication(options => { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // Ĭ֤Ϊ JWT Bearer - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // ĬսΪ JWT Bearer + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; // 设置默认认证方案为 JWT Bearer + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; // 设置默认挑战方案为 JWT Bearer }) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { - ValidateIssuer = true, // ֤ǩ - ValidateAudience = true, // ֤ - ValidateLifetime = true, // ֤Ч - ValidateIssuerSigningKey = true, // ֤ǩԿ + ValidateIssuer = true, // 验证签发人 + ValidateAudience = true, // 验证受众 + ValidateLifetime = true, // 验证令牌有效期 + ValidateIssuerSigningKey = true, // 验证签名密钥 - ValidIssuer = jwtSettings["validIssuer"], // Ϸǩ - ValidAudience = jwtSettings["validAudience"], // Ϸ - IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])) // ǩԿ + ValidIssuer = jwtSettings["validIssuer"], // 合法的签发人 + ValidAudience = jwtSettings["validAudience"], // 合法的受众 + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["securityKey"])) // 签名密钥 }; }); @@ -90,6 +91,7 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddEndpointsApiExplorer(); @@ -128,7 +130,7 @@ builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder - .WithOrigins("https://localhost:7047", "http://localhost:7047") + .WithOrigins("https://localhost:7047", "http://localhost:7047", "https://localhost:5001", "http://localhost:5001") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials()); diff --git a/TechHelper.Server/Repository/GlobalRepository.cs b/TechHelper.Server/Repository/GlobalRepository.cs new file mode 100644 index 0000000..bd6d17a --- /dev/null +++ b/TechHelper.Server/Repository/GlobalRepository.cs @@ -0,0 +1,17 @@ +using Entities.Contracts; +using Microsoft.EntityFrameworkCore; +using SharedDATA.Api; +using TechHelper.Context; + +namespace TechHelper.Repository +{ + public class GlobalRepository : Repository, IRepository + { + public GlobalRepository(ApplicationContext dbContext) : base(dbContext) + { + + } + + + } +} diff --git a/TechHelper.Server/Services/INoteService.cs b/TechHelper.Server/Services/INoteService.cs new file mode 100644 index 0000000..9a4ef54 --- /dev/null +++ b/TechHelper.Server/Services/INoteService.cs @@ -0,0 +1,13 @@ +using Entities.Contracts; +using Entities.DTO; +using System.Net; + +namespace TechHelper.Services +{ + + + public interface INoteService : IBaseService + { + + } +} diff --git a/TechHelper.Server/Services/NoteService.cs b/TechHelper.Server/Services/NoteService.cs new file mode 100644 index 0000000..814a3a2 --- /dev/null +++ b/TechHelper.Server/Services/NoteService.cs @@ -0,0 +1,107 @@ +using AutoMapper; +using Entities.Contracts; +using Entities.DTO; +using Microsoft.AspNetCore.Identity; +using SharedDATA.Api; +using System.Net; + +namespace TechHelper.Services +{ + public class NoteService : INoteService + { + private readonly IUnitOfWork _work; + private readonly IMapper _mapper; + + public NoteService(IUnitOfWork work) + { + _work = work; + } + + public async Task AddAsync(GlobalDto model) + { + try + { + var globalEntity = new Global + { + Area = model.SubjectArea, + Info = model.Data + }; + + await _work.GetRepository().InsertAsync(globalEntity); + await _work.SaveChangesAsync(); + + return ApiResponse.Success("数据已成功添加。"); + } + catch (Exception ex) + { + return ApiResponse.Error($"添加数据时发生错误: {ex.Message}"); + } + } + + public async Task DeleteAsync(byte id) + { + var globalRepo = _work.GetRepository(); + var globalEntity = await globalRepo.GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)id); + + if (globalEntity == null) + { + return ApiResponse.Error("未找到要删除的数据。"); + } + + globalRepo.Delete(globalEntity); + await _work.SaveChangesAsync(); + + return ApiResponse.Success("数据已成功删除。"); + } + + public async Task GetAllAsync(QueryParameter query) + { + var repository = _work.GetRepository(); + // 获取所有实体,并将其 Info 属性作为结果返回 + var entities = await repository.GetAllAsync(); + + // 直接返回字符串列表 + var resultData = entities.Select(e => e.Info).ToList(); + + return ApiResponse.Success("数据已成功检索。", resultData); + } + + public async Task GetAsync(byte id) + { + var globalEntity = await _work.GetRepository().GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)id); + + if (globalEntity == null) + { + return ApiResponse.Error("未找到数据。"); + } + + // 直接返回 Info 字符串 + return ApiResponse.Success("数据已成功检索。", globalEntity.Info); + } + + public async Task UpdateAsync(GlobalDto model) + { + try + { + var repository = _work.GetRepository(); + var existingEntity = await repository.GetFirstOrDefaultAsync(predicate: x => x.Area == (SubjectAreaEnum)model.SubjectArea); + + if (existingEntity == null) + { + return ApiResponse.Error("未找到要更新的数据。"); + } + + // 直接将传入的字符串赋值给 Info 属性 + existingEntity.Info = model.Data; + repository.Update(existingEntity); + await _work.SaveChangesAsync(); + + return ApiResponse.Success("数据已成功更新。"); + } + catch (Exception ex) + { + return ApiResponse.Error($"更新数据时发生错误: {ex.Message}"); + } + } + } +} diff --git a/TechHelper.Server/TechHelper.Server.csproj b/TechHelper.Server/TechHelper.Server.csproj index 407c0f9..5c00dd7 100644 --- a/TechHelper.Server/TechHelper.Server.csproj +++ b/TechHelper.Server/TechHelper.Server.csproj @@ -23,6 +23,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive +