using Entities.DTO; using System.Text.Json.Serialization; using System.Text.Json; namespace TechHelper.Client.Exam { public static class ExamPaperExtensions { public static ExamDto ConvertToExamDTO(this ExamPaper examPaper) { ExamDto dto = new ExamDto(); dto.AssignmentTitle = examPaper.AssignmentTitle; dto.Description = examPaper.Description; dto.SubjectArea = examPaper.SubjectArea; dto.QuestionGroups.Title = examPaper.AssignmentTitle; dto.QuestionGroups.Descript = examPaper.Description; foreach (var qg in examPaper.QuestionGroups) { var qgd = new QuestionGroupDto(); ParseMajorQuestionGroup(qg, qgd, false); dto.QuestionGroups.SubQuestionGroups.Add(qgd); } foreach (var question in examPaper.TopLevelQuestions) { if (question.SubQuestions != null && question.SubQuestions.Any()) { var qgDto = new QuestionGroupDto { Title = question.Stem, Score = (int)question.Score, Descript = "", }; qgDto.ValidQuestionGroup = !string.IsNullOrEmpty(qgDto.Descript); ParseQuestionWithSubQuestions(question, qgDto, qgDto.ValidQuestionGroup); dto.QuestionGroups.SubQuestionGroups.Add(qgDto); } else { var qgDto = new QuestionGroupDto { Title = question.Stem, Score = (int)question.Score, Descript = "", }; qgDto.ValidQuestionGroup = !string.IsNullOrEmpty(qgDto.Descript); var subQuestionDto = new SubQuestionDto(); ParseSingleQuestion(question, subQuestionDto, !qgDto.ValidQuestionGroup); qgDto.SubQuestions.Add(subQuestionDto); dto.QuestionGroups.SubQuestionGroups.Add(qgDto); } } return dto; } private static void ParseMajorQuestionGroup(MajorQuestionGroup qg, QuestionGroupDto qgd, bool isParentGroupValidChain) { qgd.Title = qg.Title; qgd.Score = (int)qg.Score; qgd.Descript = qg.Descript; qgd.ValidQuestionGroup = !string.IsNullOrEmpty(qg.Descript) && !isParentGroupValidChain; bool nextIsParentGroupValidChain = qgd.ValidQuestionGroup || isParentGroupValidChain; if (qg.SubQuestionGroups != null) { qg.SubQuestionGroups.ForEach(sqg => { var sqgd = new QuestionGroupDto(); sqgd.Index = (byte)qg.SubQuestionGroups.IndexOf(sqg); ParseMajorQuestionGroup(sqg, sqgd, nextIsParentGroupValidChain); qgd.SubQuestionGroups.Add(sqgd); }); } // 处理 MajorQuestionGroup 下的 SubQuestions if (qg.SubQuestions != null) { qg.SubQuestions.ForEach(sq => { // 如果 MajorQuestionGroup 下的 Question 包含子问题,则转为 QuestionGroupDto if (sq.SubQuestions != null && sq.SubQuestions.Any()) { var subQgd = new QuestionGroupDto { Title = sq.Stem, Index = (byte)qg.SubQuestions.IndexOf(sq), Score = (int)sq.Score, Descript = "" // 默认为空 }; // 判断当前组是否有效:如果有描述,并且其父级链中没有任何一个组是有效组,则当前组有效 subQgd.ValidQuestionGroup = !string.IsNullOrEmpty(subQgd.Descript) && !nextIsParentGroupValidChain; ParseQuestionWithSubQuestions(sq, subQgd, subQgd.ValidQuestionGroup || nextIsParentGroupValidChain); qgd.SubQuestionGroups.Add(subQgd); } else // 如果 MajorQuestionGroup 下的 Question 没有子问题,则转为 SubQuestionDto { var subQd = new SubQuestionDto(); // 只有当所有父组(包括当前组)都不是有效组时,这个题目才有效 ParseSingleQuestion(sq, subQd, !nextIsParentGroupValidChain); subQd.Index = (byte)qg.SubQuestions.IndexOf(sq); qgd.SubQuestions.Add(subQd); } }); } } // 解析包含子问题的 Question,将其转换为 QuestionGroupDto // isParentGroupValidChain 参数表示从顶层到当前组的任一父组是否已经是“有效组” private static void ParseQuestionWithSubQuestions(Question question, QuestionGroupDto qgd, bool isParentGroupValidChain) { qgd.Title = question.Stem; qgd.Score = (int)question.Score; qgd.Descript = ""; // 默认为空 // 判断当前组是否有效:如果有描述,并且其父级链中没有任何一个组是有效组,则当前组有效 qgd.ValidQuestionGroup = !string.IsNullOrEmpty(qgd.Descript) && !isParentGroupValidChain; // 更新传递给子项的 isParentGroupValidChain 状态 bool nextIsParentGroupValidChain = qgd.ValidQuestionGroup || isParentGroupValidChain; if (question.SubQuestions != null) { question.SubQuestions.ForEach(subQ => { // 如果子问题本身还有子问题(多层嵌套),则继续创建 QuestionGroupDto if (subQ.SubQuestions != null && subQ.SubQuestions.Any()) { var nestedQgd = new QuestionGroupDto { Title = subQ.Stem, Score = (int)subQ.Score, Descript = "" // 默认为空 }; // 判断当前组是否有效:如果有描述,并且其父级链中没有任何一个组是有效组,则当前组有效 nestedQgd.ValidQuestionGroup = !string.IsNullOrEmpty(nestedQgd.Descript) && !nextIsParentGroupValidChain; ParseQuestionWithSubQuestions(subQ, nestedQgd, nestedQgd.ValidQuestionGroup || nextIsParentGroupValidChain); qgd.SubQuestionGroups.Add(nestedQgd); } else // 如果子问题没有子问题,则直接创建 SubQuestionDto { var subQd = new SubQuestionDto(); // 只有当所有父组(包括当前组)都不是有效组时,这个题目才有效 ParseSingleQuestion(subQ, subQd, !nextIsParentGroupValidChain); qgd.SubQuestions.Add(subQd); } }); } } // 解析单个 Question (没有子问题) 为 SubQuestionDto private static void ParseSingleQuestion(Question question, SubQuestionDto subQd, bool validQ) { subQd.Stem = question.Stem; subQd.Score = (int)question.Score; subQd.ValidQuestion = validQ; // 根据传入的 validQ 确定是否是“有效题目” subQd.SampleAnswer = question.SampleAnswer; subQd.QuestionType = question.QuestionType; // 注意:DifficultyLevel 在本地 Question 中没有,如果服务器需要,可能需要补充默认值或从其他地方获取 // subQd.DifficultyLevel = ...; if (question.Options != null) { question.Options.ForEach(o => { subQd.Options.Add(new OptionDto { Value = o.Label + o.Text }); }); } } public static void SeqIndex(this ExamDto dto) { dto.QuestionGroups.SeqQGroupIndex(); } public static void SeqQGroupIndex(this QuestionGroupDto dto) { dto.SubQuestions?.ForEach(sq => { sq.Index = (byte)dto.SubQuestions.IndexOf(sq); }); dto.SubQuestionGroups?.ForEach(sqg => { sqg.Index = (byte)dto.SubQuestionGroups.IndexOf(sqg); sqg.SeqQGroupIndex(); }); } public static string SerializeExamDto(this ExamDto dto) { // 配置序列化选项(可选) var options = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; return JsonSerializer.Serialize(dto, options); } public static ExamDto DeserializeExamDto(string jsonString) { var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; return JsonSerializer.Deserialize(jsonString, options); } } }