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

338 lines
9.7 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;
using Entities.Contracts;
using Microsoft.Extensions.Options;
namespace TechHelper.Client.Exam
{
public class ParentStructInfo
{
public string Number { get; set; }
public SubjectAreaEnum SubjectArea { get; set; }
public byte Index { get; set; }
}
public static class ExamPaperExtensions
{
public static AssignmentDto ConvertToExamDTO(this ExamPaper examPaper)
{
AssignmentDto dto = new AssignmentDto();
dto.Title = examPaper.AssignmentTitle;
dto.Description = examPaper.Description;
var SubjectArea = SubjectAreaEnum.Literature;
Enum.TryParse<SubjectAreaEnum>(examPaper.SubjectArea, out SubjectArea);
dto.SubjectArea = SubjectArea;
AssignmentStructDto examStruct = new AssignmentStructDto();
foreach (var qg in examPaper.QuestionGroups)
{
examStruct.ChildrenGroups.Add(ParseMajorQuestionGroup(qg));
examStruct.ChildrenGroups.Last().Index = (byte)(examStruct.ChildrenGroups.Count());
}
dto.ExamStruct = examStruct;
return dto;
}
private static AssignmentStructDto ParseMajorQuestionGroup(MajorQuestionGroup sqg)
{
var examStruct = new AssignmentStructDto();
if (sqg.SubQuestionGroups != null)
{
examStruct.Title = sqg.Title;
examStruct.Score = sqg.Score;
examStruct.ChildrenGroups = new List<AssignmentStructDto>();
sqg.SubQuestionGroups?.ForEach(ssqg =>
{
if (string.IsNullOrEmpty(ssqg.Descript))
{
examStruct.ChildrenGroups.Add(ParseMajorQuestionGroup(ssqg));
examStruct.ChildrenGroups.Last().Index = (byte)(examStruct.ChildrenGroups.Count());
}
else
{
examStruct.AssignmentQuestions.Add(ParseGroupToAssignmentQuestion(ssqg, false));
examStruct.AssignmentQuestions.Last().Index = (byte)(examStruct.AssignmentQuestions.Count());
}
});
}
if (sqg.SubQuestions != null)
{
sqg.SubQuestions?.ForEach(sq =>
{
if(sq.SubQuestions.Any())
{
}
examStruct.AssignmentQuestions.Add(ParseAssignmentQuestion(sq));
examStruct.AssignmentQuestions.Last().Index = (byte)(examStruct.AssignmentQuestions.Count());
});
}
return examStruct;
}
public static List<string> ParseOptionsFromText(this string optionsText)
{
return optionsText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
.Where(line => !string.IsNullOrWhiteSpace(line)).ToList();
}
private static QuestionDto ParseGroupToQuestion(MajorQuestionGroup qg, bool subQ = true)
{
var dq = new QuestionDto();
dq.Title = qg.Title + Environment.NewLine + qg.Descript;
if (subQ) dq.GroupState = QuestionGroupState.Subquestion;
else dq.GroupState = QuestionGroupState.Group;
qg.SubQuestions?.ForEach(ssq =>
{
dq.ChildrenQuestion.Add(ParseQuestion(ssq));
});
qg.SubQuestionGroups?.ForEach(sqg =>
{
dq.ChildrenQuestion.Add(ParseGroupToQuestion(sqg));
});
return dq;
}
private static AssignmentQuestionDto ParseGroupToAssignmentQuestion(MajorQuestionGroup qg, bool subQ = true)
{
var aq = new AssignmentQuestionDto();
aq.Score = qg.Score;
qg.SubQuestions?.ForEach(ssq =>
{
aq.Question.ChildrenQuestion.Add(ParseQuestion(ssq));
aq.Question.ChildrenQuestion.Last().Index = (byte)aq.Question.ChildrenQuestion.Count;
});
qg.SubQuestionGroups?.ForEach(sqg =>
{
aq.Question.ChildrenQuestion.Add(ParseGroupToQuestion(sqg));
aq.Question.ChildrenQuestion.Last().Index = (byte)aq.Question.ChildrenQuestion.Count;
});
return aq;
}
private static AssignmentQuestionDto ParseAssignmentQuestion(PaperQuestion sq)
{
var aq = new AssignmentQuestionDto();
aq.Score = sq.Score;
aq.Question = ParseQuestion(sq);
sq.SubQuestions?.ForEach(ssq =>
{
aq.Question.ChildrenQuestion.Add(ParseQuestion(ssq));
aq.Question.ChildrenQuestion.Last().Index = (byte)aq.Question.ChildrenQuestion.Count;
});
return aq;
}
private static QuestionDto ParseQuestion(PaperQuestion sq)
{
var dq = new QuestionDto();
dq.Title = sq.Stem;
dq.Options = string.Join(Environment.NewLine, sq.Options.Select(opt => $"{opt.Label} {opt.Text}"));
dq.Score = sq.Score;
sq.SubQuestions?.ForEach(ssq =>
{
dq.ChildrenQuestion.Add(ParseQuestion(ssq));
dq.ChildrenQuestion.Last().Index = (byte)dq.ChildrenQuestion.Count;
});
return dq;
}
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);
});
}
if (qg.SubQuestions != null)
{
qg.SubQuestions.ForEach(sq =>
{
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(PaperQuestion 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(PaperQuestion 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.ExamStruct.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<ExamDto>(jsonString, options);
}
}
}