- 添加学生提交管理服务 (StudentSubmissionService, StudentSubmissionDetailService) - 新增学生提交相关控制器 (StudentSubmissionController, StudentSubmissionDetailController) - 添加学生提交数据传输对象 (StudentSubmissionDetailDto, StudentSubmissionSummaryDto) - 新增学生提交相关页面组件 (StudentExamView, ExamDetailView, StudentCard等) - 添加学生提交信息卡片组件 (SubmissionInfoCard, TeacherSubmissionInfoCard) - 更新数据库迁移文件以支持提交系统
This commit is contained in:
		
							
								
								
									
										7
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    // 使用 IntelliSense 了解相关属性。 
 | 
			
		||||
    // 悬停以查看现有属性的描述。
 | 
			
		||||
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
 | 
			
		||||
    "version": "0.2.0",
 | 
			
		||||
    "configurations": []
 | 
			
		||||
}
 | 
			
		||||
@@ -48,6 +48,8 @@ namespace Entities.Contracts
 | 
			
		||||
		[Column("score")]
 | 
			
		||||
		public float? Score { get; set; }
 | 
			
		||||
 | 
			
		||||
		public bool BCorrect { get; set; }
 | 
			
		||||
 | 
			
		||||
		[Column("deleted")]
 | 
			
		||||
		public bool IsDeleted { get; set; }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,7 +34,7 @@ namespace Entities.Contracts
 | 
			
		||||
		public DateTime SubmissionTime { get; set; }
 | 
			
		||||
 | 
			
		||||
		[Column("overall_grade")]
 | 
			
		||||
		public float? OverallGrade { get; set; }
 | 
			
		||||
		public float OverallGrade { get; set; } = 0;
 | 
			
		||||
 | 
			
		||||
		[Column("overall_feedback")]
 | 
			
		||||
		public string? OverallFeedback { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -13,8 +13,9 @@ namespace Entities.DTO
 | 
			
		||||
		public string? DisplayName { get; set; }
 | 
			
		||||
 | 
			
		||||
		public UInt32 ErrorQuestionNum { get; set; }
 | 
			
		||||
		public Dictionary<QuestionType, UInt32> ErrorQuestionTypes { get; set; } = new Dictionary<QuestionType, UInt32>();	
 | 
			
		||||
		public Dictionary<string, UInt32> ErrorQuestionTypes { get; set; } = new Dictionary<string, UInt32>();	
 | 
			
		||||
		public Dictionary<SubjectAreaEnum, UInt32> SubjectAreaErrorQuestionDis { get; set; } = new Dictionary<SubjectAreaEnum, UInt32>();	
 | 
			
		||||
		public Dictionary<byte, UInt32> LessonErrorDis { get; set; } = new Dictionary<byte, UInt32>();	
 | 
			
		||||
		public float Score { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								Entities/DTO/StudentSubmissionDetailDto.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								Entities/DTO/StudentSubmissionDetailDto.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
using Entities.Contracts;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
 | 
			
		||||
namespace Entities.DTO
 | 
			
		||||
{
 | 
			
		||||
    public class StudentSubmissionDetailDto
 | 
			
		||||
    {
 | 
			
		||||
        // 基本信息
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public Guid AssignmentId { get; set; }
 | 
			
		||||
        public Guid StudentId { get; set; }
 | 
			
		||||
        public DateTime SubmissionTime { get; set; }
 | 
			
		||||
        public float OverallGrade { get; set; }
 | 
			
		||||
        public string OverallFeedback { get; set; } = string.Empty;
 | 
			
		||||
        public SubmissionStatus Status { get; set; }
 | 
			
		||||
        
 | 
			
		||||
        // Assignment信息
 | 
			
		||||
        public AssignmentDto Assignment { get; set; } = new AssignmentDto();
 | 
			
		||||
        
 | 
			
		||||
        // 错误分析
 | 
			
		||||
        public Dictionary<string, int> ErrorTypeDistribution { get; set; } = new Dictionary<string, int>();
 | 
			
		||||
        public Dictionary<string, float> ErrorTypeScoreDistribution { get; set; } = new Dictionary<string, float>();
 | 
			
		||||
        
 | 
			
		||||
        // 成绩统计
 | 
			
		||||
        public int TotalRank { get; set; }
 | 
			
		||||
        public List<float> AllScores { get; set; } = new List<float>();
 | 
			
		||||
        public float AverageScore { get; set; }
 | 
			
		||||
        public float ClassAverageScore { get; set; }
 | 
			
		||||
        
 | 
			
		||||
        // 课文分布
 | 
			
		||||
        public Dictionary<string, int> LessonErrorDistribution { get; set; } = new Dictionary<string, int>();
 | 
			
		||||
        public Dictionary<string, int> KeyPointErrorDistribution { get; set; } = new Dictionary<string, int>();
 | 
			
		||||
        
 | 
			
		||||
        // 基础统计
 | 
			
		||||
        public int TotalQuestions { get; set; }
 | 
			
		||||
        public int CorrectCount { get; set; }
 | 
			
		||||
        public int ErrorCount { get; set; }
 | 
			
		||||
        public float AccuracyRate { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								Entities/DTO/StudentSubmissionSummaryDto.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Entities/DTO/StudentSubmissionSummaryDto.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Entities.DTO
 | 
			
		||||
{
 | 
			
		||||
    public class StudentSubmissionSummaryDto
 | 
			
		||||
    {
 | 
			
		||||
        public Guid Id { get; set; }
 | 
			
		||||
        public string AssignmentName { get; set; }
 | 
			
		||||
        public int ErrorCount { get; set; }
 | 
			
		||||
        public DateTime CreatedDate { get; set; }
 | 
			
		||||
        public float Score { get; set; }
 | 
			
		||||
        public int TotalQuestions { get; set; }
 | 
			
		||||
        public string StudentName { get; set; }
 | 
			
		||||
        public string Status { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class StudentSubmissionSummaryResponseDto
 | 
			
		||||
    {
 | 
			
		||||
        public List<StudentSubmissionSummaryDto> Submissions { get; set; }
 | 
			
		||||
        public int TotalCount { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -24,13 +24,9 @@
 | 
			
		||||
				<MudNavMenu Bordered="true" Dense="true" Rounded="true" Color="Color.Error" Margin="Margin.Dense">
 | 
			
		||||
					<ApplicationMainIconCard></ApplicationMainIconCard>
 | 
			
		||||
					<MudDivider Class="my-2" />
 | 
			
		||||
					<MudNavLink Href="/">Dashboard</MudNavLink>
 | 
			
		||||
					<MudNavLink Href="/">Home</MudNavLink>
 | 
			
		||||
					<MudNavLink Href="/exam">Exam</MudNavLink>
 | 
			
		||||
					<MudNavLink Href="/exam">Billing</MudNavLink>
 | 
			
		||||
					<MudNavGroup Title="Settings" Expanded="true">
 | 
			
		||||
						<MudNavLink Href="/users">Users</MudNavLink>
 | 
			
		||||
						<MudNavLink Href="/security">Security</MudNavLink>
 | 
			
		||||
					</MudNavGroup>
 | 
			
		||||
					<MudNavLink Href="/students">Students</MudNavLink>
 | 
			
		||||
 | 
			
		||||
					<MudSpacer />
 | 
			
		||||
					<MudNavLink Class="align-content-end" Href="/about">About</MudNavLink>
 | 
			
		||||
@@ -38,7 +34,7 @@
 | 
			
		||||
				<MudSpacer />
 | 
			
		||||
				<MudNavMenu Class="align-content-end " Bordered="true" Dense="true" Rounded="true" Margin="Margin.Dense">
 | 
			
		||||
					<TechHelper.Client.Pages.Global.LoginInOut.LoginInOut></TechHelper.Client.Pages.Global.LoginInOut.LoginInOut>
 | 
			
		||||
					<MudNavLink Class="align-content-end" Href="/about">Setting</MudNavLink>
 | 
			
		||||
					<MudNavLink Class="align-content-end" Href="/Account/Manage">Setting</MudNavLink>
 | 
			
		||||
				</MudNavMenu>
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
		</MudDrawerHeader>
 | 
			
		||||
@@ -46,7 +42,7 @@
 | 
			
		||||
	<MudMainContent Style="background: #f5f6fb">
 | 
			
		||||
		<SnackErrorBoundary @ref="errorBoundary">
 | 
			
		||||
			<MudPaper Height="calc(100vh - 64px)" Style="background-color:transparent" Class="overflow-hidden px-1 py-2" Elevation="0">
 | 
			
		||||
				<MudPaper Style="background-color:#eeeeeeef" Elevation="3" Class="d-flex w-100 h-100 overflow-hidden pa-2 rounded-xl">
 | 
			
		||||
				<MudPaper Style="background-color:transparent" Elevation="0" Class="d-flex w-100 h-100 overflow-hidden pa-2 rounded-xl">
 | 
			
		||||
					@Body
 | 
			
		||||
				</MudPaper>
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
 | 
			
		||||
	<MudGrid Class="h-100">
 | 
			
		||||
 | 
			
		||||
		<MudItem xs="12" sm="2" Class="h-100 pa-1 mt-1">
 | 
			
		||||
		<MudItem sm="2" Class="h-100 pa-1 mt-1">
 | 
			
		||||
			<MudStack Class="h-100">
 | 
			
		||||
				<MudText Style="color:white"> BETA版本 </MudText>
 | 
			
		||||
				<MudText Style="color:white" Typo="Typo.h3"><b> 75  </b></MudText>
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
		</MudItem>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<MudItem xs="12" sm="9">
 | 
			
		||||
		<MudItem sm="9">
 | 
			
		||||
			<MudPaper Style="background-color:transparent" Class="w-100 mt-n3" Height="100%" Elevation="0">
 | 
			
		||||
				<MudChart ChartType="ChartType.Line" Class="pt-0" ChartSeries="@Series" XAxisLabels="@XAxisLabels" CanHideSeries
 | 
			
		||||
						  Height="150px" Width="100%" AxisChartOptions="_axisChartOptions" ChartOptions="options">
 | 
			
		||||
@@ -72,12 +72,6 @@
 | 
			
		||||
	</MudGrid>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
@* 				<MudChip Size="Size.Small" Text="pink" Variant="Variant.Text" Color="Color.Secondary">成绩趋势</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="blue" Variant="Variant.Text" Color="Color.Info">分值区间</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="green" Variant="Variant.Text" Color="Color.Success">Success</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="orange" Variant="Variant.Text" Color="Color.Warning">Warning</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="red" Variant="Variant.Text" Color="Color.Error">Error</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="black" Variant="Variant.Text" Color="Color.Dark">Dark</MudChip> *@
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	public double[] data = { 25, 77, 28, 5 };
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										191
									
								
								TechHelper.Client/Pages/Common/Exam/SubmissionInfoCard.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										191
									
								
								TechHelper.Client/Pages/Common/Exam/SubmissionInfoCard.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,191 @@
 | 
			
		||||
@using Entities.DTO
 | 
			
		||||
@using TechHelper.Client.Services
 | 
			
		||||
<MudPaper Class="rounded-xl w-100 px-10 ma-3 pt-5" Elevation="5" Height="170px" Style="background-color:#6bc6be">
 | 
			
		||||
 | 
			
		||||
	<MudGrid Class="h-100">
 | 
			
		||||
 | 
			
		||||
		<MudItem sm="2" Class="h-100 pa-1 mt-1">
 | 
			
		||||
			<MudStack Class="h-100">
 | 
			
		||||
				<MudText Style="color:white"> BETA版本 </MudText>
 | 
			
		||||
				<MudText Style="color:white" Typo="Typo.h3"><b> @StudentSubmissionDetail.AverageScore </b></MudText>
 | 
			
		||||
 | 
			
		||||
				<MudPaper Elevation=0 Class="h-100 w-100" Style="background-color:transparent">
 | 
			
		||||
					<MudStack Class="h-100" Row=true>
 | 
			
		||||
						<MudPaper Elevation=0 Height="100%" Width="100%" Style="background-color:transparent">
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									总数:
 | 
			
		||||
									<span style="color: #fefefe;"> @StudentSubmissionDetail.TotalQuestions </span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									总分:
 | 
			
		||||
									<span style="color: #fefefe;"> 150 </span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
						</MudPaper>
 | 
			
		||||
 | 
			
		||||
						<MudPaper Elevation=0 Height="100%" Width="100%" Style="background-color:transparent">
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									排名:
 | 
			
		||||
									<span style="color: #fefefe;"> @StudentSubmissionDetail.TotalRank </span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									平均:
 | 
			
		||||
									<span style="color: #fefefe;"> @StudentSubmissionDetail.ClassAverageScore </span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
						</MudPaper>
 | 
			
		||||
					</MudStack>
 | 
			
		||||
				</MudPaper>
 | 
			
		||||
			</MudStack>
 | 
			
		||||
		</MudItem>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<MudItem sm="9">
 | 
			
		||||
			<MudPaper Style="background-color:transparent" Class="w-100 mt-n3" Height="100%" Elevation="0">
 | 
			
		||||
				<MudChart ChartType="ChartType.Line" Class="pt-0" ChartSeries="@Series" XAxisLabels="@XAxisLabels" CanHideSeries
 | 
			
		||||
						  Height="150px" Width="100%" AxisChartOptions="_axisChartOptions" ChartOptions="options">
 | 
			
		||||
					<CustomGraphics>
 | 
			
		||||
						<style>
 | 
			
		||||
							.heavy {
 | 
			
		||||
								font: normal 12px helvetica;
 | 
			
		||||
								fill: rgb(255,255,255);
 | 
			
		||||
								letter-spacing: 2px;
 | 
			
		||||
							}
 | 
			
		||||
						</style>
 | 
			
		||||
 | 
			
		||||
						<text x="60" y="15" class="heavy"> 成绩的整体分布情况 </text>
 | 
			
		||||
					</CustomGraphics>
 | 
			
		||||
				</MudChart>
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
 | 
			
		||||
		</MudItem>
 | 
			
		||||
		<MudItem xs="12" sm="1">
 | 
			
		||||
			<MudChipSet T="string" SelectedValuesChanged="HandleSelectedValuesChanged" SelectedValues="@_selected" SelectionMode="SelectionMode.MultiSelection" CheckMark="true">
 | 
			
		||||
				<MudChip Size="Size.Small" Text="类型错误数量分布" Variant="Variant.Text" Color="Color.Default">类型分布</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="类型错误成绩分布" Variant="Variant.Text" Color="Color.Primary">课时分布</MudChip>
 | 
			
		||||
			</MudChipSet>
 | 
			
		||||
		</MudItem>
 | 
			
		||||
	</MudGrid>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
@* 				<MudChip Size="Size.Small" Text="pink" Variant="Variant.Text" Color="Color.Secondary">成绩趋势</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="blue" Variant="Variant.Text" Color="Color.Info">分值区间</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="green" Variant="Variant.Text" Color="Color.Success">Success</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="orange" Variant="Variant.Text" Color="Color.Warning">Warning</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="red" Variant="Variant.Text" Color="Color.Error">Error</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="black" Variant="Variant.Text" Color="Color.Dark">Dark</MudChip> *@
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	private AxisChartOptions _axisChartOptions = new AxisChartOptions
 | 
			
		||||
	{
 | 
			
		||||
	};
 | 
			
		||||
	private ChartOptions options = new ChartOptions
 | 
			
		||||
	{
 | 
			
		||||
		InterpolationOption = InterpolationOption.NaturalSpline,
 | 
			
		||||
		YAxisFormat = "c2",
 | 
			
		||||
		ShowLegend = false,
 | 
			
		||||
		YAxisLines = false,
 | 
			
		||||
		XAxisLines = false,
 | 
			
		||||
		XAxisLabelPosition = XAxisLabelPosition.None,
 | 
			
		||||
		YAxisLabelPosition = YAxisLabelPosition.None,
 | 
			
		||||
		YAxisTicks = 100,
 | 
			
		||||
		ShowLabels = false,
 | 
			
		||||
		ShowLegendLabels = false
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
	public List<ChartSeries> Series = new List<ChartSeries>()
 | 
			
		||||
	{
 | 
			
		||||
		new ChartSeries() { Name = "类型错误数量分布", Data = new double[] { 35, 41, 35, 51, 49, 62, 69, 91, 148 } },
 | 
			
		||||
	};
 | 
			
		||||
	public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" };
 | 
			
		||||
 | 
			
		||||
	Random random = new Random();
 | 
			
		||||
	protected override void OnInitialized()
 | 
			
		||||
	{
 | 
			
		||||
		options.InterpolationOption = InterpolationOption.NaturalSpline;
 | 
			
		||||
		options.YAxisFormat = "c2";
 | 
			
		||||
		options.ShowLegend = false;
 | 
			
		||||
		options.YAxisLines = false;
 | 
			
		||||
		options.XAxisLines = false;
 | 
			
		||||
		options.XAxisLabelPosition = XAxisLabelPosition.None;
 | 
			
		||||
		options.YAxisLabelPosition = YAxisLabelPosition.None;
 | 
			
		||||
		options.ShowLabels = false;
 | 
			
		||||
		options.ShowLegendLabels = false;
 | 
			
		||||
		options.LineStrokeWidth = 1;
 | 
			
		||||
		_axisChartOptions.MatchBoundsToSize = true;
 | 
			
		||||
 | 
			
		||||
		Series[0].LineDisplayType = LineDisplayType.Area;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[Parameter]
 | 
			
		||||
	public Guid SubmissionID { get; set; } = Guid.Empty;
 | 
			
		||||
 | 
			
		||||
	private StudentSubmissionDetailDto StudentSubmissionDetail { get; set; } = new StudentSubmissionDetailDto();
 | 
			
		||||
	private IReadOnlyCollection<string> _selected;
 | 
			
		||||
#pragma warning restore 1998
 | 
			
		||||
#nullable restore
 | 
			
		||||
#line (82, 8) - (143, 1) "D:\AllWX\AllC\TechHelper\TechHelper.Client\Pages\Common\Exam\SubmissionInfoCard.razor"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	[Inject]
 | 
			
		||||
	public IStudentSubmissionDetailService StudentSubmissionDetailService { get; set; }
 | 
			
		||||
	[Inject]
 | 
			
		||||
	public ISnackbar Snackbar { get; set; }
 | 
			
		||||
	protected override async Task OnInitializedAsync()
 | 
			
		||||
	{
 | 
			
		||||
		if (SubmissionID != Guid.Empty)
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
			StudentSubmissionDetailDto result;
 | 
			
		||||
			try
 | 
			
		||||
			{
 | 
			
		||||
				result = await StudentSubmissionDetailService.GetSubmissionDetailAsync(SubmissionID);
 | 
			
		||||
 | 
			
		||||
				if (result != null)
 | 
			
		||||
				{
 | 
			
		||||
					StudentSubmissionDetail = result;
 | 
			
		||||
					XAxisLabels = result.ErrorTypeDistribution.Keys.ToArray();
 | 
			
		||||
					Series.Clear();
 | 
			
		||||
					Series.Add(new ChartSeries
 | 
			
		||||
					{
 | 
			
		||||
						Name = "类型错误数量分布",
 | 
			
		||||
						Data = result.ErrorTypeDistribution.Values.Select(d => (double)d).ToArray()
 | 
			
		||||
					});
 | 
			
		||||
					Series.Add(new ChartSeries
 | 
			
		||||
					{
 | 
			
		||||
						Name = "类型错误成绩分布",
 | 
			
		||||
						Data = result.ErrorTypeScoreDistribution.Values.Select(d => (double)d).ToArray()
 | 
			
		||||
					});
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			catch (Exception ex)
 | 
			
		||||
			{
 | 
			
		||||
				Snackbar.Add($"获取提交错误, 请重试, {ex.Message}", Severity.Warning);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private void HandleSelectedValuesChanged(IReadOnlyCollection<string> selected)
 | 
			
		||||
	{
 | 
			
		||||
		Series.ForEach(x => x.Visible = false);
 | 
			
		||||
 | 
			
		||||
		foreach (var item in selected)
 | 
			
		||||
		{
 | 
			
		||||
			var sv = Series.FirstOrDefault(predicate: x => x.Name == item);
 | 
			
		||||
			if (sv != null)
 | 
			
		||||
			{
 | 
			
		||||
				sv.Visible = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,143 @@
 | 
			
		||||
<MudPaper Class="rounded-xl w-100 px-10 ma-3 pt-5" Elevation="5" Height="170px" Style="background-color:#6bc6be">
 | 
			
		||||
 | 
			
		||||
	<MudGrid Class="h-100">
 | 
			
		||||
 | 
			
		||||
		<MudItem xs="12" sm="2" Class="h-100 pa-1 mt-1">
 | 
			
		||||
			<MudStack Class="h-100">
 | 
			
		||||
				<MudText Style="color:white"> BETA版本 </MudText>
 | 
			
		||||
				<MudText Style="color:white" Typo="Typo.h3"><b> 75  </b></MudText>
 | 
			
		||||
 | 
			
		||||
				<MudPaper Elevation=0 Class="h-100 w-100" Style="background-color:transparent">
 | 
			
		||||
					<MudStack Class="h-100" Row=true>
 | 
			
		||||
						<MudPaper Elevation=0 Height="100%" Width="100%" Style="background-color:transparent">
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									总数:
 | 
			
		||||
									<span style="color: #fefefe;">15</span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									总分:
 | 
			
		||||
									<span style="color: #fefefe;">15</span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
						</MudPaper>
 | 
			
		||||
 | 
			
		||||
						<MudPaper Elevation=0 Height="100%" Width="100%" Style="background-color:transparent">
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText  Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									中位:
 | 
			
		||||
									<span style="color: #fefefe;">15</span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
							<MudPaper Elevation=0 Height="50%" Style="background-color:transparent">
 | 
			
		||||
								<MudText Style="color:#9ed5f7" Typo="Typo.body2">
 | 
			
		||||
									方差:
 | 
			
		||||
									<span style="color: #fefefe;">15</span>
 | 
			
		||||
								</MudText>
 | 
			
		||||
							</MudPaper>
 | 
			
		||||
						</MudPaper>
 | 
			
		||||
					</MudStack>
 | 
			
		||||
				</MudPaper>
 | 
			
		||||
			</MudStack>
 | 
			
		||||
		</MudItem>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		<MudItem xs="12" sm="9">
 | 
			
		||||
			<MudPaper Style="background-color:transparent" Class="w-100 mt-n3" Height="100%" Elevation="0">
 | 
			
		||||
				<MudChart ChartType="ChartType.Line" Class="pt-0" ChartSeries="@Series" XAxisLabels="@XAxisLabels" CanHideSeries
 | 
			
		||||
						  Height="150px" Width="100%" AxisChartOptions="_axisChartOptions" ChartOptions="options">
 | 
			
		||||
					<CustomGraphics>
 | 
			
		||||
						<style>
 | 
			
		||||
							.heavy {
 | 
			
		||||
								font: normal 12px helvetica;
 | 
			
		||||
								fill: rgb(255,255,255);
 | 
			
		||||
								letter-spacing: 2px;
 | 
			
		||||
							}
 | 
			
		||||
						</style>
 | 
			
		||||
 | 
			
		||||
						<text x="60" y="15" class="heavy"> 成绩的整体分布情况 </text>
 | 
			
		||||
					</CustomGraphics>
 | 
			
		||||
				</MudChart>
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
 | 
			
		||||
		</MudItem>
 | 
			
		||||
		<MudItem xs="12" sm="1">
 | 
			
		||||
			<MudChipSet T="string" SelectedValuesChanged="HandleSelectedValuesChanged" SelectedValues="@_selected" SelectionMode="SelectionMode.MultiSelection" CheckMark="true">
 | 
			
		||||
				<MudChip Size="Size.Small" Text="类型错误数量分布" Variant="Variant.Text" Color="Color.Default">类型分布</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="类型错误成绩分布" Variant="Variant.Text" Color="Color.Primary">课时分布</MudChip>
 | 
			
		||||
			</MudChipSet>
 | 
			
		||||
		</MudItem>
 | 
			
		||||
	</MudGrid>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
@* 				<MudChip Size="Size.Small" Text="pink" Variant="Variant.Text" Color="Color.Secondary">成绩趋势</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="blue" Variant="Variant.Text" Color="Color.Info">分值区间</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="green" Variant="Variant.Text" Color="Color.Success">Success</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="orange" Variant="Variant.Text" Color="Color.Warning">Warning</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="red" Variant="Variant.Text" Color="Color.Error">Error</MudChip>
 | 
			
		||||
				<MudChip Size="Size.Small" Text="black" Variant="Variant.Text" Color="Color.Dark">Dark</MudChip> *@
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	public double[] data = { 25, 77, 28, 5 };
 | 
			
		||||
	public string[] labels = { "Oil", "Coal", "Gas", "Biomass" };
 | 
			
		||||
	private AxisChartOptions _axisChartOptions = new AxisChartOptions
 | 
			
		||||
	{
 | 
			
		||||
	};
 | 
			
		||||
	private ChartOptions options = new ChartOptions
 | 
			
		||||
	{
 | 
			
		||||
		InterpolationOption = InterpolationOption.NaturalSpline,
 | 
			
		||||
		YAxisFormat = "c2",
 | 
			
		||||
		ShowLegend = false,
 | 
			
		||||
		YAxisLines = false,
 | 
			
		||||
		XAxisLines = false,
 | 
			
		||||
		XAxisLabelPosition = XAxisLabelPosition.None,
 | 
			
		||||
		YAxisLabelPosition = YAxisLabelPosition.None,
 | 
			
		||||
		YAxisTicks = 100,
 | 
			
		||||
		ShowLabels = false,
 | 
			
		||||
		ShowLegendLabels = false
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
	public List<ChartSeries> Series = new List<ChartSeries>()
 | 
			
		||||
	{
 | 
			
		||||
		new ChartSeries() { Name = "类型错误数量分布", Data = new double[] { 35, 41, 35, 51, 49, 62, 69, 91, 148 } },
 | 
			
		||||
		new ChartSeries() { Name = "类型错误成绩分布", Data = new double[] { 55, 21, 45, 11, 45, 23, 11, 56, 13 } },
 | 
			
		||||
	};
 | 
			
		||||
	public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" };
 | 
			
		||||
 | 
			
		||||
	Random random = new Random();
 | 
			
		||||
	protected override void OnInitialized()
 | 
			
		||||
	{
 | 
			
		||||
		options.InterpolationOption = InterpolationOption.NaturalSpline;
 | 
			
		||||
		options.YAxisFormat = "c2";
 | 
			
		||||
		options.ShowLegend = false;
 | 
			
		||||
		options.YAxisLines = false;
 | 
			
		||||
		options.XAxisLines = false;
 | 
			
		||||
		options.XAxisLabelPosition = XAxisLabelPosition.None;
 | 
			
		||||
		options.YAxisLabelPosition = YAxisLabelPosition.None;
 | 
			
		||||
		options.ShowLabels = false;
 | 
			
		||||
		options.ShowLegendLabels = false;
 | 
			
		||||
		options.LineStrokeWidth = 1;
 | 
			
		||||
		_axisChartOptions.MatchBoundsToSize = true;
 | 
			
		||||
 | 
			
		||||
		Series[0].LineDisplayType = LineDisplayType.Area;
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private IReadOnlyCollection<string> _selected;
 | 
			
		||||
 | 
			
		||||
	private void HandleSelectedValuesChanged(IReadOnlyCollection<string> selected)
 | 
			
		||||
	{
 | 
			
		||||
		Series.ForEach(x => x.Visible = false);
 | 
			
		||||
 | 
			
		||||
		foreach(var item in selected)
 | 
			
		||||
		{
 | 
			
		||||
			var sv = Series.FirstOrDefault(predicate: x => x.Name == item);
 | 
			
		||||
			if(sv != null)
 | 
			
		||||
			{
 | 
			
		||||
				sv.Visible = true;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								TechHelper.Client/Pages/Exam/AssignmentManagerCard.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								TechHelper.Client/Pages/Exam/AssignmentManagerCard.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
<MudPaper>
 | 
			
		||||
	<MudText> ExamName </MudText>
 | 
			
		||||
	<MudText> 已经指派人数 </MudText>
 | 
			
		||||
	<MudText> 总人数 </MudText>
 | 
			
		||||
	<MudText> 平均S </MudText>
 | 
			
		||||
	<MudText> 指派 </MudText>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
@@ -21,8 +21,6 @@ else
 | 
			
		||||
<MudPaper Class="d-flex flex-wrap flex-grow-0 gap-4" Height="100%" Width="100%">
 | 
			
		||||
	@foreach (var item in examDtos)
 | 
			
		||||
	{
 | 
			
		||||
		@* <ExamPreview AssignmentDto="item" Width="256px" Height="256px"> </ExamPreview> *@
 | 
			
		||||
		<AssignmentInfoCard></AssignmentInfoCard>
 | 
			
		||||
	}
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,34 @@
 | 
			
		||||
@page "/exam"
 | 
			
		||||
@using TechHelper.Client.Pages.Student.BaseInfoCard
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
 | 
			
		||||
<AuthorizeView Roles="Teacher">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
		<MudPaper Class="rounded-xl ma-2 px-2 overflow-auto w-100 h-100">
 | 
			
		||||
			<StudentSubmissionPreviewTableCard />
 | 
			
		||||
		</MudPaper>
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<MudText>HELLO WORLD</MudText>
 | 
			
		||||
<AuthorizeView Roles="Student">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
		<MudPaper Class="rounded-xl ma-2 px-2 overflow-auto w-100 h-100">
 | 
			
		||||
			<StudentSubmissionPreviewTableCard />
 | 
			
		||||
		</MudPaper>
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
@code {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
	[CascadingParameter]
 | 
			
		||||
	private Task<AuthenticationState> authenticationStateTask { get; set; }
 | 
			
		||||
 | 
			
		||||
	protected override void OnParametersSet()
 | 
			
		||||
	{
 | 
			
		||||
		if (authenticationStateTask is null)
 | 
			
		||||
		{
 | 
			
		||||
			NavigationManager.Refresh(forceReload: true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								TechHelper.Client/Pages/Exam/StudentExamView.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								TechHelper.Client/Pages/Exam/StudentExamView.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,6 @@
 | 
			
		||||
		<TechHelper.Client.Pages.Student.HomePage />
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	[CascadingParameter]
 | 
			
		||||
	private Task<AuthenticationState> authenticationStateTask { get; set; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +1,97 @@
 | 
			
		||||
<MudPaper Class="ma-2 pa-2 rounded-xl d-flex flex-column flex-grow-1 overflow-auto" MaxHeight="100%">
 | 
			
		||||
@using TechHelper.Client.Services
 | 
			
		||||
@inject IStudentSubmissionService StudentSubmissionService
 | 
			
		||||
 | 
			
		||||
<MudPaper Class="ma-2 pa-2 rounded-xl d-flex flex-column flex-grow-1 overflow-auto" MaxHeight="100%">
 | 
			
		||||
 | 
			
		||||
	<StudentSubmissionPreviewCard />
 | 
			
		||||
	@foreach (var submission in _studentSubmissions)
 | 
			
		||||
	@if (_isLoading)
 | 
			
		||||
	{
 | 
			
		||||
		<StudentSubmissionPreviewCard StudentSubmission="@submission" />
 | 
			
		||||
		<div class="d-flex justify-content-center align-items-center" style="height: 200px;">
 | 
			
		||||
			<MudProgressCircular Color="Color.Primary" Size="Size.Large" />
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
	else if (_studentSubmissions == null || _studentSubmissions.Count == 0)
 | 
			
		||||
	{
 | 
			
		||||
		<div class="d-flex justify-content-center align-items-center" style="height: 200px;">
 | 
			
		||||
			<MudText TextColor="Color.TextSecondary" Align="Align.Center">暂无提交记录</MudText>
 | 
			
		||||
		</div>
 | 
			
		||||
	}
 | 
			
		||||
	else
 | 
			
		||||
	{
 | 
			
		||||
		@foreach (var submission in _studentSubmissions)
 | 
			
		||||
		{
 | 
			
		||||
			<StudentSubmissionPreviewCard StudentSubmission="@submission" />
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	// 假设的学生提交数据模型
 | 
			
		||||
	// 学生提交数据模型
 | 
			
		||||
	public class StudentSubmission
 | 
			
		||||
	{
 | 
			
		||||
		public string StudentName { get; set; }
 | 
			
		||||
		public int TotalProblems { get; set; }
 | 
			
		||||
		public int ErrorCount { get; set; }
 | 
			
		||||
		public DateTime CreatedDate { get; set; }
 | 
			
		||||
		public float Score { get; set; }
 | 
			
		||||
		public string AssignmentName { get; set; }
 | 
			
		||||
		public string Status { get; set; }
 | 
			
		||||
		public TimeSpan TimeSpent { get; set; }
 | 
			
		||||
		public int Score { get; set; }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 模拟数据列表
 | 
			
		||||
	// 学生提交列表
 | 
			
		||||
	private List<StudentSubmission> _studentSubmissions = new();
 | 
			
		||||
	private bool _isLoading = true;
 | 
			
		||||
 | 
			
		||||
	protected override void OnInitialized()
 | 
			
		||||
	protected override async Task OnInitializedAsync()
 | 
			
		||||
	{
 | 
			
		||||
		// 模拟获取或初始化数据,实际应用中可能来自数据库或API
 | 
			
		||||
		_studentSubmissions = new List<StudentSubmission>
 | 
			
		||||
		{
 | 
			
		||||
			new() { StudentName = "张三", TotalProblems = 10, ErrorCount = 2, TimeSpent = TimeSpan.FromMinutes(25), Score = 80 },
 | 
			
		||||
			new() { StudentName = "李四", TotalProblems = 10, ErrorCount = 1, TimeSpent = TimeSpan.FromMinutes(20), Score = 90 },
 | 
			
		||||
			new() { StudentName = "王五", TotalProblems = 10, ErrorCount = 5, TimeSpent = TimeSpan.FromMinutes(30), Score = 50 },
 | 
			
		||||
			new() { StudentName = "赵六", TotalProblems = 10, ErrorCount = 3, TimeSpent = TimeSpan.FromMinutes(28), Score = 70 },
 | 
			
		||||
			new() { StudentName = "钱七", TotalProblems = 10, ErrorCount = 0, TimeSpent = TimeSpan.FromMinutes(18), Score = 100 }
 | 
			
		||||
            // ... 可以添加更多模拟数据
 | 
			
		||||
        };
 | 
			
		||||
		await LoadStudentSubmissions();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	private async Task LoadStudentSubmissions()
 | 
			
		||||
	{
 | 
			
		||||
		try
 | 
			
		||||
		{
 | 
			
		||||
			_isLoading = true;
 | 
			
		||||
			StateHasChanged();
 | 
			
		||||
 | 
			
		||||
			var result = await StudentSubmissionService.GetMySubmissionsAsync();
 | 
			
		||||
			
 | 
			
		||||
			if (result.Status && result.Result != null)
 | 
			
		||||
			{
 | 
			
		||||
				// 从服务器获取的数据映射到我们的模型
 | 
			
		||||
				var submissions = result.Result as List<Entities.DTO.StudentSubmissionSummaryDto>;
 | 
			
		||||
				
 | 
			
		||||
				if (submissions != null)
 | 
			
		||||
				{
 | 
			
		||||
					_studentSubmissions = submissions.Select(submission => new StudentSubmission
 | 
			
		||||
					{
 | 
			
		||||
						AssignmentName = submission.AssignmentName,
 | 
			
		||||
						CreatedDate = submission.CreatedDate,
 | 
			
		||||
						ErrorCount = submission.ErrorCount,
 | 
			
		||||
						Score = submission.Score,
 | 
			
		||||
						StudentName = submission.StudentName,
 | 
			
		||||
						Status = submission.Status,
 | 
			
		||||
						TotalProblems = submission.TotalQuestions,
 | 
			
		||||
						TimeSpent = TimeSpan.FromMinutes(30) // 默认值,实际应用中可以从服务器获取
 | 
			
		||||
					}).ToList();
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else
 | 
			
		||||
			{
 | 
			
		||||
				// 如果API调用失败,使用空列表
 | 
			
		||||
				_studentSubmissions = new List<StudentSubmission>();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		catch (Exception ex)
 | 
			
		||||
		{
 | 
			
		||||
			// 处理异常,可以记录日志
 | 
			
		||||
			_studentSubmissions = new List<StudentSubmission>();
 | 
			
		||||
		}
 | 
			
		||||
		finally
 | 
			
		||||
		{
 | 
			
		||||
			_isLoading = false;
 | 
			
		||||
			StateHasChanged();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
@using TechHelper.Client.Pages.Student.BaseInfoCard;
 | 
			
		||||
@using TechHelper.Client.Pages.Common;
 | 
			
		||||
 | 
			
		||||
<MudPaper Class="w-100 h-100 d-flex flex-row">
 | 
			
		||||
	<MudPaper Class="flex-grow-1 mx-2 d-flex flex-column">
 | 
			
		||||
		<AssignmentInfoCard></AssignmentInfoCard>
 | 
			
		||||
		<MudPaper Class="d-flex flex-row">
 | 
			
		||||
<MudPaper Class="w-100 h-100 d-flex flex-row" Style="background-color: transparent" Elevation="0">
 | 
			
		||||
	<MudPaper Class="flex-grow-1 mx-2 d-flex flex-column" Style="background-color:transparent" Elevation="0">
 | 
			
		||||
		<SubmissionInfoCard ></SubmissionInfoCard>
 | 
			
		||||
		<MudPaper Class="d-flex flex-row" Style="background-color:transparent" Elevation="0">
 | 
			
		||||
			<NotifyCard></NotifyCard>
 | 
			
		||||
			<HomeworkCard></HomeworkCard>
 | 
			
		||||
			<NotifyCard></NotifyCard>
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
		</MudPaper>
 | 
			
		||||
		<StudentSubmissionPreviewTableCard></StudentSubmissionPreviewTableCard>
 | 
			
		||||
	</MudPaper>
 | 
			
		||||
	<MudPaper Width="300px" Class="mx-2 align-content-center d-flex flex-column flex-grow-1">
 | 
			
		||||
	<MudPaper Width="300px" Class="mx-2 align-content-center d-flex flex-column flex-grow-1" Style="background-color: transparent" Elevation="0">
 | 
			
		||||
		<HeadIconCard></HeadIconCard>
 | 
			
		||||
		<TotalErrorQuestionType></TotalErrorQuestionType>
 | 
			
		||||
 | 
			
		||||
@@ -22,42 +22,4 @@
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	public double[] data = { 25, 77, 28, 5 };
 | 
			
		||||
	public string[] labels = { "Oil", "Coal", "Gas", "Biomass" };
 | 
			
		||||
	private AxisChartOptions _axisChartOptions = new AxisChartOptions();
 | 
			
		||||
	private ChartOptions options = new ChartOptions();
 | 
			
		||||
	public List<ChartSeries> Series = new List<ChartSeries>()
 | 
			
		||||
	{
 | 
			
		||||
		new ChartSeries() { Name = "Series 1", Data = new double[] { 90, 79, 72, 69, 62, 62, 55, 65, 70 } },
 | 
			
		||||
		new ChartSeries() { Name = "Series 2", Data = new double[] { 35, 41, 35, 51, 49, 62, 69, 91, 148 } },
 | 
			
		||||
	};
 | 
			
		||||
	public string[] XAxisLabels = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep" };
 | 
			
		||||
 | 
			
		||||
	Random random = new Random();
 | 
			
		||||
	protected override void OnInitialized()
 | 
			
		||||
	{
 | 
			
		||||
		options.InterpolationOption = InterpolationOption.NaturalSpline;
 | 
			
		||||
		options.YAxisFormat = "c2";
 | 
			
		||||
		_axisChartOptions.MatchBoundsToSize = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	public void RandomizeData()
 | 
			
		||||
	{
 | 
			
		||||
		foreach (var series in Series)
 | 
			
		||||
		{
 | 
			
		||||
			for (int i = 0; i < series.Data.Length - 1; i++)
 | 
			
		||||
			{
 | 
			
		||||
				series.Data[i] = random.NextDouble() * 100 + 10;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		StateHasChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void OnClickMenu(InterpolationOption interpolationOption)
 | 
			
		||||
	{
 | 
			
		||||
		options.InterpolationOption = interpolationOption;
 | 
			
		||||
		StateHasChanged();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										3
									
								
								TechHelper.Client/Pages/Teacher/ExamDetailView.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								TechHelper.Client/Pages/Teacher/ExamDetailView.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
<MudPaper>
 | 
			
		||||
	<MudText> EXAM NAME</MudText>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
							
								
								
									
										7
									
								
								TechHelper.Client/Pages/Teacher/StudentCard.razor
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								TechHelper.Client/Pages/Teacher/StudentCard.razor
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
@using Entities.DTO
 | 
			
		||||
<h3>StudentCard</h3>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
	[Parameter]
 | 
			
		||||
	public StudentDto StudentDto{ get; set; }
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,9 @@
 | 
			
		||||
 | 
			
		||||
@foreach(var cs in ClassStudents)
 | 
			
		||||
{
 | 
			
		||||
	<MudText> @cs.DisplayName </MudText>
 | 
			
		||||
	<StudentCard StudentDto="@cs">
 | 
			
		||||
	</StudentCard>
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,8 @@ builder.Services.AddLocalStorageServices();
 | 
			
		||||
 | 
			
		||||
builder.Services.AddScoped<IAuthenticationClientService, AuthenticationClientService>();
 | 
			
		||||
builder.Services.AddScoped<IExamService, ExamService>();
 | 
			
		||||
builder.Services.AddScoped<IStudentSubmissionService, StudentSubmissionService>();
 | 
			
		||||
builder.Services.AddScoped<IStudentSubmissionDetailService, StudentSubmissionDetailService>();
 | 
			
		||||
builder.Services.AddScoped<AuthenticationStateProvider, AuthStateProvider>();
 | 
			
		||||
builder.Services.AddScoped<IRefreshTokenService, RefreshTokenService>();
 | 
			
		||||
builder.Services.AddScoped<IClassServices, ClasssServices>();
 | 
			
		||||
 
 | 
			
		||||
@@ -54,6 +54,11 @@ namespace TechHelper.Client.Services
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public StudentDto GetStudents(byte Class)
 | 
			
		||||
		{
 | 
			
		||||
			throw new NotImplementedException();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public async Task<ResponseDto> UserRegister(UserRegistrationToClassDto userRegistrationToClassDto)
 | 
			
		||||
		{
 | 
			
		||||
			try
 | 
			
		||||
 
 | 
			
		||||
@@ -10,5 +10,6 @@ namespace TechHelper.Client.Services
 | 
			
		||||
		public Task<ResponseDto> CreateClass(UserRegistrationToClassDto userClass);
 | 
			
		||||
		public Task<ApiResponse> GetClassStudents();
 | 
			
		||||
		public Task<ApiResponse> GetGradeClasses(byte grade);
 | 
			
		||||
		public StudentDto GetStudents(byte Class);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,14 @@
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Client.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface IStudentSubmissionDetailService
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取学生提交的详细信息
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="submissionId">提交ID</param>
 | 
			
		||||
        /// <returns>学生提交详细信息</returns>
 | 
			
		||||
        Task<StudentSubmissionDetailDto> GetSubmissionDetailAsync(Guid submissionId);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								TechHelper.Client/Services/IStudentSubmissionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								TechHelper.Client/Services/IStudentSubmissionService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
using TechHelper.Services;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Client.Services
 | 
			
		||||
{
 | 
			
		||||
    public interface IStudentSubmissionService
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的所有提交摘要
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>学生提交摘要列表</returns>
 | 
			
		||||
        Task<ApiResponse> GetMySubmissionsAsync();
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的提交摘要(分页)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="pageNumber">页码,默认为1</param>
 | 
			
		||||
        /// <param name="pageSize">每页数量,默认为10</param>
 | 
			
		||||
        /// <returns>分页的学生提交摘要列表</returns>
 | 
			
		||||
        Task<ApiResponse> GetMySubmissionsPagedAsync(int pageNumber = 1, int pageSize = 10);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										26
									
								
								TechHelper.Client/Services/StudentSubmissionDetailService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								TechHelper.Client/Services/StudentSubmissionDetailService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
using TechHelper.Client.HttpRepository;
 | 
			
		||||
using System.Net.Http.Json;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Client.Services
 | 
			
		||||
{
 | 
			
		||||
    public class StudentSubmissionDetailService : IStudentSubmissionDetailService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly HttpClient _httpClient;
 | 
			
		||||
 | 
			
		||||
        public StudentSubmissionDetailService(HttpClient httpClient)
 | 
			
		||||
        {
 | 
			
		||||
            _httpClient = httpClient;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<StudentSubmissionDetailDto> GetSubmissionDetailAsync(Guid submissionId)
 | 
			
		||||
        {
 | 
			
		||||
            var response = await _httpClient.GetAsync($"api/student-submission-detail/{submissionId}");
 | 
			
		||||
            if (response.IsSuccessStatusCode)
 | 
			
		||||
            {
 | 
			
		||||
                return await response.Content.ReadFromJsonAsync<StudentSubmissionDetailDto>();
 | 
			
		||||
            }
 | 
			
		||||
            throw new HttpRequestException($"获取学生提交详细信息失败: {response.StatusCode}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								TechHelper.Client/Services/StudentSubmissionService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								TechHelper.Client/Services/StudentSubmissionService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
using TechHelper.Services;
 | 
			
		||||
using System.Net.Http.Json;
 | 
			
		||||
using Newtonsoft.Json;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Client.Services
 | 
			
		||||
{
 | 
			
		||||
    public class StudentSubmissionService : IStudentSubmissionService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly HttpClient _client;
 | 
			
		||||
 | 
			
		||||
        public StudentSubmissionService(HttpClient client)
 | 
			
		||||
        {
 | 
			
		||||
            _client = client;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的所有提交摘要
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>学生提交摘要列表</returns>
 | 
			
		||||
        public async Task<ApiResponse> GetMySubmissionsAsync()
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var response = await _client.GetAsync("student-submission/my-submissions");
 | 
			
		||||
                
 | 
			
		||||
                if (response.IsSuccessStatusCode)
 | 
			
		||||
                {
 | 
			
		||||
                    var content = await response.Content.ReadAsStringAsync();
 | 
			
		||||
                    var submissions = JsonConvert.DeserializeObject<List<StudentSubmissionSummaryDto>>(content);
 | 
			
		||||
                    return ApiResponse.Success(result: submissions);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    var errorContent = await response.Content.ReadAsStringAsync();
 | 
			
		||||
                    return ApiResponse.Error(message: $"获取学生提交信息失败: {response.StatusCode} - {errorContent}");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return ApiResponse.Error(message: $"内部错误: {ex.Message}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的提交摘要(分页)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="pageNumber">页码,默认为1</param>
 | 
			
		||||
        /// <param name="pageSize">每页数量,默认为10</param>
 | 
			
		||||
        /// <returns>分页的学生提交摘要列表</returns>
 | 
			
		||||
        public async Task<ApiResponse> GetMySubmissionsPagedAsync(int pageNumber = 1, int pageSize = 10)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                var response = await _client.GetAsync($"student-submission/my-submissions-paged?pageNumber={pageNumber}&pageSize={pageSize}");
 | 
			
		||||
                
 | 
			
		||||
                if (response.IsSuccessStatusCode)
 | 
			
		||||
                {
 | 
			
		||||
                    var content = await response.Content.ReadAsStringAsync();
 | 
			
		||||
                    var result = JsonConvert.DeserializeObject<StudentSubmissionSummaryResponseDto>(content);
 | 
			
		||||
                    return ApiResponse.Success(result: result);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    var errorContent = await response.Content.ReadAsStringAsync();
 | 
			
		||||
                    return ApiResponse.Error(message: $"获取学生提交信息失败: {response.StatusCode} - {errorContent}");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return ApiResponse.Error(message: $"内部错误: {ex.Message}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,16 +1,44 @@
 | 
			
		||||
@inherits LayoutComponentBase
 | 
			
		||||
@layout AccountLayout
 | 
			
		||||
@inject NavigationManager NavigationManager
 | 
			
		||||
 | 
			
		||||
<AuthorizeView Roles="Teacher">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
		<MudPaper Class="d-flex flex-row flex-grow-1 overflow-hidden" Height="100%">
 | 
			
		||||
			<MudPaper Width="200px">
 | 
			
		||||
				<MudDivider Class="flex-grow-0" />
 | 
			
		||||
				<ExamNavMenu />
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
			<MudPaper Class="d-flex flex-grow-1 overflow-auto">
 | 
			
		||||
				@Body
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
		</MudPaper>
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
 | 
			
		||||
<MudPaper Class="d-flex flex-row flex-grow-1 overflow-hidden" Height="100%">
 | 
			
		||||
<AuthorizeView Roles="Student">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
		<MudPaper Class="d-flex flex-row flex-grow-1 overflow-hidden" Height="100%">
 | 
			
		||||
			<MudPaper Width="200px">
 | 
			
		||||
				<MudDivider Class="flex-grow-0" />
 | 
			
		||||
				<ExamNavMenu />
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
			<MudPaper Class="d-flex h-100 w-100 flex-grow-1 overflow-auto">
 | 
			
		||||
				@Body
 | 
			
		||||
			</MudPaper>
 | 
			
		||||
		</MudPaper>
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
 | 
			
		||||
	<MudPaper Width="200px">
 | 
			
		||||
		<MudDivider Class="flex-grow-0" />
 | 
			
		||||
		<ExamNavMenu />
 | 
			
		||||
@code {
 | 
			
		||||
	[CascadingParameter]
 | 
			
		||||
	private Task<AuthenticationState> authenticationStateTask { get; set; }
 | 
			
		||||
 | 
			
		||||
	</MudPaper>
 | 
			
		||||
 | 
			
		||||
	<MudPaper Class="flex-grow-1 overflow-auto">
 | 
			
		||||
		@Body
 | 
			
		||||
	</MudPaper>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
	protected override void OnParametersSet()
 | 
			
		||||
	{
 | 
			
		||||
		if (authenticationStateTask is null)
 | 
			
		||||
		{
 | 
			
		||||
			NavigationManager.Refresh(forceReload: true);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,39 @@
 | 
			
		||||
@using Microsoft.AspNetCore.Identity
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<ul class="nav nav-pills flex-column">
 | 
			
		||||
    <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="exam/create" Match="NavLinkMatch.All">创建</NavLink>
 | 
			
		||||
    </li>
 | 
			
		||||
    <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="exam/manage">管理</NavLink>
 | 
			
		||||
    </li>
 | 
			
		||||
    <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/ChangePassword">Password</NavLink>
 | 
			
		||||
    </li>
 | 
			
		||||
@*     <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/TwoFactorAuthentication">Two-factor authentication</NavLink>
 | 
			
		||||
    </li> *@
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
<AuthorizeView Roles="Teacher">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
 | 
			
		||||
		<ul class="nav nav-pills flex-column">
 | 
			
		||||
			<li class="nav-item">
 | 
			
		||||
				<NavLink class="nav-link" href="exam/create" Match="NavLinkMatch.All">创建</NavLink>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="nav-item">
 | 
			
		||||
				<NavLink class="nav-link" href="exam/manage">管理</NavLink>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="nav-item">
 | 
			
		||||
				<NavLink class="nav-link" href="Account/Manage/ChangePassword">Password</NavLink>
 | 
			
		||||
			</li>
 | 
			
		||||
		</ul>
 | 
			
		||||
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<AuthorizeView Roles="Student">
 | 
			
		||||
	<Authorized>
 | 
			
		||||
		<ul class="nav nav-pills flex-column">
 | 
			
		||||
			<li class="nav-item">
 | 
			
		||||
				<NavLink class="nav-link" href="exam/studentHomework" Match="NavLinkMatch.All">Homework</NavLink>
 | 
			
		||||
			</li>
 | 
			
		||||
			<li class="nav-item">
 | 
			
		||||
				<NavLink class="nav-link" href="exam/manage">管理</NavLink>
 | 
			
		||||
			</li>
 | 
			
		||||
		</ul>
 | 
			
		||||
	</Authorized>
 | 
			
		||||
</AuthorizeView>
 | 
			
		||||
@code {
 | 
			
		||||
    private bool hasExternalLogins;
 | 
			
		||||
	private bool hasExternalLogins;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,3 +9,17 @@
 | 
			
		||||
		@Body
 | 
			
		||||
	</MudStack>
 | 
			
		||||
</MudPaper>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
    [CascadingParameter]
 | 
			
		||||
    private Task<AuthenticationState> authenticationStateTask { get; set; }
 | 
			
		||||
 | 
			
		||||
    protected override void OnParametersSet()
 | 
			
		||||
    {
 | 
			
		||||
        if (authenticationStateTask is null)
 | 
			
		||||
        {
 | 
			
		||||
            // NavigationManager.Refresh(forceReload: true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,11 +9,8 @@
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/Class">Class</NavLink>
 | 
			
		||||
    </li>
 | 
			
		||||
    <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/ChangePassword">Password</NavLink>
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/ChangePassword">重设密码</NavLink>
 | 
			
		||||
    </li>
 | 
			
		||||
@*     <li class="nav-item">
 | 
			
		||||
        <NavLink class="nav-link" href="Account/Manage/TwoFactorAuthentication">Two-factor authentication</NavLink>
 | 
			
		||||
    </li> *@
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
@code {
 | 
			
		||||
 
 | 
			
		||||
@@ -58,6 +58,17 @@ namespace TechHelper.Context
 | 
			
		||||
 | 
			
		||||
			CreateMap<SubmissionDetailDto, SubmissionDetail>().ReverseMap();
 | 
			
		||||
 | 
			
		||||
			// Student Submission Detail
 | 
			
		||||
			CreateMap<Submission, StudentSubmissionDetailDto>()
 | 
			
		||||
				.ForMember(dest => dest.AssignmentId, opt => opt.MapFrom(src => src.AssignmentId))
 | 
			
		||||
				.ForMember(dest => dest.StudentId, opt => opt.MapFrom(src => src.StudentId))
 | 
			
		||||
				.ForMember(dest => dest.SubmissionTime, opt => opt.MapFrom(src => src.SubmissionTime))
 | 
			
		||||
				.ForMember(dest => dest.OverallGrade, opt => opt.MapFrom(src => src.OverallGrade))
 | 
			
		||||
				.ForMember(dest => dest.OverallFeedback, opt => opt.MapFrom(src => src.OverallFeedback))
 | 
			
		||||
				.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status));
 | 
			
		||||
 | 
			
		||||
			CreateMap<Assignment, AssignmentDto>().ReverseMap();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
			CreateMap<SubjectTypeMetadataDto, Global>()
 | 
			
		||||
					   .ForMember(dest => dest.Info, opt => opt.MapFrom(src => JsonConvert.SerializeObject(src.Data)));
 | 
			
		||||
 
 | 
			
		||||
@@ -39,6 +39,7 @@ namespace TechHelper.Context.Configuration
 | 
			
		||||
 | 
			
		||||
			builder.Property(s => s.OverallGrade)
 | 
			
		||||
				.HasColumnName("overall_grade")
 | 
			
		||||
				.IsRequired()
 | 
			
		||||
				.HasPrecision(5, 2); // 应用精度设置
 | 
			
		||||
 | 
			
		||||
			// OverallFeedback
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										127
									
								
								TechHelper.Server/Controllers/StudentSubmissionController.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								TechHelper.Server/Controllers/StudentSubmissionController.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
			
		||||
using Entities.Contracts;
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Identity;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using TechHelper.Server.Services;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Server.Controllers
 | 
			
		||||
{
 | 
			
		||||
    [Route("api/student-submission")]
 | 
			
		||||
    [ApiController]
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    public class StudentSubmissionController : ControllerBase
 | 
			
		||||
    {
 | 
			
		||||
        private readonly IStudentSubmissionService _studentSubmissionService;
 | 
			
		||||
        private readonly UserManager<User> _userManager;
 | 
			
		||||
 | 
			
		||||
        public StudentSubmissionController(
 | 
			
		||||
            IStudentSubmissionService studentSubmissionService,
 | 
			
		||||
            UserManager<User> userManager)
 | 
			
		||||
        {
 | 
			
		||||
            _studentSubmissionService = studentSubmissionService;
 | 
			
		||||
            _userManager = userManager;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的所有提交摘要
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <returns>学生提交摘要列表</returns>
 | 
			
		||||
        [HttpGet("my-submissions")]
 | 
			
		||||
        public async Task<IActionResult> GetMySubmissions()
 | 
			
		||||
        {
 | 
			
		||||
            var user = await _userManager.FindByEmailAsync(User.Identity.Name);
 | 
			
		||||
            if (user == null)
 | 
			
		||||
                return NotFound("未找到用户信息");
 | 
			
		||||
 | 
			
		||||
            var result = await _studentSubmissionService.GetStudentSubmissionsAsync(user.Id);
 | 
			
		||||
 | 
			
		||||
            if (result.Status)
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(result.Result);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return BadRequest(result.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取当前学生的提交摘要(分页)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="pageNumber">页码,默认为1</param>
 | 
			
		||||
        /// <param name="pageSize">每页数量,默认为10</param>
 | 
			
		||||
        /// <returns>分页的学生提交摘要列表</returns>
 | 
			
		||||
        [HttpGet("my-submissions-paged")]
 | 
			
		||||
        public async Task<IActionResult> GetMySubmissionsPaged(int pageNumber = 1, int pageSize = 10)
 | 
			
		||||
        {
 | 
			
		||||
            if (pageNumber < 1) pageNumber = 1;
 | 
			
		||||
            if (pageSize < 1) pageSize = 10;
 | 
			
		||||
            if (pageSize > 100) pageSize = 100; // 限制最大页面大小
 | 
			
		||||
 | 
			
		||||
            var user = await _userManager.FindByEmailAsync(User.Identity.Name);
 | 
			
		||||
            if (user == null)
 | 
			
		||||
                return NotFound("未找到用户信息");
 | 
			
		||||
 | 
			
		||||
            var result = await _studentSubmissionService.GetStudentSubmissionsPagedAsync(user.Id, pageNumber, pageSize);
 | 
			
		||||
 | 
			
		||||
            if (result.Status)
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(result.Result);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return BadRequest(result.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取指定学生的提交摘要(仅教师可使用)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="studentId">学生ID</param>
 | 
			
		||||
        /// <returns>学生提交摘要列表</returns>
 | 
			
		||||
        [HttpGet("student/{studentId:guid}")]
 | 
			
		||||
        [Authorize(Roles = "Teacher")]
 | 
			
		||||
        public async Task<IActionResult> GetStudentSubmissions(Guid studentId)
 | 
			
		||||
        {
 | 
			
		||||
            var result = await _studentSubmissionService.GetStudentSubmissionsAsync(studentId);
 | 
			
		||||
 | 
			
		||||
            if (result.Status)
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(result.Result);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return BadRequest(result.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取指定学生的提交摘要(分页,仅教师可使用)
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="studentId">学生ID</param>
 | 
			
		||||
        /// <param name="pageNumber">页码,默认为1</param>
 | 
			
		||||
        /// <param name="pageSize">每页数量,默认为10</param>
 | 
			
		||||
        /// <returns>分页的学生提交摘要列表</returns>
 | 
			
		||||
        [HttpGet("student/{studentId:guid}/paged")]
 | 
			
		||||
        [Authorize(Roles = "Teacher")]
 | 
			
		||||
        public async Task<IActionResult> GetStudentSubmissionsPaged(Guid studentId, int pageNumber = 1, int pageSize = 10)
 | 
			
		||||
        {
 | 
			
		||||
            if (pageNumber < 1) pageNumber = 1;
 | 
			
		||||
            if (pageSize < 1) pageSize = 10;
 | 
			
		||||
            if (pageSize > 100) pageSize = 100; // 限制最大页面大小
 | 
			
		||||
 | 
			
		||||
            var result = await _studentSubmissionService.GetStudentSubmissionsPagedAsync(studentId, pageNumber, pageSize);
 | 
			
		||||
 | 
			
		||||
            if (result.Status)
 | 
			
		||||
            {
 | 
			
		||||
                return Ok(result.Result);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                return BadRequest(result.Message);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,81 @@
 | 
			
		||||
using Entities.Contracts;
 | 
			
		||||
using Entities.DTO;
 | 
			
		||||
using Microsoft.AspNetCore.Authorization;
 | 
			
		||||
using Microsoft.AspNetCore.Identity;
 | 
			
		||||
using Microsoft.AspNetCore.Mvc;
 | 
			
		||||
using TechHelper.Server.Services;
 | 
			
		||||
using TechHelper.Context;
 | 
			
		||||
using TechHelper.Repository;
 | 
			
		||||
using SharedDATA.Api;
 | 
			
		||||
using System.Security.Claims;
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Server.Controllers
 | 
			
		||||
{
 | 
			
		||||
    [Route("api/student-submission-detail")]
 | 
			
		||||
    [ApiController]
 | 
			
		||||
    [Authorize]
 | 
			
		||||
    public class StudentSubmissionDetailController : ControllerBase
 | 
			
		||||
    {
 | 
			
		||||
    private readonly IStudentSubmissionDetailService _studentSubmissionDetailService;
 | 
			
		||||
    private readonly UserManager<User> _userManager;
 | 
			
		||||
    private readonly IUnitOfWork _unitOfWork;
 | 
			
		||||
 | 
			
		||||
    public StudentSubmissionDetailController(
 | 
			
		||||
        IStudentSubmissionDetailService studentSubmissionDetailService,
 | 
			
		||||
        UserManager<User> userManager,
 | 
			
		||||
        IUnitOfWork unitOfWork)
 | 
			
		||||
    {
 | 
			
		||||
        _studentSubmissionDetailService = studentSubmissionDetailService;
 | 
			
		||||
        _userManager = userManager;
 | 
			
		||||
        _unitOfWork = unitOfWork;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 获取学生提交的详细信息
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="submissionId">提交ID</param>
 | 
			
		||||
        /// <returns>学生提交详细信息</returns>
 | 
			
		||||
        [HttpGet("{submissionId:guid}")]
 | 
			
		||||
        public async Task<IActionResult> GetSubmissionDetail(Guid submissionId)
 | 
			
		||||
        {
 | 
			
		||||
            try
 | 
			
		||||
            {
 | 
			
		||||
                // 验证用户权限 - 只有学生本人或教师可以查看
 | 
			
		||||
                var user = await _userManager.FindByEmailAsync(User.Identity.Name);
 | 
			
		||||
                if (user == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return NotFound("未找到用户信息");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var submission = await _unitOfWork.GetRepository<Submission>()
 | 
			
		||||
                    .GetFirstOrDefaultAsync(predicate: s => s.Id == submissionId);
 | 
			
		||||
 | 
			
		||||
                if (submission == null)
 | 
			
		||||
                {
 | 
			
		||||
                    return NotFound("未找到指定的提交记录");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 检查权限:学生只能查看自己的提交,教师可以查看所有提交
 | 
			
		||||
                if (user.Id != submission.StudentId && !User.IsInRole("Teacher"))
 | 
			
		||||
                {
 | 
			
		||||
                    return Forbid("您没有权限查看此提交记录");
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var result = await _studentSubmissionDetailService.GetSubmissionDetailAsync(submissionId);
 | 
			
		||||
 | 
			
		||||
                if (result.Status)
 | 
			
		||||
                {
 | 
			
		||||
                    return Ok(result.Result);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    return BadRequest(result.Message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            catch (Exception ex)
 | 
			
		||||
            {
 | 
			
		||||
                return StatusCode(500, $"获取学生提交详细信息失败: {ex.Message}");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1299
									
								
								TechHelper.Server/Migrations/20250904101811_submission_up_2.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1299
									
								
								TechHelper.Server/Migrations/20250904101811_submission_up_2.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,97 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Server.Migrations
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public partial class submission_up_2 : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AlterColumn<float>(
 | 
			
		||||
                name: "overall_grade",
 | 
			
		||||
                table: "submissions",
 | 
			
		||||
                type: "float",
 | 
			
		||||
                precision: 5,
 | 
			
		||||
                scale: 2,
 | 
			
		||||
                nullable: false,
 | 
			
		||||
                defaultValue: 0f,
 | 
			
		||||
                oldClrType: typeof(float),
 | 
			
		||||
                oldType: "float",
 | 
			
		||||
                oldPrecision: 5,
 | 
			
		||||
                oldScale: 2,
 | 
			
		||||
                oldNullable: true);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("264e4290-9d15-478d-8c49-8d0935e5a6e1"), null, "Administrator", "ADMINISTRATOR" },
 | 
			
		||||
                    { new Guid("73cafcee-3e99-43ae-86c5-c01a1cbc6124"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("f06927ff-4bba-4ab6-8f0a-e45a765c2fcc"), null, "Student", "STUDENT" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("264e4290-9d15-478d-8c49-8d0935e5a6e1"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("73cafcee-3e99-43ae-86c5-c01a1cbc6124"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("f06927ff-4bba-4ab6-8f0a-e45a765c2fcc"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AlterColumn<float>(
 | 
			
		||||
                name: "overall_grade",
 | 
			
		||||
                table: "submissions",
 | 
			
		||||
                type: "float",
 | 
			
		||||
                precision: 5,
 | 
			
		||||
                scale: 2,
 | 
			
		||||
                nullable: true,
 | 
			
		||||
                oldClrType: typeof(float),
 | 
			
		||||
                oldType: "float",
 | 
			
		||||
                oldPrecision: 5,
 | 
			
		||||
                oldScale: 2);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"), null, "Student", "STUDENT" },
 | 
			
		||||
                    { new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"), null, "Administrator", "ADMINISTRATOR" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1299
									
								
								TechHelper.Server/Migrations/20250904102023_submission_up_3.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1299
									
								
								TechHelper.Server/Migrations/20250904102023_submission_up_3.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Server.Migrations
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public partial class submission_up_3 : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("264e4290-9d15-478d-8c49-8d0935e5a6e1"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("73cafcee-3e99-43ae-86c5-c01a1cbc6124"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("f06927ff-4bba-4ab6-8f0a-e45a765c2fcc"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("388fdb1d-8cd5-4e8f-b49c-06dbee60527b"), null, "Administrator", "ADMINISTRATOR" },
 | 
			
		||||
                    { new Guid("ba4054d5-2f8a-4c7f-bd56-0fc864720c7d"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("c758a0d2-faea-4cf1-aa14-d162f3d0a1e9"), null, "Student", "STUDENT" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("388fdb1d-8cd5-4e8f-b49c-06dbee60527b"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("ba4054d5-2f8a-4c7f-bd56-0fc864720c7d"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("c758a0d2-faea-4cf1-aa14-d162f3d0a1e9"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("264e4290-9d15-478d-8c49-8d0935e5a6e1"), null, "Administrator", "ADMINISTRATOR" },
 | 
			
		||||
                    { new Guid("73cafcee-3e99-43ae-86c5-c01a1cbc6124"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("f06927ff-4bba-4ab6-8f0a-e45a765c2fcc"), null, "Student", "STUDENT" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1302
									
								
								TechHelper.Server/Migrations/20250905101308_tee.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										1302
									
								
								TechHelper.Server/Migrations/20250905101308_tee.Designer.cs
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										82
									
								
								TechHelper.Server/Migrations/20250905101308_tee.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								TechHelper.Server/Migrations/20250905101308_tee.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
using System;
 | 
			
		||||
using Microsoft.EntityFrameworkCore.Migrations;
 | 
			
		||||
 | 
			
		||||
#nullable disable
 | 
			
		||||
 | 
			
		||||
#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional
 | 
			
		||||
 | 
			
		||||
namespace TechHelper.Server.Migrations
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public partial class tee : Migration
 | 
			
		||||
    {
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Up(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("388fdb1d-8cd5-4e8f-b49c-06dbee60527b"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("ba4054d5-2f8a-4c7f-bd56-0fc864720c7d"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("c758a0d2-faea-4cf1-aa14-d162f3d0a1e9"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.AddColumn<bool>(
 | 
			
		||||
                name: "BCorrect",
 | 
			
		||||
                table: "assignment_questions",
 | 
			
		||||
                type: "tinyint(1)",
 | 
			
		||||
                nullable: false,
 | 
			
		||||
                defaultValue: false);
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("d480cdca-7de2-4abe-8129-73bbaa6c1b32"), null, "Student", "STUDENT" },
 | 
			
		||||
                    { new Guid("d7bcfb37-3f1c-467b-a3f0-b2339a8a990d"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("f4a6788a-04d8-499c-9e64-73dfba97ca6b"), null, "Administrator", "ADMINISTRATOR" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc />
 | 
			
		||||
        protected override void Down(MigrationBuilder migrationBuilder)
 | 
			
		||||
        {
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("d480cdca-7de2-4abe-8129-73bbaa6c1b32"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("d7bcfb37-3f1c-467b-a3f0-b2339a8a990d"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DeleteData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                keyColumn: "Id",
 | 
			
		||||
                keyValue: new Guid("f4a6788a-04d8-499c-9e64-73dfba97ca6b"));
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.DropColumn(
 | 
			
		||||
                name: "BCorrect",
 | 
			
		||||
                table: "assignment_questions");
 | 
			
		||||
 | 
			
		||||
            migrationBuilder.InsertData(
 | 
			
		||||
                table: "AspNetRoles",
 | 
			
		||||
                columns: new[] { "Id", "ConcurrencyStamp", "Name", "NormalizedName" },
 | 
			
		||||
                values: new object[,]
 | 
			
		||||
                {
 | 
			
		||||
                    { new Guid("388fdb1d-8cd5-4e8f-b49c-06dbee60527b"), null, "Administrator", "ADMINISTRATOR" },
 | 
			
		||||
                    { new Guid("ba4054d5-2f8a-4c7f-bd56-0fc864720c7d"), null, "Teacher", "TEACHER" },
 | 
			
		||||
                    { new Guid("c758a0d2-faea-4cf1-aa14-d162f3d0a1e9"), null, "Student", "STUDENT" }
 | 
			
		||||
                });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -175,6 +175,9 @@ namespace TechHelper.Server.Migrations
 | 
			
		||||
                    b.Property<Guid?>("AssignmentId")
 | 
			
		||||
                        .HasColumnType("char(36)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<bool>("BCorrect")
 | 
			
		||||
                        .HasColumnType("tinyint(1)");
 | 
			
		||||
 | 
			
		||||
                    b.Property<DateTime>("CreatedAt")
 | 
			
		||||
                        .HasColumnType("datetime(6)")
 | 
			
		||||
                        .HasColumnName("created_at");
 | 
			
		||||
@@ -558,7 +561,7 @@ namespace TechHelper.Server.Migrations
 | 
			
		||||
                        .HasColumnType("longtext")
 | 
			
		||||
                        .HasColumnName("overall_feedback");
 | 
			
		||||
 | 
			
		||||
                    b.Property<float?>("OverallGrade")
 | 
			
		||||
                    b.Property<float>("OverallGrade")
 | 
			
		||||
                        .HasPrecision(5, 2)
 | 
			
		||||
                        .HasColumnType("float")
 | 
			
		||||
                        .HasColumnName("overall_grade");
 | 
			
		||||
@@ -800,19 +803,19 @@ namespace TechHelper.Server.Migrations
 | 
			
		||||
                    b.HasData(
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("8c6c5e8e-ef00-444c-9c7c-cba5cd6f7043"),
 | 
			
		||||
                            Id = new Guid("d480cdca-7de2-4abe-8129-73bbaa6c1b32"),
 | 
			
		||||
                            Name = "Student",
 | 
			
		||||
                            NormalizedName = "STUDENT"
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("2670f35a-df0c-4071-8879-80eb99d138a1"),
 | 
			
		||||
                            Id = new Guid("d7bcfb37-3f1c-467b-a3f0-b2339a8a990d"),
 | 
			
		||||
                            Name = "Teacher",
 | 
			
		||||
                            NormalizedName = "TEACHER"
 | 
			
		||||
                        },
 | 
			
		||||
                        new
 | 
			
		||||
                        {
 | 
			
		||||
                            Id = new Guid("9eda9d90-0cd2-4fbe-b07e-f90bd01f32db"),
 | 
			
		||||
                            Id = new Guid("f4a6788a-04d8-499c-9e64-73dfba97ca6b"),
 | 
			
		||||
                            Name = "Administrator",
 | 
			
		||||
                            NormalizedName = "ADMINISTRATOR"
 | 
			
		||||
                        });
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,10 @@ builder.Services.AddDbContext<ApplicationContext>(options =>
 | 
			
		||||
.AddCustomRepository<Question, QuestionRepository>()
 | 
			
		||||
.AddCustomRepository<QuestionContext, QuestionContextRepository>()
 | 
			
		||||
.AddCustomRepository<Submission, SubmissionRepository>()
 | 
			
		||||
.AddCustomRepository<User, UserRepository>()
 | 
			
		||||
.AddCustomRepository<Global, GlobalRepository>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
builder.Services.AddAutoMapper(typeof(AutoMapperProFile).Assembly);
 | 
			
		||||
 | 
			
		||||
// 3. 配置服务 (IOptions)
 | 
			
		||||
@@ -90,6 +92,8 @@ builder.Services.AddScoped<IClassService, ClassService>();
 | 
			
		||||
builder.Services.AddScoped<IExamService, ExamService>();
 | 
			
		||||
builder.Services.AddScoped<IUserSerivces, UserServices>();
 | 
			
		||||
builder.Services.AddScoped<ISubmissionServices, SubmissionServices>();
 | 
			
		||||
builder.Services.AddScoped<IStudentSubmissionService, StudentSubmissionService>();
 | 
			
		||||
builder.Services.AddScoped<IStudentSubmissionDetailService, StudentSubmissionDetailService>();
 | 
			
		||||
builder.Services.AddScoped<IExamRepository, ExamRepository>();	
 | 
			
		||||
builder.Services.AddScoped<INoteService, NoteService>();	
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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