using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using DotNext; using Database; namespace server.Controllers; /// /// 实验控制器 /// [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; } /// /// 获取所有实验列表 /// /// 实验列表 [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}"); } } /// /// 根据实验ID获取实验详细信息 /// /// 实验ID /// 实验详细信息 [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}"); } } /// /// 创建新实验 /// /// 创建实验请求 /// 创建结果 [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}"); } } /// /// 更新实验信息 /// /// 更新实验请求 /// 更新结果 [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}"); } } /// /// 提交作业 /// /// 实验ID /// 提交的文件 /// 提交结果 [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 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}"); } } /// /// 获取用户在指定实验中的提交记录 /// /// 实验ID /// 提交记录列表 [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}"); } } /// /// 删除提交记录 /// /// 提交记录ID /// 删除结果 [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}"); } } } /// /// 实验信息 /// public class ExamInfo { /// /// 实验的唯一标识符 /// public string ID { get; set; } /// /// 实验名称 /// public string Name { get; set; } /// /// 实验描述 /// public string Description { get; set; } /// /// 实验创建时间 /// public DateTime CreatedTime { get; set; } /// /// 实验最后更新时间 /// public DateTime UpdatedTime { get; set; } /// /// 实验标签 /// public string[] Tags { get; set; } = Array.Empty(); /// /// 实验难度(1-5) /// public int Difficulty { get; set; } = 1; /// /// 普通用户是否可见 /// 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; } } /// /// 统一的实验数据传输对象 /// public class ExamDto { /// /// 实验的唯一标识符 /// public required string ID { get; set; } /// /// 实验名称 /// public required string Name { get; set; } /// /// 实验描述 /// public required string Description { get; set; } /// /// 实验标签 /// public string[] Tags { get; set; } = Array.Empty(); /// /// 实验难度(1-5) /// public int Difficulty { get; set; } = 1; /// /// 普通用户是否可见 /// public bool IsVisibleToUsers { get; set; } = true; }