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; // 处理顶级 QuestionGroups foreach (var qg in examPaper.QuestionGroups) { var qgd = new QuestionGroupDto(); // 顶级 QuestionGroup,其父组当然无效 (false),所以 isParentGroupValidChain 为 false ParseMajorQuestionGroup(qg, qgd, false); dto.QuestionGroups.SubQuestionGroups.Add(qgd); } // 处理 TopLevelQuestions foreach (var question in examPaper.TopLevelQuestions) { // 对于 TopLevelQuestions,它们没有父组,所以 isParentGroupValidChain 初始为 false // 如果顶级 Question 包含子问题,则将其视为一个 QuestionGroupDto if (question.SubQuestions != null && question.SubQuestions.Any()) { var qgDto = new QuestionGroupDto { Title = question.Stem, Score = (int)question.Score, Descript = "", // 顶级 Question 默认无描述 }; // 判断当前组是否有效:如果有描述,则为有效组 qgDto.ValidQuestionGroup = !string.IsNullOrEmpty(qgDto.Descript); // 传递给子项的 isParentGroupValidChain 状态:如果当前组有效,则传递 true;否则继承父级状态 (此处为 false) ParseQuestionWithSubQuestions(question, qgDto, qgDto.ValidQuestionGroup); dto.QuestionGroups.SubQuestionGroups.Add(qgDto); } else // 如果顶级 Question 没有子问题,则它本身就是一个独立的 SubQuestionDto,放在一个容器 QuestionGroupDto 中 { var qgDto = new QuestionGroupDto { Title = question.Stem, Score = (int)question.Score, Descript = "", // 独立题目的容器组通常无描述 }; // 独立题目的容器组,如果没有描述,则不是“有效组” qgDto.ValidQuestionGroup = !string.IsNullOrEmpty(qgDto.Descript); var subQuestionDto = new SubQuestionDto(); // 此时,qgDto.ValidQuestionGroup 为 false,所以传入 true,表示题目是有效的 // 因为其父组链 (此处为自身) 不是有效组 ParseSingleQuestion(question, subQuestionDto, !qgDto.ValidQuestionGroup); qgDto.SubQuestions.Add(subQuestionDto); dto.QuestionGroups.SubQuestionGroups.Add(qgDto); } } return dto; } // 解析 MajorQuestionGroup 及其子项 // isParentGroupValidChain 参数表示从顶层到当前组的任一父组是否已经是“有效组” 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; // 更新传递给子项的 isParentGroupValidChain 状态: // 如果当前组是有效组 (即 qgd.ValidQuestionGroup 为 true),那么子项的父级链就包含了有效组 // 否则,子项的父级链有效性继承自其父级 (isParentGroupValidChain) bool nextIsParentGroupValidChain = qgd.ValidQuestionGroup || isParentGroupValidChain; // 处理子 QuestionGroup if (qg.SubQuestionGroups != null) { qg.SubQuestionGroups.ForEach(sqg => { var sqgd = new QuestionGroupDto(); 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, 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); 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 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); } } }