diff --git a/Entities/Contracts/AppMainStruct.cs b/Entities/Contracts/AppMainStruct.cs
index 5246b5c..9ca0285 100644
--- a/Entities/Contracts/AppMainStruct.cs
+++ b/Entities/Contracts/AppMainStruct.cs
@@ -76,4 +76,15 @@ namespace Entities.Contracts
Option
}
+
+
+ public enum SubmissionStatus
+ {
+ Pending, // 待提交/未开始
+ Submitted, // 已提交
+ Graded, // 已批改
+ Resubmission, // 待重新提交 (如果允许)
+ Late, // 迟交
+ Draft, // 草稿
+ }
}
diff --git a/Entities/Contracts/AssignmentQuestion.cs b/Entities/Contracts/AssignmentQuestion.cs
index 232244e..cfbaf91 100644
--- a/Entities/Contracts/AssignmentQuestion.cs
+++ b/Entities/Contracts/AssignmentQuestion.cs
@@ -31,6 +31,9 @@ namespace Entities.Contracts
[Column("question_number")]
public byte Index { get; set; }
+ [Column("sequence")]
+ public string Sequence { get; set; } = string.Empty;
+
[Column("parent_question_group_id")]
public Guid? ParentAssignmentQuestionId { get; set; }
diff --git a/Entities/Contracts/Submission.cs b/Entities/Contracts/Submission.cs
index 832da6f..636816c 100644
--- a/Entities/Contracts/Submission.cs
+++ b/Entities/Contracts/Submission.cs
@@ -37,7 +37,7 @@ namespace Entities.Contracts
public float? OverallGrade { get; set; }
[Column("overall_feedback")]
- public string OverallFeedback { get; set; }
+ public string? OverallFeedback { get; set; }
[Column("graded_by")]
[ForeignKey("Grader")]
@@ -66,13 +66,4 @@ namespace Entities.Contracts
}
}
- public enum SubmissionStatus
- {
- Pending, // 待提交/未开始
- Submitted, // 已提交
- Graded, // 已批改
- Resubmission, // 待重新提交 (如果允许)
- Late, // 迟交
- Draft, // 草稿
- }
}
diff --git a/Entities/Contracts/SubmissionDetail.cs b/Entities/Contracts/SubmissionDetail.cs
index 121b78f..6fc580a 100644
--- a/Entities/Contracts/SubmissionDetail.cs
+++ b/Entities/Contracts/SubmissionDetail.cs
@@ -31,16 +31,16 @@ namespace Entities.Contracts
public Guid AssignmentQuestionId { get; set; }
[Column("student_answer")]
- public string StudentAnswer { get; set; }
+ public string? StudentAnswer { get; set; }
[Column("is_correct")]
public bool? IsCorrect { get; set; }
[Column("points_awarded")]
- public float? PointsAwarded { get; set; }
+ public float? PointsAwarded { get; set; } // score
[Column("teacher_feedback")]
- public string TeacherFeedback { get; set; }
+ public string? TeacherFeedback { get; set; }
[Column("created_at")]
public DateTime CreatedAt { get; set; }
@@ -54,8 +54,10 @@ namespace Entities.Contracts
[ForeignKey(nameof(StudentId))]
public User Student { get; set; }
+ [ForeignKey(nameof(SubmissionId))]
public Submission Submission { get; set; }
+ [ForeignKey(nameof(AssignmentQuestionId))]
public AssignmentQuestion AssignmentQuestion { get; set; }
public SubmissionDetail()
diff --git a/Entities/DTO/AssignmentClassDto.cs b/Entities/DTO/AssignmentClassDto.cs
new file mode 100644
index 0000000..e6a0a99
--- /dev/null
+++ b/Entities/DTO/AssignmentClassDto.cs
@@ -0,0 +1,23 @@
+using Entities.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace Entities.DTO
+{
+
+
+ public class AssignmentClassDto
+ {
+ public AssignmentDto Assignment { get; set; }
+ public Class ClassId { get; set; }
+ public DateTime AssignedAt { get; set; }
+ }
+
+
+
+
+}
diff --git a/Entities/DTO/AssignmentDto.cs b/Entities/DTO/AssignmentDto.cs
index 9a9af91..4a32497 100644
--- a/Entities/DTO/AssignmentDto.cs
+++ b/Entities/DTO/AssignmentDto.cs
@@ -1,4 +1,5 @@
-using System;
+using Entities.Contracts;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -6,4 +7,20 @@ using System.Threading.Tasks;
namespace Entities.DTO
{
+ public class AssignmentDto
+ {
+ public Guid Id { get; set; } = Guid.Empty;
+ public string Title { get; set; } = string.Empty;
+ public string Description { get; set; } = string.Empty;
+
+ public byte TotalQuestions { get; set; }
+ public float Score { get; set; } = 0;
+ public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown;
+ public DateTime CreatedAt { get; set; }
+ public DateTime UpdatedAt { get; set; }
+ public DateTime DueDate { get; set; }
+ public Guid CreatorId { get; set; }
+
+ public AssignmentQuestionDto ExamStruct { get; set; } = new AssignmentQuestionDto();
+ }
}
diff --git a/Entities/DTO/AssignmentQuestionDto.cs b/Entities/DTO/AssignmentQuestionDto.cs
index 5cb66a9..702e130 100644
--- a/Entities/DTO/AssignmentQuestionDto.cs
+++ b/Entities/DTO/AssignmentQuestionDto.cs
@@ -15,6 +15,7 @@ namespace Entities.DTO
public byte Index { get; set; } = 0;
public float Score { get; set; } = 0;
+ public string Sequence { get; set; } = string.Empty;
public Layout Layout { get; set; } = Layout.horizontal;
public AssignmentStructType StructType { get; set; } = AssignmentStructType.Question;
@@ -24,4 +25,6 @@ namespace Entities.DTO
public QuestionDto? Question { get; set; }
}
+
+
}
diff --git a/Entities/DTO/ExamDto.cs b/Entities/DTO/ExamDto.cs
deleted file mode 100644
index 1c675dd..0000000
--- a/Entities/DTO/ExamDto.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using Entities.Contracts;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using System.Xml.Serialization;
-
-namespace Entities.DTO
-{
- public class AssignmentDto
- {
- public Guid Id { get; set; } = Guid.Empty;
- public string Title { get; set; } = string.Empty;
- public string Description { get; set; } = string.Empty;
-
- public byte TotalQuestions { get; set; }
- public float Score { get; set; } = 0;
- public SubjectAreaEnum SubjectArea { get; set; } = SubjectAreaEnum.Unknown;
- public DateTime CreatedAt { get; set; }
- public DateTime UpdatedAt { get; set; }
- public DateTime DueDate { get; set; }
- public Guid CreatorId { get; set; }
-
- public AssignmentQuestionDto ExamStruct { get; set; } = new AssignmentQuestionDto();
- }
-
- public class AssignmentClassDto
- {
- public AssignmentDto Assignment { get; set; }
- public Class ClassId { get; set; }
- public DateTime AssignedAt { get; set; }
- }
-
- public class QuestionContextDto
- {
- public Guid Id { get; set; } = Guid.Empty;
- public string Description { get; set; } = string.Empty;
- }
-
- public class OptionDto
- {
- public string? Value { get; set; } = string.Empty;
- }
-
-}
diff --git a/Entities/DTO/QuestionContextDto.cs b/Entities/DTO/QuestionContextDto.cs
new file mode 100644
index 0000000..523fe79
--- /dev/null
+++ b/Entities/DTO/QuestionContextDto.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entities.DTO
+{
+
+ public class QuestionContextDto
+ {
+ public Guid Id { get; set; } = Guid.Empty;
+ public string Description { get; set; } = string.Empty;
+ }
+}
diff --git a/Entities/DTO/QuestionDto.cs b/Entities/DTO/QuestionDto.cs
index d30ab1d..a35ff20 100644
--- a/Entities/DTO/QuestionDto.cs
+++ b/Entities/DTO/QuestionDto.cs
@@ -36,4 +36,13 @@ namespace Entities.DTO
public DateTime UpdatedAt { get; set; } = DateTime.Now;
}
+
+
+ ///
+ /// Can be removed because the class isn't used
+ ///
+ public class OptionDto
+ {
+ public string? Value { get; set; } = string.Empty;
+ }
}
diff --git a/Entities/DTO/StudentDto.cs b/Entities/DTO/StudentDto.cs
new file mode 100644
index 0000000..2bbb3bc
--- /dev/null
+++ b/Entities/DTO/StudentDto.cs
@@ -0,0 +1,20 @@
+using Entities.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entities.DTO
+{
+ public class StudentDto
+ {
+ public Guid Id { get; set; }
+ public string? DisplayName { get; set; }
+
+ public UInt32 ErrorQuestionNum { get; set; }
+ public Dictionary ErrorQuestionTypes { get; set; } = new Dictionary();
+ public Dictionary SubjectAreaErrorQuestionDis { get; set; } = new Dictionary();
+ public Dictionary LessonErrorDis { get; set; } = new Dictionary();
+ }
+}
diff --git a/Entities/DTO/SubmissionDetailDto.cs b/Entities/DTO/SubmissionDetailDto.cs
new file mode 100644
index 0000000..1a7aba9
--- /dev/null
+++ b/Entities/DTO/SubmissionDetailDto.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entities.DTO
+{
+ public class SubmissionDetailDto
+ {
+ public Guid Id { get; set; } = Guid.Empty;
+ public Guid StudentId { get; set; }
+ public Guid AssignmentQuestionId { get; set; }
+ public string? StudentAnswer { get; set; }
+ public bool? IsCorrect { get; set; }
+ public float? PointsAwarded { get; set; }
+ public string? TeacherFeedback { get; set; }
+ }
+}
diff --git a/Entities/DTO/SubmissionDto.cs b/Entities/DTO/SubmissionDto.cs
new file mode 100644
index 0000000..14343fb
--- /dev/null
+++ b/Entities/DTO/SubmissionDto.cs
@@ -0,0 +1,23 @@
+using Entities.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entities.DTO
+{
+ public class SubmissionDto
+ {
+ public Guid Id { get; set; } = Guid.Empty;
+ public Guid AssignmentId { get; set; }
+ public Guid StudentId { get; set; }
+ public DateTime SubmissionTime { get; set; }
+ public float OverallGrade { get; set; } = 0;
+ public string OverallFeedback { get; set; } = string.Empty;
+ public Guid? GraderId { get; set; }
+ public DateTime? GradedAt { get; set; }
+ public SubmissionStatus Status { get; set; }
+ public List SubmissionDetails { get; set; } = new List();
+ }
+}
diff --git a/Entities/DTO/UserDto.cs b/Entities/DTO/UserDto.cs
new file mode 100644
index 0000000..502d1d6
--- /dev/null
+++ b/Entities/DTO/UserDto.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Entities.DTO
+{
+ public class UserDto
+ {
+ public Guid Id { get; set; }
+ public string? DisplayName { get; set; }
+
+ }
+}
diff --git a/TechHelper.Client/Content/AutoMapperProFile.cs b/TechHelper.Client/Content/AutoMapperProFile.cs
index aff8517..22d9846 100644
--- a/TechHelper.Client/Content/AutoMapperProFile.cs
+++ b/TechHelper.Client/Content/AutoMapperProFile.cs
@@ -15,6 +15,9 @@ namespace TechHelper.Context
CreateMap()
.ForMember(d=>d.Description, o=>o.Ignore());
CreateMap();
+
+
+ CreateMap();
}
}
diff --git a/TechHelper.Client/Exam/ExamStruct.cs b/TechHelper.Client/Exam/AssignmentCheckData.cs
similarity index 52%
rename from TechHelper.Client/Exam/ExamStruct.cs
rename to TechHelper.Client/Exam/AssignmentCheckData.cs
index f6b1627..0510537 100644
--- a/TechHelper.Client/Exam/ExamStruct.cs
+++ b/TechHelper.Client/Exam/AssignmentCheckData.cs
@@ -2,18 +2,20 @@
namespace TechHelper.Client.Exam
{
- public class ExamStruct
+ public class AssignmentCheckData
{
- public string Title { get; set; }
- public List Questions { get; set; } = new List();
+ public string Title { get; set; }
+ public Guid AssignmentId { get; set; }
+ public Guid StudentId { get; set; }
+ public List Questions { get; set; } = new List();
+ }
- public class QuestionItem
- {
- public string Sequence { get; set; } = string.Empty;
- public string QuestionText { get; set; } = string.Empty;
- public float Score { get; set; }
- }
+ public class AssignmentCheckQuestion
+ {
+ public string Sequence { get; set; } = string.Empty;
+ public AssignmentQuestionDto AssignmentQuestionDto { get; set; } = new AssignmentQuestionDto();
+ public float Score { get; set; }
}
public class Student
@@ -24,32 +26,32 @@ namespace TechHelper.Client.Exam
public class QuestionAnswerStatus
{
- public string QuestionSequence { get; set; } = string.Empty; // 题目序号,例如 "1.1"
- public string QuestionText { get; set; } = string.Empty; // 题目文本
- public float QuestionScore { get; set; } // 题目分值
+ public string QuestionSequence { get; set; } = string.Empty; // 题目序号,例如 "1.1"
+ public string QuestionText { get; set; } = string.Empty; // 题目文本
+ public float QuestionScore { get; set; } // 题目分值
public Dictionary StudentCorrectStatus { get; set; } = new Dictionary();
// Key: Student.Id, Value: true 表示正确,false 表示错误
}
public class QuestionRowData
{
- public ExamStruct.QuestionItem QuestionItem { get; set; } // 原始题目信息
+ public AssignmentCheckQuestion QuestionItem { get; set; } // 原始题目信息
public Dictionary StudentAnswers { get; set; } = new Dictionary();
}
public static class ExamStructExtensions
{
- public static ExamStruct GetStruct(this AssignmentDto dto)
+ public static AssignmentCheckData GetStruct(this AssignmentDto dto)
{
if (dto == null)
{
- return new ExamStruct { Title = "无效试卷", Questions = new List() };
+ return new AssignmentCheckData { Title = "无效试卷", Questions = new List() };
}
- var examStruct = new ExamStruct
+ var examStruct = new AssignmentCheckData
{
- Title = dto.Title
+ Title = dto.Title
};
GetSeqRecursive(dto.ExamStruct, null, examStruct.Questions);
@@ -65,8 +67,8 @@ namespace TechHelper.Client.Exam
/// 用于收集所有生成题目项的列表。
private static void GetSeqRecursive(
AssignmentQuestionDto currentGroup,
- string? parentSequence,
- List allQuestions)
+ string? parentSequence,
+ List allQuestions)
{
string currentGroupSequence = parentSequence != null
? $"{parentSequence}.{currentGroup.Index}"
@@ -76,6 +78,17 @@ namespace TechHelper.Client.Exam
{
GetSeqRecursive(subGroup, currentGroupSequence, allQuestions);
}
+ if (!string.IsNullOrEmpty(currentGroup.Sequence))
+ {
+
+ allQuestions.Add(new AssignmentCheckQuestion
+ {
+ AssignmentQuestionDto = currentGroup,
+ //Sequence = currentGroupSequence,
+ Sequence = currentGroup.Sequence,
+ Score = currentGroup.Score,
+ });
+ }
}
}
}
diff --git a/TechHelper.Client/Exam/ExamPaperExtensions .cs b/TechHelper.Client/Exam/ExamPaperExtensions .cs
index 699b1f5..e082727 100644
--- a/TechHelper.Client/Exam/ExamPaperExtensions .cs
+++ b/TechHelper.Client/Exam/ExamPaperExtensions .cs
@@ -7,7 +7,7 @@ using AutoMapper;
namespace TechHelper.Client.Exam
{
- public static class ExamPaperExtensions
+ public static class AssignmentExtensions
{
public static List ParseOptionsFromText(this string optionsText)
@@ -25,9 +25,9 @@ namespace TechHelper.Client.Exam
public static void SeqQGroupIndex(this AssignmentQuestionDto dto)
{
- foreach(var sqg in dto.ChildrenAssignmentQuestion)
+ foreach (var sqg in dto.ChildrenAssignmentQuestion)
{
- sqg.Index = (byte)dto.ChildrenAssignmentQuestion.IndexOf(sqg);
+ sqg.Index = (byte)(dto.ChildrenAssignmentQuestion.IndexOf(sqg) + 1);
sqg.SeqQGroupIndex();
}
diff --git a/TechHelper.Client/Exam/ExamParse.cs b/TechHelper.Client/Exam/ExamParse.cs
index a59dad0..69b9770 100644
--- a/TechHelper.Client/Exam/ExamParse.cs
+++ b/TechHelper.Client/Exam/ExamParse.cs
@@ -56,6 +56,7 @@ namespace TechHelper.Client.Exam
public string Description { get; set; } = string.Empty;
public byte Index { get; set; } = 0;
public float Score { get; set; }
+ public string Sequence { get; set; } = string.Empty;
public QuestionEx? Question { get; set; }
public AssignmentStructType Type { get; set; }
public List ChildrenAssignmentQuestion { get; set; } = new List();
@@ -344,6 +345,8 @@ namespace TechHelper.Client.Exam
assignmentQuestionStack.Pop();
}
+ string sequence = assignmentQuestionStack.Count > 0 ? assignmentQuestionStack.Peek().Sequence : string.Empty;
+
// 验证捕获组:Group 1 是编号,Group 2 是题目内容
if (pm.RegexMatch.Groups.Count < 3 || !pm.RegexMatch.Groups[1].Success || string.IsNullOrWhiteSpace(pm.RegexMatch.Groups[2].Value))
{
@@ -372,6 +375,8 @@ namespace TechHelper.Client.Exam
// 提取标题,这里使用 Group 2 的值,它不包含分数
string title = pm.RegexMatch.Groups[2].Value.Trim();
+ string seq = pm.RegexMatch.Groups[1].Value.Trim();
+ seq = string.IsNullOrEmpty(seq) || string.IsNullOrEmpty(sequence) ? seq : " ." + seq;
AssignmentQuestionEx newAssignmentQuestion;
if (pm.PatternConfig.Type == AssignmentStructType.Struct)
@@ -380,6 +385,7 @@ namespace TechHelper.Client.Exam
{
Title = title,
Score = score,
+ Sequence = sequence + seq,
Priority = pm.PatternConfig.Priority,
Type = pm.PatternConfig.Type
};
@@ -390,6 +396,7 @@ namespace TechHelper.Client.Exam
{
Priority = pm.PatternConfig.Priority,
Type = pm.PatternConfig.Type,
+ Sequence = sequence + seq,
Score = score,
Question = new QuestionEx
{
diff --git a/TechHelper.Client/HttpRepository/AuthenticationClientService.cs b/TechHelper.Client/HttpRepository/AuthenticationClientService.cs
index 5dc1436..311acdc 100644
--- a/TechHelper.Client/HttpRepository/AuthenticationClientService.cs
+++ b/TechHelper.Client/HttpRepository/AuthenticationClientService.cs
@@ -92,7 +92,6 @@ namespace TechHelper.Client.HttpRepository
public async Task RegisterUserAsync(UserForRegistrationDto userForRegistrationDto)
{
- // 移除 using (_client = _clientFactory.CreateClient("Default"))
userForRegistrationDto.ClientURI = Path.Combine(
_navigationManager.BaseUri, "emailconfirmation");
diff --git a/TechHelper.Client/Pages/Exam/ExamCheck.razor b/TechHelper.Client/Pages/Exam/ExamCheck.razor
index 0314b32..07b8286 100644
--- a/TechHelper.Client/Pages/Exam/ExamCheck.razor
+++ b/TechHelper.Client/Pages/Exam/ExamCheck.razor
@@ -9,216 +9,227 @@
@if (_isLoading)
{
-
- 正在加载试卷和学生数据...
+
+ 正在加载试卷和学生数据...
}
else if (_questionsForTable.Any() && _students.Any())
{
-
-
- 序号
- 分值
- @foreach (var student in _students)
- {
-
- @student.Name
-
- ToggleStudentAllAnswers(student.Id)" />
-
-
- }
-
-
- @context.QuestionItem.Sequence
- @context.QuestionItem.Score
- @foreach (var student in _students)
- {
-
- @if (context.StudentAnswers.ContainsKey(student.Id))
- {
-
- }
- else
- {
- N/A
- }
-
- }
-
-
-
-
-
+
+
+ 序号
+ 分值
+ @foreach (var student in _students)
+ {
+
+ @student.DisplayName
+
+ ToggleStudentAllAnswers(student.Id)" />
+
+
+ }
+
+
+ @context.QuestionItem.Sequence
+ @context.QuestionItem.Score
+ @foreach (var student in _students)
+ {
+
+ @if (context.StudentAnswers.ContainsKey(student.Id))
+ {
+
+ }
+ else
+ {
+ N/A
+ }
+
+ }
+
+
+
+
+
-
- 学生总分预览:
- @foreach (var student in _students)
- {
-
- @student.Name: @GetStudentTotalScore(student.Id)
-
- }
-
- 提交批改结果 (模拟)
-
-
+
+ 学生总分预览:
+ @foreach (var student in _students)
+ {
+
+ @student.DisplayName: @GetStudentTotalScore(student.Id)
+
+ }
+
+ 提交批改结果 (模拟)
+
+
}
else
{
- 无法加载试卷或题目信息。
- 返回试卷列表
+ 无法加载试卷或题目信息。
+ 返回试卷列表
}
@code {
- [Parameter]
- public string ExamId { get; set; }
+ [Parameter]
+ public string ExamId { get; set; }
- [Inject]
- public IExamService ExamService { get; set; }
+ [Inject]
+ public IExamService ExamService { get; set; }
- [Inject]
- private ISnackbar Snackbar { get; set; }
+ [Inject]
+ private ISnackbar Snackbar { get; set; }
- [Inject]
- private NavigationManager Navigation { get; set; }
+ [Inject]
+ private NavigationManager Navigation { get; set; }
- private MudTable _table = new();
- private AssignmentDto Assignment { get; set; } = new AssignmentDto();
- private ExamStruct _examStruct = new ExamStruct();
+ private MudTable _table = new();
+ private AssignmentDto Assignment { get; set; } = new AssignmentDto();
+ private AssignmentCheckData _examStruct = new AssignmentCheckData();
- private List _students = new List();
- private List _questionsForTable = new List();
+ private List _students = new List();
+ private List _questionsForTable = new List();
- private bool _isLoading = true;
+ private bool _isLoading = true;
+
+ [Inject]
+ public IClassServices ClassServices { get; set; }
+
+ protected override async Task OnInitializedAsync()
+ {
+ _isLoading = true;
+ await LoadExamData();
+
+ var result = await ClassServices.GetClassStudents();
+ if (!result.Status) Snackbar.Add($"获取学生失败, {result.Message}", Severity.Error);
+ _students = result.Result as List ?? new List();
+ BuildTable();
+ _isLoading = false;
+ }
+
+ private void BuildTable()
+ {
+ _questionsForTable = _examStruct.Questions.Select(q =>
+ {
+ var rowData = new QuestionRowData
+ {
+ QuestionItem = q,
+ StudentAnswers = new Dictionary()
+ };
+ foreach (var student in _students)
+ {
+ rowData.StudentAnswers[student.Id] = false;
+ }
+ return rowData;
+ }).ToList();
+ }
+
+ private async Task LoadExamData()
+ {
+ if (Guid.TryParse(ExamId, out Guid parsedExamId))
+ {
+ try
+ {
+ var result = await ExamService.GetExam(parsedExamId);
+ if (result.Status)
+ {
+ Assignment = result.Result as AssignmentDto ?? new AssignmentDto();
+ _examStruct = Assignment.GetStruct();
+ }
+ else
+ {
+ Snackbar?.Add($"获取试卷失败: {result.Message}", Severity.Error);
+ Navigation.NavigateTo("/exam/manager");
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.Error.WriteLine($"获取试卷时发生错误: {ex.Message}");
+ Snackbar?.Add($"获取试卷失败: {ex.Message}", Severity.Error);
+ Navigation.NavigateTo("/exam/manager");
+ }
+ }
+ else
+ {
+ Console.Error.WriteLine($"错误:路由参数 ExamId '{ExamId}' 不是一个有效的 GUID 格式。");
+ Snackbar?.Add("无效的试卷ID,无法加载。", Severity.Error);
+ Navigation.NavigateTo("/exam/manager");
+ }
+ }
- protected override async Task OnInitializedAsync()
- {
- _isLoading = true;
- await LoadExamData();
- GenerateTemporaryStudentsAndAnswers();
- _isLoading = false;
- }
+
+ private float GetStudentTotalScore(Guid studentId)
+ {
+ float totalScore = 0;
+ foreach (var row in _questionsForTable)
+ {
+ if (row.StudentAnswers.TryGetValue(studentId, out bool isCorrect) && isCorrect)
+ {
+ totalScore += row.QuestionItem.Score;
+ }
+ }
+ return totalScore;
+ }
+
+ private void ToggleStudentAllAnswers(Guid studentId)
+ {
+ bool allCorrect = _questionsForTable.All(row => row.StudentAnswers.ContainsKey(studentId) && row.StudentAnswers[studentId]);
+
+ foreach (var row in _questionsForTable)
+ {
+ if (row.StudentAnswers.ContainsKey(studentId))
+ {
+ row.StudentAnswers[studentId] = !allCorrect;
+ }
+ }
+ StateHasChanged();
+ }
+
+ private void SubmitGrading()
+ {
+
+ List submissionDto = new List();
- private async Task LoadExamData()
- {
- if (Guid.TryParse(ExamId, out Guid parsedExamId))
- {
- try
- {
- var result = await ExamService.GetExam(parsedExamId);
- if (result.Status)
- {
- Assignment = result.Result as AssignmentDto ?? new AssignmentDto();
- _examStruct = Assignment.GetStruct();
- }
- else
- {
- Snackbar?.Add($"获取试卷失败: {result.Message}", Severity.Error);
- Navigation.NavigateTo("/exam/manager");
- }
- }
- catch (Exception ex)
- {
- Console.Error.WriteLine($"获取试卷时发生错误: {ex.Message}");
- Snackbar?.Add($"获取试卷失败: {ex.Message}", Severity.Error);
- Navigation.NavigateTo("/exam/manager");
- }
- }
- else
- {
- Console.Error.WriteLine($"错误:路由参数 ExamId '{ExamId}' 不是一个有效的 GUID 格式。");
- Snackbar?.Add("无效的试卷ID,无法加载。", Severity.Error);
- Navigation.NavigateTo("/exam/manager");
- }
- }
+ foreach (var student in _students)
+ {
+ var newSubmission = new SubmissionDto();
+ newSubmission.StudentId = student.Id;
+ newSubmission.AssignmentId = Assignment.Id;
+ newSubmission.SubmissionTime = DateTime.Now;
+ newSubmission.Status = Entities.Contracts.SubmissionStatus.Submitted;
- // 生成临时学生和作答数据
- private void GenerateTemporaryStudentsAndAnswers()
- {
- _students = new List();
- // 生成 40 个学生
- for (int i = 1; i <= 40; i++)
- {
- _students.Add(new Student { Name = $"学生{i}" });
- }
- _questionsForTable = _examStruct.Questions.Select(qItem =>
- {
- var rowData = new QuestionRowData
- {
- QuestionItem = qItem,
- StudentAnswers = new Dictionary()
- };
+ foreach (var row in _questionsForTable)
+ {
+ if (row.QuestionItem.AssignmentQuestionDto.StructType == Entities.Contracts.AssignmentStructType.Struct) continue;
+ if (row.StudentAnswers.TryGetValue(student.Id, out bool isCorrect))
+ {
+ newSubmission.SubmissionDetails.Add(new SubmissionDetailDto
+ {
+ IsCorrect = isCorrect,
+ StudentId = student.Id,
+ AssignmentQuestionId = row.QuestionItem.AssignmentQuestionDto.Id,
+ PointsAwarded = isCorrect ? row.QuestionItem.AssignmentQuestionDto.Score : 0
+ });
- // 为每个学生随机生成初始的对错状态
- var random = new Random();
- foreach (var student in _students)
- {
- // 模拟随机对错,50%的概率
- rowData.StudentAnswers[student.Id] = random.Next(0, 2) == 1;
- }
- return rowData;
- }).ToList();
- }
+ newSubmission.OverallGrade += isCorrect ? row.QuestionItem.AssignmentQuestionDto.Score : 0;
+ }
+ }
+ submissionDto.Add(newSubmission);
+ }
- // 当某个学生的某个题目的作答状态改变时触发
- private void OnAnswerChanged(string questionSequence, Guid studentId, bool isCorrect)
- {
- // 可以在这里添加额外的逻辑,例如记录更改
- Console.WriteLine($"题目 {questionSequence}, 学生 {studentId} 的答案变为: {isCorrect}");
- // 由于是 @bind-Checked,数据模型已经自动更新,这里只是日志
- }
+ submissionDto.ForEach(async s =>
+ {
+ Snackbar?.Add($"正在提交: {_students.FirstOrDefault(std => std.Id == s.StudentId)?.DisplayName} 的试卷", Severity.Info);
+ await ExamService.SubmissionAssignment(s);
+ });
- // 计算某个学生的总分
- private float GetStudentTotalScore(Guid studentId)
- {
- float totalScore = 0;
- foreach (var row in _questionsForTable)
- {
- if (row.StudentAnswers.TryGetValue(studentId, out bool isCorrect) && isCorrect)
- {
- totalScore += row.QuestionItem.Score;
- }
- }
- return totalScore;
- }
- // 切换某个学生所有题目的对错状态 (用于快速批改)
- private void ToggleStudentAllAnswers(Guid studentId)
- {
- bool allCorrect = _questionsForTable.All(row => row.StudentAnswers.ContainsKey(studentId) && row.StudentAnswers[studentId]);
- foreach (var row in _questionsForTable)
- {
- if (row.StudentAnswers.ContainsKey(studentId))
- {
- row.StudentAnswers[studentId] = !allCorrect; // 全部取反
- }
- }
- StateHasChanged(); // 手动通知 Blazor 刷新 UI
- }
-
- // 提交批改结果(模拟)
- private void SubmitGrading()
- {
- Console.WriteLine("--- 提交批改结果 ---");
- foreach (var student in _students)
- {
- Console.WriteLine($"学生: {student.Name}, 总分: {GetStudentTotalScore(student.Id)}");
- foreach (var row in _questionsForTable)
- {
- if (row.StudentAnswers.TryGetValue(student.Id, out bool isCorrect))
- {
- Console.WriteLine($" - 题目 {row.QuestionItem.Sequence}: {(isCorrect ? "正确" : "错误")}");
- }
- }
- }
- Snackbar?.Add("批改结果已提交(模拟)", Severity.Success);
- // 实际应用中,这里会将 _questionsForTable 和 _students 的数据发送到后端API
- }
+ Snackbar?.Add("批改结果已提交(模拟)", Severity.Success);
+ }
}
\ No newline at end of file
diff --git a/TechHelper.Client/Pages/Exam/ExamManager.razor b/TechHelper.Client/Pages/Exam/ExamManager.razor
index bcdaad1..9105f17 100644
--- a/TechHelper.Client/Pages/Exam/ExamManager.razor
+++ b/TechHelper.Client/Pages/Exam/ExamManager.razor
@@ -49,7 +49,7 @@ else
{
isloding = true;
Snackbar.Add("正在加载", Severity.Info);
- var result = await ExamService.GetAllExam(authenticationStateTask.Result.User.Identity.Name);
+ var result = await ExamService.GetAllExam();
examDtos = result.Result as List ?? new List();
isloding = false;
Snackbar.Add("加载成功", Severity.Info);
diff --git a/TechHelper.Client/Pages/Exam/ExamPreview.razor b/TechHelper.Client/Pages/Exam/ExamPreview.razor
index 0021fd2..05d2839 100644
--- a/TechHelper.Client/Pages/Exam/ExamPreview.razor
+++ b/TechHelper.Client/Pages/Exam/ExamPreview.razor
@@ -12,7 +12,11 @@
详情
-
+ @if (bteacher)
+ {
+
+ }
+
@@ -21,6 +25,10 @@
@code {
+ [CascadingParameter]
+ private Task authenticationStateTask { get; set; }
+
+ private bool bteacher = false;
[Inject]
public NavigationManager navigationManager { get; set; }
@@ -45,6 +53,12 @@
public string? MaxHeight { get; set; } = "64";
+ protected override Task OnInitializedAsync()
+ {
+ bteacher = authenticationStateTask.Result.User.IsInRole("Teacher");
+ return base.OnInitializedAsync();
+ }
+
private void ExamClick()
{
navigationManager.NavigateTo($"exam/edit/{AssignmentDto.Id}");
diff --git a/TechHelper.Client/Pages/Home.razor b/TechHelper.Client/Pages/Home.razor
index ab98721..8b26af7 100644
--- a/TechHelper.Client/Pages/Home.razor
+++ b/TechHelper.Client/Pages/Home.razor
@@ -1,118 +1,10 @@
@page "/"
@using Microsoft.AspNetCore.Authorization
-
+
- Hello @context.User.Identity.Name
- @foreach (var item in context.User.Claims)
- {
-
- @item.Value
- @item.Issuer
- @item.Subject
- @item.Properties
- @item.ValueType
-
- }
- Welcome to your new app.
-
-
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
-Hello
+@code {
+ [CascadingParameter]
+ private Task authenticationStateTask { get; set; }
+}
\ No newline at end of file
diff --git a/TechHelper.Client/Pages/Teacher/StudentsView.razor b/TechHelper.Client/Pages/Teacher/StudentsView.razor
new file mode 100644
index 0000000..5c89b17
--- /dev/null
+++ b/TechHelper.Client/Pages/Teacher/StudentsView.razor
@@ -0,0 +1,28 @@
+@using Entities.Contracts
+@using Entities.DTO
+@using TechHelper.Client.Services
+StudentsView
+
+
+@foreach(var cs in ClassStudents)
+{
+ @cs.DisplayName
+}
+
+
+@code {
+ [CascadingParameter]
+ private Task authenticationStateTask { get; set; }
+
+ private List ClassStudents { get; set; } = new List();
+
+ [Inject]
+ public IClassServices ClassServices { get; set; }
+
+ protected override async Task OnInitializedAsync()
+ {
+ var result = await ClassServices.GetClassStudents();
+ ClassStudents = result.Result as List ?? new List();
+ StateHasChanged();
+ }
+}
diff --git a/TechHelper.Client/Services/ClasssServices.cs b/TechHelper.Client/Services/ClasssServices.cs
index 5a93d29..89bb362 100644
--- a/TechHelper.Client/Services/ClasssServices.cs
+++ b/TechHelper.Client/Services/ClasssServices.cs
@@ -1,6 +1,9 @@
-using Entities.DTO;
+using Entities.Contracts;
+using Entities.DTO;
+using Newtonsoft.Json;
using System.Net.Http.Json;
using TechHelper.Client.HttpRepository;
+using TechHelper.Services;
namespace TechHelper.Client.Services
{
@@ -20,6 +23,21 @@ namespace TechHelper.Client.Services
throw new NotImplementedException();
}
+ public async Task GetClassStudents()
+ {
+ try
+ {
+ var result = await _client.PostAsJsonAsync("class/getClassStudents","");
+ var content = await result.Content.ReadAsStringAsync();
+ 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 4c0812b..ce06297 100644
--- a/TechHelper.Client/Services/ExamService.cs
+++ b/TechHelper.Client/Services/ExamService.cs
@@ -9,13 +9,13 @@ namespace TechHelper.Client.Services
{
public class ExamService : IExamService
{
- private readonly IAIService _aIService;
- private readonly HttpClient _client;
+ private readonly IAIService _aIService;
+ private readonly HttpClient _client;
- public ExamService(IAIService aIService, HttpClient client)
+ public ExamService(IAIService aIService, HttpClient client)
{
_aIService = aIService;
- _client = client;
+ _client = client;
}
public ApiResponse ConvertToXML(string xmlContent)
@@ -86,9 +86,9 @@ namespace TechHelper.Client.Services
}
}
- public async Task GetAllExam(string user)
+ public async Task GetAllExam()
{
- var response = await _client.GetAsync($"exam/getAllPreview?user={user}");
+ var response = await _client.GetAsync($"exam/getAllPreview");
if (response.IsSuccessStatusCode)
{
@@ -104,6 +104,25 @@ namespace TechHelper.Client.Services
}
}
+ public async Task GetAllSubmission()
+ {
+ try
+ {
+ var response = await _client.GetAsync($"exam/getAllSubmission");
+ if (response.IsSuccessStatusCode)
+ {
+ var content = await response.Content.ReadAsStringAsync();
+ var exam = JsonConvert.DeserializeObject(content);
+ return ApiResponse.Success();
+ }
+ return ApiResponse.Error(message: "获取失败");
+ }
+ catch (Exception ex)
+ {
+ return ApiResponse.Error(message: $"内部错误{ex.Message}, InerEx{ex.InnerException}");
+ }
+ }
+
public async Task GetExam(Guid guid)
{
@@ -158,5 +177,18 @@ namespace TechHelper.Client.Services
return ApiResponse.Error(message: $"保存试题失败: {response.StatusCode} - {errorContent}");
}
}
+
+ public async Task SubmissionAssignment(SubmissionDto submission)
+ {
+ var response = await _client.PostAsJsonAsync("exam/submission", submission);
+ if (response.IsSuccessStatusCode)
+ {
+ return ApiResponse.Success("提交成功");
+ }
+ else
+ {
+ return ApiResponse.Error("提交失败");
+ }
+ }
}
}
\ No newline at end of file
diff --git a/TechHelper.Client/Services/IClassServices.cs b/TechHelper.Client/Services/IClassServices.cs
index 84a081b..3123b40 100644
--- a/TechHelper.Client/Services/IClassServices.cs
+++ b/TechHelper.Client/Services/IClassServices.cs
@@ -8,5 +8,6 @@ namespace TechHelper.Client.Services
{
public Task UserRegister(UserRegistrationToClassDto userRegistrationToClassDto);
public Task CreateClass(UserRegistrationToClassDto userClass);
+ public Task GetClassStudents();
}
}
diff --git a/TechHelper.Client/Services/IExamService.cs b/TechHelper.Client/Services/IExamService.cs
index 7b4364c..f04c733 100644
--- a/TechHelper.Client/Services/IExamService.cs
+++ b/TechHelper.Client/Services/IExamService.cs
@@ -11,7 +11,9 @@ namespace TechHelper.Client.Services
public Task ParseSingleQuestionGroup(string examContent);
public ApiResponse ConvertToXML(string xmlContent);
- public Task GetAllExam(string user);
+ public Task GetAllExam();
public Task GetExam(Guid guid);
+ public Task SubmissionAssignment(SubmissionDto submission);
+ public Task GetAllSubmission();
}
}
diff --git a/TechHelper.Server/Context/AutoMapperProFile.cs b/TechHelper.Server/Context/AutoMapperProFile.cs
index 60c799c..4d31504 100644
--- a/TechHelper.Server/Context/AutoMapperProFile.cs
+++ b/TechHelper.Server/Context/AutoMapperProFile.cs
@@ -36,20 +36,22 @@ namespace TechHelper.Context
+ // Assignment
+ CreateMap().ReverseMap();
- CreateMap();
+ CreateMap().ReverseMap();
+
+ CreateMap().ReverseMap();
CreateMap().ReverseMap();
- CreateMap();
- CreateMap();
- CreateMap();
- CreateMap();
+ // Submission
+ CreateMap().ReverseMap();
- CreateMap();
+ CreateMap().ReverseMap();
}
}
diff --git a/TechHelper.Server/Context/Configuration/AssignmentConfiguration.cs b/TechHelper.Server/Context/Configuration/AssignmentConfiguration.cs
index 9d159c9..4192749 100644
--- a/TechHelper.Server/Context/Configuration/AssignmentConfiguration.cs
+++ b/TechHelper.Server/Context/Configuration/AssignmentConfiguration.cs
@@ -57,26 +57,13 @@ namespace TechHelper.Context.Configuration
.HasForeignKey(a => a.CreatorId)
.IsRequired(); // CreatedBy 是必填的,对应 [Required] 在外键属性上
- // 关系: Assignment (一) 到 AssignmentClass (多)
- // 假设 AssignmentClass 实体包含一个名为 AssignmentId 的外键属性
- builder.HasMany(a => a.AssignmentClasses)
- .WithOne(ac => ac.Assignment) // AssignmentClass 没有指向 Assignment 的导航属性 (或我们不知道)
- .HasForeignKey("AssignmentId") // 指定外键名称为 AssignmentId
- .OnDelete(DeleteBehavior.Cascade); // 如果 Assignment 被删除,关联的 AssignmentClass 也会被删除
- // 关系: Assignment (一) 到 AssignmentAttachment (多)
- // 假设 AssignmentAttachment 实体包含一个名为 AssignmentId 的外键属性
- builder.HasMany(a => a.AssignmentAttachments)
- .WithOne(aa => aa.Assignment)
- .HasForeignKey("AssignmentId")
- .OnDelete(DeleteBehavior.Cascade);
- // 关系: Assignment (一) 到 Submission (多)
- // 假设 Submission 实体包含一个名为 AssignmentId 的外键属性
- builder.HasMany(a => a.Submissions)
- .WithOne(s => s.Assignment)
- .HasForeignKey("AssignmentId")
- .OnDelete(DeleteBehavior.Cascade);
+ builder.HasOne(a=>a.ExamStruct)
+ .WithOne()
+ .HasForeignKey(a=>a.ExamStructId)
+ .OnDelete(DeleteBehavior.Cascade);
+
}
}
}
diff --git a/TechHelper.Server/Context/Configuration/AssignmentQuestionConfiguration.cs b/TechHelper.Server/Context/Configuration/AssignmentQuestionConfiguration.cs
index e999dc1..0d435f2 100644
--- a/TechHelper.Server/Context/Configuration/AssignmentQuestionConfiguration.cs
+++ b/TechHelper.Server/Context/Configuration/AssignmentQuestionConfiguration.cs
@@ -42,35 +42,24 @@ namespace TechHelper.Context.Configuration
.HasColumnName("deleted")
.HasDefaultValue(false); // 适用于软删除策略
- // 4. 配置导航属性和外键关系
- // ---
- // 配置 AssignmentQuestion 到 Question 的关系 (多对一)
- // 一个 AssignmentQuestion 属于一个 Question。
- //
- // 假设 `Question` 实体中有一个名为 `AssignmentQuestions` 的 `ICollection` 集合属性。
- builder.HasOne(aq => aq.Question) // 当前 AssignmentQuestion 有一个 Question
- .WithMany(q => q.AssignmentQuestions) // 那个 Question 可以有多个 AssignmentQuestion
- .HasForeignKey(aq => aq.QuestionId) // 外键是 AssignmentQuestion.QuestionId
- .OnDelete(DeleteBehavior.Cascade); // 当 Question 被删除时,相关的 AssignmentQuestion 也级联删除。
+ builder.HasOne(aq => aq.Question)
+ .WithMany(q => q.AssignmentQuestions)
+ .HasForeignKey(aq => aq.QuestionId)
+ .OnDelete(DeleteBehavior.Cascade);
+ builder.HasOne(aq => aq.ParentAssignmentQuestion)
+ .WithMany(aq => aq.ChildrenAssignmentQuestion)
+ .HasForeignKey(aq => aq.ParentAssignmentQuestionId)
+ .OnDelete(DeleteBehavior.Cascade);
builder.HasOne(aq => aq.QuestionContext)
.WithMany(qc => qc.Questions)
.HasForeignKey(aq => aq.QuestionContextId)
.OnDelete(DeleteBehavior.SetNull);
- // ---
- // 配置 AssignmentQuestion 到 SubmissionDetail 的关系 (一对多)
- // 一个 AssignmentQuestion 可以有多个 SubmissionDetail。
- //
- // 这个关系通常从 "多" 的一方(`SubmissionDetail` 实体)来配置外键。
- // 假设 `SubmissionDetail` 实体有一个 `AssignmentQuestionId` 外键和 `AssignmentQuestion` 导航属性。
- builder.HasMany(aq => aq.SubmissionDetails) // 当前 AssignmentQuestion 有多个 SubmissionDetail
- .WithOne(sd => sd.AssignmentQuestion); // 每一个 SubmissionDetail 都有一个 AssignmentQuestion
- // .HasForeignKey(sd => sd.AssignmentQuestionId); // 外键的配置应在 `SubmissionDetailConfiguration` 中进行
- }
- }
-
+ }
+
+ }
}
diff --git a/TechHelper.Server/Context/Configuration/SubmissionConfiguration.cs b/TechHelper.Server/Context/Configuration/SubmissionConfiguration.cs
index 2db8c42..800fd1a 100644
--- a/TechHelper.Server/Context/Configuration/SubmissionConfiguration.cs
+++ b/TechHelper.Server/Context/Configuration/SubmissionConfiguration.cs
@@ -75,16 +75,13 @@ namespace TechHelper.Context.Configuration
builder.HasOne(s => s.Student) // 当前 Submission 有一个 Student (User)
.WithMany(u => u.SubmissionsAsStudent) // 那个 User (Student) 可以有多个 Submission
.HasForeignKey(s => s.StudentId) // 外键是 Submission.StudentId
- .OnDelete(DeleteBehavior.Restrict); // 当 User (Student) 被删除时,如果还有其提交的 Submission,则会阻止删除。
+ .OnDelete(DeleteBehavior.Cascade); // 当 User (Student) 被删除时,如果还有其提交的 Submission,则会阻止删除。
builder.HasOne(s => s.Grader) // 当前 Submission 有一个 Grader (User),可以是空的
.WithMany(u => u.GradedSubmissions) // 那个 User (Grader) 可以批改多个 Submission
.HasForeignKey(s => s.GraderId) // 外键是 Submission.GradedBy
.OnDelete(DeleteBehavior.SetNull); // 当 User (Grader) 被删除时,如果 GradedBy 是可空的,则将其设置为 NULL。
-
- builder.HasMany(s => s.SubmissionDetails) // 当前 Submission 有多个 SubmissionDetail
- .WithOne(sd => sd.Submission); // 每一个 SubmissionDetail 都有一个 Submission
}
}
}
diff --git a/TechHelper.Server/Context/Configuration/SubmissionDetailConfiguration.cs b/TechHelper.Server/Context/Configuration/SubmissionDetailConfiguration.cs
index ffcf3b6..3d495fb 100644
--- a/TechHelper.Server/Context/Configuration/SubmissionDetailConfiguration.cs
+++ b/TechHelper.Server/Context/Configuration/SubmissionDetailConfiguration.cs
@@ -64,7 +64,7 @@ namespace TechHelper.Context.Configuration
builder.HasOne(sd => sd.Student) // 当前 SubmissionDetail 有一个 User (作为学生)
.WithMany(u => u.SubmissionDetails)
.HasForeignKey(sd => sd.StudentId) // 外键是 SubmissionDetail.StudentId
- .OnDelete(DeleteBehavior.Restrict); // 当 User (学生) 被删除时,如果他/她还有提交详情,则会阻止删除。
+ .OnDelete(DeleteBehavior.Cascade); // 当 User (学生) 被删除时,如果他/她还有提交详情,则会阻止删除。
// 这是一个更安全的选择,以防止意外数据丢失。
builder.HasOne(sd => sd.AssignmentQuestion) // 当前 SubmissionDetail 有一个 AssignmentQuestion
diff --git a/TechHelper.Server/Controllers/ClassController.cs b/TechHelper.Server/Controllers/ClassController.cs
index f157b83..424b8e2 100644
--- a/TechHelper.Server/Controllers/ClassController.cs
+++ b/TechHelper.Server/Controllers/ClassController.cs
@@ -1,7 +1,10 @@
-using Entities.DTO;
+using Entities.Contracts;
+using Entities.DTO;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Net;
+using System.Security.Claims;
using TechHelper.Services;
namespace TechHelper.Server.Controllers
@@ -11,25 +14,76 @@ namespace TechHelper.Server.Controllers
public class ClassController : ControllerBase
{
private IClassService _classService;
-
- public ClassController(IClassService classService)
+ private UserManager _userManager;
+ public ClassController(IClassService classService, UserManager userManager)
{
_classService = classService;
+ _userManager = userManager;
}
[HttpPost("userRegiste")]
public async Task UserRegisterToClass(
[FromBody] UserRegistrationToClassDto toClass)
{
-
+
var result = await _classService.UserRegister(toClass);
- if(!result.Status) return BadRequest(result.Message);
+ if (!result.Status) return BadRequest(result.Message);
- return Ok();
+ return Ok();
}
+
+
+ [HttpPost("getClassStudents")]
+ public async Task GetClassStudents()
+ {
+ if (User.IsInRole("Teacher"))
+ {
+ var gradeClaim = User.FindFirst("Grade")?.Value;
+ var classClaim = User.FindFirst("Class")?.Value;
+
+ if (string.IsNullOrEmpty(gradeClaim) || string.IsNullOrEmpty(classClaim))
+ {
+ return BadRequest("未识别到你加入的班级信息(年级或班级声明缺失)。");
+ }
+
+ if (!byte.TryParse(gradeClaim, out byte grade) || !byte.TryParse(classClaim, out byte cla))
+ {
+ return BadRequest("你班级或年级信息格式不正确。");
+ }
+
+ var classDto = new ClassDto
+ {
+ Grade = grade,
+ Class = cla
+ };
+
+ var result = await _classService.GetClassStudents(classDto);
+ var css = result.Result as ICollection;
+ if(css == null) return BadRequest("你还没有学生");
+
+
+ List sts = new List();
+ css?.ToList().ForEach(s => sts.Add(new StudentDto
+ {
+ DisplayName = s.Student.DisplayName,
+ Id = s.Student.Id,
+ }));
+
+ if (!result.Status)
+ {
+ return BadRequest(result.Message);
+ }
+ return Ok(sts);
+ }
+ else
+ {
+ return BadRequest("你没有权限查看,未识别到你的教师身份。");
+ }
+ }
+
[HttpPost("Create")]
public async Task Create(
[FromBody] ClassDto classDto)
diff --git a/TechHelper.Server/Controllers/ExamController.cs b/TechHelper.Server/Controllers/ExamController.cs
index 3676c2b..1ce660c 100644
--- a/TechHelper.Server/Controllers/ExamController.cs
+++ b/TechHelper.Server/Controllers/ExamController.cs
@@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using TechHelper.Server.Services;
using System.Security.Claims;
+using TechHelper.Services;
namespace TechHelper.Server.Controllers
@@ -31,7 +32,7 @@ namespace TechHelper.Server.Controllers
[FromBody] AssignmentDto examDto)
{
var user = await _userManager.FindByEmailAsync(User.Identity?.Name ?? "");
- if(user == null) return BadRequest("无效的用户");
+ if (user == null) return BadRequest("无效的用户");
examDto.CreatorId = user.Id;
var result = await _examService.CreateExamAsync(examDto);
@@ -45,6 +46,29 @@ namespace TechHelper.Server.Controllers
}
}
+ [HttpPost("submission")]
+ public async Task SubmissionAssignment(
+ [FromBody] SubmissionDto submissionDto)
+ {
+ if (User == null) return BadRequest("无效的用户");
+ if (User.IsInRole("Teacher"))
+ {
+ var result = await _examService.SubmissionAssignment(submissionDto);
+ if (result.Status)
+ {
+ return Ok(result);
+ }
+ else
+ {
+ return BadRequest(result.Message);
+ }
+ }
+ else
+ {
+ return BadRequest("你没有权限修改");
+ }
+ }
+
[HttpGet("get")]
public async Task GetExamById(Guid id)
{
@@ -58,16 +82,26 @@ namespace TechHelper.Server.Controllers
[HttpGet("getAllPreview")]
- public async Task GetAllExamPreview(string user)
+ public async Task GetAllExamPreview()
{
- string? userId = User.Identity.Name;
+ if (User == null) return BadRequest("用户验证失败, 无效用户");
- var userid = await _userManager.FindByEmailAsync(user);
- if (userid == null) return BadRequest("用户验证失败, 无效用户");
+ var userid = await _userManager.FindByEmailAsync(User.Identity.Name);
-
- var result = await _examService.GetAllExamPreviewsAsync(userid.Id);
+ var result = new ApiResponse();
+ if (User.IsInRole("Teacher"))
+ {
+ result = await _examService.GetAllExamPreviewsAsync(userid.Id);
+ }
+ else if (User.IsInRole("Student"))
+ {
+ result = await _examService.GetAllSubmissionAsync(userid.Id);
+ }
+ else
+ {
+ return BadRequest("你没有相应的权限");
+ }
if (result.Status)
{
@@ -76,5 +110,34 @@ namespace TechHelper.Server.Controllers
return BadRequest(result);
}
+
+ [HttpGet("getAllSubmission")]
+ public async Task GetAllSubmission()
+ {
+ if (User == null) return BadRequest("用户验证失败, 无效用户");
+
+ var userid = await _userManager.FindByEmailAsync(User.Identity.Name);
+
+ var result = await _examService.GetAllSubmissionAsync(userid.Id);
+
+ if (result.Status)
+ {
+ return Ok(result.Result);
+ }
+ return BadRequest(result);
+ }
+
+
+ [Authorize(Roles = "Teacher")]
+ [HttpDelete("{guid}")]
+ public async Task DeleteAsync(Guid guid)
+ {
+ var deleteResult = await _examService.DeleteAsync(guid);
+ if (deleteResult.Status)
+ {
+ return Ok();
+ }
+ return BadRequest();
+ }
}
}
diff --git a/TechHelper.Server/Controllers/UserController.cs b/TechHelper.Server/Controllers/UserController.cs
new file mode 100644
index 0000000..4121dde
--- /dev/null
+++ b/TechHelper.Server/Controllers/UserController.cs
@@ -0,0 +1,30 @@
+using Entities.Contracts;
+using Entities.DTO;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Identity;
+using Microsoft.AspNetCore.Mvc;
+using TechHelper.Services;
+
+namespace TechHelper.Server.Controllers
+{
+ [Route("api/user")]
+ [ApiController]
+ public class UserController : ControllerBase
+ {
+ private IClassService _classService;
+ private UserManager _userManager;
+ public UserController(IClassService classService, UserManager userManager)
+ {
+ _classService = classService;
+ _userManager = userManager;
+ }
+
+
+ [HttpPost("get")]
+ public async Task GetAsync(
+ [FromBody] UserRegistrationToClassDto toClass)
+ {
+ return Ok();
+ }
+ }
+}
diff --git a/TechHelper.Server/Migrations/20250625032845_up.cs b/TechHelper.Server/Migrations/20250625032845_up.cs
deleted file mode 100644
index 5f073a2..0000000
--- a/TechHelper.Server/Migrations/20250625032845_up.cs
+++ /dev/null
@@ -1,95 +0,0 @@
-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 : Migration
- {
- ///
- protected override void Up(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_class_teachers_AspNetUsers_teacher_id",
- table: "class_teachers");
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("8b814a50-fd96-4bfa-a666-b247c0f13e22"));
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("ab2c8f8c-1ade-4ff5-9eb4-2925a89567b1"));
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("c6f92bbf-190f-47a5-b4d6-ed6874a378e8"));
-
- migrationBuilder.InsertData(
- table: "AspNetRoles",
- columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
- values: new object[,]
- {
- { new Guid("195b19c5-fd30-455c-9f38-9842b44bf5c3"), null, "Teacher", "TEACHER" },
- { new Guid("53cc63db-74bc-47a8-b71a-7e120d4018a9"), null, "Administrator", "ADMINISTRATOR" },
- { new Guid("a203eb76-97f0-418f-bc06-9549297d2ac3"), null, "Student", "STUDENT" }
- });
-
- migrationBuilder.AddForeignKey(
- name: "FK_class_teachers_AspNetUsers_teacher_id",
- table: "class_teachers",
- column: "teacher_id",
- principalTable: "AspNetUsers",
- principalColumn: "Id",
- onDelete: ReferentialAction.Cascade);
- }
-
- ///
- protected override void Down(MigrationBuilder migrationBuilder)
- {
- migrationBuilder.DropForeignKey(
- name: "FK_class_teachers_AspNetUsers_teacher_id",
- table: "class_teachers");
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("195b19c5-fd30-455c-9f38-9842b44bf5c3"));
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("53cc63db-74bc-47a8-b71a-7e120d4018a9"));
-
- migrationBuilder.DeleteData(
- table: "AspNetRoles",
- keyColumn: "Id",
- keyValue: new Guid("a203eb76-97f0-418f-bc06-9549297d2ac3"));
-
- migrationBuilder.InsertData(
- table: "AspNetRoles",
- columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
- values: new object[,]
- {
- { new Guid("8b814a50-fd96-4bfa-a666-b247c0f13e22"), null, "Administrator", "ADMINISTRATOR" },
- { new Guid("ab2c8f8c-1ade-4ff5-9eb4-2925a89567b1"), null, "Student", "STUDENT" },
- { new Guid("c6f92bbf-190f-47a5-b4d6-ed6874a378e8"), null, "Teacher", "TEACHER" }
- });
-
- migrationBuilder.AddForeignKey(
- name: "FK_class_teachers_AspNetUsers_teacher_id",
- table: "class_teachers",
- column: "teacher_id",
- principalTable: "AspNetUsers",
- principalColumn: "Id",
- onDelete: ReferentialAction.Restrict);
- }
- }
-}
diff --git a/TechHelper.Server/Migrations/20250625032845_up.Designer.cs b/TechHelper.Server/Migrations/20250626073834_init.Designer.cs
similarity index 99%
rename from TechHelper.Server/Migrations/20250625032845_up.Designer.cs
rename to TechHelper.Server/Migrations/20250626073834_init.Designer.cs
index 48552ae..b0f9267 100644
--- a/TechHelper.Server/Migrations/20250625032845_up.Designer.cs
+++ b/TechHelper.Server/Migrations/20250626073834_init.Designer.cs
@@ -12,8 +12,8 @@ using TechHelper.Context;
namespace TechHelper.Server.Migrations
{
[DbContext(typeof(ApplicationContext))]
- [Migration("20250625032845_up")]
- partial class up
+ [Migration("20250626073834_init")]
+ partial class init
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -512,7 +512,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("deleted");
b.Property("OverallFeedback")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("overall_feedback");
@@ -579,7 +578,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("points_awarded");
b.Property("StudentAnswer")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("student_answer");
@@ -592,7 +590,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("submission_id");
b.Property("TeacherFeedback")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("teacher_feedback");
@@ -747,19 +744,19 @@ namespace TechHelper.Server.Migrations
b.HasData(
new
{
- Id = new Guid("a203eb76-97f0-418f-bc06-9549297d2ac3"),
+ Id = new Guid("3cfe35e8-73d5-4170-9856-f1d078554822"),
Name = "Student",
NormalizedName = "STUDENT"
},
new
{
- Id = new Guid("195b19c5-fd30-455c-9f38-9842b44bf5c3"),
+ Id = new Guid("754c4967-6af2-4a81-b970-1e90a3a269b3"),
Name = "Teacher",
NormalizedName = "TEACHER"
},
new
{
- Id = new Guid("53cc63db-74bc-47a8-b71a-7e120d4018a9"),
+ Id = new Guid("8546457c-185c-4b79-bece-bc21e41d02e7"),
Name = "Administrator",
NormalizedName = "ADMINISTRATOR"
});
diff --git a/TechHelper.Server/Migrations/20250624103910_init.cs b/TechHelper.Server/Migrations/20250626073834_init.cs
similarity index 98%
rename from TechHelper.Server/Migrations/20250624103910_init.cs
rename to TechHelper.Server/Migrations/20250626073834_init.cs
index e7f0219..eebadc0 100644
--- a/TechHelper.Server/Migrations/20250624103910_init.cs
+++ b/TechHelper.Server/Migrations/20250626073834_init.cs
@@ -323,7 +323,7 @@ namespace TechHelper.Server.Migrations
column: x => x.teacher_id,
principalTable: "AspNetUsers",
principalColumn: "Id",
- onDelete: ReferentialAction.Restrict);
+ onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_class_teachers_classes_class_id",
column: x => x.class_id,
@@ -565,7 +565,7 @@ namespace TechHelper.Server.Migrations
attempt_number = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
submission_time = table.Column(type: "datetime(6)", nullable: false),
overall_grade = table.Column(type: "float", precision: 5, scale: 2, nullable: true),
- overall_feedback = table.Column(type: "longtext", nullable: false)
+ overall_feedback = table.Column(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
graded_by = table.Column(type: "char(36)", nullable: true, collation: "ascii_general_ci"),
graded_at = table.Column(type: "datetime(6)", nullable: true),
@@ -604,11 +604,11 @@ namespace TechHelper.Server.Migrations
submission_id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
student_id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
assignment_question_id = table.Column(type: "char(36)", nullable: false, collation: "ascii_general_ci"),
- student_answer = table.Column(type: "longtext", nullable: false)
+ student_answer = table.Column(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
is_correct = table.Column(type: "tinyint(1)", nullable: true),
points_awarded = table.Column(type: "float", precision: 5, scale: 2, nullable: true),
- teacher_feedback = table.Column(type: "longtext", nullable: false)
+ teacher_feedback = table.Column(type: "longtext", nullable: true)
.Annotation("MySql:CharSet", "utf8mb4"),
created_at = table.Column(type: "datetime(6)", nullable: false)
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
@@ -644,9 +644,9 @@ namespace TechHelper.Server.Migrations
columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
values: new object[,]
{
- { new Guid("8b814a50-fd96-4bfa-a666-b247c0f13e22"), null, "Administrator", "ADMINISTRATOR" },
- { new Guid("ab2c8f8c-1ade-4ff5-9eb4-2925a89567b1"), null, "Student", "STUDENT" },
- { new Guid("c6f92bbf-190f-47a5-b4d6-ed6874a378e8"), null, "Teacher", "TEACHER" }
+ { new Guid("3cfe35e8-73d5-4170-9856-f1d078554822"), null, "Student", "STUDENT" },
+ { new Guid("754c4967-6af2-4a81-b970-1e90a3a269b3"), null, "Teacher", "TEACHER" },
+ { new Guid("8546457c-185c-4b79-bece-bc21e41d02e7"), null, "Administrator", "ADMINISTRATOR" }
});
migrationBuilder.CreateIndex(
diff --git a/TechHelper.Server/Migrations/20250624103910_init.Designer.cs b/TechHelper.Server/Migrations/20250627101025_upd.Designer.cs
similarity index 98%
rename from TechHelper.Server/Migrations/20250624103910_init.Designer.cs
rename to TechHelper.Server/Migrations/20250627101025_upd.Designer.cs
index 658ff55..5cab7e3 100644
--- a/TechHelper.Server/Migrations/20250624103910_init.Designer.cs
+++ b/TechHelper.Server/Migrations/20250627101025_upd.Designer.cs
@@ -12,8 +12,8 @@ using TechHelper.Context;
namespace TechHelper.Server.Migrations
{
[DbContext(typeof(ApplicationContext))]
- [Migration("20250624103910_init")]
- partial class init
+ [Migration("20250627101025_upd")]
+ partial class upd
{
///
protected override void BuildTargetModel(ModelBuilder modelBuilder)
@@ -198,6 +198,11 @@ namespace TechHelper.Server.Migrations
.HasColumnType("float")
.HasColumnName("score");
+ b.Property("Sequence")
+ .IsRequired()
+ .HasColumnType("longtext")
+ .HasColumnName("sequence");
+
b.Property("StructType")
.HasColumnType("tinyint unsigned")
.HasColumnName("group_state");
@@ -512,7 +517,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("deleted");
b.Property("OverallFeedback")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("overall_feedback");
@@ -579,7 +583,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("points_awarded");
b.Property("StudentAnswer")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("student_answer");
@@ -592,7 +595,6 @@ namespace TechHelper.Server.Migrations
.HasColumnName("submission_id");
b.Property("TeacherFeedback")
- .IsRequired()
.HasColumnType("longtext")
.HasColumnName("teacher_feedback");
@@ -747,19 +749,19 @@ namespace TechHelper.Server.Migrations
b.HasData(
new
{
- Id = new Guid("ab2c8f8c-1ade-4ff5-9eb4-2925a89567b1"),
+ Id = new Guid("c310acf7-9605-4c55-8b9f-9bf9cd2dadb9"),
Name = "Student",
NormalizedName = "STUDENT"
},
new
{
- Id = new Guid("c6f92bbf-190f-47a5-b4d6-ed6874a378e8"),
+ Id = new Guid("5f0c1b3c-ad05-4ca9-b9fd-a359cb518236"),
Name = "Teacher",
NormalizedName = "TEACHER"
},
new
{
- Id = new Guid("8b814a50-fd96-4bfa-a666-b247c0f13e22"),
+ Id = new Guid("a81f5de2-9691-45fa-8d31-ae4ffeb34453"),
Name = "Administrator",
NormalizedName = "ADMINISTRATOR"
});
@@ -925,7 +927,8 @@ namespace TechHelper.Server.Migrations
{
b.HasOne("Entities.Contracts.AssignmentQuestion", "ParentAssignmentQuestion")
.WithMany("ChildrenAssignmentQuestion")
- .HasForeignKey("ParentAssignmentQuestionId");
+ .HasForeignKey("ParentAssignmentQuestionId")
+ .OnDelete(DeleteBehavior.Cascade);
b.HasOne("Entities.Contracts.QuestionContext", "QuestionContext")
.WithMany("Questions")
@@ -985,7 +988,7 @@ namespace TechHelper.Server.Migrations
b.HasOne("Entities.Contracts.User", "Teacher")
.WithMany("TaughtClassesLink")
.HasForeignKey("TeacherId")
- .OnDelete(DeleteBehavior.Restrict)
+ .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Class");
@@ -1067,7 +1070,7 @@ namespace TechHelper.Server.Migrations
b.HasOne("Entities.Contracts.User", "Student")
.WithMany("SubmissionsAsStudent")
.HasForeignKey("StudentId")
- .OnDelete(DeleteBehavior.Restrict)
+ .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Assignment");
@@ -1088,7 +1091,7 @@ namespace TechHelper.Server.Migrations
b.HasOne("Entities.Contracts.User", "Student")
.WithMany("SubmissionDetails")
.HasForeignKey("StudentId")
- .OnDelete(DeleteBehavior.Restrict)
+ .OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Entities.Contracts.Submission", "Submission")
diff --git a/TechHelper.Server/Migrations/20250627101025_upd.cs b/TechHelper.Server/Migrations/20250627101025_upd.cs
new file mode 100644
index 0000000..849e9ec
--- /dev/null
+++ b/TechHelper.Server/Migrations/20250627101025_upd.cs
@@ -0,0 +1,153 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
+
+namespace TechHelper.Server.Migrations
+{
+ ///
+ public partial class upd : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_assignment_questions_assignment_questions_parent_question_gr~",
+ table: "assignment_questions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_submission_details_AspNetUsers_student_id",
+ table: "submission_details");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_submissions_AspNetUsers_student_id",
+ table: "submissions");
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("3cfe35e8-73d5-4170-9856-f1d078554822"));
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("754c4967-6af2-4a81-b970-1e90a3a269b3"));
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("8546457c-185c-4b79-bece-bc21e41d02e7"));
+
+ migrationBuilder.AddColumn(
+ name: "sequence",
+ table: "assignment_questions",
+ type: "longtext",
+ nullable: false)
+ .Annotation("MySql:CharSet", "utf8mb4");
+
+ migrationBuilder.InsertData(
+ table: "AspNetRoles",
+ columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
+ values: new object[,]
+ {
+ { new Guid("5f0c1b3c-ad05-4ca9-b9fd-a359cb518236"), null, "Teacher", "TEACHER" },
+ { new Guid("a81f5de2-9691-45fa-8d31-ae4ffeb34453"), null, "Administrator", "ADMINISTRATOR" },
+ { new Guid("c310acf7-9605-4c55-8b9f-9bf9cd2dadb9"), null, "Student", "STUDENT" }
+ });
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_assignment_questions_assignment_questions_parent_question_gr~",
+ table: "assignment_questions",
+ column: "parent_question_group_id",
+ principalTable: "assignment_questions",
+ principalColumn: "id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_submission_details_AspNetUsers_student_id",
+ table: "submission_details",
+ column: "student_id",
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_submissions_AspNetUsers_student_id",
+ table: "submissions",
+ column: "student_id",
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropForeignKey(
+ name: "FK_assignment_questions_assignment_questions_parent_question_gr~",
+ table: "assignment_questions");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_submission_details_AspNetUsers_student_id",
+ table: "submission_details");
+
+ migrationBuilder.DropForeignKey(
+ name: "FK_submissions_AspNetUsers_student_id",
+ table: "submissions");
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("5f0c1b3c-ad05-4ca9-b9fd-a359cb518236"));
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("a81f5de2-9691-45fa-8d31-ae4ffeb34453"));
+
+ migrationBuilder.DeleteData(
+ table: "AspNetRoles",
+ keyColumn: "Id",
+ keyValue: new Guid("c310acf7-9605-4c55-8b9f-9bf9cd2dadb9"));
+
+ migrationBuilder.DropColumn(
+ name: "sequence",
+ table: "assignment_questions");
+
+ migrationBuilder.InsertData(
+ table: "AspNetRoles",
+ columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
+ values: new object[,]
+ {
+ { new Guid("3cfe35e8-73d5-4170-9856-f1d078554822"), null, "Student", "STUDENT" },
+ { new Guid("754c4967-6af2-4a81-b970-1e90a3a269b3"), null, "Teacher", "TEACHER" },
+ { new Guid("8546457c-185c-4b79-bece-bc21e41d02e7"), null, "Administrator", "ADMINISTRATOR" }
+ });
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_assignment_questions_assignment_questions_parent_question_gr~",
+ table: "assignment_questions",
+ column: "parent_question_group_id",
+ principalTable: "assignment_questions",
+ principalColumn: "id");
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_submission_details_AspNetUsers_student_id",
+ table: "submission_details",
+ column: "student_id",
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+
+ migrationBuilder.AddForeignKey(
+ name: "FK_submissions_AspNetUsers_student_id",
+ table: "submissions",
+ column: "student_id",
+ principalTable: "AspNetUsers",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Restrict);
+ }
+ }
+}
diff --git a/TechHelper.Server/Migrations/20250627101514_upde.Designer.cs b/TechHelper.Server/Migrations/20250627101514_upde.Designer.cs
new file mode 100644
index 0000000..b6b8e47
--- /dev/null
+++ b/TechHelper.Server/Migrations/20250627101514_upde.Designer.cs
@@ -0,0 +1,1241 @@
+//
+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("20250627101514_upde")]
+ partial class upde
+ {
+ ///
+ 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("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("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("char(36)")
+ .HasColumnName("attempt_number");
+
+ 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.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("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