重构试卷相关内容

This commit is contained in:
SpecialX
2025-06-13 19:01:32 +08:00
parent b77ed0b30f
commit bcf351ff25
23 changed files with 980 additions and 84 deletions

View File

@@ -15,49 +15,44 @@ namespace TechHelper.Client.Exam
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 默认无描述
Descript = "",
};
// 判断当前组是否有效:如果有描述,则为有效组
qgDto.ValidQuestionGroup = !string.IsNullOrEmpty(qgDto.Descript);
// 传递给子项的 isParentGroupValidChain 状态:如果当前组有效,则传递 true否则继承父级状态 (此处为 false)
ParseQuestionWithSubQuestions(question, qgDto, qgDto.ValidQuestionGroup);
dto.QuestionGroups.SubQuestionGroups.Add(qgDto);
}
else // 如果顶级 Question 没有子问题,则它本身就是一个独立的 SubQuestionDto放在一个容器 QuestionGroupDto 中
else
{
var qgDto = new QuestionGroupDto
{
Title = question.Stem,
Score = (int)question.Score,
Descript = "", // 独立题目的容器组通常无描述
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);
@@ -67,28 +62,25 @@ namespace TechHelper.Client.Exam
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();
sqgd.Index = (byte)qg.SubQuestionGroups.IndexOf(sqg);
ParseMajorQuestionGroup(sqg, sqgd, nextIsParentGroupValidChain);
qgd.SubQuestionGroups.Add(sqgd);
});
@@ -105,6 +97,7 @@ namespace TechHelper.Client.Exam
var subQgd = new QuestionGroupDto
{
Title = sq.Stem,
Index = (byte)qg.SubQuestions.IndexOf(sq),
Score = (int)sq.Score,
Descript = "" // 默认为空
};
@@ -119,6 +112,7 @@ namespace TechHelper.Client.Exam
var subQd = new SubQuestionDto();
// 只有当所有父组(包括当前组)都不是有效组时,这个题目才有效
ParseSingleQuestion(sq, subQd, !nextIsParentGroupValidChain);
subQd.Index = (byte)qg.SubQuestions.IndexOf(sq);
qgd.SubQuestions.Add(subQd);
}
});
@@ -191,15 +185,35 @@ namespace TechHelper.Client.Exam
}
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
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
return JsonSerializer.Serialize(dto, options);
@@ -207,10 +221,10 @@ namespace TechHelper.Client.Exam
public static ExamDto DeserializeExamDto(string jsonString)
{
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
return JsonSerializer.Deserialize<ExamDto>(jsonString, options);