ExamEdit&Check

This commit is contained in:
SpecialX
2025-06-12 19:01:05 +08:00
parent e26881ec2f
commit b77ed0b30f
18 changed files with 729 additions and 328 deletions

View File

@@ -1,24 +1,21 @@
using System.Xml.Serialization;
using System.Xml.Serialization; // 用于 XML 序列化/反序列化
using TechHelper.Client.AI;
using TechHelper.Services;
using Entities.DTO;
using System.Net.Http.Json;
using Newtonsoft.Json;
using TechHelper.Client.Pages.Exam;
using System.Net.Http.Json; // 用于 PostAsJsonAsync
using Newtonsoft.Json; // 用于 JSON 反序列化
namespace TechHelper.Client.Exam
{
public class ExamService : IExamService
{
private IAIService aIService;
private IHttpClientFactory httpClientFactory;
private readonly IAIService _aIService; // 遵循命名规范,字段前加下划线
private readonly HttpClient _client; // 直接注入 HttpClient
public ExamService(IAIService aIService,
IHttpClientFactory httpClientFactory)
public ExamService(IAIService aIService, HttpClient client) // 修正点:直接注入 HttpClient
{
this.aIService = aIService;
this.httpClientFactory = httpClientFactory;
_aIService = aIService;
_client = client; // 赋值注入的 HttpClient 实例
}
public ApiResponse ConvertToXML<T>(string xmlContent)
@@ -31,32 +28,16 @@ namespace TechHelper.Client.Exam
try
{
T deserializedObject = (T)serializer.Deserialize(reader);
// 成功时返回 ApiResponse
return new ApiResponse
{
Status = true,
Result = deserializedObject,
Message = "XML 反序列化成功。"
};
return ApiResponse.Success(result: deserializedObject, message: "XML 反序列化成功。");
}
catch (InvalidOperationException ex)
{
return new ApiResponse
{
Status = false,
Result = null,
Message = $"XML 反序列化操作错误: {ex.Message}. 内部异常: {ex.InnerException?.Message ?? ""}"
};
return ApiResponse.Error(
message: $"XML 反序列化操作错误: {ex.Message}. 内部异常: {ex.InnerException?.Message ?? ""}");
}
catch (Exception ex)
{
return new ApiResponse
{
Status = false,
Result = null,
Message = $"处理 XML 反序列化时发生未知错误: {ex.Message}"
};
return ApiResponse.Error(message: $"处理 XML 反序列化时发生未知错误: {ex.Message}");
}
}
}
@@ -65,35 +46,21 @@ namespace TechHelper.Client.Exam
{
try
{
string respon = await aIService.CallGLM(examContent, AIConfiguration.BreakQuestions);
string? response = await _aIService.CallGLM(examContent, AIConfiguration.BreakQuestions);
if (respon != null)
if (response != null)
{
return new ApiResponse
{
Status = true,
Result = respon,
Message = "试题分割成功。"
};
return ApiResponse.Success(result: response, message: "试题分割成功。");
}
else
{
return new ApiResponse
{
Status = false,
Result = null,
Message = "AI 服务未能返回有效内容,或返回内容为空。"
};
return ApiResponse.Error(message: "AI 服务未能返回有效内容,或返回内容为空。");
}
}
catch (Exception ex)
{
return new ApiResponse
{
Status = false,
Result = null,
Message = $"处理试题分割时发生内部错误: {ex.Message}"
};
// 实际应用中,这里应该加入日志记录
return ApiResponse.Error(message: $"处理试题分割时发生内部错误: {ex.Message}");
}
}
@@ -101,112 +68,96 @@ namespace TechHelper.Client.Exam
{
try
{
string respon = await aIService.CallGLM(examContent, AIConfiguration.Format);
string? response = await _aIService.CallGLM(examContent, AIConfiguration.Format);
if (respon != null)
if (response != null)
{
return new ApiResponse
{
Status = true,
Result = respon,
Message = "试题格式化成功。"
};
return ApiResponse.Success(result: response, message: "试题格式化成功。");
}
else
{
return new ApiResponse
{
Status = false,
Result = null,
Message = "AI 服务未能返回有效内容,或返回内容为空。"
};
return ApiResponse.Error(message: "AI 服务未能返回有效内容,或返回内容为空。");
}
}
catch (Exception ex)
{
return new ApiResponse
{
Status = false,
Result = null,
Message = $"处理试题格式化时发生内部错误: {ex.Message}"
};
// 实际应用中,这里应该加入日志记录
return ApiResponse.Error(message: $"处理试题格式化时发生内部错误: {ex.Message}");
}
}
public async Task<ApiResponse> GetAllExam(string user)
{
using (var client = httpClientFactory.CreateClient("Default"))
// 直接使用注入的 _client 实例
var response = await _client.GetAsync($"exam/getAllPreview?user={user}");
if (response.IsSuccessStatusCode)
{
var response = await client.GetAsync($"exam/getAllPreview?user={user}");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<ExamDto>>(content);
return ApiResponse.Success(result: result);
}
else
{
return ApiResponse.Error(await response.Content.ReadAsStringAsync());
}
var content = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<List<ExamDto>>(content);
return ApiResponse.Success(result: result);
}
else
{
// 读取错误信息,并返回 ApiResponse
var errorContent = await response.Content.ReadAsStringAsync();
return ApiResponse.Error(message: $"获取所有试题失败: {response.StatusCode} - {errorContent}");
}
}
public async Task<ApiResponse> GetExam(Guid guid)
{
return ApiResponse.Success("HELLO");
var response = await _client.GetAsync($"exam/get?id={guid}");
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
var exam = JsonConvert.DeserializeObject<ExamDto>(content);
return ApiResponse.Success(result: exam);
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
return ApiResponse.Error(message: $"获取试题失败: {response.StatusCode} - {errorContent}");
}
}
public async Task<ApiResponse> ParseSingleQuestionGroup(string examContent)
{
try
{
string respon = await aIService.CallGLM(examContent, AIConfiguration.ParseSignelQuestion2);
string? response = await _aIService.CallGLM(examContent, AIConfiguration.ParseSignelQuestion2);
if (respon != null)
if (response != null)
{
return new ApiResponse
{
Status = true,
Result = respon,
Message = "试题解析成功。"
};
return ApiResponse.Success(result: response, message: "试题解析成功。");
}
else
{
return new ApiResponse
{
Status = false,
Result = null,
Message = "AI 服务未能返回有效内容,或返回内容为空。"
};
return ApiResponse.Error(message: "AI 服务未能返回有效内容,或返回内容为空。");
}
}
catch (Exception ex)
{
return new ApiResponse
{
Status = false,
Result = null,
Message = $"处理试题解析时发生内部错误: {ex.Message}"
};
// 实际应用中,这里应该加入日志记录
return ApiResponse.Error(message: $"处理试题解析时发生内部错误: {ex.Message}");
}
}
public async Task<ApiResponse> SaveParsedExam(ExamDto examDto)
{
using (var client = httpClientFactory.CreateClient("Default"))
{
var respont = await client.PostAsJsonAsync("exam/add",
examDto);
// 直接使用注入的 _client 实例
var response = await _client.PostAsJsonAsync("exam/add", examDto);
if (respont.StatusCode == System.Net.HttpStatusCode.OK)
{
return new ApiResponse(true, "ok");
}
return new ApiResponse("false");
if (response.IsSuccessStatusCode) // 检查是否是成功的状态码,例如 200 OK, 201 Created 等
{
return ApiResponse.Success(message: "试题保存成功。");
}
else
{
var errorContent = await response.Content.ReadAsStringAsync();
return ApiResponse.Error(message: $"保存试题失败: {response.StatusCode} - {errorContent}");
}
}
}
}
}

