533 lines
19 KiB
C#
533 lines
19 KiB
C#
using Microsoft.AspNetCore.Authorization;
|
||
using Microsoft.AspNetCore.Cors;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using DotNext;
|
||
using Database;
|
||
|
||
namespace server.Controllers;
|
||
|
||
/// <summary>
|
||
/// 实验控制器
|
||
/// </summary>
|
||
[ApiController]
|
||
[Route("api/[controller]")]
|
||
public class ExamController : ControllerBase
|
||
{
|
||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||
|
||
private readonly ExamManager _examManager;
|
||
private readonly ResourceManager _resourceManager;
|
||
private readonly UserManager _userManager;
|
||
|
||
public ExamController(
|
||
ExamManager examManager,
|
||
ResourceManager resourceManager,
|
||
UserManager userManager)
|
||
{
|
||
_examManager = examManager;
|
||
_resourceManager = resourceManager;
|
||
_userManager = userManager;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取所有实验列表
|
||
/// </summary>
|
||
/// <returns>实验列表</returns>
|
||
[Authorize]
|
||
[HttpGet("list")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(ExamInfo[]), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetExamList()
|
||
{
|
||
try
|
||
{
|
||
var exams = _examManager.GetAllExams();
|
||
|
||
var examInfos = exams.Select(exam => new ExamInfo(exam)).ToArray();
|
||
|
||
logger.Info($"成功获取实验列表,共 {examInfos.Length} 个实验");
|
||
return Ok(examInfos);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"获取实验列表时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"获取实验列表失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据实验ID获取实验详细信息
|
||
/// </summary>
|
||
/// <param name="examId">实验ID</param>
|
||
/// <returns>实验详细信息</returns>
|
||
[Authorize]
|
||
[HttpGet("{examId}")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(ExamInfo), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetExam(string examId)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(examId))
|
||
return BadRequest("实验ID不能为空");
|
||
|
||
try
|
||
{
|
||
var result = _examManager.GetExamByID(examId);
|
||
|
||
if (!result.IsSuccessful)
|
||
{
|
||
logger.Error($"获取实验时出错: {result.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"获取实验失败: {result.Error.Message}");
|
||
}
|
||
|
||
if (!result.Value.HasValue)
|
||
{
|
||
logger.Warn($"实验不存在: {examId}");
|
||
return NotFound($"实验 {examId} 不存在");
|
||
}
|
||
|
||
var exam = result.Value.Value;
|
||
var examInfo = new ExamInfo(exam);
|
||
|
||
logger.Info($"成功获取实验信息: {examId}");
|
||
return Ok(examInfo);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"获取实验 {examId} 时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"获取实验失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建新实验
|
||
/// </summary>
|
||
/// <param name="request">创建实验请求</param>
|
||
/// <returns>创建结果</returns>
|
||
[Authorize("Admin")]
|
||
[HttpPost("create")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(ExamInfo), StatusCodes.Status201Created)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
[ProducesResponseType(StatusCodes.Status409Conflict)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult CreateExam([FromBody] ExamDto request)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(request.ID) || string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.Description))
|
||
return BadRequest("实验ID、名称和描述不能为空");
|
||
|
||
try
|
||
{
|
||
var result = _examManager.CreateExam(request.ID, request.Name, request.Description, request.Tags, request.Difficulty, request.IsVisibleToUsers);
|
||
|
||
if (!result.IsSuccessful)
|
||
{
|
||
if (result.Error.Message.Contains("已存在"))
|
||
return Conflict(result.Error.Message);
|
||
|
||
logger.Error($"创建实验时出错: {result.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"创建实验失败: {result.Error.Message}");
|
||
}
|
||
|
||
var exam = result.Value;
|
||
var examInfo = new ExamInfo(exam);
|
||
|
||
logger.Info($"成功创建实验: {request.ID}");
|
||
return CreatedAtAction(nameof(GetExam), new { examId = request.ID }, examInfo);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"创建实验 {request.ID} 时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"创建实验失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新实验信息
|
||
/// </summary>
|
||
/// <param name="request">更新实验请求</param>
|
||
/// <returns>更新结果</returns>
|
||
[Authorize("Admin")]
|
||
[HttpPost("update")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(ExamInfo), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult UpdateExam([FromBody] ExamDto request)
|
||
{
|
||
var examId = request.ID;
|
||
|
||
try
|
||
{
|
||
// 首先检查实验是否存在
|
||
var existingExamResult = _examManager.GetExamByID(examId);
|
||
if (!existingExamResult.IsSuccessful)
|
||
{
|
||
logger.Error($"检查实验是否存在时出错: {existingExamResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"检查实验失败: {existingExamResult.Error.Message}");
|
||
}
|
||
|
||
if (!existingExamResult.Value.HasValue)
|
||
{
|
||
logger.Warn($"要更新的实验不存在: {examId}");
|
||
return NotFound($"实验 {examId} 不存在");
|
||
}
|
||
|
||
// 执行更新
|
||
var updateResult = _examManager.UpdateExam(
|
||
examId,
|
||
request.Name,
|
||
request.Description,
|
||
request.Tags,
|
||
request.Difficulty,
|
||
request.IsVisibleToUsers
|
||
);
|
||
|
||
if (!updateResult.IsSuccessful)
|
||
{
|
||
logger.Error($"更新实验时出错: {updateResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"更新实验失败: {updateResult.Error.Message}");
|
||
}
|
||
|
||
// 获取更新后的实验信息并返回
|
||
var updatedExamResult = _examManager.GetExamByID(examId);
|
||
if (!updatedExamResult.IsSuccessful || !updatedExamResult.Value.HasValue)
|
||
{
|
||
logger.Error($"获取更新后的实验信息失败: {examId}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, "更新成功但获取更新后信息失败");
|
||
}
|
||
|
||
var updatedExam = updatedExamResult.Value.Value;
|
||
var examInfo = new ExamInfo(updatedExam);
|
||
|
||
logger.Info($"成功更新实验: {examId},更新记录数: {updateResult.Value}");
|
||
return Ok(examInfo);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"更新实验 {examId} 时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"更新实验失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 提交作业
|
||
/// </summary>
|
||
/// <param name="examId">实验ID</param>
|
||
/// <param name="file">提交的文件</param>
|
||
/// <returns>提交结果</returns>
|
||
[Authorize]
|
||
[HttpPost("commit/{examId}")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(Resource), StatusCodes.Status201Created)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public async Task<IActionResult> SubmitHomework(string examId, IFormFile file)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(examId))
|
||
return BadRequest("实验ID不能为空");
|
||
|
||
if (file == null || file.Length == 0)
|
||
return BadRequest("文件不能为空");
|
||
|
||
try
|
||
{
|
||
// 获取当前用户信息
|
||
var userName = User.Identity?.Name;
|
||
if (string.IsNullOrEmpty(userName))
|
||
return Unauthorized("无法获取用户信息");
|
||
|
||
var userResult = _userManager.GetUserByName(userName);
|
||
if (!userResult.IsSuccessful || !userResult.Value.HasValue)
|
||
return Unauthorized("用户不存在");
|
||
|
||
var user = userResult.Value.Value;
|
||
|
||
// 检查实验是否存在
|
||
var examResult = _examManager.GetExamByID(examId);
|
||
if (!examResult.IsSuccessful)
|
||
{
|
||
logger.Error($"检查实验是否存在时出错: {examResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"检查实验失败: {examResult.Error.Message}");
|
||
}
|
||
|
||
if (!examResult.Value.HasValue)
|
||
{
|
||
logger.Warn($"实验不存在: {examId}");
|
||
return NotFound($"实验 {examId} 不存在");
|
||
}
|
||
|
||
// 读取文件内容
|
||
byte[] fileData;
|
||
using (var memoryStream = new MemoryStream())
|
||
{
|
||
await file.CopyToAsync(memoryStream);
|
||
fileData = memoryStream.ToArray();
|
||
}
|
||
|
||
// 提交作业
|
||
var commitResult = _resourceManager.AddResource(
|
||
user.ID, ResourceTypes.Compression, ResourcePurpose.Homework,
|
||
file.FileName, fileData, examId);
|
||
if (!commitResult.IsSuccessful)
|
||
{
|
||
logger.Error($"提交作业时出错: {commitResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"提交作业失败: {commitResult.Error.Message}");
|
||
}
|
||
|
||
var commit = commitResult.Value;
|
||
|
||
logger.Info($"用户 {userName} 成功提交实验 {examId} 的作业,Commit ID: {commit.ID}");
|
||
return CreatedAtAction(nameof(GetCommitsByExamId), new { examId = examId }, commit);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"提交实验 {examId} 作业时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"提交作业失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取用户在指定实验中的提交记录
|
||
/// </summary>
|
||
/// <param name="examId">实验ID</param>
|
||
/// <returns>提交记录列表</returns>
|
||
[Authorize]
|
||
[HttpGet("commits/{examId}")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(typeof(Resource[]), StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult GetCommitsByExamId(string examId)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(examId))
|
||
return BadRequest("实验ID不能为空");
|
||
|
||
try
|
||
{
|
||
// 获取当前用户信息
|
||
var userName = User.Identity?.Name;
|
||
if (string.IsNullOrEmpty(userName))
|
||
return Unauthorized("无法获取用户信息");
|
||
|
||
var userResult = _userManager.GetUserByName(userName);
|
||
if (!userResult.IsSuccessful || !userResult.Value.HasValue)
|
||
return Unauthorized("用户不存在");
|
||
|
||
var user = userResult.Value.Value;
|
||
|
||
// 检查实验是否存在
|
||
var examResult = _examManager.GetExamByID(examId);
|
||
if (!examResult.IsSuccessful)
|
||
{
|
||
logger.Error($"检查实验是否存在时出错: {examResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"检查实验失败: {examResult.Error.Message}");
|
||
}
|
||
|
||
if (!examResult.Value.HasValue)
|
||
{
|
||
logger.Warn($"实验不存在: {examId}");
|
||
return NotFound($"实验 {examId} 不存在");
|
||
}
|
||
|
||
// 获取用户的提交记录
|
||
var commitsResult = _resourceManager.GetResourceListByType(
|
||
ResourceTypes.Compression, ResourcePurpose.Homework, examId);
|
||
if (!commitsResult.IsSuccessful)
|
||
{
|
||
logger.Error($"获取提交记录时出错: {commitsResult.Error.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"获取提交记录失败: {commitsResult.Error.Message}");
|
||
}
|
||
|
||
var commits = commitsResult.Value;
|
||
|
||
logger.Info($"成功获取用户 {userName} 在实验 {examId} 中的提交记录,共 {commits.Length} 条");
|
||
return Ok(commits);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"获取实验 {examId} 提交记录时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"获取提交记录失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 删除提交记录
|
||
/// </summary>
|
||
/// <param name="commitId">提交记录ID</param>
|
||
/// <returns>删除结果</returns>
|
||
[Authorize]
|
||
[HttpDelete("commit/{commitId}")]
|
||
[EnableCors("Users")]
|
||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||
public IActionResult DeleteCommit(string commitId)
|
||
{
|
||
if (!Guid.TryParse(commitId, out _))
|
||
return BadRequest("提交记录ID格式不正确");
|
||
|
||
try
|
||
{
|
||
// 获取当前用户信息
|
||
var userName = User.Identity?.Name;
|
||
if (string.IsNullOrEmpty(userName))
|
||
return Unauthorized("无法获取用户信息");
|
||
|
||
var userResult = _userManager.GetUserByName(userName);
|
||
if (!userResult.IsSuccessful || !userResult.Value.HasValue)
|
||
return Unauthorized("用户不存在");
|
||
|
||
var user = userResult.Value.Value;
|
||
|
||
// 检查是否是管理员
|
||
var isAdmin = user.Permission == UserPermission.Admin;
|
||
|
||
// 如果不是管理员,检查提交记录是否属于当前用户
|
||
if (!isAdmin)
|
||
{
|
||
var commitResult = _resourceManager.GetResourceById(commitId);
|
||
if (!commitResult.HasValue)
|
||
{
|
||
logger.Warn($"提交记录不存在: {commitId}");
|
||
return NotFound($"提交记录 {commitId} 不存在");
|
||
}
|
||
|
||
var commit = commitResult.Value;
|
||
if (commit.UserID != user.ID)
|
||
{
|
||
logger.Warn($"用户 {userName} 尝试删除不属于自己的提交记录: {commitId}");
|
||
return Forbid("您只能删除自己的提交记录");
|
||
}
|
||
}
|
||
|
||
// 执行删除
|
||
var deleteResult = _resourceManager.DeleteResource(commitId);
|
||
if (!deleteResult)
|
||
{
|
||
logger.Warn($"提交记录不存在: {commitId}");
|
||
return NotFound($"提交记录 {commitId} 不存在");
|
||
}
|
||
|
||
logger.Info($"用户 {userName} 成功删除提交记录: {commitId}");
|
||
return Ok($"提交记录 {commitId} 已成功删除");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
logger.Error($"删除提交记录 {commitId} 时出错: {ex.Message}");
|
||
return StatusCode(StatusCodes.Status500InternalServerError, $"删除提交记录失败: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 实验信息
|
||
/// </summary>
|
||
public class ExamInfo
|
||
{
|
||
/// <summary>
|
||
/// 实验的唯一标识符
|
||
/// </summary>
|
||
public string ID { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验名称
|
||
/// </summary>
|
||
public string Name { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验描述
|
||
/// </summary>
|
||
public string Description { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验创建时间
|
||
/// </summary>
|
||
public DateTime CreatedTime { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验最后更新时间
|
||
/// </summary>
|
||
public DateTime UpdatedTime { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验标签
|
||
/// </summary>
|
||
public string[] Tags { get; set; } = Array.Empty<string>();
|
||
|
||
/// <summary>
|
||
/// 实验难度(1-5)
|
||
/// </summary>
|
||
public int Difficulty { get; set; } = 1;
|
||
|
||
/// <summary>
|
||
/// 普通用户是否可见
|
||
/// </summary>
|
||
public bool IsVisibleToUsers { get; set; } = true;
|
||
|
||
public ExamInfo(Exam exam)
|
||
{
|
||
ID = exam.ID;
|
||
Name = exam.Name;
|
||
Description = exam.Description;
|
||
CreatedTime = exam.CreatedTime;
|
||
UpdatedTime = exam.UpdatedTime;
|
||
Tags = exam.GetTagsList();
|
||
Difficulty = exam.Difficulty;
|
||
IsVisibleToUsers = exam.IsVisibleToUsers;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 统一的实验数据传输对象
|
||
/// </summary>
|
||
public class ExamDto
|
||
{
|
||
/// <summary>
|
||
/// 实验的唯一标识符
|
||
/// </summary>
|
||
public required string ID { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验名称
|
||
/// </summary>
|
||
public required string Name { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验描述
|
||
/// </summary>
|
||
public required string Description { get; set; }
|
||
|
||
/// <summary>
|
||
/// 实验标签
|
||
/// </summary>
|
||
public string[] Tags { get; set; } = Array.Empty<string>();
|
||
|
||
/// <summary>
|
||
/// 实验难度(1-5)
|
||
/// </summary>
|
||
public int Difficulty { get; set; } = 1;
|
||
|
||
/// <summary>
|
||
/// 普通用户是否可见
|
||
/// </summary>
|
||
public bool IsVisibleToUsers { get; set; } = true;
|
||
}
|