diff --git a/EmailLib/Dockerfile b/EmailLib/Dockerfile new file mode 100644 index 0000000..4c0efc1 --- /dev/null +++ b/EmailLib/Dockerfile @@ -0,0 +1,8 @@ +# ./EmailLib/Dockerfile +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS eamillib +WORKDIR /src +COPY ../EmailLib/*.csproj ./EmailLib/ +RUN dotnet restore "EmailLib/EmailLib.csproj" + +COPY ../EmailLib/. ./EmailLib/ +RUN dotnet publish "EmailLib/EmailLib.csproj" -c Release -o /publish \ No newline at end of file diff --git a/Entities/Contracts/AppMainStruct.cs b/Entities/Contracts/AppMainStruct.cs index 9ca0285..65b1896 100644 --- a/Entities/Contracts/AppMainStruct.cs +++ b/Entities/Contracts/AppMainStruct.cs @@ -21,7 +21,7 @@ namespace Entities.Contracts 部编版, } - public enum Grade : byte + public enum GradeEnum : byte { Unknown = 0, 一年级 = 1, @@ -69,14 +69,25 @@ namespace Entities.Contracts public enum AssignmentStructType : byte { + Root, Question, - Composite, + Group, Struct, SubQuestion, Option } + public enum ExamType : byte + { + MidtermExam, // 期中 + FinalExam, // 期末 + MonthlyExam, // 月考 + WeeklyExam, // 周考 + DailyTest, // 平时测试 + AITest, // AI测试 + } + public enum SubmissionStatus { diff --git a/Entities/Contracts/Assignment.cs b/Entities/Contracts/Assignment.cs index aac37c0..1dbc9f7 100644 --- a/Entities/Contracts/Assignment.cs +++ b/Entities/Contracts/Assignment.cs @@ -41,6 +41,10 @@ namespace Entities.Contracts [Column("score")] public float Score { get; set; } + public string Name { get; set; } = string.Empty; + + public ExamType ExamType { get; set; } = ExamType.DailyTest; + [Column("created_by")] public Guid CreatorId { get; set; } diff --git a/Entities/Contracts/Question.cs b/Entities/Contracts/Question.cs index fe115ea..7d91b6d 100644 --- a/Entities/Contracts/Question.cs +++ b/Entities/Contracts/Question.cs @@ -48,7 +48,7 @@ namespace Entities.Contracts [Column("lesson")] public Guid? LessonId { get; set; } - + [Required] [Column("created_by")] public Guid CreatorId { get; set; } diff --git a/Entities/Contracts/Submission.cs b/Entities/Contracts/Submission.cs index 0b1ad3e..aea74bf 100644 --- a/Entities/Contracts/Submission.cs +++ b/Entities/Contracts/Submission.cs @@ -49,6 +49,12 @@ namespace Entities.Contracts [Column("deleted")] public bool IsDeleted { get; set; } + public byte TotalQuesNum { get; set; } + + public byte ErrorQuesNum { get; set; } + + public byte TotalScore { get; set; } + [Required] [Column("status")] public SubmissionStatus Status { get; set; } diff --git a/Entities/Contracts/Textbook/Textbook.cs b/Entities/Contracts/Textbook/Textbook.cs index 952f45b..5764381 100644 --- a/Entities/Contracts/Textbook/Textbook.cs +++ b/Entities/Contracts/Textbook/Textbook.cs @@ -15,7 +15,7 @@ namespace Entities.Contracts [Key] public Guid Id { get; set; } - public Grade Grade { get; set; } = Grade.Unknown; + public GradeEnum Grade { get; set; } = GradeEnum.Unknown; public string Title { get; set; } = string.Empty; diff --git a/Entities/DTO/AssignmentDto.cs b/Entities/DTO/AssignmentDto.cs index 4a32497..fab94bf 100644 --- a/Entities/DTO/AssignmentDto.cs +++ b/Entities/DTO/AssignmentDto.cs @@ -21,6 +21,9 @@ namespace Entities.DTO public DateTime DueDate { get; set; } public Guid CreatorId { get; set; } + public string Name { get; set; } = string.Empty; + public ExamType ExamType { get; set; } = ExamType.DailyTest; + public AssignmentQuestionDto ExamStruct { get; set; } = new AssignmentQuestionDto(); } } diff --git a/Entities/DTO/AssignmentQuestionDto.cs b/Entities/DTO/AssignmentQuestionDto.cs index 702e130..c38774a 100644 --- a/Entities/DTO/AssignmentQuestionDto.cs +++ b/Entities/DTO/AssignmentQuestionDto.cs @@ -16,6 +16,7 @@ namespace Entities.DTO public byte Index { get; set; } = 0; public float Score { get; set; } = 0; public string Sequence { get; set; } = string.Empty; + public bool BCorrect { get; set; } = true; public Layout Layout { get; set; } = Layout.horizontal; public AssignmentStructType StructType { get; set; } = AssignmentStructType.Question; diff --git a/Entities/Dockerfile b/Entities/Dockerfile new file mode 100644 index 0000000..67a8faa --- /dev/null +++ b/Entities/Dockerfile @@ -0,0 +1,8 @@ +# ./Entities/Dockerfile +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS entities +WORKDIR /src +COPY ../Entities/*.csproj ./Entities/ +RUN dotnet restore "Entities/Entities.csproj" + +COPY ../Entities/. ./Entities/ +RUN dotnet publish "Entities/Entities.csproj" -c Release -o /publish \ No newline at end of file diff --git a/TechHelper.Client/Dockerfile b/TechHelper.Client/Dockerfile new file mode 100644 index 0000000..e5d4062 --- /dev/null +++ b/TechHelper.Client/Dockerfile @@ -0,0 +1,26 @@ +# 构建阶段 +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS client + + +#COPY --from=entitieslib:latest /publish /publish/entities +#COPY --from=emaillib:latest /publish /publish/emaillib + +WORKDIR /app + +# 复制依赖文件 & 恢复 +#COPY ./TechHelper.Client.sln ./ +COPY ../TechHelper.Client/*.csproj ../TechHelper.Client/ + +RUN dotnet nuget locals all --clear +RUN dotnet restore "/app/TechHelper.Client/TechHelper.Client.csproj" + +# 复制代码 & 发布 +COPY . ./ +WORKDIR /app/TechHelper.Client +RUN dotnet publish "/app/TechHelper.Client/TechHelper.Client.csproj" -c Release -o /publish + +FROM nginx:alpine AS final +RUN rm -rf /usr/share/nginx/html +COPY --from=client /publish/wwwroot /usr/share/nginx/html + +EXPOSE 80 \ No newline at end of file diff --git a/TechHelper.Client/Exam/ExamParse.cs b/TechHelper.Client/Exam/ExamParse.cs index 69b9770..7c1a910 100644 --- a/TechHelper.Client/Exam/ExamParse.cs +++ b/TechHelper.Client/Exam/ExamParse.cs @@ -50,6 +50,7 @@ namespace TechHelper.Client.Exam public List Errors { get; set; } = new List(); } + // 试题的包裹器, 或者 单独作为一个试题结构存在 public class AssignmentQuestionEx { public string Title { get; set; } = string.Empty; @@ -115,7 +116,7 @@ namespace TechHelper.Client.Exam QuestionPatterns.Add(new RegexPatternConfig(@"^([一二三四五六七八九十]+)[.\、]\s*(.+)", 1, AssignmentStructType.Struct)); // 例如:(一) 这是第一子题组 - QuestionPatterns.Add(new RegexPatternConfig(@"^\(([一二三四五六七八九十]{1,2}|十[一二三四五六七八九])\)\s*(.+)", 2, AssignmentStructType.Composite)); + QuestionPatterns.Add(new RegexPatternConfig(@"^\(([一二三四五六七八九十]{1,2}|十[一二三四五六七八九])\)\s*(.+)", 2, AssignmentStructType.Group)); // 例如:1. 这是第一道题目 或 1 这是第一道题目 QuestionPatterns.Add(new RegexPatternConfig(@"^(\d+)\.?\s*(.+)", 3, AssignmentStructType.Question)); diff --git a/TechHelper.Client/Pages/Author/Component.razor b/TechHelper.Client/Pages/Author/Component.razor new file mode 100644 index 0000000..e69de29 diff --git a/TechHelper.Client/Pages/Author/Login.razor b/TechHelper.Client/Pages/Author/Login.razor index 7f358b1..40f3750 100644 --- a/TechHelper.Client/Pages/Author/Login.razor +++ b/TechHelper.Client/Pages/Author/Login.razor @@ -1,48 +1,59 @@ @page "/login" - Login Account +
- - - - - - - - - - - Register - +
+ - - - - - - Validation Summary - @if (!ShowRegistrationErrors) - { - Success - } - else - { - - - - } - - - - - Fill out the form correctly to see the success message. - - - - + + + + TechHelper + 轻松管理,高效学习。 + + 教育不是注满一桶水, + 而是点燃一把火。 + + + + + + + + + + + + 登录账户 + + + + + + + 点击登录,即表示你同意我们的服务条款和隐私政策。 + + + LOGIN + + + + + + 还没有账户? + Sign in + + + + + + + + + + + +
+
diff --git a/TechHelper.Client/Pages/Author/Login.razor.cs b/TechHelper.Client/Pages/Author/Login.razor.cs index 7036b38..34cfb3f 100644 --- a/TechHelper.Client/Pages/Author/Login.razor.cs +++ b/TechHelper.Client/Pages/Author/Login.razor.cs @@ -16,6 +16,8 @@ namespace TechHelper.Client.Pages.Author [Inject] public NavigationManager NavigationManager { get; set; } + + public bool Basic_CheckBox2 { get; set; } = true; public bool ShowRegistrationErrors { get; set; } public string Error { get; set; } diff --git a/TechHelper.Client/Pages/Author/Login.razor.css b/TechHelper.Client/Pages/Author/Login.razor.css new file mode 100644 index 0000000..42b0f51 --- /dev/null +++ b/TechHelper.Client/Pages/Author/Login.razor.css @@ -0,0 +1,29 @@ +.page-containerr { + width: 100%; + height: 100%; /* ӿڸ߶ȣȷ㹻ĸ߶Ԫ */ + display: flex; + flex-direction: column; /* ֱԪ */ + align-items: center; /* ˮƽ */ + justify-content: center; /* ֱ */ + background-color: white; +} + +.mud-paper-full-width { + width: 80%; /* һpaperĿ */ + height: auto; + padding: 20px; +} + +.mud-paper-80-percent-centeredd { + width: 80%; + min-height: 80%; + padding: 0px; + margin: 0 auto; + background-color: #959dff; + border: 1px solid #ccc; + display: flex; + align-items: center; + border-radius: 40px; + justify-content: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); +} diff --git a/TechHelper.Client/Pages/Author/Registration.razor b/TechHelper.Client/Pages/Author/Registration.razor index d647f92..cbaf041 100644 --- a/TechHelper.Client/Pages/Author/Registration.razor +++ b/TechHelper.Client/Pages/Author/Registration.razor @@ -1,96 +1,96 @@ @page "/register" @using System.ComponentModel.DataAnnotations +@using Microsoft.AspNetCore.Components @using Entities.Contracts +@using System.Globalization; + + @inject ISnackbar Snackbar +
- Create Account +
+ - - - - - - - - - - - - @foreach (UserRoles item in Enum.GetValues(typeof(UserRoles))) - { - if (item != UserRoles.Administrator) - { - @item.ToString() - } - } - - + + + + TechHelper + 快速注册,开始你的管理之旅。 + + 学而不思则罔, + 思而不学则殆。 + + + - + - - + + + + + 注册账户 + + + + + + Student + Teacher + - - - - Register - - - - - - - Validation Summary - @if (success) - { - Success - } - else - { - - - - } - - - - - Fill out the form correctly to see the success message. - - - - - + + + @foreach (GradeEnum item in Enum.GetValues(typeof(GradeEnum))) + { + @item + } + + + @foreach (byte item in Classes) + { + @item + } + + + + + + + + + + 点击注册,即表示你同意我们的服务条款和隐私政策。 + + + LOGIN + + + + + + 已有账户? + Sign up + + + + + + + + + + + + + +
+
diff --git a/TechHelper.Client/Pages/Author/Registration.razor.cs b/TechHelper.Client/Pages/Author/Registration.razor.cs index e9035a8..b65c532 100644 --- a/TechHelper.Client/Pages/Author/Registration.razor.cs +++ b/TechHelper.Client/Pages/Author/Registration.razor.cs @@ -5,6 +5,7 @@ using MudBlazor; using System.Text.RegularExpressions; using TechHelper.Features; using Entities.Contracts; +using TechHelper.Client.Services; namespace TechHelper.Client.Pages.Author { @@ -12,14 +13,19 @@ namespace TechHelper.Client.Pages.Author { private UserForRegistrationDto _userForRegistration = new UserForRegistrationDto(); - + private GradeEnum grade = GradeEnum.Unknown; [Inject] public IAuthenticationClientService AuthenticationService { get; set; } [Inject] public NavigationManager NavigationManager { get; set; } + [Inject] + public IClassServices ClassServices { get; set; } + public bool Basic_CheckBox2 { get; set; } = true; + public byte selectclass { get; set; } = 0; + public List Classes { get; set; } = new List(); public bool ShowRegistrationErrors { get; set; } public string[] Errors { get; set; } @@ -51,6 +57,25 @@ namespace TechHelper.Client.Pages.Author [Inject] public IEmailSender emailSender { get; set; } + private async void HandleSelectedValuesChanged(GradeEnum selectedValues) + { + grade = selectedValues; + Snackbar.Add("ȴ,ڻȡ꼶༶", Severity.Info); + var result = await ClassServices.GetGradeClasses((byte)selectedValues); + if (result.Status) + { + Classes = result.Result as List ?? new List(); + Snackbar.Add("ȡɹ", Severity.Success); + return; + } + Snackbar.Add("ȡʧ", Severity.Error); + } + private void HandleListSelectedValuesChanged(byte selectedValues) + { + selectclass = selectedValues; + + } + public async void SendEmail() { string eamilTo = "1928360026@qq.com"; diff --git a/TechHelper.Client/Pages/Author/Registration.razor.css b/TechHelper.Client/Pages/Author/Registration.razor.css new file mode 100644 index 0000000..d15a69b --- /dev/null +++ b/TechHelper.Client/Pages/Author/Registration.razor.css @@ -0,0 +1,29 @@ +.page-container { + width: 100%; + height: 100%; /* ӿڸ߶ȣȷ㹻ĸ߶Ԫ */ + display: flex; + flex-direction: column; /* ֱԪ */ + align-items: center; /* ˮƽ */ + justify-content: center; /* ֱ */ + background-color: white; +} + +.mud-paper-full-width { + width: 80%; /* һpaperĿ */ + height: auto; + padding: 20px; +} + +.mud-paper-80-percent-centered { + width: 80%; + min-height: 80%; + padding: 0px; + margin: 0 auto; + background-color: #959dff; + border: 1px solid #ccc; + display: flex; + align-items: center; + border-radius: 40px; + justify-content: center; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); +} diff --git a/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor b/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor new file mode 100644 index 0000000..f70f9f0 --- /dev/null +++ b/TechHelper.Client/Pages/Common/Exam/AssignmentInfoCard.razor @@ -0,0 +1,143 @@ + + + + + + + 期中测试BETA版本 + 75 + + + + + + + TotalNumber: + 15 + + + + + TotalScore: + 15 + + + + + + + + 中位数: + 15 + + + + + 方差: + 15 + + + + + + + + + + + + + + + + 成绩的整体分布情况 + + + + + + + + 类型分布 + 课时分布 + 成绩趋势 + 分值区间 + Success + Warning + Error + Dark + + + + + + +@code { + public double[] data = { 25, 77, 28, 5 }; + public string[] labels = { "Oil", "Coal", "Gas", "Biomass" }; + private AxisChartOptions _axisChartOptions = new AxisChartOptions + { + }; + private ChartOptions options = new ChartOptions + { + InterpolationOption = InterpolationOption.NaturalSpline, + YAxisFormat = "c2", + ShowLegend = false, + YAxisLines = false, + XAxisLines = false, + XAxisLabelPosition = XAxisLabelPosition.None, + YAxisLabelPosition = YAxisLabelPosition.None, + YAxisTicks = 100, + ShowLabels = false, + ShowLegendLabels = false + + }; + public List Series = new List() + { + new ChartSeries() { Name = "类型错误数量分布", Data = new double[] { 35, 41, 35, 51, 49, 62, 69, 91, 148 } }, + new ChartSeries() { Name = "类型错误成绩分布", Data = new double[] { 55, 21, 45, 11, 45, 23, 11, 56, 13 } }, + }; + public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" }; + + Random random = new Random(); + protected override void OnInitialized() + { + options.InterpolationOption = InterpolationOption.NaturalSpline; + options.YAxisFormat = "c2"; + options.ShowLegend = false; + options.YAxisLines = false; + options.XAxisLines = false; + options.XAxisLabelPosition = XAxisLabelPosition.None; + options.YAxisLabelPosition = YAxisLabelPosition.None; + options.ShowLabels = false; + options.ShowLegendLabels = false; + options.LineStrokeWidth = 1; + _axisChartOptions.MatchBoundsToSize = true; + + Series[0].LineDisplayType = LineDisplayType.Area; + + } + + private IReadOnlyCollection _selected; + + private void HandleSelectedValuesChanged(IReadOnlyCollection selected) + { + Series.ForEach(x => x.Visible = false); + + foreach(var item in selected) + { + var sv = Series.FirstOrDefault(predicate: x => x.Name == item); + if(sv != null) + { + sv.Visible = true; + } + } + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/Exam/StudentSubmissionPreview.razor b/TechHelper.Client/Pages/Common/Exam/StudentSubmissionPreview.razor new file mode 100644 index 0000000..c42d7bf --- /dev/null +++ b/TechHelper.Client/Pages/Common/Exam/StudentSubmissionPreview.razor @@ -0,0 +1,6 @@ + + + Name + 错误数量 + 分数 + \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/Exam/SubmissionPreview.razor b/TechHelper.Client/Pages/Common/Exam/SubmissionPreview.razor new file mode 100644 index 0000000..b95acd8 --- /dev/null +++ b/TechHelper.Client/Pages/Common/Exam/SubmissionPreview.razor @@ -0,0 +1,6 @@ + + + Name + 平均数 + 中为数 + \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/PublishExamDialog.razor b/TechHelper.Client/Pages/Common/PublishExamDialog.razor new file mode 100644 index 0000000..b093d23 --- /dev/null +++ b/TechHelper.Client/Pages/Common/PublishExamDialog.razor @@ -0,0 +1,67 @@ +@using Entities.DTO +@inject ISnackbar Snackbar +@using Entities.Contracts + + + + + + + + 发布! + + + + + + + + + + + + + @SubjectAreaEnum.Literature + @SubjectAreaEnum.Mathematics + @SubjectAreaEnum.English + @SubjectAreaEnum.ComputerScience + + + + + + + + @ExamType.DailyTest + @ExamType.WeeklyExam + @ExamType.MonthlyExam + @ExamType.MidtermExam + @ExamType.FinalExam + @ExamType.AITest + + + + + + Cancel + 确认 + + + +@code { + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } + + [Parameter] + public AssignmentDto Exam { get; set; } = new AssignmentDto(); + + + public SubjectAreaEnum SubjectArea { get; set; } + private void Cancel() => MudDialog.Cancel(); + + private void Confirm() + { + Snackbar.Add("属性已更新", Severity.Success); + MudDialog.Close(DialogResult.Ok(Exam)); + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/QuestionCardDialog.razor b/TechHelper.Client/Pages/Common/QuestionCardDialog.razor new file mode 100644 index 0000000..ac77875 --- /dev/null +++ b/TechHelper.Client/Pages/Common/QuestionCardDialog.razor @@ -0,0 +1,34 @@ +@using Entities.DTO +@inject ISnackbar Snackbar + + + + + + 编辑属性 + + + + + + + Cancel + 确认 + + + +@code { + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } + + [Parameter] + public AssignmentQuestionDto Questions { get; set; } = new AssignmentQuestionDto(); + + private void Cancel() => MudDialog.Cancel(); + + private void Confirm() + { + 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 new file mode 100644 index 0000000..b370d2b --- /dev/null +++ b/TechHelper.Client/Pages/Common/SimpleCard.razor @@ -0,0 +1,22 @@ + + @TitleContent + @BodyContent + @FooterContent + + + +@code { + [Parameter] + public string Style { get; set; } + [Parameter] + public string Height { get; set; } = "200px"; + + [Parameter] + public RenderFragment TitleContent { get; set; } + + [Parameter] + public RenderFragment BodyContent { get; set; } + + [Parameter] + public RenderFragment FooterContent { get; set; } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Common/TextEditorDialog.razor b/TechHelper.Client/Pages/Common/TextEditorDialog.razor new file mode 100644 index 0000000..b6dcf99 --- /dev/null +++ b/TechHelper.Client/Pages/Common/TextEditorDialog.razor @@ -0,0 +1,86 @@ +@using Entities.DTO +@inject ISnackbar Snackbar + + + + + + 编辑属性 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cancel + 确认 + + + +@code { + [CascadingParameter] + private IMudDialogInstance MudDialog { get; set; } + + [Parameter] + public BlazoredTextEditor TextEditor { get; set; } = new BlazoredTextEditor(); + [Parameter] + public string EditorText { get; set; } = "{}"; + + private void HandleClick() + { + TextEditor.InsertText(EditorText); + } + + private void Cancel() => MudDialog.Cancel(); + + + protected async override Task OnInitializedAsync() + { + await DelayInsert(); + await base.OnInitializedAsync(); + } + + + private async Task DelayInsert() + { + await Task.Delay(100); + HandleClick(); + } + + private async void Confirm() + { + Snackbar.Add("属性已更新", Severity.Success); + EditorText = await TextEditor.GetText(); + MudDialog.Close(DialogResult.Ok(TextEditor)); + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor b/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor index 3abc642..86e5af7 100644 --- a/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor +++ b/TechHelper.Client/Pages/Exam/AssignmentQuestionEdit.razor @@ -1,17 +1,66 @@ @using Entities.DTO +@using Entities.Contracts @using TechHelper.Client.Exam +@using TechHelper.Client.Pages.Exam.QuestionCard - - @AssignmentQuestion.Id - - - - + + @* @AssignmentQuestion.Id *@ + + 包裹器属性 + + + + + @AssignmentStructType.Struct + @AssignmentStructType.Group + @AssignmentStructType.Question + @AssignmentStructType.SubQuestion + @AssignmentStructType.Option + + + @if (AssignmentQuestion.Question != null) + { + + } @code { [Parameter] public AssignmentQuestionDto AssignmentQuestion { get; set; } = new AssignmentQuestionDto(); + public QuestionDto TempQuesdto; + protected override void OnInitialized() + { + base.OnInitialized(); + if (AssignmentQuestion.Question != null) + { + TempQuesdto = AssignmentQuestion.Question; + } + } + + private void HandleSelectedValueChanged(AssignmentStructType type) + { + AssignmentQuestion.StructType = type; + if (type != AssignmentStructType.Question && AssignmentQuestion.Question != null) + { + AssignmentQuestion.Title = AssignmentQuestion.Question.Title; + AssignmentQuestion.Question = null; + } + + if (type == AssignmentStructType.Question && AssignmentQuestion.Question == null) + { + if (TempQuesdto != null) + { + AssignmentQuestion.Question = TempQuesdto; + if (AssignmentQuestion.Title == AssignmentQuestion.Question.Title) + { + AssignmentQuestion.Title = ""; + } + } + else + AssignmentQuestion.Question = new QuestionDto { }; + } + StateHasChanged(); + } } diff --git a/TechHelper.Client/Pages/Exam/ExamCreate.razor b/TechHelper.Client/Pages/Exam/ExamCreate.razor index 836385f..ea870a9 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 TechHelper.Client.Pages.Common +@using TechHelper.Client.Pages.Exam.ExamView @using TechHelper.Client.Services @using Blazored.TextEditor @using Entities.DTO @@ -9,9 +11,9 @@ @using Microsoft.AspNetCore.Components @using System.Globalization; @using TechHelper.Client.Pages.Editor +@inject IDialogService DialogService - - + @@ -34,45 +36,18 @@ - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + 文本编辑器 + 载入 + 发布 + 指派 + + + @if (_parsedExam.Errors.Any()) @@ -83,12 +58,6 @@ } } - - - - - - @@ -99,7 +68,7 @@ private Task authenticationStateTask { get; set; } private AssignmentQuestionDto selectedAssignmentQuestion = new AssignmentQuestionDto(); - + private IReadOnlyCollection _selected; private bool _open = false; private bool _edit = false; @@ -113,16 +82,53 @@ private AssignmentDto ExamContent = new AssignmentDto(); private ExamParserConfig _examParserConfig { get; set; } = new ExamParserConfig(); + private string EditorText = ""; + [Inject] public IMapper Mapper { get; set; } - private void HandleClickedStruct(AssignmentQuestionDto dto) + private async void OpenEditor() { - _open = true; - _edit = true; - selectedAssignmentQuestion = dto; + var parameters = new DialogParameters { { x => x.TextEditor, _textEditor } }; + parameters.Add("EditorText", EditorText); + + var dialog = await DialogService.ShowAsync("TextEditor", parameters); + var result = await dialog.Result; + if (!result.Canceled) + { + _textEditor = result.Data as BlazoredTextEditor ?? new BlazoredTextEditor(); + await ParseExam(); + } + StateHasChanged(); + } + private async void OpenPublish() + { + var parameters = new DialogParameters { { x => x.Exam, ExamContent } }; + + var dialog = await DialogService.ShowAsync("PublishExam", parameters); + var result = await dialog.Result; + if (!result.Canceled) + { + await Publish(); + } + StateHasChanged(); + } + + private async void HandleClickedStruct(AssignmentQuestionDto dto) + { + // _open = true; + // _edit = true; + // StateHasChanged(); + + var parameters = new DialogParameters { { x => x.Questions, dto } }; + + var dialog = await DialogService.ShowAsync("Edit_Question", parameters); + var result = await dialog.Result; + if (!result.Canceled) + { + } StateHasChanged(); } @@ -130,7 +136,7 @@ { var plainText = await _textEditor.GetText(); - + EditorText = plainText; if (!string.IsNullOrWhiteSpace(plainText)) { @@ -170,4 +176,54 @@ Snackbar.Add(apiRespon.Message); } -} \ No newline at end of file +} + + + + + + + +@* + + + + + *@ +@* + + + + + + + + + + + + + + + + + + + + + + + + + + + *@ + + \ No newline at end of file diff --git a/TechHelper.Client/Pages/Exam/ExamEdit.razor b/TechHelper.Client/Pages/Exam/ExamEdit.razor index 31c205b..91f3734 100644 --- a/TechHelper.Client/Pages/Exam/ExamEdit.razor +++ b/TechHelper.Client/Pages/Exam/ExamEdit.razor @@ -1,5 +1,6 @@ @page "/exam/edit/{ExamId}" @using Entities.DTO +@using TechHelper.Client.Pages.Exam.ExamView @using TechHelper.Client.Services @using Entities.DTO @using TechHelper.Client.Exam diff --git a/TechHelper.Client/Pages/Exam/ExamManager.razor b/TechHelper.Client/Pages/Exam/ExamManager.razor index 352c196..8bda813 100644 --- a/TechHelper.Client/Pages/Exam/ExamManager.razor +++ b/TechHelper.Client/Pages/Exam/ExamManager.razor @@ -1,7 +1,7 @@ @using Entities.DTO @using Microsoft.AspNetCore.Authorization @using TechHelper.Client.Exam - +@using TechHelper.Client.Pages.Common.Exam @page "/exam/manage" @using Entities.DTO @@ -21,7 +21,8 @@ else @foreach (var item in examDtos) { - + @* *@ + } diff --git a/TechHelper.Client/Pages/Exam/ExamStructView.razor b/TechHelper.Client/Pages/Exam/ExamStructView.razor deleted file mode 100644 index 3e6b123..0000000 --- a/TechHelper.Client/Pages/Exam/ExamStructView.razor +++ /dev/null @@ -1,51 +0,0 @@ -@using Entities.DTO -@using TechHelper.Client.Exam - - - - - - @ExamStruct.Title - @if (ExamStruct.Score > 0) - { - 总分: @ExamStruct.Score 分 - } - - - @if (ExamStruct.Question != null) - { - - } - - @foreach (var examStruct in ExamStruct.ChildrenAssignmentQuestion) - { - - } - - - - -@code { - [Parameter] - public AssignmentQuestionDto ExamStruct { get; set; } = new AssignmentQuestionDto(); - - [Parameter] - public EventCallback ClickedStruct { get; set; } - - [Parameter] - public string Class { get; set; } = "my-2 pa-1"; - - [Parameter] - public int Elevation { get; set; } = 0; - - - private async void HandleClick() - { - await ClickedStruct.InvokeAsync(ExamStruct); - } - - private async void HandleChildStructClick(AssignmentQuestionDto clickedChildExamStruct) - { - await ClickedStruct.InvokeAsync(clickedChildExamStruct); - } -} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor b/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor new file mode 100644 index 0000000..d35d260 --- /dev/null +++ b/TechHelper.Client/Pages/Exam/ExamView/ExamStructView.razor @@ -0,0 +1,87 @@ +@using Entities.Contracts +@using Entities.DTO +@using TechHelper.Client.Exam +@using TechHelper.Client.Pages.Exam.QuestionCard + + + + + + @ExamStruct.Title + + Num: @ExamStruct.ChildrenAssignmentQuestion.Count + 总分: @ExamStruct.Score 分 + + + + + + @ExamStruct.StructType + + + + @if (ExamStruct.Question != null) + { + + } + + @foreach (var examStruct in ExamStruct.ChildrenAssignmentQuestion) + { + "background-color: #ececec", + AssignmentStructType.Group => "background-color: #ffffff", + AssignmentStructType.Struct => "background-color: #cccccccc", + AssignmentStructType.SubQuestion => "background-color: #ffffff", + AssignmentStructType.Option => "background-color: #ffffff", + _ => "background-color: transparent" + }) /> + } + + + + + +@* Style=@(examStruct.StructType switch + { + AssignmentStructType.Question => "background-color: #ffffff", + AssignmentStructType.Composite => "background-color: #ececec", + AssignmentStructType.Struct => "background-color: #dcdcdc", + AssignmentStructType.SubQuestion => "background-color: #ffffff", + AssignmentStructType.Option => "background-color: #dddddd", + _ => "background-color: transparent" + }) *@ + +@code { + [Parameter] + public AssignmentQuestionDto ExamStruct { get; set; } = new AssignmentQuestionDto(); + + [Parameter] + public EventCallback ClickedStruct { get; set; } + + [Parameter] + public string Class { get; set; } = "my-2 pa-1"; + + [Parameter] + public int Elevation { get; set; } = 0; + + [Parameter] + public string Style { get; set; } = "background-color : #eeeeee"; + + private async void HandleClick() + { + await ClickedStruct.InvokeAsync(ExamStruct); + } + + private async void HandleChildStructClick(AssignmentQuestionDto clickedChildExamStruct) + { + await ClickedStruct.InvokeAsync(clickedChildExamStruct); + } +} \ No newline at end of file diff --git a/TechHelper.Client/Pages/Exam/ExamView.razor b/TechHelper.Client/Pages/Exam/ExamView/ExamView.razor similarity index 83% rename from TechHelper.Client/Pages/Exam/ExamView.razor rename to TechHelper.Client/Pages/Exam/ExamView/ExamView.razor index cce015b..77569c4 100644 --- a/TechHelper.Client/Pages/Exam/ExamView.razor +++ b/TechHelper.Client/Pages/Exam/ExamView/ExamView.razor @@ -4,11 +4,11 @@ @if (ParsedExam != null) { - - @ParsedExam.Title + + @ParsedExam.Title @ParsedExam.Description - + } diff --git a/TechHelper.Client/Pages/Exam/QuestionCard.razor b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionCard.razor similarity index 86% rename from TechHelper.Client/Pages/Exam/QuestionCard.razor rename to TechHelper.Client/Pages/Exam/QuestionCard/QuestionCard.razor index 47d3134..6e64b6d 100644 --- a/TechHelper.Client/Pages/Exam/QuestionCard.razor +++ b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionCard.razor @@ -1,4 +1,5 @@ -@using Entities.DTO +@using Entities.Contracts +@using Entities.DTO @using TechHelper.Client.Exam @@ -28,6 +29,9 @@ [Parameter] public QuestionDto Question { get; set; } = new QuestionDto(); + [Parameter] + public AssignmentStructType Type { get; set; } = AssignmentStructType.Question; + [Parameter] public byte Index { get; set; } = 0; diff --git a/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor new file mode 100644 index 0000000..0e96fce --- /dev/null +++ b/TechHelper.Client/Pages/Exam/QuestionCard/QuestionEdit.razor @@ -0,0 +1,18 @@ +@using Entities.DTO +@using TechHelper.Client.Exam + + + + @* @Question.Id *@ + 问题属性 + + + + + + + +@code { + [Parameter] + public QuestionDto Question { get; set; } = new QuestionDto(); +} diff --git a/TechHelper.Client/Pages/Exam/QuestionEdit.razor b/TechHelper.Client/Pages/Exam/QuestionEdit.razor deleted file mode 100644 index 838dc5e..0000000 --- a/TechHelper.Client/Pages/Exam/QuestionEdit.razor +++ /dev/null @@ -1,17 +0,0 @@ -@using Entities.DTO -@using TechHelper.Client.Exam - - - - @Question.Id - - - - - - - -@code { - [Parameter] - public QuestionDto Question { get; set; } = new QuestionDto(); -} diff --git a/TechHelper.Client/Pages/Home.razor b/TechHelper.Client/Pages/Home.razor index 8b26af7..9ae147c 100644 --- a/TechHelper.Client/Pages/Home.razor +++ b/TechHelper.Client/Pages/Home.razor @@ -1,10 +1,23 @@ @page "/" @using Microsoft.AspNetCore.Authorization +@using TechHelper.Client.Pages.Common.Exam - + + + + + + + @code { [CascadingParameter] private Task authenticationStateTask { get; set; } + + protected override Task OnInitializedAsync() + { + return base.OnInitializedAsync(); + Console.WriteLine(authenticationStateTask.Result.User.IsInRole("Student")); + } } \ No newline at end of file diff --git a/TechHelper.Client/Pages/Manage/Index.razor b/TechHelper.Client/Pages/Manage/Index.razor index 93c1ce6..6129d10 100644 --- a/TechHelper.Client/Pages/Manage/Index.razor +++ b/TechHelper.Client/Pages/Manage/Index.razor @@ -3,58 +3,56 @@ @using System.ComponentModel.DataAnnotations @using Microsoft.AspNetCore.Identity + -Profile - -

Profile

- -
-
- - - -
- - -
-
- - - -
- -
-
-
+
+
+ + + +
+ + +
+
+ + + +
+ +
+
+
+
@code { - private string? username; - private string? phoneNumber; + private string? username; + private string? phoneNumber; - [CascadingParameter] - private Task authenticationStateTask { get; set; } + [CascadingParameter] + private Task authenticationStateTask { get; set; } - [SupplyParameterFromForm] - private InputModel Input { get; set; } = new(); + [SupplyParameterFromForm] + private InputModel Input { get; set; } = new(); - protected override async Task OnInitializedAsync() - { - username = authenticationStateTask.Result.User.Identity.Name; - phoneNumber = authenticationStateTask.Result.User.Identity.IsAuthenticated.ToString(); + protected override async Task OnInitializedAsync() + { + username = authenticationStateTask.Result.User.Identity.Name; + phoneNumber = authenticationStateTask.Result.User.Identity.IsAuthenticated.ToString(); - Input.PhoneNumber ??= phoneNumber; - } + Input.PhoneNumber ??= phoneNumber; + } - private async Task OnValidSubmitAsync() - { + private async Task OnValidSubmitAsync() + { - } + } - private sealed class InputModel - { - [Phone] - [Display(Name = "Phone number")] - public string? PhoneNumber { get; set; } - } + private sealed class InputModel + { + [Phone] + [Display(Name = "Phone number")] + public string? PhoneNumber { get; set; } + } } diff --git a/TechHelper.Client/Pages/Student/HomePage.razor b/TechHelper.Client/Pages/Student/HomePage.razor new file mode 100644 index 0000000..ec43a5a --- /dev/null +++ b/TechHelper.Client/Pages/Student/HomePage.razor @@ -0,0 +1,141 @@ + + + + + + + + + + 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" }; + private AxisChartOptions _axisChartOptions = new AxisChartOptions(); + private ChartOptions options = new ChartOptions(); + public List Series = new List() + { + new ChartSeries() { Name = "Series 1", Data = new double[] { 90, 79, 72, 69, 62, 62, 55, 65, 70 } }, + new ChartSeries() { Name = "Series 2", Data = new double[] { 35, 41, 35, 51, 49, 62, 69, 91, 148 } }, + }; + public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" }; + + Random random = new Random(); + protected override void OnInitialized() + { + options.InterpolationOption = InterpolationOption.NaturalSpline; + options.YAxisFormat = "c2"; + _axisChartOptions.MatchBoundsToSize = true; + } + + public void RandomizeData() + { + foreach (var series in Series) + { + for (int i = 0; i < series.Data.Length - 1; i++) + { + series.Data[i] = random.NextDouble() * 100 + 10; + } + } + + StateHasChanged(); + } + + void OnClickMenu(InterpolationOption interpolationOption) + { + options.InterpolationOption = interpolationOption; + StateHasChanged(); + } + +} \ No newline at end of file diff --git a/TechHelper.Client/Properties/launchSettings.json b/TechHelper.Client/Properties/launchSettings.json index e395441..100d6be 100644 --- a/TechHelper.Client/Properties/launchSettings.json +++ b/TechHelper.Client/Properties/launchSettings.json @@ -14,7 +14,7 @@ "dotnetRunMessages": true, "launchBrowser": true, "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", - "applicationUrl": "https://localhost:7047;http://localhost:5190", + "applicationUrl": "https://localhost:7047", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -28,4 +28,6 @@ } } } + + } diff --git a/TechHelper.Client/Services/ClasssServices.cs b/TechHelper.Client/Services/ClasssServices.cs index 89bb362..4801eb0 100644 --- a/TechHelper.Client/Services/ClasssServices.cs +++ b/TechHelper.Client/Services/ClasssServices.cs @@ -38,6 +38,22 @@ namespace TechHelper.Client.Services } } + public async Task GetGradeClasses(byte grade) + { + try + { + var result = await _client.PostAsJsonAsync("class/GetGradeClasses", grade); + var content = await result.Content.ReadAsStringAsync(); + Console.WriteLine($"服务器返回的原始内容是: {content}"); + var users = JsonConvert.DeserializeObject>(content); + return ApiResponse.Success(result: users); + } + catch (Exception ex) + { + return ApiResponse.Error($"获取失败,{ex.Message}, InnerException: {ex.InnerException}"); + } + } + public async Task UserRegister(UserRegistrationToClassDto userRegistrationToClassDto) { try diff --git a/TechHelper.Client/Services/ExamService.cs b/TechHelper.Client/Services/ExamService.cs index cdc4b97..e1ecbe8 100644 --- a/TechHelper.Client/Services/ExamService.cs +++ b/TechHelper.Client/Services/ExamService.cs @@ -126,7 +126,7 @@ namespace TechHelper.Client.Services public async Task GetExam(Guid guid) { - var response = await _client.GetAsync($"exam/get?id={guid}"); + var response = await _client.GetAsync($"exam/{guid}"); if (response.IsSuccessStatusCode) { var content = await response.Content.ReadAsStringAsync(); diff --git a/TechHelper.Client/Services/IClassServices.cs b/TechHelper.Client/Services/IClassServices.cs index 3123b40..2fda8f8 100644 --- a/TechHelper.Client/Services/IClassServices.cs +++ b/TechHelper.Client/Services/IClassServices.cs @@ -8,6 +8,7 @@ namespace TechHelper.Client.Services { public Task UserRegister(UserRegistrationToClassDto userRegistrationToClassDto); public Task CreateClass(UserRegistrationToClassDto userClass); - public Task GetClassStudents(); + public Task GetClassStudents(); + public Task GetGradeClasses(byte grade); } } diff --git a/TechHelper.Client/Shared/ExamLayout.razor b/TechHelper.Client/Shared/ExamLayout.razor index 3fd6818..d43426e 100644 --- a/TechHelper.Client/Shared/ExamLayout.razor +++ b/TechHelper.Client/Shared/ExamLayout.razor @@ -5,8 +5,6 @@ -

Manage your account

-

Change your account settings

diff --git a/TechHelper.Client/Shared/ManageLayout.razor b/TechHelper.Client/Shared/ManageLayout.razor index 004df6c..ccdb53c 100644 --- a/TechHelper.Client/Shared/ManageLayout.razor +++ b/TechHelper.Client/Shared/ManageLayout.razor @@ -3,11 +3,8 @@ - -

Manage your account

-

Change your account settings

- + @Body diff --git a/TechHelper.Client/TechHelper.Client.csproj b/TechHelper.Client/TechHelper.Client.csproj index 1631edf..a8f305d 100644 --- a/TechHelper.Client/TechHelper.Client.csproj +++ b/TechHelper.Client/TechHelper.Client.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Linux @@ -39,4 +40,8 @@ + + + + diff --git a/TechHelper.Client/wwwroot/appsettings.json b/TechHelper.Client/wwwroot/appsettings.json index ef7732f..a245e04 100644 --- a/TechHelper.Client/wwwroot/appsettings.json +++ b/TechHelper.Client/wwwroot/appsettings.json @@ -4,6 +4,6 @@ "ClientId": "33333333-3333-3333-33333333333333333" }, "ApiConfiguration": { - "BaseAddress": "http://localhost:5099" + "BaseAddress": "http://localhost:8080" } } diff --git a/TechHelper.Client/wwwroot/ref/Border.png b/TechHelper.Client/wwwroot/ref/Border.png new file mode 100644 index 0000000..0aa54d4 Binary files /dev/null and b/TechHelper.Client/wwwroot/ref/Border.png differ diff --git a/TechHelper.Client/wwwroot/ref/File.png b/TechHelper.Client/wwwroot/ref/File.png new file mode 100644 index 0000000..b597ab5 Binary files /dev/null and b/TechHelper.Client/wwwroot/ref/File.png differ diff --git a/TechHelper.Client/wwwroot/ref/Login.png b/TechHelper.Client/wwwroot/ref/Login.png new file mode 100644 index 0000000..baa70fe Binary files /dev/null and b/TechHelper.Client/wwwroot/ref/Login.png differ diff --git a/TechHelper.Client/wwwroot/ref/UnFinish.png b/TechHelper.Client/wwwroot/ref/UnFinish.png new file mode 100644 index 0000000..97d35fe Binary files /dev/null and b/TechHelper.Client/wwwroot/ref/UnFinish.png differ diff --git a/TechHelper.Client/wwwroot/ref/Working.png b/TechHelper.Client/wwwroot/ref/Working.png new file mode 100644 index 0000000..9ae6329 Binary files /dev/null and b/TechHelper.Client/wwwroot/ref/Working.png differ diff --git a/TechHelper.Server/Controllers/ClassController.cs b/TechHelper.Server/Controllers/ClassController.cs index 424b8e2..3df0e59 100644 --- a/TechHelper.Server/Controllers/ClassController.cs +++ b/TechHelper.Server/Controllers/ClassController.cs @@ -62,7 +62,7 @@ namespace TechHelper.Server.Controllers var result = await _classService.GetClassStudents(classDto); var css = result.Result as ICollection; - if(css == null) return BadRequest("你还没有学生"); + if (css == null) return BadRequest("你还没有学生"); List sts = new List(); @@ -93,5 +93,15 @@ namespace TechHelper.Server.Controllers return Ok(); } + + [HttpPost("GetGradeClasses")] + public async Task GetGradeClasses( + [FromBody] byte classDto) + { + var result = await _classService.GetGradeClasses(classDto); + if (!result.Status) return BadRequest(result.Message); + + return Ok(result.Result); + } } } diff --git a/TechHelper.Server/Controllers/ExamController.cs b/TechHelper.Server/Controllers/ExamController.cs index b457d0a..509f186 100644 --- a/TechHelper.Server/Controllers/ExamController.cs +++ b/TechHelper.Server/Controllers/ExamController.cs @@ -11,7 +11,7 @@ using TechHelper.Services; namespace TechHelper.Server.Controllers { - [Route("api/[controller]")] + [Route("api/exam")] [ApiController] [Authorize] public class ExamController : ControllerBase @@ -54,7 +54,6 @@ namespace TechHelper.Server.Controllers /// 提交的数据传输对象。 /// 提交结果或错误信息。 [HttpPost("submission")] - [Authorize(Roles = "Student")] public async Task SubmissionAssignment([FromBody] SubmissionDto submissionDto) { var result = await _examService.SubmissionAssignment(submissionDto); diff --git a/TechHelper.Server/Dockerfile b/TechHelper.Server/Dockerfile index 0192205..d1b550b 100644 --- a/TechHelper.Server/Dockerfile +++ b/TechHelper.Server/Dockerfile @@ -1,12 +1,15 @@ # 请参阅 https://aka.ms/customizecontainer 以了解如何自定义调试容器,以及 Visual Studio 如何使用此 Dockerfile 生成映像以更快地进行调试。 # 此阶段用于在快速模式(默认为调试配置)下从 VS 运行时 -FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS server USER $APP_UID WORKDIR /app EXPOSE 8080 EXPOSE 8081 +COPY --from=entitieslib:latest /publish /publish/entities +COPY --from=emaillib:latest /publish /publish/emaillib + # 此阶段用于生成服务项目 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build @@ -16,15 +19,17 @@ COPY ["TechHelper.Server/TechHelper.Server.csproj", "TechHelper.Server/"] RUN dotnet restore "./TechHelper.Server/TechHelper.Server.csproj" COPY . . WORKDIR "/src/TechHelper.Server" +RUN dotnet nuget locals all --clear RUN dotnet build "./TechHelper.Server.csproj" -c $BUILD_CONFIGURATION -o /app/build # 此阶段用于发布要复制到最终阶段的服务项目 FROM build AS publish ARG BUILD_CONFIGURATION=Release +RUN dotnet nuget locals all --clear RUN dotnet publish "./TechHelper.Server.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false # 此阶段在生产中使用,或在常规模式下从 VS 运行时使用(在不使用调试配置时为默认值) -FROM base AS final +FROM server AS final WORKDIR /app COPY --from=publish /app/publish . ENTRYPOINT ["dotnet", "TechHelper.Server.dll"] \ No newline at end of file diff --git a/TechHelper.Server/Migrations/20250828025055_score_in_submission.Designer.cs b/TechHelper.Server/Migrations/20250828025055_score_in_submission.Designer.cs new file mode 100644 index 0000000..814ffc3 --- /dev/null +++ b/TechHelper.Server/Migrations/20250828025055_score_in_submission.Designer.cs @@ -0,0 +1,1263 @@ +// +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("20250828025055_score_in_submission")] + partial class score_in_submission + { + /// + 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("IsDeleted") + .HasColumnType("tinyint(1)") + .HasColumnName("deleted"); + + 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.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("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("bbea7915-e27c-4ddc-b06c-1f579fc8104e"), + Name = "Student", + NormalizedName = "STUDENT" + }, + new + { + Id = new Guid("53307917-63c4-468a-ab05-a03882a69ef8"), + Name = "Teacher", + NormalizedName = "TEACHER" + }, + new + { + Id = new Guid("789b7819-685f-4a2b-9adf-463f397f24d1"), + 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/20250828025055_score_in_submission.cs b/TechHelper.Server/Migrations/20250828025055_score_in_submission.cs new file mode 100644 index 0000000..ad6533b --- /dev/null +++ b/TechHelper.Server/Migrations/20250828025055_score_in_submission.cs @@ -0,0 +1,104 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional + +namespace TechHelper.Server.Migrations +{ + /// + public partial class score_in_submission : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("379143a2-8d7f-4ef7-b7c0-14701b710f87")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("6d49bb08-97d6-4a38-88a7-8080925b589b")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("e330c745-f422-43e3-bcdf-1439ace3c52f")); + + migrationBuilder.AddColumn( + name: "ErrorQuesNum", + table: "submissions", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.AddColumn( + name: "TotalQuesNum", + table: "submissions", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.AddColumn( + name: "TotalScore", + table: "submissions", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("53307917-63c4-468a-ab05-a03882a69ef8"), null, "Teacher", "TEACHER" }, + { new Guid("789b7819-685f-4a2b-9adf-463f397f24d1"), null, "Administrator", "ADMINISTRATOR" }, + { new Guid("bbea7915-e27c-4ddc-b06c-1f579fc8104e"), null, "Student", "STUDENT" } + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("53307917-63c4-468a-ab05-a03882a69ef8")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("789b7819-685f-4a2b-9adf-463f397f24d1")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("bbea7915-e27c-4ddc-b06c-1f579fc8104e")); + + migrationBuilder.DropColumn( + name: "ErrorQuesNum", + table: "submissions"); + + migrationBuilder.DropColumn( + name: "TotalQuesNum", + table: "submissions"); + + migrationBuilder.DropColumn( + name: "TotalScore", + table: "submissions"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("379143a2-8d7f-4ef7-b7c0-14701b710f87"), null, "Administrator", "ADMINISTRATOR" }, + { new Guid("6d49bb08-97d6-4a38-88a7-8080925b589b"), null, "Student", "STUDENT" }, + { new Guid("e330c745-f422-43e3-bcdf-1439ace3c52f"), null, "Teacher", "TEACHER" } + }); + } + } +} diff --git a/TechHelper.Server/Migrations/20250829043403_up_assign_data.Designer.cs b/TechHelper.Server/Migrations/20250829043403_up_assign_data.Designer.cs new file mode 100644 index 0000000..197ebdc --- /dev/null +++ b/TechHelper.Server/Migrations/20250829043403_up_assign_data.Designer.cs @@ -0,0 +1,1270 @@ +// +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("20250829043403_up_assign_data")] + partial class up_assign_data + { + /// + 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.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("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("df89b9a0-65ef-42dd-b2cb-e59997a72e70"), + Name = "Student", + NormalizedName = "STUDENT" + }, + new + { + Id = new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"), + Name = "Teacher", + NormalizedName = "TEACHER" + }, + new + { + Id = new Guid("37f41430-0cb7-44e5-988b-976200bd602d"), + 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/20250829043403_up_assign_data.cs b/TechHelper.Server/Migrations/20250829043403_up_assign_data.cs new file mode 100644 index 0000000..df6349a --- /dev/null +++ b/TechHelper.Server/Migrations/20250829043403_up_assign_data.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 up_assign_data : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("53307917-63c4-468a-ab05-a03882a69ef8")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("789b7819-685f-4a2b-9adf-463f397f24d1")); + + migrationBuilder.DeleteData( + table: "AspNetRoles", + keyColumn: "Id", + keyValue: new Guid("bbea7915-e27c-4ddc-b06c-1f579fc8104e")); + + migrationBuilder.AddColumn( + name: "ExamType", + table: "assignments", + type: "tinyint unsigned", + nullable: false, + defaultValue: (byte)0); + + migrationBuilder.AddColumn( + name: "Name", + table: "assignments", + type: "longtext", + nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"); + + 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" } + }); + } + + /// + protected override void Down(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.DropColumn( + name: "ExamType", + table: "assignments"); + + migrationBuilder.DropColumn( + name: "Name", + table: "assignments"); + + migrationBuilder.InsertData( + table: "AspNetRoles", + columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" }, + values: new object[,] + { + { new Guid("53307917-63c4-468a-ab05-a03882a69ef8"), null, "Teacher", "TEACHER" }, + { new Guid("789b7819-685f-4a2b-9adf-463f397f24d1"), null, "Administrator", "ADMINISTRATOR" }, + { new Guid("bbea7915-e27c-4ddc-b06c-1f579fc8104e"), null, "Student", "STUDENT" } + }); + } + } +} diff --git a/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs b/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs index 4edc338..d50daf4 100644 --- a/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs +++ b/TechHelper.Server/Migrations/ApplicationContextModelSnapshot.cs @@ -50,10 +50,17 @@ namespace TechHelper.Server.Migrations .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"); @@ -504,6 +511,9 @@ namespace TechHelper.Server.Migrations .HasColumnType("tinyint unsigned") .HasColumnName("attempt_number"); + b.Property("ErrorQuesNum") + .HasColumnType("tinyint unsigned"); + b.Property("GradedAt") .HasColumnType("datetime(6)") .HasColumnName("graded_at"); @@ -540,6 +550,12 @@ namespace TechHelper.Server.Migrations .HasColumnType("datetime(6)") .HasColumnName("submission_time"); + b.Property("TotalQuesNum") + .HasColumnType("tinyint unsigned"); + + b.Property("TotalScore") + .HasColumnType("tinyint unsigned"); + b.HasKey("Id"); b.HasIndex("AssignmentId"); @@ -755,19 +771,19 @@ namespace TechHelper.Server.Migrations b.HasData( new { - Id = new Guid("6d49bb08-97d6-4a38-88a7-8080925b589b"), + Id = new Guid("df89b9a0-65ef-42dd-b2cb-e59997a72e70"), Name = "Student", NormalizedName = "STUDENT" }, new { - Id = new Guid("e330c745-f422-43e3-bcdf-1439ace3c52f"), + Id = new Guid("0775702a-5db7-4747-94d0-4376fad2b58b"), Name = "Teacher", NormalizedName = "TEACHER" }, new { - Id = new Guid("379143a2-8d7f-4ef7-b7c0-14701b710f87"), + Id = new Guid("37f41430-0cb7-44e5-988b-976200bd602d"), Name = "Administrator", NormalizedName = "ADMINISTRATOR" }); diff --git a/TechHelper.Server/Program.cs b/TechHelper.Server/Program.cs index c960287..717c971 100644 --- a/TechHelper.Server/Program.cs +++ b/TechHelper.Server/Program.cs @@ -128,7 +128,7 @@ builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder - .WithOrigins("https://localhost:7047", "http://localhost:5190") + .WithOrigins("https://localhost:7047", "http://localhost:7047") .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials()); @@ -137,7 +137,7 @@ builder.Services.AddCors(options => var app = builder.Build(); // Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +if (/*app.Environment.IsDevelopment()*/true) { app.UseSwagger(); app.UseSwaggerUI(); diff --git a/TechHelper.Server/Properties/launchSettings.json b/TechHelper.Server/Properties/launchSettings.json index 0664fc0..0b1a02a 100644 --- a/TechHelper.Server/Properties/launchSettings.json +++ b/TechHelper.Server/Properties/launchSettings.json @@ -8,7 +8,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "http://localhost:5099" + "applicationUrl": "http://localhost:8080" }, "https": { "commandName": "Project", @@ -18,7 +18,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7037;http://localhost:5062" + "applicationUrl": "https://localhost:8080" }, "IIS Express": { "commandName": "IISExpress", diff --git a/TechHelper.Server/Services/ClassService.cs b/TechHelper.Server/Services/ClassService.cs index cae8a87..48c1f25 100644 --- a/TechHelper.Server/Services/ClassService.cs +++ b/TechHelper.Server/Services/ClassService.cs @@ -143,6 +143,22 @@ namespace TechHelper.Services } } + public async Task GetGradeClasses(byte Grade) + { + try + { + var result = await _work.GetRepository().GetAllAsync(predicate: + c => c.Grade == Grade); + + var classes = result.Select(x => x.Number).ToList(); + return ApiResponse.Success(result: classes); + } + catch (Exception ex) + { + return ApiResponse.Error($"年级班级列表失败, {ex.Message}, {ex.InnerException}"); + } + } + public async Task GetUserClass(Guid id) { var tch = await _work.GetRepository().GetAllAsync(predicate: user => user.TeacherId == id, include: i => i diff --git a/TechHelper.Server/Services/IClassService.cs b/TechHelper.Server/Services/IClassService.cs index 5ce0db2..533ed6f 100644 --- a/TechHelper.Server/Services/IClassService.cs +++ b/TechHelper.Server/Services/IClassService.cs @@ -10,5 +10,7 @@ namespace TechHelper.Services public Task GetUserClass(Guid user); // List public Task GetUserClassRole(Guid user); // List public Task GetClassStudents(ClassDto classDto); // Class + public Task GetGradeClasses(byte Grade); // Class + } } diff --git a/TechHelper.sln b/TechHelper.sln index 8c1c429..efa6d84 100644 --- a/TechHelper.sln +++ b/TechHelper.sln @@ -45,4 +45,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {22567F58-2C4A-4DB2-9674-8F537E0891CB} + EndGlobalSection EndGlobal diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..044f785 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.8' + +services: + Entities-Lib: # 公共库独立服务 + build: + context: . + dockerfile: Entities/Dockerfile + image: entitieslib:latest + + Email-Lib: # 公共库独立服务 + build: + context: . + dockerfile: EmailLib/Dockerfile + image: emaillib:latest + + web-api: # Web API 服务 + # build: + # context: . + # dockerfile: TechHelper.Server/Dockerfile + + image: techhelperserver:latest + depends_on: + - Entities-Lib + - Email-Lib + ports: + - "8084:80" + + wasm-client: # WASM 客户端 + build: + context: . + dockerfile: TechHelper.Client/Dockerfile + depends_on: + - web-api + - Entities-Lib + - Email-Lib + ports: + - "3000:80" + \ No newline at end of file