View File

@@ -0,0 +1,92 @@
using Entities.DTO;
namespace TechHelper.Client.Exam
{
public class ExamStruct
{
public string Title { get; set; }
public List<QuestionItem> Questions { get; set; } = new List<QuestionItem>();
public class QuestionItem
{
public string Sequence { get; set; } = string.Empty;
public string QuestionText { get; set; } = string.Empty;
public float Score { get; set; }
}
}
public class Student
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
}
public class QuestionAnswerStatus
{
public string QuestionSequence { get; set; } = string.Empty; // 题目序号,例如 "1.1"
public string QuestionText { get; set; } = string.Empty; // 题目文本
public float QuestionScore { get; set; } // 题目分值
public Dictionary<Guid, bool> StudentCorrectStatus { get; set; } = new Dictionary<Guid, bool>();
// Key: Student.Id, Value: true 表示正确false 表示错误
}
public class QuestionRowData
{
public ExamStruct.QuestionItem QuestionItem { get; set; } // 原始题目信息
public Dictionary<Guid, bool> StudentAnswers { get; set; } = new Dictionary<Guid, bool>();
}
public static class ExamStructExtensions
{
public static ExamStruct GetStruct(this ExamDto dto)
{
if (dto == null)
{
return new ExamStruct { Title = "无效试卷", Questions = new List<ExamStruct.QuestionItem>() };
}
var examStruct = new ExamStruct
{
Title = dto.AssignmentTitle
};
GetSeqRecursive(dto.QuestionGroups, null, examStruct.Questions);
return examStruct;
}
/// <summary>
/// 递归方法,用于生成所有题目和子题目的完整序号。
/// </summary>
/// <param name="currentGroup">当前正在处理的题目组。</param>
/// <param name="parentSequence">当前题目组的父级序号(例如:"1", "2.1")。如果为空,则表示顶级题目组。</param>
/// <param name="allQuestions">用于收集所有生成题目项的列表。</param>
private static void GetSeqRecursive(
QuestionGroupDto currentGroup,
string? parentSequence,
List<ExamStruct.QuestionItem> allQuestions)
{
string currentGroupSequence = parentSequence != null
? $"{parentSequence}.{currentGroup.Index}"
: currentGroup.Index.ToString();
foreach (var subQuestion in currentGroup.SubQuestions)
{
string fullSequence = $"{currentGroupSequence}.{subQuestion.Index}";
allQuestions.Add(new ExamStruct.QuestionItem
{
Sequence = fullSequence,
QuestionText = subQuestion.Stem ?? string.Empty,
Score = subQuestion.Score
});
}
foreach (var subGroup in currentGroup.SubQuestionGroups)
{
GetSeqRecursive(subGroup, currentGroupSequence, allQuestions);
}
}
}
}