Files
TechHelper/TechHelper.Client/Exam/ExamPaperExtensions .cs
2025-06-11 15:02:20 +08:00

221 lines
8.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<ExamDto>(jsonString, options);
}
}
}