338 lines
9.7 KiB
C#
338 lines
9.7 KiB
C#
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);
|
||
}
|
||
|
||
}
|
||
}
|