- 添加学生提交管理服务 (StudentSubmissionService, StudentSubmissionDetailService) - 新增学生提交相关控制器 (StudentSubmissionController, StudentSubmissionDetailController) - 添加学生提交数据传输对象 (StudentSubmissionDetailDto, StudentSubmissionSummaryDto) - 新增学生提交相关页面组件 (StudentExamView, ExamDetailView, StudentCard等) - 添加学生提交信息卡片组件 (SubmissionInfoCard, TeacherSubmissionInfoCard) - 更新数据库迁移文件以支持提交系统
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
using Entities.DTO;
|
||||
using TechHelper.Services;
|
||||
|
||||
namespace TechHelper.Server.Services
|
||||
{
|
||||
public interface IStudentSubmissionDetailService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取学生提交的详细信息
|
||||
/// </summary>
|
||||
/// <param name="submissionId">提交ID</param>
|
||||
/// <returns>学生提交详细信息</returns>
|
||||
Task<ApiResponse> GetSubmissionDetailAsync(Guid submissionId);
|
||||
}
|
||||
}
|
24
TechHelper.Server/Services/IStudentSubmissionService.cs
Normal file
24
TechHelper.Server/Services/IStudentSubmissionService.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Entities.DTO;
|
||||
using TechHelper.Services;
|
||||
|
||||
namespace TechHelper.Server.Services
|
||||
{
|
||||
public interface IStudentSubmissionService : IBaseService<StudentSubmissionSummaryDto, Guid>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取学生提交的作业摘要列表
|
||||
/// </summary>
|
||||
/// <param name="studentId">学生ID</param>
|
||||
/// <returns>学生提交摘要列表</returns>
|
||||
Task<ApiResponse> GetStudentSubmissionsAsync(Guid studentId);
|
||||
|
||||
/// <summary>
|
||||
/// 获取学生提交的作业摘要列表(分页)
|
||||
/// </summary>
|
||||
/// <param name="studentId">学生ID</param>
|
||||
/// <param name="pageNumber">页码</param>
|
||||
/// <param name="pageSize">每页数量</param>
|
||||
/// <returns>分页的学生提交摘要列表</returns>
|
||||
Task<ApiResponse> GetStudentSubmissionsPagedAsync(Guid studentId, int pageNumber = 1, int pageSize = 10);
|
||||
}
|
||||
}
|
144
TechHelper.Server/Services/StudentSubmissionDetailService.cs
Normal file
144
TechHelper.Server/Services/StudentSubmissionDetailService.cs
Normal 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();
|
||||
//}
|
||||
}
|
||||
}
|
142
TechHelper.Server/Services/StudentSubmissionService.cs
Normal file
142
TechHelper.Server/Services/StudentSubmissionService.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using AutoMapper;
|
||||
using Entities.Contracts;
|
||||
using Entities.DTO;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SharedDATA.Api;
|
||||
using TechHelper.Context;
|
||||
using TechHelper.Repository;
|
||||
using TechHelper.Server.Repositories;
|
||||
using TechHelper.Services;
|
||||
|
||||
namespace TechHelper.Server.Services
|
||||
{
|
||||
public class StudentSubmissionService : IStudentSubmissionService
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly IRepository<Submission> _submissionRepository;
|
||||
private readonly IRepository<Assignment> _assignmentRepository;
|
||||
private readonly IRepository<User> _userRepository;
|
||||
|
||||
public StudentSubmissionService(
|
||||
IUnitOfWork unitOfWork,
|
||||
IMapper mapper,
|
||||
IRepository<Submission> submissionRepository,
|
||||
IRepository<Assignment> assignmentRepository,
|
||||
IRepository<User> userRepository)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_mapper = mapper;
|
||||
_submissionRepository = submissionRepository;
|
||||
_assignmentRepository = assignmentRepository;
|
||||
_userRepository = userRepository;
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> GetStudentSubmissionsAsync(Guid studentId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var submissions = await _submissionRepository
|
||||
.GetAll(s => s.StudentId == studentId)
|
||||
.Include(s => s.Assignment)
|
||||
.ThenInclude(a => a.Creator)
|
||||
.OrderByDescending(s => s.SubmissionTime)
|
||||
.ToListAsync();
|
||||
|
||||
var result = new List<StudentSubmissionSummaryDto>();
|
||||
|
||||
foreach (var submission in submissions)
|
||||
{
|
||||
var summary = new StudentSubmissionSummaryDto
|
||||
{
|
||||
Id = submission.Id,
|
||||
AssignmentName = submission.Assignment?.Title ?? "未知作业",
|
||||
ErrorCount = await CalculateErrorCountAsync(submission.Id),
|
||||
CreatedDate = submission.SubmissionTime,
|
||||
Score = (int)submission.OverallGrade,
|
||||
TotalQuestions = submission.Assignment?.TotalQuestions ?? 0,
|
||||
StudentName = submission.Assignment?.Creator?.UserName ?? "未知老师",
|
||||
Status = submission.Status.ToString()
|
||||
};
|
||||
result.Add(summary);
|
||||
}
|
||||
|
||||
return ApiResponse.Success(result: result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse.Error($"获取学生提交信息失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ApiResponse> GetStudentSubmissionsPagedAsync(Guid studentId, int pageNumber = 1, int pageSize = 10)
|
||||
{
|
||||
try
|
||||
{
|
||||
var totalCount = await _submissionRepository
|
||||
.GetAll(s => s.StudentId == studentId)
|
||||
.CountAsync();
|
||||
|
||||
var submissions = await _submissionRepository
|
||||
.GetAll(s => s.StudentId == studentId)
|
||||
.Include(s => s.Assignment)
|
||||
.ThenInclude(a => a.Creator)
|
||||
.OrderByDescending(s => s.SubmissionTime)
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
var result = new List<StudentSubmissionSummaryDto>();
|
||||
|
||||
foreach (var submission in submissions)
|
||||
{
|
||||
var summary = new StudentSubmissionSummaryDto
|
||||
{
|
||||
Id = submission.Id,
|
||||
AssignmentName = submission.Assignment?.Title ?? "未知作业",
|
||||
ErrorCount = await CalculateErrorCountAsync(submission.Id),
|
||||
CreatedDate = submission.SubmissionTime,
|
||||
Score = submission.OverallGrade,
|
||||
TotalQuestions = submission.Assignment?.TotalQuestions ?? 0,
|
||||
StudentName = submission.Assignment?.Creator?.UserName ?? "未知老师",
|
||||
Status = submission.Status.ToString()
|
||||
};
|
||||
result.Add(summary);
|
||||
}
|
||||
|
||||
var response = new StudentSubmissionSummaryResponseDto
|
||||
{
|
||||
Submissions = result,
|
||||
TotalCount = totalCount
|
||||
};
|
||||
|
||||
return ApiResponse.Success(result: response);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ApiResponse.Error($"获取学生提交信息失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<int> CalculateErrorCountAsync(Guid submissionId)
|
||||
{
|
||||
|
||||
var submissionDetails = await _unitOfWork.GetRepository<SubmissionDetail>()
|
||||
.GetAll(sd => sd.SubmissionId == submissionId)
|
||||
.ToListAsync();
|
||||
return submissionDetails.Select(x => !x.IsCorrect).Count();
|
||||
}
|
||||
|
||||
// 以下方法是IBaseService接口的实现,可以根据需要实现
|
||||
public Task<ApiResponse> GetAllAsync() => throw new NotImplementedException();
|
||||
public Task<ApiResponse> GetAsync(Guid id) => throw new NotImplementedException();
|
||||
public Task<ApiResponse> AddAsync(StudentSubmissionSummaryDto model) => throw new NotImplementedException();
|
||||
public Task<ApiResponse> UpdateAsync(StudentSubmissionSummaryDto model) => throw new NotImplementedException();
|
||||
public Task<ApiResponse> DeleteAsync(Guid id) => throw new NotImplementedException();
|
||||
|
||||
public Task<ApiResponse> GetAllAsync(QueryParameter query)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user