using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.Mvc; using DotNext; using Database; namespace server.Controllers; /// /// 资源控制器 - 提供统一的资源管理API /// [ApiController] [Route("api/[controller]")] public class ResourceController : ControllerBase { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); /// /// 资源信息类 /// public class ResourceInfo { /// /// 资源ID /// public int ID { get; set; } /// /// 资源名称 /// public required string Name { get; set; } /// /// 资源类型 /// public required string Type { get; set; } /// /// 资源用途(template/user) /// public required string Purpose { get; set; } /// /// 上传时间 /// public DateTime UploadTime { get; set; } /// /// 所属实验ID(可选) /// public string? ExamID { get; set; } /// /// MIME类型 /// public string? MimeType { get; set; } } /// /// 添加资源请求类 /// public class AddResourceRequest { /// /// 资源类型 /// public required string ResourceType { get; set; } /// /// 资源用途(template/user) /// public required string ResourcePurpose { get; set; } /// /// 所属实验ID(可选) /// public string? ExamID { get; set; } } /// /// 添加资源(文件上传) /// /// 添加资源请求 /// 资源文件 /// 添加结果 [Authorize] [HttpPost] [EnableCors("Users")] [ProducesResponseType(typeof(ResourceInfo), StatusCodes.Status201Created)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public async Task AddResource([FromForm] AddResourceRequest request, IFormFile file) { if (string.IsNullOrWhiteSpace(request.ResourceType) || string.IsNullOrWhiteSpace(request.ResourcePurpose) || file == null) return BadRequest("资源类型、资源用途和文件不能为空"); // 验证资源用途 if (request.ResourcePurpose != Resource.ResourcePurposes.Template && request.ResourcePurpose != Resource.ResourcePurposes.User) return BadRequest($"无效的资源用途: {request.ResourcePurpose}"); // 模板资源需要管理员权限 if (request.ResourcePurpose == Resource.ResourcePurposes.Template && !User.IsInRole("Admin")) return Forbid("只有管理员可以添加模板资源"); try { using var db = new Database.AppDataConnection(); // 获取当前用户ID var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("无法获取用户信息"); var userResult = db.GetUserByName(userName); if (!userResult.IsSuccessful || !userResult.Value.HasValue) return Unauthorized("用户不存在"); var user = userResult.Value.Value; // 读取文件数据 using var memoryStream = new MemoryStream(); await file.CopyToAsync(memoryStream); var fileData = memoryStream.ToArray(); var result = db.AddResource(user.ID, request.ResourceType, request.ResourcePurpose, file.FileName, fileData, request.ExamID); if (!result.IsSuccessful) { if (result.Error.Message.Contains("不存在")) return NotFound(result.Error.Message); logger.Error($"添加资源时出错: {result.Error.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"添加资源失败: {result.Error.Message}"); } var resource = result.Value; var resourceInfo = new ResourceInfo { ID = resource.ID, Name = resource.ResourceName, Type = resource.ResourceType, Purpose = resource.ResourcePurpose, UploadTime = resource.UploadTime, ExamID = resource.ExamID, MimeType = resource.MimeType }; logger.Info($"成功添加资源: {request.ResourceType}/{request.ResourcePurpose}/{file.FileName}"); return CreatedAtAction(nameof(GetResourceById), new { resourceId = resource.ID }, resourceInfo); } catch (Exception ex) { logger.Error($"添加资源 {request.ResourceType}/{request.ResourcePurpose}/{file.FileName} 时出错: {ex.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"添加资源失败: {ex.Message}"); } } /// /// 获取资源列表 /// /// 实验ID(可选) /// 资源类型(可选) /// 资源用途(可选) /// 资源列表 [Authorize] [HttpGet] [EnableCors("Users")] [ProducesResponseType(typeof(ResourceInfo[]), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult GetResourceList([FromQuery] string? examId = null, [FromQuery] string? resourceType = null, [FromQuery] string? resourcePurpose = null) { try { using var db = new Database.AppDataConnection(); // 获取当前用户ID var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("无法获取用户信息"); var userResult = db.GetUserByName(userName); if (!userResult.IsSuccessful || !userResult.Value.HasValue) return Unauthorized("用户不存在"); var user = userResult.Value.Value; // 普通用户只能查看自己的资源和模板资源 Guid? userId = null; if (!User.IsInRole("Admin")) { // 如果指定了用户资源用途,则只查看自己的资源 if (resourcePurpose == Resource.ResourcePurposes.User) { userId = user.ID; } // 如果指定了模板资源用途,则不限制用户ID else if (resourcePurpose == Resource.ResourcePurposes.Template) { userId = null; } // 如果没有指定用途,则查看自己的用户资源和所有模板资源 else { // 这种情况下需要分别查询并合并结果 var userResourcesResult = db.GetFullResourceList(examId, resourceType, Resource.ResourcePurposes.User, user.ID); var templateResourcesResult = db.GetFullResourceList(examId, resourceType, Resource.ResourcePurposes.Template, null); if (!userResourcesResult.IsSuccessful || !templateResourcesResult.IsSuccessful) { logger.Error($"获取资源列表时出错"); return StatusCode(StatusCodes.Status500InternalServerError, "获取资源列表失败"); } var allResources = userResourcesResult.Value.Concat(templateResourcesResult.Value) .OrderByDescending(r => r.UploadTime); var mergedResourceInfos = allResources.Select(r => new ResourceInfo { ID = r.ID, Name = r.ResourceName, Type = r.ResourceType, Purpose = r.ResourcePurpose, UploadTime = r.UploadTime, ExamID = r.ExamID, MimeType = r.MimeType }).ToArray(); logger.Info($"成功获取资源列表,共 {mergedResourceInfos.Length} 个资源"); return Ok(mergedResourceInfos); } } var result = db.GetFullResourceList(examId, resourceType, resourcePurpose, userId); if (!result.IsSuccessful) { logger.Error($"获取资源列表时出错: {result.Error.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"获取资源列表失败: {result.Error.Message}"); } var resources = result.Value.Select(r => new ResourceInfo { ID = r.ID, Name = r.ResourceName, Type = r.ResourceType, Purpose = r.ResourcePurpose, UploadTime = r.UploadTime, ExamID = r.ExamID, MimeType = r.MimeType }).ToArray(); logger.Info($"成功获取资源列表,共 {resources.Length} 个资源"); return Ok(resources); } catch (Exception ex) { logger.Error($"获取资源列表时出错: {ex.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"获取资源列表失败: {ex.Message}"); } } /// /// 根据资源ID下载资源 /// /// 资源ID /// 资源文件 [HttpGet("{resourceId}")] [EnableCors("Users")] [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult GetResourceById(int resourceId) { try { using var db = new Database.AppDataConnection(); var result = db.GetResourceById(resourceId); if (!result.IsSuccessful) { logger.Error($"获取资源时出错: {result.Error.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"获取资源失败: {result.Error.Message}"); } if (!result.Value.HasValue) { logger.Warn($"资源不存在: {resourceId}"); return NotFound($"资源 {resourceId} 不存在"); } var resource = result.Value.Value; logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})"); return File(resource.Data, resource.MimeType ?? "application/octet-stream", resource.ResourceName); } catch (Exception ex) { logger.Error($"获取资源 {resourceId} 时出错: {ex.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"获取资源失败: {ex.Message}"); } } /// /// 删除资源 /// /// 资源ID /// 删除结果 [Authorize] [HttpDelete("{resourceId}")] [EnableCors("Users")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] [ProducesResponseType(StatusCodes.Status403Forbidden)] [ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status500InternalServerError)] public IActionResult DeleteResource(int resourceId) { try { using var db = new Database.AppDataConnection(); // 获取当前用户信息 var userName = User.Identity?.Name; if (string.IsNullOrEmpty(userName)) return Unauthorized("无法获取用户信息"); var userResult = db.GetUserByName(userName); if (!userResult.IsSuccessful || !userResult.Value.HasValue) return Unauthorized("用户不存在"); var user = userResult.Value.Value; // 先获取资源信息以验证权限 var resourceResult = db.GetResourceById(resourceId); if (!resourceResult.IsSuccessful) { logger.Error($"获取资源时出错: {resourceResult.Error.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"获取资源失败: {resourceResult.Error.Message}"); } if (!resourceResult.Value.HasValue) { logger.Warn($"资源不存在: {resourceId}"); return NotFound($"资源 {resourceId} 不存在"); } var resource = resourceResult.Value.Value; // 权限检查:管理员可以删除所有资源,普通用户只能删除自己的用户资源 if (!User.IsInRole("Admin")) { if (resource.ResourcePurpose == Resource.ResourcePurposes.Template) return Forbid("普通用户不能删除模板资源"); if (resource.UserID != user.ID) return Forbid("只能删除自己的资源"); } var deleteResult = db.DeleteResource(resourceId); if (!deleteResult.IsSuccessful) { logger.Error($"删除资源时出错: {deleteResult.Error.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"删除资源失败: {deleteResult.Error.Message}"); } logger.Info($"成功删除资源: {resourceId} ({resource.ResourceName})"); return NoContent(); } catch (Exception ex) { logger.Error($"删除资源 {resourceId} 时出错: {ex.Message}"); return StatusCode(StatusCodes.Status500InternalServerError, $"删除资源失败: {ex.Message}"); } } }