using AutoMapper; using Entities.Contracts; using Entities.DTO; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using SharedDATA.Api; using TechHelper.Services; namespace TechHelper.Server.Services { public class ExamService : IExamService { private readonly IUnitOfWork _unitOfWork; private readonly IRepository _assignmentRepo; private readonly IRepository _assignmentGroupRepo; private readonly IRepository _assignmentQuestionRepo; private readonly IRepository _questionRepo; public ExamService(IUnitOfWork unitOfWork, IMapper mapper, UserManager userManager) { _unitOfWork = unitOfWork; _mapper = mapper; _userManager = userManager; _assignmentRepo = _unitOfWork.GetRepository(); _assignmentGroupRepo = _unitOfWork.GetRepository(); _assignmentQuestionRepo = _unitOfWork.GetRepository(); _questionRepo = _unitOfWork.GetRepository(); } private readonly IMapper _mapper; private readonly UserManager _userManager; public async Task AddAsync(ExamDto model) { try { var result = await SaveParsedExam(model); if (result.Status) { return ApiResponse.Success("保存试题成功"); } else { return ApiResponse.Error($"保存试题数据失败{result.Message}"); } } catch (Exception ex) { return ApiResponse.Error($"保存试题数据失败: {ex.Message}"); } } public Task DeleteAsync(Guid id) { throw new NotImplementedException(); } public Task GetAllAsync(QueryParameter query) { throw new NotImplementedException(); } public async Task GetAsync(Guid id) { try { var result = await GetExamByIdAsync(id); return result; } catch (Exception ex) { return ApiResponse.Error($"获取试题数据失败: {ex.Message}"); } } public Task UpdateAsync(ExamDto model) { throw new NotImplementedException(); } public async Task GetExamByIdAsync(Guid assignmentId) { try { var assignment = await _unitOfWork.GetRepository().GetFirstOrDefaultAsync( predicate: a => a.Id == assignmentId && !a.IsDeleted); if (assignment == null) { return ApiResponse.Error($"找不到 ID 为 {assignmentId} 的试卷。"); } // 获取所有相关题组和题目,并过滤掉已删除的 var allGroups = await _unitOfWork.GetRepository().GetAllAsync( predicate: ag => ag.AssignmentId == assignmentId && !ag.IsDeleted, include: source => source .Include(ag => ag.AssignmentQuestions.Where(aq => !aq.IsDeleted)) .ThenInclude(aq => aq.Question) ); if (allGroups == null || !allGroups.Any()) { // 试卷存在但没有内容,返回一个空的 ExamDto return ApiResponse.Success("试卷没有内容。", new ExamDto { AssignmentId = assignment.Id, AssignmentTitle = assignment.Title, Description = assignment.Description, SubjectArea = assignment.Submissions.ToString() }); } var rootGroups = allGroups .Where(ag => ag.ParentGroup == null) .OrderBy(ag => ag.Number) .ToList(); // 递归映射到 ExamDto var examDto = new ExamDto { AssignmentId = assignment.Id, AssignmentTitle = assignment.Title, Description = assignment.Description, SubjectArea = assignment.Submissions.ToString(), QuestionGroups = MapAssignmentGroupsToDto(rootGroups, allGroups) }; return ApiResponse.Success("试卷信息已成功获取。", examDto); } catch (Exception ex) { return ApiResponse.Error($"获取试卷时发生错误: {ex.Message}", ex); } } private List MapAssignmentGroupsToDto( List currentLevelGroups, IEnumerable allFetchedGroups) { var dtos = new List(); foreach (var group in currentLevelGroups.OrderBy(g => g.Number)) { var groupDto = new QuestionGroupDto { Title = group.Title, Score = (int)(group.TotalPoints ?? 0), QuestionReference = group.Descript, SubQuestions = group.AssignmentQuestions .OrderBy(aq => aq.QuestionNumber) .Select(aq => new SubQuestionDto { Index = aq.QuestionNumber, Stem = aq.Question.QuestionText, Score = aq.Score?? 0, // 使用 AssignmentQuestion 上的 Score SampleAnswer = aq.Question.CorrectAnswer, QuestionType = aq.Question.QuestionType.ToString(), DifficultyLevel = aq.Question.DifficultyLevel.ToString(), Options = new List() // 这里需要您根据实际存储方式填充 Option }).ToList(), // 递归映射子题组 SubQuestionGroups = MapAssignmentGroupsToDto( allFetchedGroups.Where(ag => ag.ParentGroup == group.Id && !ag.IsDeleted).ToList(), // 从所有已获取的组中筛选子组 allFetchedGroups) }; dtos.Add(groupDto); } return dtos; } public async Task SaveParsedExam(ExamDto examData) { // 获取当前登录用户 var currentUser = await _userManager.GetUserAsync(null); if (currentUser == null) { return ApiResponse.Error("未找到当前登录用户,无法保存试题。"); } var currentUserId = currentUser.Id; try { Guid assignmentId; // 创建新的 Assignment 实体 var newAssignment = new Assignment { Id = Guid.NewGuid(), Title = examData.AssignmentTitle, Description = examData.Description, SubjectArea = examData.SubjectArea, CreatedAt = DateTime.UtcNow, CreatedBy = currentUserId, IsDeleted = false }; await _assignmentRepo.InsertAsync(newAssignment); assignmentId = newAssignment.Id; // 从 ExamDto.QuestionGroups 获取根题组。 // 确保只有一个根题组,因为您的模型是“试卷只有一个根节点”。 if (examData.QuestionGroups == null || examData.QuestionGroups.Count != 1) { throw new ArgumentException("试卷必须包含且只能包含一个根题组。"); } // 递归处理根题组及其所有子题组和题目 // 传入的 assignmentId 仅用于设置根题组的 AssignmentId 字段 // 对于子题组,ProcessAndSaveAssignmentGroupsRecursive 会将 AssignmentId 设置为 null await ProcessAndSaveAssignmentGroupsRecursive( examData.QuestionGroups.Single(), examData.SubjectArea.ToString(), assignmentId, null, // 根题组没有父级 currentUserId); if (await _unitOfWork.SaveChangesAsync() > 0) { return ApiResponse.Success("试卷数据已成功保存。", new ExamDto { AssignmentId = assignmentId, AssignmentTitle = examData.AssignmentTitle }); } else { return ApiResponse.Success("没有新的试卷数据需要保存。"); } } catch (Exception ex) { return ApiResponse.Error($"保存试卷数据失败: {ex.Message}", ex); } } private async Task ProcessAndSaveAssignmentGroupsRecursive( QuestionGroupDto qgDto, string subjectarea, Guid assignmentId, Guid? parentAssignmentGroupId, Guid createdById) { byte groupNumber = 1; var newAssignmentGroup = new AssignmentGroup { Id = Guid.NewGuid(), // 后端生成 GUID Title = qgDto.Title, Descript = qgDto.QuestionReference, TotalPoints = qgDto.Score, Number = (byte)qgDto.Index, // 使用 DTO 的 Index 作为 Number ParentGroup = parentAssignmentGroupId, // 设置父级题组 GUID // 关键修正:只有当 parentAssignmentGroupId 为 null 时,才设置 AssignmentId // 这意味着当前题组是顶级题组 AssignmentId = parentAssignmentGroupId == null ? assignmentId : Guid.Empty, IsDeleted = false }; await _unitOfWork.GetRepository().InsertAsync(newAssignmentGroup); // 处理子题目 uint questionNumber = 1; foreach (var sqDto in qgDto.SubQuestions.OrderBy(s => s.Index)) { var newQuestion = _mapper.Map(sqDto); newQuestion.Id = Guid.NewGuid(); newQuestion.CreatedBy = createdById; newQuestion.CreatedAt = DateTime.UtcNow; newQuestion.UpdatedAt = DateTime.UtcNow; newQuestion.IsDeleted = false; newQuestion.SubjectArea = (SubjectAreaEnum)Enum.Parse(typeof(SubjectAreaEnum), subjectarea, true); // 处理 Options:如果 Options 是 JSON 字符串或需要其他存储方式,在这里处理 // 例如:newQuestion.QuestionText += (JsonConvert.SerializeObject(sqDto.Options)); await _unitOfWork.GetRepository().InsertAsync(newQuestion); var newAssignmentQuestion = new AssignmentQuestion { Id = Guid.NewGuid(), QuestionId = newQuestion.Id, QuestionNumber = (byte)questionNumber, // 使用递增的 questionNumber AssignmentGroupId = newAssignmentGroup.Id, // 关联到当前题组 Score = sqDto.Score, // 从 DTO 获取单个子题分数 IsDeleted = false, CreatedAt = DateTime.UtcNow }; await _unitOfWork.GetRepository().InsertAsync(newAssignmentQuestion); questionNumber++; } // 递归处理子题组 // 这里需要遍历 SubQuestionGroups,并对每个子组进行递归调用 foreach (var subQgDto in qgDto.SubQuestionGroups.OrderBy(s => s.Index)) { await ProcessAndSaveAssignmentGroupsRecursive( subQgDto, // 传入当前的子题组 DTO subjectarea, assignmentId, // 顶层 AssignmentId 依然传递下去,但子组不会直接使用它 newAssignmentGroup.Id, // 将当前题组的 ID 作为下一层递归的 parentAssignmentGroupId createdById); } } } }