221 lines
8.3 KiB
C#
221 lines
8.3 KiB
C#
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);
|
||
}
|
||
|
||
}
|
||
}
|