feat: 添加学生提交系统功能
Some checks failed
TechAct / explore-gitea-actions (push) Failing after 30s

- 添加学生提交管理服务 (StudentSubmissionService, StudentSubmissionDetailService)
- 新增学生提交相关控制器 (StudentSubmissionController, StudentSubmissionDetailController)
- 添加学生提交数据传输对象 (StudentSubmissionDetailDto, StudentSubmissionSummaryDto)
- 新增学生提交相关页面组件 (StudentExamView, ExamDetailView, StudentCard等)
- 添加学生提交信息卡片组件 (SubmissionInfoCard, TeacherSubmissionInfoCard)
- 更新数据库迁移文件以支持提交系统
This commit is contained in:
SpecialX
2025-09-09 15:42:31 +08:00
parent 6a65281850
commit 439c8a2421
47 changed files with 5486 additions and 119 deletions

View File

@@ -0,0 +1,144 @@
using AutoMapper;
using AutoMapper.Internal.Mappers;
using Entities.Contracts;
using Entities.DTO;
using Microsoft.EntityFrameworkCore;
using SharedDATA.Api;
using TechHelper.Context;
using TechHelper.Repository;
using TechHelper.Services;
namespace TechHelper.Server.Services
{
public class StudentSubmissionDetailService : IStudentSubmissionDetailService
{
private readonly IUnitOfWork _unitOfWork;
private readonly IExamService examService;
private readonly IMapper _mapper;
public StudentSubmissionDetailService(
IUnitOfWork unitOfWork,
IExamService examService,
IMapper mapper)
{
_unitOfWork = unitOfWork;
this.examService = examService;
_mapper = mapper;
}
public async Task<ApiResponse> GetSubmissionDetailAsync(Guid submissionId)
{
try
{
// 获取submission基本信息
var submission = await _unitOfWork.GetRepository<Submission>()
.GetAll(s => s.Id == submissionId)
.Include(s => s.Assignment)
.ThenInclude(a => a.Creator)
.FirstOrDefaultAsync();
if (submission == null)
{
return ApiResponse.Error("未找到指定的提交记录");
}
var assignment = await examService.GetAsync(submission.AssignmentId);
if (assignment == null)
{
return ApiResponse.Error("未找到指定的作业");
}
// 获取所有提交详情
var submissionDetails = await _unitOfWork.GetRepository<SubmissionDetail>()
.GetAll(sd => sd.SubmissionId == submissionId)
.Include(sd => sd.AssignmentQuestion)
.ThenInclude(aq => aq.Question)
.ThenInclude(q => q.Lesson)
.ThenInclude(q => q.KeyPoints)
.ToListAsync();
// 获取同作业的所有提交用于排名和成绩分布
var allSubmissions = await _unitOfWork.GetRepository<Submission>()
.GetAll(s => s.AssignmentId == submission.AssignmentId)
.ToListAsync();
// 映射基本信息
var result = _mapper.Map<StudentSubmissionDetailDto>(submission);
result.Assignment = assignment.Result as AssignmentDto ?? new AssignmentDto();
var errorQuestion = submissionDetails
.Where(sd => sd.IsCorrect == false && sd.AssignmentQuestion?.StructType == AssignmentStructType.Question && sd.AssignmentQuestion?.Question != null)
.ToList();
// 计算基础统计
result.TotalQuestions = submissionDetails.Select(x => x.AssignmentQuestion.StructType == AssignmentStructType.Question && x.AssignmentQuestion?.Question != null).Count();
result.ErrorCount = errorQuestion.Count;
result.CorrectCount = result.TotalQuestions - result.ErrorCount;
result.AccuracyRate = result.TotalQuestions > 0 ?
(float)result.CorrectCount / result.TotalQuestions : 0;
// 计算错误类型分布 - 只获取题目类型的错误
result.ErrorTypeDistribution = errorQuestion
.GroupBy(sd => sd.AssignmentQuestion.Question.Type.ToString())
.ToDictionary(g => g.Key, g => g.Count()); ;
// 计算错误类型成绩分布 - 只获取题目类型的错误
result.ErrorTypeScoreDistribution = errorQuestion
.GroupBy(sd => sd.AssignmentQuestion.Question.Type.ToString())
.ToDictionary(g => g.Key, g => g.Sum(sd => sd.PointsAwarded ?? 0));
// 计算成绩排名
var orderedSubmissions = allSubmissions
.OrderByDescending(s => s.OverallGrade)
.ToList();
result.TotalRank = orderedSubmissions.FindIndex(s => s.Id == submissionId) + 1;
SetBCorrect(result.Assignment, errorQuestion);
// 计算成绩分布
result.AllScores = allSubmissions.Select(s => s.OverallGrade).ToList();
result.AverageScore = submission.OverallGrade;
result.ClassAverageScore = allSubmissions.Average(s => s.OverallGrade);
// 计算课文错误分布
result.LessonErrorDistribution = errorQuestion
.Where(eq => eq.AssignmentQuestion.Question.Lesson != null)
.GroupBy(sd => sd.AssignmentQuestion.Question.Lesson.Title)
.ToDictionary(g => g.Key, g => g.Count());
// 计算关键点错误分布
result.KeyPointErrorDistribution = errorQuestion
.Where(eq => eq.AssignmentQuestion.Question.Lesson != null)
.GroupBy(sd => sd.AssignmentQuestion.Question.KeyPoint.Key)
.ToDictionary(g => g.Key, g => g.Count());
return ApiResponse.Success(result: result);
}
catch (Exception ex)
{
return ApiResponse.Error($"获取学生提交详细信息失败: {ex.Message}");
}
}
public void SetBCorrect(AssignmentDto assignment, List<SubmissionDetail> submissionDetails)
{
SetBCorrect(assignment.ExamStruct, submissionDetails);
}
public void SetBCorrect(AssignmentQuestionDto assignmentQuestion, List<SubmissionDetail> submissionDetails)
{
var sd = submissionDetails.FirstOrDefault(x => x.AssignmentQuestionId == assignmentQuestion.Id);
if (sd != null)
assignmentQuestion.BCorrect = sd.AssignmentQuestion.BCorrect;
else
assignmentQuestion.BCorrect = false;
assignmentQuestion.ChildrenAssignmentQuestion?.ForEach(
cq => SetBCorrect(cq, submissionDetails));
}
//Task<ApiResponse> IStudentSubmissionDetailService.GetSubmissionDetailAsync(Guid submissionId)
//{
// throw new NotImplementedException();
//}
}
}