feat: 统一资源管理
This commit is contained in:
377
server/src/Controllers/ResourceController.cs
Normal file
377
server/src/Controllers/ResourceController.cs
Normal file
@@ -0,0 +1,377 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using DotNext;
|
||||
using Database;
|
||||
|
||||
namespace server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 资源控制器 - 提供统一的资源管理API
|
||||
/// </summary>
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class ResourceController : ControllerBase
|
||||
{
|
||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// 资源信息类
|
||||
/// </summary>
|
||||
public class ResourceInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源ID
|
||||
/// </summary>
|
||||
public int ID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源名称
|
||||
/// </summary>
|
||||
public required string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源类型
|
||||
/// </summary>
|
||||
public required string Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源用途(template/user)
|
||||
/// </summary>
|
||||
public required string Purpose { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 上传时间
|
||||
/// </summary>
|
||||
public DateTime UploadTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属实验ID(可选)
|
||||
/// </summary>
|
||||
public string? ExamID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// MIME类型
|
||||
/// </summary>
|
||||
public string? MimeType { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加资源请求类
|
||||
/// </summary>
|
||||
public class AddResourceRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源类型
|
||||
/// </summary>
|
||||
public required string ResourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 资源用途(template/user)
|
||||
/// </summary>
|
||||
public required string ResourcePurpose { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属实验ID(可选)
|
||||
/// </summary>
|
||||
public string? ExamID { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加资源(文件上传)
|
||||
/// </summary>
|
||||
/// <param name="request">添加资源请求</param>
|
||||
/// <param name="file">资源文件</param>
|
||||
/// <returns>添加结果</returns>
|
||||
[Authorize]
|
||||
[HttpPost]
|
||||
[EnableCors("Users")]
|
||||
[ProducesResponseType(typeof(ResourceInfo), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||
public async Task<IActionResult> 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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源列表
|
||||
/// </summary>
|
||||
/// <param name="examId">实验ID(可选)</param>
|
||||
/// <param name="resourceType">资源类型(可选)</param>
|
||||
/// <param name="resourcePurpose">资源用途(可选)</param>
|
||||
/// <returns>资源列表</returns>
|
||||
[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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据资源ID下载资源
|
||||
/// </summary>
|
||||
/// <param name="resourceId">资源ID</param>
|
||||
/// <returns>资源文件</returns>
|
||||
[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}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除资源
|
||||
/// </summary>
|
||||
/// <param name="resourceId">资源ID</param>
|
||||
/// <returns>删除结果</returns>
|
||||
[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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user