378 lines
15 KiB
C#
378 lines
15 KiB
C#
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}");
|
||
}
|
||
}
|
||
}
|