refactor: 重构数据库相关操作
This commit is contained in:
parent
079004c17d
commit
b95a61c532
|
@ -62,7 +62,7 @@ try
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(
|
IssuerSigningKey = new SymmetricSecurityKey(
|
||||||
Encoding.UTF8.GetBytes("my secret key 1234567890my secret key 1234567890")),
|
Encoding.UTF8.GetBytes("my secret key 1234567890my secret key 1234567890")),
|
||||||
};
|
};
|
||||||
options.Authority = $"http://{Global.localhost}:5000";
|
options.Authority = $"http://{Global.LocalHost}:5000";
|
||||||
options.RequireHttpsMetadata = false;
|
options.RequireHttpsMetadata = false;
|
||||||
});
|
});
|
||||||
// Add JWT Token Authorization Policy
|
// Add JWT Token Authorization Policy
|
||||||
|
@ -152,6 +152,11 @@ try
|
||||||
builder.Services.AddSingleton<ProgressTrackerService>();
|
builder.Services.AddSingleton<ProgressTrackerService>();
|
||||||
builder.Services.AddHostedService(provider => provider.GetRequiredService<ProgressTrackerService>());
|
builder.Services.AddHostedService(provider => provider.GetRequiredService<ProgressTrackerService>());
|
||||||
|
|
||||||
|
// 添加数据库资源管理器服务
|
||||||
|
builder.Services.AddSingleton<Database.AppDataConnection>();
|
||||||
|
builder.Services.AddSingleton<Database.UserManager>();
|
||||||
|
builder.Services.AddSingleton<Database.ResourceManager>();
|
||||||
|
|
||||||
// Application Settings
|
// Application Settings
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
@ -209,7 +214,7 @@ try
|
||||||
settings.PostProcess = (document, httpRequest) =>
|
settings.PostProcess = (document, httpRequest) =>
|
||||||
{
|
{
|
||||||
document.Servers.Clear();
|
document.Servers.Clear();
|
||||||
document.Servers.Add(new NSwag.OpenApiServer { Url = $"http://{Global.localhost}:5000" });
|
document.Servers.Add(new NSwag.OpenApiServer { Url = $"http://{Global.LocalHost}:5000" });
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
app.UseSwaggerUi();
|
app.UseSwaggerUi();
|
||||||
|
@ -232,7 +237,7 @@ try
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var document = await OpenApiDocument.FromUrlAsync($"http://{Global.localhost}:5000/swagger/v1/swagger.json");
|
var document = await OpenApiDocument.FromUrlAsync($"http://{Global.LocalHost}:5000/swagger/v1/swagger.json");
|
||||||
|
|
||||||
var settings = new TypeScriptClientGeneratorSettings
|
var settings = new TypeScriptClientGeneratorSettings
|
||||||
{
|
{
|
||||||
|
|
|
@ -4,7 +4,8 @@ using System.Net.Sockets;
|
||||||
public static class Global
|
public static class Global
|
||||||
{
|
{
|
||||||
|
|
||||||
public static readonly string localhost = "127.0.0.1";
|
public static readonly string LocalHost = "127.0.0.1";
|
||||||
|
public static readonly string DataPath = Path.Combine(Environment.CurrentDirectory, "data");
|
||||||
|
|
||||||
public static string GetLocalIPAddress()
|
public static string GetLocalIPAddress()
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,38 +17,15 @@ namespace server.Controllers;
|
||||||
public class DataController : ControllerBase
|
public class DataController : ControllerBase
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
|
|
||||||
// 固定的实验板IP,端口,MAC地址
|
// 固定的实验板IP,端口,MAC地址
|
||||||
private const string BOARD_IP = "169.254.109.0";
|
private const string BOARD_IP = "169.254.109.0";
|
||||||
|
|
||||||
/// <summary>
|
public DataController(Database.UserManager userManager)
|
||||||
/// [TODO:description]
|
|
||||||
/// </summary>
|
|
||||||
public class UserInfo
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
_userManager = userManager;
|
||||||
/// 用户的唯一标识符
|
|
||||||
/// </summary>
|
|
||||||
public Guid ID { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户的名称
|
|
||||||
/// </summary>
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户的电子邮箱
|
|
||||||
/// </summary>
|
|
||||||
public required string EMail { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户关联的板卡ID
|
|
||||||
/// </summary>
|
|
||||||
public Guid BoardID { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用户绑定板子的过期时间
|
|
||||||
/// </summary>
|
|
||||||
public DateTime? BoardExpireTime { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -112,8 +89,7 @@ public class DataController : ControllerBase
|
||||||
public IActionResult Login(string name, string password)
|
public IActionResult Login(string name, string password)
|
||||||
{
|
{
|
||||||
// 验证用户密码
|
// 验证用户密码
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.CheckUserPassword(name, password);
|
||||||
var ret = db.CheckUserPassword(name, password);
|
|
||||||
if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
if (!ret.IsSuccessful) return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
||||||
if (!ret.Value.HasValue) return BadRequest("用户名或密码错误");
|
if (!ret.Value.HasValue) return BadRequest("用户名或密码错误");
|
||||||
var user = ret.Value.Value;
|
var user = ret.Value.Value;
|
||||||
|
@ -188,8 +164,7 @@ public class DataController : ControllerBase
|
||||||
return Unauthorized("未找到用户名信息");
|
return Unauthorized("未找到用户名信息");
|
||||||
|
|
||||||
// Get User Info
|
// Get User Info
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.GetUserByName(userName);
|
||||||
var ret = db.GetUserByName(userName);
|
|
||||||
if (!ret.IsSuccessful)
|
if (!ret.IsSuccessful)
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
||||||
|
|
||||||
|
@ -236,8 +211,7 @@ public class DataController : ControllerBase
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.AddUser(name, email, password);
|
||||||
var ret = db.AddUser(name, email, password);
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -265,15 +239,14 @@ public class DataController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return Unauthorized("未找到用户名信息");
|
return Unauthorized("未找到用户名信息");
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return BadRequest("用户不存在");
|
return BadRequest("用户不存在");
|
||||||
|
|
||||||
var user = userRet.Value.Value;
|
var user = userRet.Value.Value;
|
||||||
var expireTime = DateTime.UtcNow.AddHours(durationHours);
|
var expireTime = DateTime.UtcNow.AddHours(durationHours);
|
||||||
|
|
||||||
var boardOpt = db.GetAvailableBoard(user.ID, expireTime);
|
var boardOpt = _userManager.GetAvailableBoard(user.ID, expireTime);
|
||||||
if (!boardOpt.HasValue)
|
if (!boardOpt.HasValue)
|
||||||
return NotFound("没有可用的实验板");
|
return NotFound("没有可用的实验板");
|
||||||
|
|
||||||
|
@ -309,13 +282,12 @@ public class DataController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return Unauthorized("未找到用户名信息");
|
return Unauthorized("未找到用户名信息");
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return BadRequest("用户不存在");
|
return BadRequest("用户不存在");
|
||||||
|
|
||||||
var user = userRet.Value.Value;
|
var user = userRet.Value.Value;
|
||||||
var result = db.UnbindUserFromBoard(user.ID);
|
var result = _userManager.UnbindUserFromBoard(user.ID);
|
||||||
return Ok(result > 0);
|
return Ok(result > 0);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -338,8 +310,7 @@ public class DataController : ControllerBase
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.GetBoardByID(id);
|
||||||
var ret = db.GetBoardByID(id);
|
|
||||||
if (!ret.IsSuccessful)
|
if (!ret.IsSuccessful)
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
return StatusCode(StatusCodes.Status500InternalServerError, "数据库操作失败");
|
||||||
if (!ret.Value.HasValue)
|
if (!ret.Value.HasValue)
|
||||||
|
@ -375,8 +346,7 @@ public class DataController : ControllerBase
|
||||||
return BadRequest("板子名称不能为空");
|
return BadRequest("板子名称不能为空");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.AddBoard(name);
|
||||||
var ret = db.AddBoard(name);
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -402,8 +372,7 @@ public class DataController : ControllerBase
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var ret = _userManager.DeleteBoardByID(id);
|
||||||
var ret = db.DeleteBoardByID(id);
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -425,8 +394,7 @@ public class DataController : ControllerBase
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var boards = _userManager.GetAllBoard();
|
||||||
var boards = db.GetAllBoard();
|
|
||||||
return Ok(boards);
|
return Ok(boards);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -453,8 +421,7 @@ public class DataController : ControllerBase
|
||||||
return BadRequest("新名称不能为空");
|
return BadRequest("新名称不能为空");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var result = _userManager.UpdateBoardName(boardId, newName);
|
||||||
var result = db.UpdateBoardName(boardId, newName);
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -479,8 +446,7 @@ public class DataController : ControllerBase
|
||||||
return BadRequest("板子Guid不能为空");
|
return BadRequest("板子Guid不能为空");
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var result = _userManager.UpdateBoardStatus(boardId, newStatus);
|
||||||
var result = db.UpdateBoardStatus(boardId, newStatus);
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -489,4 +455,36 @@ public class DataController : ControllerBase
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "更新失败,请稍后重试");
|
return StatusCode(StatusCodes.Status500InternalServerError, "更新失败,请稍后重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// [TODO:description]
|
||||||
|
/// </summary>
|
||||||
|
public class UserInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
public Guid ID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的名称
|
||||||
|
/// </summary>
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的电子邮箱
|
||||||
|
/// </summary>
|
||||||
|
public required string EMail { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户关联的板卡ID
|
||||||
|
/// </summary>
|
||||||
|
public Guid BoardID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户绑定板子的过期时间
|
||||||
|
/// </summary>
|
||||||
|
public DateTime? BoardExpireTime { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,77 +15,11 @@ public class DebuggerController : ControllerBase
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
/// <summary>
|
private readonly Database.UserManager _userManager;
|
||||||
/// 表示单个信号通道的配置信息
|
|
||||||
/// </summary>
|
|
||||||
public class ChannelConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 通道名称
|
|
||||||
/// </summary>
|
|
||||||
required public string name;
|
|
||||||
/// <summary>
|
|
||||||
/// 通道显示颜色(如前端波形显示用)
|
|
||||||
/// </summary>
|
|
||||||
required public string color;
|
|
||||||
/// <summary>
|
|
||||||
/// 通道信号线宽度(位数)
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 wireWidth;
|
|
||||||
/// <summary>
|
|
||||||
/// 信号线在父端口中的起始索引(bit)
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 wireStartIndex;
|
|
||||||
/// <summary>
|
|
||||||
/// 父端口编号
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 parentPort;
|
|
||||||
/// <summary>
|
|
||||||
/// 捕获模式(如上升沿、下降沿等)
|
|
||||||
/// </summary>
|
|
||||||
required public CaptureMode mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public DebuggerController(Database.UserManager userManager)
|
||||||
/// 调试器整体配置信息
|
|
||||||
/// </summary>
|
|
||||||
public class DebuggerConfig
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
this._userManager = userManager;
|
||||||
/// 时钟频率
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 clkFreq;
|
|
||||||
/// <summary>
|
|
||||||
/// 总端口数量
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 totalPortNum;
|
|
||||||
/// <summary>
|
|
||||||
/// 捕获深度(采样点数)
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 captureDepth;
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器数量
|
|
||||||
/// </summary>
|
|
||||||
required public UInt32 triggerNum;
|
|
||||||
/// <summary>
|
|
||||||
/// 所有信号通道的配置信息
|
|
||||||
/// </summary>
|
|
||||||
required public ChannelConfig[] channelConfigs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 单个通道的捕获数据
|
|
||||||
/// </summary>
|
|
||||||
public class ChannelCaptureData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 通道名称
|
|
||||||
/// </summary>
|
|
||||||
required public string name;
|
|
||||||
/// <summary>
|
|
||||||
/// 通道捕获到的数据(Base64编码的UInt32数组)
|
|
||||||
/// </summary>
|
|
||||||
required public string data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -99,8 +33,7 @@ public class DebuggerController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -108,7 +41,7 @@ public class DebuggerController : ControllerBase
|
||||||
if (user.BoardID == Guid.Empty)
|
if (user.BoardID == Guid.Empty)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(user.BoardID);
|
var boardRet = _userManager.GetBoardByID(user.BoardID);
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -464,4 +397,77 @@ public class DebuggerController : ControllerBase
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 表示单个信号通道的配置信息
|
||||||
|
/// </summary>
|
||||||
|
public class ChannelConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 通道名称
|
||||||
|
/// </summary>
|
||||||
|
required public string name;
|
||||||
|
/// <summary>
|
||||||
|
/// 通道显示颜色(如前端波形显示用)
|
||||||
|
/// </summary>
|
||||||
|
required public string color;
|
||||||
|
/// <summary>
|
||||||
|
/// 通道信号线宽度(位数)
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 wireWidth;
|
||||||
|
/// <summary>
|
||||||
|
/// 信号线在父端口中的起始索引(bit)
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 wireStartIndex;
|
||||||
|
/// <summary>
|
||||||
|
/// 父端口编号
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 parentPort;
|
||||||
|
/// <summary>
|
||||||
|
/// 捕获模式(如上升沿、下降沿等)
|
||||||
|
/// </summary>
|
||||||
|
required public CaptureMode mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调试器整体配置信息
|
||||||
|
/// </summary>
|
||||||
|
public class DebuggerConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 时钟频率
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 clkFreq;
|
||||||
|
/// <summary>
|
||||||
|
/// 总端口数量
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 totalPortNum;
|
||||||
|
/// <summary>
|
||||||
|
/// 捕获深度(采样点数)
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 captureDepth;
|
||||||
|
/// <summary>
|
||||||
|
/// 触发器数量
|
||||||
|
/// </summary>
|
||||||
|
required public UInt32 triggerNum;
|
||||||
|
/// <summary>
|
||||||
|
/// 所有信号通道的配置信息
|
||||||
|
/// </summary>
|
||||||
|
required public ChannelConfig[] channelConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个通道的捕获数据
|
||||||
|
/// </summary>
|
||||||
|
public class ChannelCaptureData
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 通道名称
|
||||||
|
/// </summary>
|
||||||
|
required public string name;
|
||||||
|
/// <summary>
|
||||||
|
/// 通道捕获到的数据(Base64编码的UInt32数组)
|
||||||
|
/// </summary>
|
||||||
|
required public string data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,163 @@ public class ExamController : ControllerBase
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Database.ExamManager _examManager;
|
||||||
|
|
||||||
|
public ExamController(Database.ExamManager examManager)
|
||||||
|
{
|
||||||
|
_examManager = examManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有实验列表
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>实验列表</returns>
|
||||||
|
[Authorize]
|
||||||
|
[HttpGet("list")]
|
||||||
|
[EnableCors("Users")]
|
||||||
|
[ProducesResponseType(typeof(ExamSummary[]), StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
public IActionResult GetExamList()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var exams = _examManager.GetAllExams();
|
||||||
|
|
||||||
|
var examSummaries = exams.Select(exam => new ExamSummary
|
||||||
|
{
|
||||||
|
ID = exam.ID,
|
||||||
|
Name = exam.Name,
|
||||||
|
CreatedTime = exam.CreatedTime,
|
||||||
|
UpdatedTime = exam.UpdatedTime,
|
||||||
|
Tags = exam.GetTagsList(),
|
||||||
|
Difficulty = exam.Difficulty,
|
||||||
|
IsVisibleToUsers = exam.IsVisibleToUsers
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
logger.Info($"成功获取实验列表,共 {examSummaries.Length} 个实验");
|
||||||
|
return Ok(examSummaries);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
{
|
||||||
|
ID = exam.ID,
|
||||||
|
Name = exam.Name,
|
||||||
|
Description = exam.Description,
|
||||||
|
CreatedTime = exam.CreatedTime,
|
||||||
|
UpdatedTime = exam.UpdatedTime,
|
||||||
|
Tags = exam.GetTagsList(),
|
||||||
|
Difficulty = exam.Difficulty,
|
||||||
|
IsVisibleToUsers = exam.IsVisibleToUsers
|
||||||
|
};
|
||||||
|
|
||||||
|
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]
|
||||||
|
[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] CreateExamRequest 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
|
||||||
|
{
|
||||||
|
ID = exam.ID,
|
||||||
|
Name = exam.Name,
|
||||||
|
Description = exam.Description,
|
||||||
|
CreatedTime = exam.CreatedTime,
|
||||||
|
UpdatedTime = exam.UpdatedTime,
|
||||||
|
Tags = exam.GetTagsList(),
|
||||||
|
Difficulty = exam.Difficulty,
|
||||||
|
IsVisibleToUsers = exam.IsVisibleToUsers
|
||||||
|
};
|
||||||
|
|
||||||
|
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>
|
||||||
/// 实验信息类
|
/// 实验信息类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -136,156 +293,4 @@ public class ExamController : ControllerBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsVisibleToUsers { get; set; } = true;
|
public bool IsVisibleToUsers { get; set; } = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取所有实验列表
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>实验列表</returns>
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet("list")]
|
|
||||||
[EnableCors("Users")]
|
|
||||||
[ProducesResponseType(typeof(ExamSummary[]), StatusCodes.Status200OK)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
|
||||||
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
|
||||||
public IActionResult GetExamList()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var db = new Database.AppDataConnection();
|
|
||||||
var exams = db.GetAllExams();
|
|
||||||
|
|
||||||
var examSummaries = exams.Select(exam => new ExamSummary
|
|
||||||
{
|
|
||||||
ID = exam.ID,
|
|
||||||
Name = exam.Name,
|
|
||||||
CreatedTime = exam.CreatedTime,
|
|
||||||
UpdatedTime = exam.UpdatedTime,
|
|
||||||
Tags = exam.GetTagsList(),
|
|
||||||
Difficulty = exam.Difficulty,
|
|
||||||
IsVisibleToUsers = exam.IsVisibleToUsers
|
|
||||||
}).ToArray();
|
|
||||||
|
|
||||||
logger.Info($"成功获取实验列表,共 {examSummaries.Length} 个实验");
|
|
||||||
return Ok(examSummaries);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
{
|
|
||||||
using var db = new Database.AppDataConnection();
|
|
||||||
var result = db.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
|
|
||||||
{
|
|
||||||
ID = exam.ID,
|
|
||||||
Name = exam.Name,
|
|
||||||
Description = exam.Description,
|
|
||||||
CreatedTime = exam.CreatedTime,
|
|
||||||
UpdatedTime = exam.UpdatedTime,
|
|
||||||
Tags = exam.GetTagsList(),
|
|
||||||
Difficulty = exam.Difficulty,
|
|
||||||
IsVisibleToUsers = exam.IsVisibleToUsers
|
|
||||||
};
|
|
||||||
|
|
||||||
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]
|
|
||||||
[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] CreateExamRequest request)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(request.ID) || string.IsNullOrWhiteSpace(request.Name) || string.IsNullOrWhiteSpace(request.Description))
|
|
||||||
return BadRequest("实验ID、名称和描述不能为空");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using var db = new Database.AppDataConnection();
|
|
||||||
var result = db.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
|
|
||||||
{
|
|
||||||
ID = exam.ID,
|
|
||||||
Name = exam.Name,
|
|
||||||
Description = exam.Description,
|
|
||||||
CreatedTime = exam.CreatedTime,
|
|
||||||
UpdatedTime = exam.UpdatedTime,
|
|
||||||
Tags = exam.GetTagsList(),
|
|
||||||
Difficulty = exam.Difficulty,
|
|
||||||
IsVisibleToUsers = exam.IsVisibleToUsers
|
|
||||||
};
|
|
||||||
|
|
||||||
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using server.Services;
|
using server.Services;
|
||||||
using Database;
|
|
||||||
|
|
||||||
namespace server.Controllers;
|
namespace server.Controllers;
|
||||||
|
|
||||||
|
@ -12,12 +11,15 @@ namespace server.Controllers;
|
||||||
[EnableCors("Users")]
|
[EnableCors("Users")]
|
||||||
public class HdmiVideoStreamController : ControllerBase
|
public class HdmiVideoStreamController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly HttpHdmiVideoStreamService _videoStreamService;
|
|
||||||
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
public HdmiVideoStreamController(HttpHdmiVideoStreamService videoStreamService)
|
private readonly HttpHdmiVideoStreamService _videoStreamService;
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
|
|
||||||
|
public HdmiVideoStreamController(HttpHdmiVideoStreamService videoStreamService, Database.UserManager userManager)
|
||||||
{
|
{
|
||||||
_videoStreamService = videoStreamService;
|
_videoStreamService = videoStreamService;
|
||||||
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 管理员获取所有板子的 endpoints
|
// 管理员获取所有板子的 endpoints
|
||||||
|
@ -40,11 +42,7 @@ public class HdmiVideoStreamController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return Unauthorized("User name not found in claims.");
|
return Unauthorized("User name not found in claims.");
|
||||||
|
|
||||||
var db = new AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
if (db == null)
|
|
||||||
return NotFound("Database connection failed.");
|
|
||||||
|
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return NotFound("User not found.");
|
return NotFound("User not found.");
|
||||||
|
|
||||||
|
@ -53,7 +51,7 @@ public class HdmiVideoStreamController : ControllerBase
|
||||||
if (boardId == Guid.Empty)
|
if (boardId == Guid.Empty)
|
||||||
return NotFound("No board bound to this user.");
|
return NotFound("No board bound to this user.");
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(boardId);
|
var boardRet = _userManager.GetBoardByID(boardId);
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
return NotFound("Board not found.");
|
return NotFound("Board not found.");
|
||||||
|
|
||||||
|
@ -70,11 +68,7 @@ public class HdmiVideoStreamController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return Unauthorized("User name not found in claims.");
|
return Unauthorized("User name not found in claims.");
|
||||||
|
|
||||||
var db = new AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
if (db == null)
|
|
||||||
return NotFound("Database connection failed.");
|
|
||||||
|
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return NotFound("User not found.");
|
return NotFound("User not found.");
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,17 @@ public class JtagController : ControllerBase
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
private readonly ProgressTrackerService _tracker;
|
private readonly ProgressTrackerService _tracker;
|
||||||
|
private readonly UserManager _userManager;
|
||||||
|
private readonly ResourceManager _resourceManager;
|
||||||
|
|
||||||
private const string BITSTREAM_PATH = "bitstream/Jtag";
|
private const string BITSTREAM_PATH = "bitstream/Jtag";
|
||||||
|
|
||||||
public JtagController(ProgressTrackerService tracker)
|
public JtagController(
|
||||||
|
ProgressTrackerService tracker, UserManager userManager, ResourceManager resourceManager)
|
||||||
{
|
{
|
||||||
_tracker = tracker;
|
_tracker = tracker;
|
||||||
|
_userManager = userManager;
|
||||||
|
_resourceManager = resourceManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -127,6 +132,7 @@ public class JtagController : ControllerBase
|
||||||
/// <param name="address">JTAG 设备地址</param>
|
/// <param name="address">JTAG 设备地址</param>
|
||||||
/// <param name="port">JTAG 设备端口</param>
|
/// <param name="port">JTAG 设备端口</param>
|
||||||
/// <param name="bitstreamId">比特流ID</param>
|
/// <param name="bitstreamId">比特流ID</param>
|
||||||
|
/// <param name="cancelToken">取消令牌</param>
|
||||||
/// <returns>进度跟踪TaskID</returns>
|
/// <returns>进度跟踪TaskID</returns>
|
||||||
[HttpPost("DownloadBitstream")]
|
[HttpPost("DownloadBitstream")]
|
||||||
[EnableCors("Users")]
|
[EnableCors("Users")]
|
||||||
|
@ -134,7 +140,7 @@ public class JtagController : ControllerBase
|
||||||
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
|
||||||
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
|
||||||
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
public async ValueTask<IResult> DownloadBitstream(string address, int port, int bitstreamId, CancellationToken cancelToken)
|
public IResult DownloadBitstream(string address, int port, int bitstreamId, CancellationToken cancelToken)
|
||||||
{
|
{
|
||||||
logger.Info($"User {User.Identity?.Name} initiating bitstream download to device {address}:{port} using bitstream ID: {bitstreamId}");
|
logger.Info($"User {User.Identity?.Name} initiating bitstream download to device {address}:{port} using bitstream ID: {bitstreamId}");
|
||||||
|
|
||||||
|
@ -149,35 +155,39 @@ public class JtagController : ControllerBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从数据库获取用户信息
|
// 从数据库获取用户信息
|
||||||
using var db = new Database.AppDataConnection();
|
var userResult = _userManager.GetUserByName(username);
|
||||||
var userResult = db.GetUserByName(username);
|
|
||||||
if (!userResult.IsSuccessful || !userResult.Value.HasValue)
|
if (!userResult.IsSuccessful || !userResult.Value.HasValue)
|
||||||
{
|
{
|
||||||
logger.Error($"User {username} not found in database");
|
logger.Error($"User {username} not found in database");
|
||||||
return TypedResults.BadRequest("用户不存在");
|
return TypedResults.BadRequest("用户不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = userResult.Value.Value;
|
|
||||||
|
|
||||||
// 从数据库获取比特流
|
// 从数据库获取比特流
|
||||||
var bitstreamResult = db.GetResourceById(bitstreamId);
|
var user = userResult.Value.Value;
|
||||||
|
var resourceRet = _resourceManager.GetResourceById(bitstreamId);
|
||||||
|
|
||||||
if (!bitstreamResult.IsSuccessful)
|
if (!resourceRet.IsSuccessful)
|
||||||
{
|
{
|
||||||
logger.Error($"User {username} failed to get bitstream from database: {bitstreamResult.Error}");
|
logger.Error($"User {username} failed to get bitstream from database: {resourceRet.Error}");
|
||||||
return TypedResults.InternalServerError($"数据库查询失败: {bitstreamResult.Error?.Message}");
|
return TypedResults.InternalServerError($"数据库查询失败: {resourceRet.Error?.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bitstreamResult.Value.HasValue)
|
if (!resourceRet.Value.HasValue)
|
||||||
{
|
{
|
||||||
logger.Warn($"User {username} attempted to download non-existent bitstream ID: {bitstreamId}");
|
logger.Warn($"User {username} attempted to download non-existent bitstream ID: {bitstreamId}");
|
||||||
return TypedResults.BadRequest("比特流不存在");
|
return TypedResults.BadRequest("比特流不存在");
|
||||||
}
|
}
|
||||||
|
|
||||||
var bitstream = bitstreamResult.Value.Value;
|
|
||||||
|
|
||||||
// 处理比特流数据
|
// 处理比特流数据
|
||||||
var fileBytes = bitstream.Data;
|
var resource = resourceRet.Value.Value;
|
||||||
|
var bitstreamRet = _resourceManager.ReadBytesFromPath(resource.Path);
|
||||||
|
if (!bitstreamRet.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"User {username} failed to read bitstream file: {bitstreamRet.Error}");
|
||||||
|
return TypedResults.InternalServerError($"比特流读取失败: {bitstreamRet.Error?.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileBytes = bitstreamRet.Value;
|
||||||
if (fileBytes == null || fileBytes.Length == 0)
|
if (fileBytes == null || fileBytes.Length == 0)
|
||||||
{
|
{
|
||||||
logger.Warn($"User {username} found empty bitstream data for ID: {bitstreamId}");
|
logger.Warn($"User {username} found empty bitstream data for ID: {bitstreamId}");
|
||||||
|
@ -235,7 +245,7 @@ public class JtagController : ControllerBase
|
||||||
|
|
||||||
if (ret.IsSuccessful)
|
if (ret.IsSuccessful)
|
||||||
{
|
{
|
||||||
logger.Info($"User {username} successfully downloaded bitstream '{bitstream.ResourceName}' to device {address}");
|
logger.Info($"User {username} successfully downloaded bitstream '{resource.ResourceName}' to device {address}");
|
||||||
progress.Finish();
|
progress.Finish();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -15,56 +15,11 @@ public class LogicAnalyzerController : ControllerBase
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
/// <summary>
|
private readonly Database.UserManager _userManager;
|
||||||
/// 信号触发配置
|
|
||||||
/// </summary>
|
public LogicAnalyzerController(Database.UserManager userManager)
|
||||||
public class SignalTriggerConfig
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
_userManager = userManager;
|
||||||
/// 信号索引 (0-7)
|
|
||||||
/// </summary>
|
|
||||||
public int SignalIndex { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 操作符
|
|
||||||
/// </summary>
|
|
||||||
public SignalOperator Operator { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 信号值
|
|
||||||
/// </summary>
|
|
||||||
public SignalValue Value { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 捕获配置
|
|
||||||
/// </summary>
|
|
||||||
public class CaptureConfig
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 全局触发模式
|
|
||||||
/// </summary>
|
|
||||||
public GlobalCaptureMode GlobalMode { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 捕获深度
|
|
||||||
/// </summary>
|
|
||||||
public int CaptureLength { get; set; } = 2048 * 32;
|
|
||||||
/// <summary>
|
|
||||||
/// 预采样深度
|
|
||||||
/// </summary>
|
|
||||||
public int PreCaptureLength { get; set; } = 2048;
|
|
||||||
/// <summary>
|
|
||||||
/// 有效通道
|
|
||||||
/// </summary>
|
|
||||||
public AnalyzerChannelDiv ChannelDiv { get; set; } = AnalyzerChannelDiv.EIGHT;
|
|
||||||
/// <summary>
|
|
||||||
/// 时钟分频系数
|
|
||||||
/// </summary>
|
|
||||||
public AnalyzerClockDiv ClockDiv { get; set; } = AnalyzerClockDiv.DIV1;
|
|
||||||
/// <summary>
|
|
||||||
/// 信号触发配置列表
|
|
||||||
/// </summary>
|
|
||||||
public SignalTriggerConfig[] SignalConfigs { get; set; } = Array.Empty<SignalTriggerConfig>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -78,8 +33,7 @@ public class LogicAnalyzerController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -87,7 +41,7 @@ public class LogicAnalyzerController : ControllerBase
|
||||||
if (user.BoardID == Guid.Empty)
|
if (user.BoardID == Guid.Empty)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(user.BoardID);
|
var boardRet = _userManager.GetBoardByID(user.BoardID);
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -422,4 +376,57 @@ public class LogicAnalyzerController : ControllerBase
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 信号触发配置
|
||||||
|
/// </summary>
|
||||||
|
public class SignalTriggerConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 信号索引 (0-7)
|
||||||
|
/// </summary>
|
||||||
|
public int SignalIndex { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 操作符
|
||||||
|
/// </summary>
|
||||||
|
public SignalOperator Operator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 信号值
|
||||||
|
/// </summary>
|
||||||
|
public SignalValue Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 捕获配置
|
||||||
|
/// </summary>
|
||||||
|
public class CaptureConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 全局触发模式
|
||||||
|
/// </summary>
|
||||||
|
public GlobalCaptureMode GlobalMode { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 捕获深度
|
||||||
|
/// </summary>
|
||||||
|
public int CaptureLength { get; set; } = 2048 * 32;
|
||||||
|
/// <summary>
|
||||||
|
/// 预采样深度
|
||||||
|
/// </summary>
|
||||||
|
public int PreCaptureLength { get; set; } = 2048;
|
||||||
|
/// <summary>
|
||||||
|
/// 有效通道
|
||||||
|
/// </summary>
|
||||||
|
public AnalyzerChannelDiv ChannelDiv { get; set; } = AnalyzerChannelDiv.EIGHT;
|
||||||
|
/// <summary>
|
||||||
|
/// 时钟分频系数
|
||||||
|
/// </summary>
|
||||||
|
public AnalyzerClockDiv ClockDiv { get; set; } = AnalyzerClockDiv.DIV1;
|
||||||
|
/// <summary>
|
||||||
|
/// 信号触发配置列表
|
||||||
|
/// </summary>
|
||||||
|
public SignalTriggerConfig[] SignalConfigs { get; set; } = Array.Empty<SignalTriggerConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,71 +15,11 @@ public class OscilloscopeApiController : ControllerBase
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
/// <summary>
|
private readonly Database.UserManager _userManager;
|
||||||
/// 示波器完整配置
|
|
||||||
/// </summary>
|
public OscilloscopeApiController(Database.UserManager userManager)
|
||||||
public class OscilloscopeFullConfig
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
_userManager = userManager;
|
||||||
/// 是否启动捕获
|
|
||||||
/// </summary>
|
|
||||||
public bool CaptureEnabled { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发电平(0-255)
|
|
||||||
/// </summary>
|
|
||||||
public byte TriggerLevel { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发边沿(true为上升沿,false为下降沿)
|
|
||||||
/// </summary>
|
|
||||||
public bool TriggerRisingEdge { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 水平偏移量(0-1023)
|
|
||||||
/// </summary>
|
|
||||||
public ushort HorizontalShift { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 抽样率(0-1023)
|
|
||||||
/// </summary>
|
|
||||||
public ushort DecimationRate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否自动刷新RAM
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoRefreshRAM { get; set; } = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 示波器状态和数据
|
|
||||||
/// </summary>
|
|
||||||
public class OscilloscopeDataResponse
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// AD采样频率
|
|
||||||
/// </summary>
|
|
||||||
public uint ADFrequency { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AD采样幅度
|
|
||||||
/// </summary>
|
|
||||||
public byte ADVpp { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AD采样最大值
|
|
||||||
/// </summary>
|
|
||||||
public byte ADMax { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// AD采样最小值
|
|
||||||
/// </summary>
|
|
||||||
public byte ADMin { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 波形数据(Base64编码)
|
|
||||||
/// </summary>
|
|
||||||
public string WaveformData { get; set; } = string.Empty;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -93,8 +33,7 @@ public class OscilloscopeApiController : ControllerBase
|
||||||
if (string.IsNullOrEmpty(userName))
|
if (string.IsNullOrEmpty(userName))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
using var db = new Database.AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -102,7 +41,7 @@ public class OscilloscopeApiController : ControllerBase
|
||||||
if (user.BoardID == Guid.Empty)
|
if (user.BoardID == Guid.Empty)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(user.BoardID);
|
var boardRet = _userManager.GetBoardByID(user.BoardID);
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -481,4 +420,72 @@ public class OscilloscopeApiController : ControllerBase
|
||||||
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
return StatusCode(StatusCodes.Status500InternalServerError, "操作失败,请稍后重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 示波器完整配置
|
||||||
|
/// </summary>
|
||||||
|
public class OscilloscopeFullConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否启动捕获
|
||||||
|
/// </summary>
|
||||||
|
public bool CaptureEnabled { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发电平(0-255)
|
||||||
|
/// </summary>
|
||||||
|
public byte TriggerLevel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 触发边沿(true为上升沿,false为下降沿)
|
||||||
|
/// </summary>
|
||||||
|
public bool TriggerRisingEdge { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 水平偏移量(0-1023)
|
||||||
|
/// </summary>
|
||||||
|
public ushort HorizontalShift { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 抽样率(0-1023)
|
||||||
|
/// </summary>
|
||||||
|
public ushort DecimationRate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否自动刷新RAM
|
||||||
|
/// </summary>
|
||||||
|
public bool AutoRefreshRAM { get; set; } = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 示波器状态和数据
|
||||||
|
/// </summary>
|
||||||
|
public class OscilloscopeDataResponse
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// AD采样频率
|
||||||
|
/// </summary>
|
||||||
|
public uint ADFrequency { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AD采样幅度
|
||||||
|
/// </summary>
|
||||||
|
public byte ADVpp { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AD采样最大值
|
||||||
|
/// </summary>
|
||||||
|
public byte ADMax { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// AD采样最小值
|
||||||
|
/// </summary>
|
||||||
|
public byte ADMin { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 波形数据(Base64编码)
|
||||||
|
/// </summary>
|
||||||
|
public string WaveformData { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -15,6 +15,316 @@ public class ResourceController : ControllerBase
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly UserManager _userManager;
|
||||||
|
private readonly ResourceManager _resourceManager;
|
||||||
|
|
||||||
|
public ResourceController(UserManager userManager, ResourceManager resourceManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_resourceManager = resourceManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
{
|
||||||
|
// 获取当前用户ID
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 读取文件数据
|
||||||
|
using var memoryStream = new MemoryStream();
|
||||||
|
await file.CopyToAsync(memoryStream);
|
||||||
|
var fileData = memoryStream.ToArray();
|
||||||
|
|
||||||
|
var result = _resourceManager.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
|
||||||
|
{
|
||||||
|
// 获取当前用户ID
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 普通用户只能查看自己的资源和模板资源
|
||||||
|
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 = _resourceManager.GetFullResourceList(examId, resourceType, Resource.ResourcePurposes.User, user.ID);
|
||||||
|
var templateResourcesResult = _resourceManager.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 = _resourceManager.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
|
||||||
|
{
|
||||||
|
var result = _resourceManager.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})");
|
||||||
|
|
||||||
|
var dataRet = _resourceManager.ReadBytesFromPath(resource.Path);
|
||||||
|
if (!dataRet.IsSuccessful)
|
||||||
|
{
|
||||||
|
logger.Error($"读取资源数据时出错: {dataRet.Error.Message}");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, $"读取资源数据失败: {dataRet.Error.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return File(dataRet.Value, 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
|
||||||
|
{
|
||||||
|
// 获取当前用户信息
|
||||||
|
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 resourceResult = _resourceManager.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 = _resourceManager.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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 资源信息类
|
/// 资源信息类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -77,301 +387,4 @@ public class ResourceController : ControllerBase
|
||||||
public string? ExamID { get; set; }
|
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}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using Database;
|
|
||||||
using DotNext;
|
using DotNext;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -15,44 +14,21 @@ using DotNext;
|
||||||
public class VideoStreamController : ControllerBase
|
public class VideoStreamController : ControllerBase
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
private readonly server.Services.HttpVideoStreamService _videoStreamService;
|
private readonly server.Services.HttpVideoStreamService _videoStreamService;
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
/// <summary>
|
|
||||||
/// 分辨率配置请求模型
|
|
||||||
/// </summary>
|
|
||||||
public class ResolutionConfigRequest
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 宽度
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
[Range(640, 1920, ErrorMessage = "宽度必须在640-1920范围内")]
|
|
||||||
public int Width { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 高度
|
|
||||||
/// </summary>
|
|
||||||
[Required]
|
|
||||||
[Range(480, 1080, ErrorMessage = "高度必须在480-1080范围内")]
|
|
||||||
public int Height { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AvailableResolutionsResponse
|
|
||||||
{
|
|
||||||
public int Width { get; set; }
|
|
||||||
public int Height { get; set; }
|
|
||||||
public string Name { get; set; } = string.Empty;
|
|
||||||
public string Value => $"{Width}x{Height}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化HTTP视频流控制器
|
/// 初始化HTTP视频流控制器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="videoStreamService">HTTP视频流服务</param>
|
/// <param name="videoStreamService">HTTP视频流服务</param>
|
||||||
public VideoStreamController(server.Services.HttpVideoStreamService videoStreamService)
|
/// <param name="userManager">用户管理服务</param>
|
||||||
|
public VideoStreamController(
|
||||||
|
server.Services.HttpVideoStreamService videoStreamService, Database.UserManager userManager)
|
||||||
{
|
{
|
||||||
logger.Info("创建VideoStreamController,命名空间:{Namespace}", this.GetType().Namespace);
|
logger.Info("创建VideoStreamController,命名空间:{Namespace}", this.GetType().Namespace);
|
||||||
_videoStreamService = videoStreamService;
|
_videoStreamService = videoStreamService;
|
||||||
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<string> TryGetBoardId()
|
private Optional<string> TryGetBoardId()
|
||||||
|
@ -64,14 +40,7 @@ public class VideoStreamController : ControllerBase
|
||||||
return Optional<string>.None;
|
return Optional<string>.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
var db = new AppDataConnection();
|
var userRet = _userManager.GetUserByName(userName);
|
||||||
if (db == null)
|
|
||||||
{
|
|
||||||
logger.Error("Database connection failed.");
|
|
||||||
return Optional<string>.None;
|
|
||||||
}
|
|
||||||
|
|
||||||
var userRet = db.GetUserByName(userName);
|
|
||||||
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
if (!userRet.IsSuccessful || !userRet.Value.HasValue)
|
||||||
{
|
{
|
||||||
logger.Error("User not found.");
|
logger.Error("User not found.");
|
||||||
|
@ -349,4 +318,32 @@ public class VideoStreamController : ControllerBase
|
||||||
return TypedResults.InternalServerError($"执行自动对焦失败: {ex.Message}");
|
return TypedResults.InternalServerError($"执行自动对焦失败: {ex.Message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 分辨率配置请求模型
|
||||||
|
/// </summary>
|
||||||
|
public class ResolutionConfigRequest
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 宽度
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[Range(640, 1920, ErrorMessage = "宽度必须在640-1920范围内")]
|
||||||
|
public int Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 高度
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[Range(480, 1080, ErrorMessage = "高度必须在480-1080范围内")]
|
||||||
|
public int Height { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AvailableResolutionsResponse
|
||||||
|
{
|
||||||
|
public int Width { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Value => $"{Width}x{Height}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,98 @@
|
||||||
|
using DotNext;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Data;
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用程序数据连接类,用于与数据库交互
|
||||||
|
/// </summary>
|
||||||
|
public class AppDataConnection : DataConnection
|
||||||
|
{
|
||||||
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
static readonly string DATABASE_FILEPATH = $"{Global.DataPath}/Database.sqlite";
|
||||||
|
|
||||||
|
static readonly LinqToDB.DataOptions options =
|
||||||
|
new LinqToDB.DataOptions().UseSQLite($"Data Source={DATABASE_FILEPATH}");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户表
|
||||||
|
/// </summary>
|
||||||
|
public ITable<User> UserTable => this.GetTable<User>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子表
|
||||||
|
/// </summary>
|
||||||
|
public ITable<Board> BoardTable => this.GetTable<Board>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验表
|
||||||
|
/// </summary>
|
||||||
|
public ITable<Exam> ExamTable => this.GetTable<Exam>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源表(统一管理实验资源、用户比特流等)
|
||||||
|
/// </summary>
|
||||||
|
public ITable<Resource> ResourceTable => this.GetTable<Resource>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化应用程序数据连接
|
||||||
|
/// </summary>
|
||||||
|
public AppDataConnection() : base(options)
|
||||||
|
{
|
||||||
|
var filePath = Path.GetDirectoryName(DATABASE_FILEPATH);
|
||||||
|
if (!string.IsNullOrEmpty(filePath) && !Directory.Exists(filePath))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Path.Exists(DATABASE_FILEPATH))
|
||||||
|
{
|
||||||
|
logger.Info($"数据库文件不存在,正在创建新数据库: {DATABASE_FILEPATH}");
|
||||||
|
LinqToDB.DataProvider.SQLite.SQLiteTools.CreateDatabase(DATABASE_FILEPATH);
|
||||||
|
this.CreateAllTables();
|
||||||
|
var user = new User()
|
||||||
|
{
|
||||||
|
Name = "Admin",
|
||||||
|
EMail = "selfconfusion@gmail.com",
|
||||||
|
Password = "12345678",
|
||||||
|
Permission = Database.User.UserPermission.Admin,
|
||||||
|
};
|
||||||
|
this.Insert(user);
|
||||||
|
logger.Info("默认管理员用户已创建");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Info($"数据库连接已建立: {DATABASE_FILEPATH}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建所有数据库表
|
||||||
|
/// </summary>
|
||||||
|
public void CreateAllTables()
|
||||||
|
{
|
||||||
|
logger.Info("正在创建数据库表...");
|
||||||
|
this.CreateTable<User>();
|
||||||
|
this.CreateTable<Board>();
|
||||||
|
this.CreateTable<Exam>();
|
||||||
|
this.CreateTable<Resource>();
|
||||||
|
logger.Info("数据库表创建完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除所有数据库表
|
||||||
|
/// </summary>
|
||||||
|
public void DropAllTables()
|
||||||
|
{
|
||||||
|
logger.Warn("正在删除所有数据库表...");
|
||||||
|
this.DropTable<User>();
|
||||||
|
this.DropTable<Board>();
|
||||||
|
this.DropTable<Exam>();
|
||||||
|
this.DropTable<Resource>();
|
||||||
|
logger.Warn("所有数据库表已删除");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,154 @@
|
||||||
|
using DotNext;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Data;
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
public class ExamManager
|
||||||
|
{
|
||||||
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly AppDataConnection _db;
|
||||||
|
|
||||||
|
public ExamManager(AppDataConnection db)
|
||||||
|
{
|
||||||
|
this._db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建新实验
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">实验ID</param>
|
||||||
|
/// <param name="name">实验名称</param>
|
||||||
|
/// <param name="description">实验描述</param>
|
||||||
|
/// <param name="tags">实验标签</param>
|
||||||
|
/// <param name="difficulty">实验难度</param>
|
||||||
|
/// <param name="isVisibleToUsers">普通用户是否可见</param>
|
||||||
|
/// <returns>创建的实验</returns>
|
||||||
|
public Result<Exam> CreateExam(string id, string name, string description, string[]? tags = null, int difficulty = 1, bool isVisibleToUsers = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 检查实验ID是否已存在
|
||||||
|
var existingExam = _db.ExamTable.Where(e => e.ID == id).FirstOrDefault();
|
||||||
|
if (existingExam != null)
|
||||||
|
{
|
||||||
|
logger.Error($"实验ID已存在: {id}");
|
||||||
|
return new(new Exception($"实验ID已存在: {id}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var exam = new Exam
|
||||||
|
{
|
||||||
|
ID = id,
|
||||||
|
Name = name,
|
||||||
|
Description = description,
|
||||||
|
Difficulty = Math.Max(1, Math.Min(5, difficulty)),
|
||||||
|
IsVisibleToUsers = isVisibleToUsers,
|
||||||
|
CreatedTime = DateTime.Now,
|
||||||
|
UpdatedTime = DateTime.Now
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tags != null)
|
||||||
|
{
|
||||||
|
exam.SetTagsList(tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
_db.Insert(exam);
|
||||||
|
logger.Info($"新实验已创建: {id} ({name})");
|
||||||
|
return new(exam);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"创建实验时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新实验信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">实验ID</param>
|
||||||
|
/// <param name="name">实验名称</param>
|
||||||
|
/// <param name="description">实验描述</param>
|
||||||
|
/// <param name="tags">实验标签</param>
|
||||||
|
/// <param name="difficulty">实验难度</param>
|
||||||
|
/// <param name="isVisibleToUsers">普通用户是否可见</param>
|
||||||
|
/// <returns>更新的记录数</returns>
|
||||||
|
public Result<int> UpdateExam(string id, string? name = null, string? description = null, string[]? tags = null, int? difficulty = null, bool? isVisibleToUsers = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if (name != null)
|
||||||
|
{
|
||||||
|
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Name, name).Update();
|
||||||
|
}
|
||||||
|
if (description != null)
|
||||||
|
{
|
||||||
|
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Description, description).Update();
|
||||||
|
}
|
||||||
|
if (tags != null)
|
||||||
|
{
|
||||||
|
var tagsString = string.Join(",", tags.Where(tag => !string.IsNullOrWhiteSpace(tag)).Select(tag => tag.Trim()));
|
||||||
|
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Tags, tagsString).Update();
|
||||||
|
}
|
||||||
|
if (difficulty.HasValue)
|
||||||
|
{
|
||||||
|
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Difficulty, Math.Max(1, Math.Min(5, difficulty.Value))).Update();
|
||||||
|
}
|
||||||
|
if (isVisibleToUsers.HasValue)
|
||||||
|
{
|
||||||
|
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.IsVisibleToUsers, isVisibleToUsers.Value).Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新时间
|
||||||
|
_db.ExamTable.Where(e => e.ID == id).Set(e => e.UpdatedTime, DateTime.Now).Update();
|
||||||
|
|
||||||
|
logger.Info($"实验已更新: {id},更新记录数: {result}");
|
||||||
|
return new(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"更新实验时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有实验信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>所有实验的数组</returns>
|
||||||
|
public Exam[] GetAllExams()
|
||||||
|
{
|
||||||
|
var exams = _db.ExamTable.OrderBy(e => e.ID).ToArray();
|
||||||
|
logger.Debug($"获取所有实验,共 {exams.Length} 个");
|
||||||
|
return exams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据实验ID获取实验信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="examId">实验ID</param>
|
||||||
|
/// <returns>包含实验信息的结果,如果未找到则返回空</returns>
|
||||||
|
public Result<Optional<Exam>> GetExamByID(string examId)
|
||||||
|
{
|
||||||
|
var exams = _db.ExamTable.Where(exam => exam.ID == examId).ToArray();
|
||||||
|
|
||||||
|
if (exams.Length > 1)
|
||||||
|
{
|
||||||
|
logger.Error($"数据库中存在多个相同ID的实验: {examId}");
|
||||||
|
return new(new Exception($"数据库中存在多个相同ID的实验: {examId}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exams.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到ID对应的实验: {examId}");
|
||||||
|
return new(Optional<Exam>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"成功获取实验信息: {examId}");
|
||||||
|
return new(exams[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,357 @@
|
||||||
|
using DotNext;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Data;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
public class ResourceManager
|
||||||
|
{
|
||||||
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly AppDataConnection _db;
|
||||||
|
|
||||||
|
public ResourceManager(AppDataConnection db)
|
||||||
|
{
|
||||||
|
this._db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据文件扩展名获取MIME类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="extension">文件扩展名</param>
|
||||||
|
/// <param name="fileName">文件名(可选,用于特殊文件判断)</param>
|
||||||
|
/// <returns>MIME类型</returns>
|
||||||
|
private string GetMimeTypeFromExtension(string extension, string fileName = "")
|
||||||
|
{
|
||||||
|
// 特殊文件名处理
|
||||||
|
if (!string.IsNullOrEmpty(fileName) && fileName.Equals("diagram.json", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "application/json";
|
||||||
|
}
|
||||||
|
|
||||||
|
return extension.ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
".png" => "image/png",
|
||||||
|
".jpg" or ".jpeg" => "image/jpeg",
|
||||||
|
".gif" => "image/gif",
|
||||||
|
".bmp" => "image/bmp",
|
||||||
|
".svg" => "image/svg+xml",
|
||||||
|
".bit" => "application/octet-stream",
|
||||||
|
".sbit" => "application/octet-stream",
|
||||||
|
".bin" => "application/octet-stream",
|
||||||
|
".mcs" => "application/octet-stream",
|
||||||
|
".hex" => "text/plain",
|
||||||
|
".json" => "application/json",
|
||||||
|
".zip" => "application/zip",
|
||||||
|
".md" => "text/markdown",
|
||||||
|
_ => "application/octet-stream"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将二进制数据写入指定路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">目标文件路径</param>
|
||||||
|
/// <param name="data">要写入的二进制数据</param>
|
||||||
|
/// <returns>写入是否成功</returns>
|
||||||
|
public Result<bool> WriteBytesToPath(string path, byte[] data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(Global.DataPath, path);
|
||||||
|
var directory = Path.GetDirectoryName(filePath);
|
||||||
|
if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(directory);
|
||||||
|
}
|
||||||
|
File.WriteAllBytes(filePath, data);
|
||||||
|
logger.Info($"成功写入文件: {filePath},大小: {data.Length} bytes");
|
||||||
|
return new(true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"写入文件时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从指定路径读取二进制数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">要读取的文件路径</param>
|
||||||
|
/// <returns>读取到的二进制数据</returns>
|
||||||
|
public Result<byte[]> ReadBytesFromPath(string path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var filePath = Path.Combine(Global.DataPath, path);
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
logger.Error($"文件不存在: {filePath}");
|
||||||
|
return new(new Exception($"文件不存在: {filePath}"));
|
||||||
|
}
|
||||||
|
var data = File.ReadAllBytes(filePath);
|
||||||
|
logger.Info($"成功读取文件: {filePath},大小: {data.Length} bytes");
|
||||||
|
return new(data);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"读取文件时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">上传用户ID</param>
|
||||||
|
/// <param name="resourceType">资源类型</param>
|
||||||
|
/// <param name="resourcePurpose">资源用途(template 或 user)</param>
|
||||||
|
/// <param name="resourceName">资源名称</param>
|
||||||
|
/// <param name="data">资源二进制数据</param>
|
||||||
|
/// <param name="examId">所属实验ID(可选)</param>
|
||||||
|
/// <param name="mimeType">MIME类型(可选,将根据文件扩展名自动确定)</param>
|
||||||
|
/// <returns>创建的资源</returns>
|
||||||
|
public Result<Resource> AddResource(
|
||||||
|
Guid userId, string resourceType, string resourcePurpose,
|
||||||
|
string resourceName, byte[] data, string? examId = null, string? mimeType = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// 验证用户是否存在
|
||||||
|
var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
logger.Error($"用户不存在: {userId}");
|
||||||
|
return new(new Exception($"用户不存在: {userId}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果指定了实验ID,验证实验是否存在
|
||||||
|
if (!string.IsNullOrEmpty(examId))
|
||||||
|
{
|
||||||
|
var exam = _db.ExamTable.Where(e => e.ID == examId).FirstOrDefault();
|
||||||
|
if (exam == null)
|
||||||
|
{
|
||||||
|
logger.Error($"实验不存在: {examId}");
|
||||||
|
return new(new Exception($"实验不存在: {examId}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证资源用途
|
||||||
|
if (resourcePurpose != Resource.ResourcePurposes.Template &&
|
||||||
|
resourcePurpose != Resource.ResourcePurposes.User)
|
||||||
|
{
|
||||||
|
logger.Error($"无效的资源用途: {resourcePurpose}");
|
||||||
|
return new(new Exception($"无效的资源用途: {resourcePurpose}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果未指定MIME类型,根据文件扩展名自动确定
|
||||||
|
if (string.IsNullOrEmpty(mimeType))
|
||||||
|
{
|
||||||
|
var extension = Path.GetExtension(resourceName).ToLowerInvariant();
|
||||||
|
mimeType = GetMimeTypeFromExtension(extension, resourceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算数据的SHA256
|
||||||
|
var sha256 = SHA256.HashData(data).ToString();
|
||||||
|
if (string.IsNullOrEmpty(sha256))
|
||||||
|
{
|
||||||
|
logger.Error($"SHA256计算失败");
|
||||||
|
return new(new Exception("SHA256计算失败"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var duplicateResource = _db.ResourceTable.Where(r => r.SHA256 == sha256).FirstOrDefault();
|
||||||
|
if (duplicateResource != null && duplicateResource.ResourceName == resourceName)
|
||||||
|
{
|
||||||
|
logger.Info($"资源已存在: {resourceName}");
|
||||||
|
return new(new Exception($"资源已存在: {resourceName}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var nowTime = DateTime.Now;
|
||||||
|
var resource = new Resource
|
||||||
|
{
|
||||||
|
UserID = userId,
|
||||||
|
ExamID = examId,
|
||||||
|
ResourceType = resourceType,
|
||||||
|
ResourcePurpose = resourcePurpose,
|
||||||
|
ResourceName = resourceName,
|
||||||
|
Path = duplicateResource == null ?
|
||||||
|
Path.Combine(resourceType, nowTime.ToString("yyyyMMddHH"), resourceName) :
|
||||||
|
duplicateResource.Path,
|
||||||
|
SHA256 = sha256,
|
||||||
|
MimeType = mimeType,
|
||||||
|
UploadTime = nowTime
|
||||||
|
};
|
||||||
|
|
||||||
|
var insertedId = _db.InsertWithIdentity(resource);
|
||||||
|
resource.ID = Convert.ToInt32(insertedId);
|
||||||
|
|
||||||
|
var writeRet = WriteBytesToPath(resource.Path, data);
|
||||||
|
if (writeRet.IsSuccessful && writeRet.Value)
|
||||||
|
{
|
||||||
|
logger.Info($"新资源已添加: {userId}/{resourceType}/{resourcePurpose}/{resourceName} ({data.Length} bytes)" +
|
||||||
|
(examId != null ? $" [实验: {examId}]" : "") + $" [ID: {resource.ID}]");
|
||||||
|
return new(resource);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_db.ResourceTable.Where(r => r.ID == resource.ID).Delete();
|
||||||
|
|
||||||
|
logger.Error($"写入资源文件时出错: {writeRet.Error}");
|
||||||
|
return new(new Exception(writeRet.Error?.ToString() ?? $"写入失败"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"添加资源时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取资源信息列表(返回ID和名称)
|
||||||
|
/// <param name="resourceType">资源类型</param>
|
||||||
|
/// <param name="examId">实验ID(可选)</param>
|
||||||
|
/// <param name="resourcePurpose">资源用途(可选)</param>
|
||||||
|
/// <param name="userId">用户ID(可选)</param>
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>资源信息列表</returns>
|
||||||
|
public Result<(int ID, string Name)[]> GetResourceList(string resourceType, string? examId = null, string? resourcePurpose = null, Guid? userId = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var query = _db.ResourceTable.Where(r => r.ResourceType == resourceType);
|
||||||
|
|
||||||
|
if (examId != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.ExamID == examId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourcePurpose != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.ResourcePurpose == resourcePurpose);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.UserID == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var resources = query
|
||||||
|
.Select(r => new { r.ID, r.ResourceName })
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
var result = resources.Select(r => (r.ID, r.ResourceName)).ToArray();
|
||||||
|
logger.Info($"获取资源列表: {resourceType}" +
|
||||||
|
(examId != null ? $"/{examId}" : "") +
|
||||||
|
(resourcePurpose != null ? $"/{resourcePurpose}" : "") +
|
||||||
|
(userId != null ? $"/{userId}" : "") +
|
||||||
|
$",共 {result.Length} 个资源");
|
||||||
|
return new(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"获取资源列表时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取完整的资源列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="examId">实验ID(可选)</param>
|
||||||
|
/// <param name="resourceType">资源类型(可选)</param>
|
||||||
|
/// <param name="resourcePurpose">资源用途(可选)</param>
|
||||||
|
/// <param name="userId">用户ID(可选)</param>
|
||||||
|
/// <returns>完整的资源对象列表</returns>
|
||||||
|
public Result<List<Resource>> GetFullResourceList(string? examId = null, string? resourceType = null, string? resourcePurpose = null, Guid? userId = null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var query = _db.ResourceTable.AsQueryable();
|
||||||
|
|
||||||
|
if (examId != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.ExamID == examId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceType != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.ResourceType == resourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourcePurpose != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.ResourcePurpose == resourcePurpose);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId != null)
|
||||||
|
{
|
||||||
|
query = query.Where(r => r.UserID == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var resources = query.OrderByDescending(r => r.UploadTime).ToList();
|
||||||
|
logger.Info($"获取完整资源列表" +
|
||||||
|
(examId != null ? $" [实验: {examId}]" : "") +
|
||||||
|
(resourceType != null ? $" [类型: {resourceType}]" : "") +
|
||||||
|
(resourcePurpose != null ? $" [用途: {resourcePurpose}]" : "") +
|
||||||
|
(userId != null ? $" [用户: {userId}]" : "") +
|
||||||
|
$",共 {resources.Count} 个资源");
|
||||||
|
return new(resources);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"获取完整资源列表时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据资源ID获取资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceId">资源ID</param>
|
||||||
|
/// <returns>资源数据</returns>
|
||||||
|
public Result<Optional<Resource>> GetResourceById(int resourceId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var resource = _db.ResourceTable.Where(r => r.ID == resourceId).FirstOrDefault();
|
||||||
|
|
||||||
|
if (resource == null)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到资源: {resourceId}");
|
||||||
|
return new(Optional<Resource>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})");
|
||||||
|
return new(resource);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"获取资源时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceId">资源ID</param>
|
||||||
|
/// <returns>删除的记录数</returns>
|
||||||
|
public Result<int> DeleteResource(int resourceId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = _db.ResourceTable.Where(r => r.ID == resourceId).Delete();
|
||||||
|
logger.Info($"资源已删除: {resourceId},删除记录数: {result}");
|
||||||
|
return new(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.Error($"删除资源时出错: {ex.Message}");
|
||||||
|
return new(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,341 @@
|
||||||
|
using DotNext;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Mapping;
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户类,表示用户信息
|
||||||
|
/// </summary>
|
||||||
|
public class User
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
[PrimaryKey]
|
||||||
|
public Guid ID { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的名称
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的电子邮箱
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string EMail { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户的密码(应该进行哈希处理)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string Password { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户权限等级
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required UserPermission Permission { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定的实验板ID,如果未绑定则为空
|
||||||
|
/// </summary>
|
||||||
|
[Nullable]
|
||||||
|
public Guid BoardID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户绑定板子的过期时间
|
||||||
|
/// </summary>
|
||||||
|
[Nullable]
|
||||||
|
public DateTime? BoardExpireTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户权限枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum UserPermission
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 管理员权限,可以管理用户和实验板
|
||||||
|
/// </summary>
|
||||||
|
Admin,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 普通用户权限,只能使用实验板
|
||||||
|
/// </summary>
|
||||||
|
Normal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子类,表示板子信息
|
||||||
|
/// </summary>
|
||||||
|
public class Board
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
[PrimaryKey]
|
||||||
|
public Guid ID { get; set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的名称
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string BoardName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的IP地址
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string IpAddr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的MAC地址
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string MacAddr { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的通信端口
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public int Port { get; set; } = 1234;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的当前状态
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required BoardStatus Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 占用该板子的用户的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
[Nullable]
|
||||||
|
public Guid OccupiedUserID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 占用该板子的用户的用户名
|
||||||
|
/// </summary>
|
||||||
|
[Nullable]
|
||||||
|
public string? OccupiedUserName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子的固件版本号
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public string FirmVersion { get; set; } = "1.0.0";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// FPGA 板子状态枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum BoardStatus
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未启用状态,无法被使用
|
||||||
|
/// </summary>
|
||||||
|
Disabled,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 繁忙状态,正在被用户使用
|
||||||
|
/// </summary>
|
||||||
|
Busy,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可用状态,可以被分配给用户
|
||||||
|
/// </summary>
|
||||||
|
Available,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验类,表示实验信息
|
||||||
|
/// </summary>
|
||||||
|
public class Exam
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 实验的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
[PrimaryKey]
|
||||||
|
public required string ID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验名称
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验描述
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验创建时间
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public DateTime CreatedTime { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验最后更新时间
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public DateTime UpdatedTime { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验标签(以逗号分隔的字符串)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public string Tags { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实验难度(1-5,1为最简单)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public int Difficulty { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 普通用户是否可见
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public bool IsVisibleToUsers { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取标签列表
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>标签数组</returns>
|
||||||
|
public string[] GetTagsList()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(Tags))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return Tags.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(tag => tag.Trim())
|
||||||
|
.Where(tag => !string.IsNullOrEmpty(tag))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置标签列表
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tags">标签数组</param>
|
||||||
|
public void SetTagsList(string[] tags)
|
||||||
|
{
|
||||||
|
Tags = string.Join(",", tags.Where(tag => !string.IsNullOrWhiteSpace(tag)).Select(tag => tag.Trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源类,统一管理实验资源、用户比特流等各类资源
|
||||||
|
/// </summary>
|
||||||
|
public class Resource
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 资源的唯一标识符
|
||||||
|
/// </summary>
|
||||||
|
[PrimaryKey, Identity]
|
||||||
|
public int ID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上传资源的用户ID
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required Guid UserID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所属实验ID(可选,如果不属于特定实验则为空)
|
||||||
|
/// </summary>
|
||||||
|
[Nullable]
|
||||||
|
public string? ExamID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源类型(images, markdown, bitstream, diagram, project等)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string ResourceType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源用途:template(模板)或 user(用户上传)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string ResourcePurpose { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源名称(包含文件扩展名)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string ResourceName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源路径(包含文件名和扩展名)
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string Path { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源SHA256哈希值
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public required string SHA256 { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源创建/上传时间
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public DateTime UploadTime { get; set; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源的MIME类型
|
||||||
|
/// </summary>
|
||||||
|
[NotNull]
|
||||||
|
public string MimeType { get; set; } = "application/octet-stream";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源类型枚举
|
||||||
|
/// </summary>
|
||||||
|
public static class ResourceTypes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 图片资源类型
|
||||||
|
/// </summary>
|
||||||
|
public const string Images = "images";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Markdown文档资源类型
|
||||||
|
/// </summary>
|
||||||
|
public const string Markdown = "markdown";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 比特流文件资源类型
|
||||||
|
/// </summary>
|
||||||
|
public const string Bitstream = "bitstream";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 原理图资源类型
|
||||||
|
/// </summary>
|
||||||
|
public const string Diagram = "diagram";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 项目文件资源类型
|
||||||
|
/// </summary>
|
||||||
|
public const string Project = "project";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 资源用途枚举
|
||||||
|
/// </summary>
|
||||||
|
public static class ResourcePurposes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 模板资源,通常由管理员上传,供用户参考
|
||||||
|
/// </summary>
|
||||||
|
public const string Template = "template";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用户上传的资源
|
||||||
|
/// </summary>
|
||||||
|
public const string User = "user";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,458 @@
|
||||||
|
using DotNext;
|
||||||
|
using LinqToDB;
|
||||||
|
using LinqToDB.Data;
|
||||||
|
|
||||||
|
namespace Database;
|
||||||
|
|
||||||
|
public class UserManager
|
||||||
|
{
|
||||||
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly AppDataConnection _db;
|
||||||
|
|
||||||
|
public UserManager(AppDataConnection db)
|
||||||
|
{
|
||||||
|
this._db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加一个新的用户到数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">用户的名称</param>
|
||||||
|
/// <param name="email">用户的电子邮箱地址</param>
|
||||||
|
/// <param name="password">用户的密码</param>
|
||||||
|
/// <returns>插入的记录数</returns>
|
||||||
|
public int AddUser(string name, string email, string password)
|
||||||
|
{
|
||||||
|
var user = new User()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
EMail = email,
|
||||||
|
Password = password,
|
||||||
|
Permission = Database.User.UserPermission.Normal,
|
||||||
|
};
|
||||||
|
var result = _db.Insert(user);
|
||||||
|
logger.Info($"新用户已添加: {name} ({email})");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据用户名获取用户信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">用户名</param>
|
||||||
|
/// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
|
||||||
|
public Result<Optional<User>> GetUserByName(string name)
|
||||||
|
{
|
||||||
|
var user = _db.UserTable.Where((user) => user.Name == name).ToArray();
|
||||||
|
|
||||||
|
if (user.Length > 1)
|
||||||
|
{
|
||||||
|
logger.Error($"数据库中存在多个同名用户: {name}");
|
||||||
|
return new(new Exception($"数据库中存在多个同名用户: {name}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到用户: {name}");
|
||||||
|
return new(Optional<User>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"成功获取用户信息: {name}");
|
||||||
|
return new(user[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据电子邮箱获取用户信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">用户的电子邮箱地址</param>
|
||||||
|
/// <returns>包含用户信息的结果,如果未找到或出错则返回相应状态</returns>
|
||||||
|
public Result<Optional<User>> GetUserByEMail(string email)
|
||||||
|
{
|
||||||
|
var user = _db.UserTable.Where((user) => user.EMail == email).ToArray();
|
||||||
|
|
||||||
|
if (user.Length > 1)
|
||||||
|
{
|
||||||
|
logger.Error($"数据库中存在多个相同邮箱的用户: {email}");
|
||||||
|
return new(new Exception($"数据库中存在多个相同邮箱的用户: {email}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到邮箱对应的用户: {email}");
|
||||||
|
return new(Optional<User>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"成功获取用户信息: {email}");
|
||||||
|
return new(user[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证用户密码
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">用户名</param>
|
||||||
|
/// <param name="password">用户密码</param>
|
||||||
|
/// <returns>如果密码正确返回用户信息,否则返回空</returns>
|
||||||
|
public Result<Optional<User>> CheckUserPassword(string name, string password)
|
||||||
|
{
|
||||||
|
var ret = GetUserByName(name);
|
||||||
|
if (!ret.IsSuccessful)
|
||||||
|
return new(ret.Error);
|
||||||
|
|
||||||
|
if (!ret.Value.HasValue)
|
||||||
|
return new(Optional<User>.None);
|
||||||
|
|
||||||
|
var user = ret.Value.Value;
|
||||||
|
|
||||||
|
if (user.Password == password)
|
||||||
|
{
|
||||||
|
logger.Info($"用户 {name} 密码验证成功");
|
||||||
|
return new(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Warn($"用户 {name} 密码验证失败");
|
||||||
|
return new(Optional<User>.None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绑定用户与实验板
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">用户的唯一标识符</param>
|
||||||
|
/// <param name="boardId">实验板的唯一标识符</param>
|
||||||
|
/// <param name="expireTime">绑定过期时间</param>
|
||||||
|
/// <returns>更新的记录数</returns>
|
||||||
|
public int BindUserToBoard(Guid userId, Guid boardId, DateTime expireTime)
|
||||||
|
{
|
||||||
|
// 获取用户信息
|
||||||
|
var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
logger.Error($"未找到用户: {userId}");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户的板子绑定信息
|
||||||
|
var userResult = _db.UserTable
|
||||||
|
.Where(u => u.ID == userId)
|
||||||
|
.Set(u => u.BoardID, boardId)
|
||||||
|
.Set(u => u.BoardExpireTime, expireTime)
|
||||||
|
.Update();
|
||||||
|
|
||||||
|
// 更新板子的用户绑定信息
|
||||||
|
var boardResult = _db.BoardTable
|
||||||
|
.Where(b => b.ID == boardId)
|
||||||
|
.Set(b => b.Status, Board.BoardStatus.Busy)
|
||||||
|
.Set(b => b.OccupiedUserID, userId)
|
||||||
|
.Set(b => b.OccupiedUserName, user.Name)
|
||||||
|
.Update();
|
||||||
|
|
||||||
|
logger.Info($"用户 {userId} ({user.Name}) 已绑定到实验板 {boardId},过期时间: {expireTime}");
|
||||||
|
return userResult + boardResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解除用户与实验板的绑定
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">用户的唯一标识符</param>
|
||||||
|
/// <returns>更新的记录数</returns>
|
||||||
|
public int UnbindUserFromBoard(Guid userId)
|
||||||
|
{
|
||||||
|
// 获取用户当前绑定的板子ID
|
||||||
|
var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||||
|
Guid boardId = user?.BoardID ?? Guid.Empty;
|
||||||
|
|
||||||
|
// 清空用户的板子绑定信息
|
||||||
|
var userResult = _db.UserTable
|
||||||
|
.Where(u => u.ID == userId)
|
||||||
|
.Set(u => u.BoardID, Guid.Empty)
|
||||||
|
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||||
|
.Update();
|
||||||
|
|
||||||
|
// 如果用户原本绑定了板子,则清空板子的用户绑定信息
|
||||||
|
int boardResult = 0;
|
||||||
|
if (boardId != Guid.Empty)
|
||||||
|
{
|
||||||
|
boardResult = _db.BoardTable
|
||||||
|
.Where(b => b.ID == boardId)
|
||||||
|
.Set(b => b.Status, Board.BoardStatus.Available)
|
||||||
|
.Set(b => b.OccupiedUserID, Guid.Empty)
|
||||||
|
.Set(b => b.OccupiedUserName, (string?)null)
|
||||||
|
.Update();
|
||||||
|
logger.Info($"实验板 {boardId} 状态已设置为空闲,用户绑定信息已清空");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info($"用户 {userId} 已解除实验板绑定");
|
||||||
|
return userResult + boardResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分配一个未被占用的IP地址
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>分配的IP地址字符串</returns>
|
||||||
|
public string AllocateIpAddr()
|
||||||
|
{
|
||||||
|
var usedIps = _db.BoardTable.Select(b => b.IpAddr).ToArray();
|
||||||
|
for (int i = 1; i <= 254; i++)
|
||||||
|
{
|
||||||
|
string ip = $"169.254.109.{i}";
|
||||||
|
if (!usedIps.Contains(ip))
|
||||||
|
return ip;
|
||||||
|
}
|
||||||
|
throw new Exception("没有可用的IP地址");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动分配一个未被占用的MAC地址
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>分配的MAC地址字符串</returns>
|
||||||
|
public string AllocateMacAddr()
|
||||||
|
{
|
||||||
|
var usedMacs = _db.BoardTable.Select(b => b.MacAddr).ToArray();
|
||||||
|
// 以 02-00-00-xx-xx-xx 格式分配,02 表示本地管理地址
|
||||||
|
for (int i = 1; i <= 0xFFFFFF; i++)
|
||||||
|
{
|
||||||
|
string mac = $"02-00-00-{(i >> 16) & 0xFF:X2}-{(i >> 8) & 0xFF:X2}-{i & 0xFF:X2}";
|
||||||
|
if (!usedMacs.Contains(mac))
|
||||||
|
return mac;
|
||||||
|
}
|
||||||
|
throw new Exception("没有可用的MAC地址");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加一块新的 FPGA 板子到数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">FPGA 板子的名称</param>
|
||||||
|
/// <returns>插入的记录数</returns>
|
||||||
|
public Guid AddBoard(string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name) || name.Contains('\'') || name.Contains(';'))
|
||||||
|
{
|
||||||
|
logger.Error("实验板名称非法,包含不允许的字符");
|
||||||
|
throw new ArgumentException("实验板名称非法");
|
||||||
|
}
|
||||||
|
var board = new Board()
|
||||||
|
{
|
||||||
|
BoardName = name,
|
||||||
|
IpAddr = AllocateIpAddr(),
|
||||||
|
MacAddr = AllocateMacAddr(),
|
||||||
|
Status = Database.Board.BoardStatus.Disabled,
|
||||||
|
};
|
||||||
|
var result = _db.Insert(board);
|
||||||
|
logger.Info($"新实验板已添加: {name} ({board.IpAddr}:{board.MacAddr})");
|
||||||
|
return board.ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据名称删除实验板
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">实验板的名称</param>
|
||||||
|
/// <returns>删除的记录数</returns>
|
||||||
|
public int DeleteBoardByName(string name)
|
||||||
|
{
|
||||||
|
// 先获取要删除的板子信息
|
||||||
|
var board = _db.BoardTable.Where(b => b.BoardName == name).FirstOrDefault();
|
||||||
|
if (board == null)
|
||||||
|
{
|
||||||
|
logger.Warn($"未找到名称为 {name} 的实验板");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果板子被占用,先解除绑定
|
||||||
|
if (board.OccupiedUserID != Guid.Empty)
|
||||||
|
{
|
||||||
|
_db.UserTable
|
||||||
|
.Where(u => u.ID == board.OccupiedUserID)
|
||||||
|
.Set(u => u.BoardID, Guid.Empty)
|
||||||
|
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||||
|
.Update();
|
||||||
|
logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {name} 的绑定");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _db.BoardTable.Where(b => b.BoardName == name).Delete();
|
||||||
|
logger.Info($"实验板已删除: {name},删除记录数: {result}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据ID删除实验板
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">实验板的唯一标识符</param>
|
||||||
|
/// <returns>删除的记录数</returns>
|
||||||
|
public int DeleteBoardByID(Guid id)
|
||||||
|
{
|
||||||
|
// 先获取要删除的板子信息
|
||||||
|
var board = _db.BoardTable.Where(b => b.ID == id).FirstOrDefault();
|
||||||
|
if (board == null)
|
||||||
|
{
|
||||||
|
logger.Warn($"未找到ID为 {id} 的实验板");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果板子被占用,先解除绑定
|
||||||
|
if (board.OccupiedUserID != Guid.Empty)
|
||||||
|
{
|
||||||
|
_db.UserTable
|
||||||
|
.Where(u => u.ID == board.OccupiedUserID)
|
||||||
|
.Set(u => u.BoardID, Guid.Empty)
|
||||||
|
.Set(u => u.BoardExpireTime, (DateTime?)null)
|
||||||
|
.Update();
|
||||||
|
logger.Info($"已解除用户 {board.OccupiedUserID} 与实验板 {id} 的绑定");
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = _db.BoardTable.Where(b => b.ID == id).Delete();
|
||||||
|
logger.Info($"实验板已删除: {id},删除记录数: {result}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据实验板ID获取实验板信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">实验板的唯一标识符</param>
|
||||||
|
/// <returns>包含实验板信息的结果,如果未找到则返回空</returns>
|
||||||
|
public Result<Optional<Board>> GetBoardByID(Guid id)
|
||||||
|
{
|
||||||
|
var boards = _db.BoardTable.Where(board => board.ID == id).ToArray();
|
||||||
|
|
||||||
|
if (boards.Length > 1)
|
||||||
|
{
|
||||||
|
logger.Error($"数据库中存在多个相同ID的实验板: {id}");
|
||||||
|
return new(new Exception($"数据库中存在多个相同ID的实验板: {id}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boards.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到ID对应的实验板: {id}");
|
||||||
|
return new(Optional<Board>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"成功获取实验板信息: {id}");
|
||||||
|
return new(boards[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据用户名获取实验板信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userName">用户名</param>
|
||||||
|
/// <returns>包含实验板信息的结果,如果未找到则返回空</returns>
|
||||||
|
public Result<Optional<Board>> GetBoardByUserName(string userName)
|
||||||
|
{
|
||||||
|
var boards = _db.BoardTable.Where(board => board.OccupiedUserName == userName).ToArray();
|
||||||
|
|
||||||
|
if (boards.Length > 1)
|
||||||
|
{
|
||||||
|
logger.Error($"数据库中存在多个相同用户名的实验板: {userName}");
|
||||||
|
return new(new Exception($"数据库中存在多个相同用户名的实验板: {userName}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boards.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Info($"未找到用户名对应的实验板: {userName}");
|
||||||
|
return new(Optional<Board>.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"成功获取实验板信息: {userName}");
|
||||||
|
return new(boards[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有实验板信息
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>所有实验板的数组</returns>
|
||||||
|
public Board[] GetAllBoard()
|
||||||
|
{
|
||||||
|
var boards = _db.BoardTable.ToArray();
|
||||||
|
logger.Debug($"获取所有实验板,共 {boards.Length} 块");
|
||||||
|
return boards;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取一块可用的实验板并将其状态设置为繁忙
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">要分配板子的用户ID</param>
|
||||||
|
/// <param name="expireTime">绑定过期时间</param>
|
||||||
|
/// <returns>可用的实验板,如果没有可用的板子则返回空</returns>
|
||||||
|
public Optional<Board> GetAvailableBoard(Guid userId, DateTime expireTime)
|
||||||
|
{
|
||||||
|
var boards = _db.BoardTable.Where(
|
||||||
|
(board) => board.Status == Database.Board.BoardStatus.Available
|
||||||
|
).ToArray();
|
||||||
|
|
||||||
|
if (boards.Length == 0)
|
||||||
|
{
|
||||||
|
logger.Warn("没有可用的实验板");
|
||||||
|
return new(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var board = boards[0];
|
||||||
|
var user = _db.UserTable.Where(u => u.ID == userId).FirstOrDefault();
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
logger.Error($"未找到用户: {userId}");
|
||||||
|
return new(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新板子状态和用户绑定信息
|
||||||
|
_db.BoardTable
|
||||||
|
.Where(target => target.ID == board.ID)
|
||||||
|
.Set(target => target.Status, Board.BoardStatus.Busy)
|
||||||
|
.Set(target => target.OccupiedUserID, userId)
|
||||||
|
.Set(target => target.OccupiedUserName, user.Name)
|
||||||
|
.Update();
|
||||||
|
|
||||||
|
// 更新用户的板子绑定信息
|
||||||
|
_db.UserTable
|
||||||
|
.Where(u => u.ID == userId)
|
||||||
|
.Set(u => u.BoardID, board.ID)
|
||||||
|
.Set(u => u.BoardExpireTime, expireTime)
|
||||||
|
.Update();
|
||||||
|
|
||||||
|
board.Status = Database.Board.BoardStatus.Busy;
|
||||||
|
board.OccupiedUserID = userId;
|
||||||
|
board.OccupiedUserName = user.Name;
|
||||||
|
|
||||||
|
logger.Info($"实验板 {board.BoardName} ({board.ID}) 已分配给用户 {user.Name} ({userId}),过期时间: {expireTime}");
|
||||||
|
return new(board);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// [TODO:description]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="boardId">[TODO:parameter]</param>
|
||||||
|
/// <param name="newName">[TODO:parameter]</param>
|
||||||
|
/// <returns>[TODO:return]</returns>
|
||||||
|
public int UpdateBoardName(Guid boardId, string newName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(newName) || newName.Contains('\'') || newName.Contains(';'))
|
||||||
|
{
|
||||||
|
logger.Error("实验板名称非法,包含不允许的字符");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var result = _db.BoardTable
|
||||||
|
.Where(b => b.ID == boardId)
|
||||||
|
.Set(b => b.BoardName, newName)
|
||||||
|
.Update();
|
||||||
|
logger.Info($"实验板名称已更新: {boardId} -> {newName}");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// [TODO:description]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="boardId">[TODO:parameter]</param>
|
||||||
|
/// <param name="newStatus">[TODO:parameter]</param>
|
||||||
|
/// <returns>[TODO:return]</returns>
|
||||||
|
public int UpdateBoardStatus(Guid boardId, Board.BoardStatus newStatus)
|
||||||
|
{
|
||||||
|
var result = _db.BoardTable
|
||||||
|
.Where(b => b.ID == boardId)
|
||||||
|
.Set(b => b.Status, newStatus)
|
||||||
|
.Update();
|
||||||
|
logger.Info($"TODO");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -28,22 +28,24 @@ public interface IJtagReceiver
|
||||||
public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
||||||
{
|
{
|
||||||
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly IHubContext<JtagHub, IJtagReceiver> _hubContext;
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
|
|
||||||
private static ConcurrentDictionary<string, int> FreqTable = new();
|
private static ConcurrentDictionary<string, int> FreqTable = new();
|
||||||
private static ConcurrentDictionary<string, CancellationTokenSource> CancellationTokenSourceTable = new();
|
private static ConcurrentDictionary<string, CancellationTokenSource> CancellationTokenSourceTable = new();
|
||||||
|
|
||||||
private readonly IHubContext<JtagHub, IJtagReceiver> _hubContext;
|
public JtagHub(IHubContext<JtagHub, IJtagReceiver> hubContext, Database.UserManager userManager)
|
||||||
|
|
||||||
public JtagHub(IHubContext<JtagHub, IJtagReceiver> hubContext)
|
|
||||||
{
|
{
|
||||||
_hubContext = hubContext;
|
_hubContext = hubContext;
|
||||||
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Peripherals.JtagClient.Jtag> GetJtagClient(string userName)
|
private Optional<Peripherals.JtagClient.Jtag> GetJtagClient(string userName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var db = new Database.AppDataConnection();
|
var board = _userManager.GetBoardByUserName(userName);
|
||||||
var board = db.GetBoardByUserName(userName);
|
|
||||||
if (!board.IsSuccessful)
|
if (!board.IsSuccessful)
|
||||||
{
|
{
|
||||||
logger.Error($"Find Board {board.Value.Value.ID} failed because {board.Error}");
|
logger.Error($"Find Board {board.Value.Value.ID} failed because {board.Error}");
|
||||||
|
@ -97,7 +99,7 @@ public class JtagHub : Hub<IJtagReceiver>, IJtagHub
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
await SetBoundaryScanFreq(freq);
|
SetBoundaryScanFreq(freq);
|
||||||
var cts = new CancellationTokenSource();
|
var cts = new CancellationTokenSource();
|
||||||
CancellationTokenSourceTable.AddOrUpdate(userName, cts, (key, value) => cts);
|
CancellationTokenSourceTable.AddOrUpdate(userName, cts, (key, value) => cts);
|
||||||
|
|
||||||
|
|
|
@ -15,15 +15,23 @@ public class HdmiVideoStreamEndpoint
|
||||||
public class HttpHdmiVideoStreamService : BackgroundService
|
public class HttpHdmiVideoStreamService : BackgroundService
|
||||||
{
|
{
|
||||||
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
|
|
||||||
private HttpListener? _httpListener;
|
private HttpListener? _httpListener;
|
||||||
private readonly int _serverPort = 4322;
|
private readonly int _serverPort = 4322;
|
||||||
private readonly ConcurrentDictionary<string, HdmiIn> _hdmiInDict = new();
|
private readonly ConcurrentDictionary<string, HdmiIn> _hdmiInDict = new();
|
||||||
private readonly ConcurrentDictionary<string, CancellationTokenSource> _hdmiInCtsDict = new();
|
private readonly ConcurrentDictionary<string, CancellationTokenSource> _hdmiInCtsDict = new();
|
||||||
|
|
||||||
|
public HttpHdmiVideoStreamService(Database.UserManager userManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_httpListener = new HttpListener();
|
_httpListener = new HttpListener();
|
||||||
_httpListener.Prefixes.Add($"http://{Global.localhost}:{_serverPort}/");
|
_httpListener.Prefixes.Add($"http://{Global.LocalHost}:{_serverPort}/");
|
||||||
_httpListener.Start();
|
_httpListener.Start();
|
||||||
logger.Info($"HDMI Video Stream Service started on port {_serverPort}");
|
logger.Info($"HDMI Video Stream Service started on port {_serverPort}");
|
||||||
|
|
||||||
|
@ -133,14 +141,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
|
||||||
return hdmiIn;
|
return hdmiIn;
|
||||||
}
|
}
|
||||||
|
|
||||||
var db = new Database.AppDataConnection();
|
var boardRet = _userManager.GetBoardByID(Guid.Parse(boardId));
|
||||||
if (db == null)
|
|
||||||
{
|
|
||||||
logger.Error("Failed to create HdmiIn instance");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(Guid.Parse(boardId));
|
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
{
|
{
|
||||||
logger.Error($"Failed to get board with ID {boardId}");
|
logger.Error($"Failed to get board with ID {boardId}");
|
||||||
|
@ -366,8 +367,7 @@ public class HttpHdmiVideoStreamService : BackgroundService
|
||||||
/// <returns>返回所有可用的HDMI视频流终端点列表</returns>
|
/// <returns>返回所有可用的HDMI视频流终端点列表</returns>
|
||||||
public List<HdmiVideoStreamEndpoint>? GetAllVideoEndpoints()
|
public List<HdmiVideoStreamEndpoint>? GetAllVideoEndpoints()
|
||||||
{
|
{
|
||||||
var db = new Database.AppDataConnection();
|
var boards = _userManager.GetAllBoard();
|
||||||
var boards = db?.GetAllBoard();
|
|
||||||
if (boards == null)
|
if (boards == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
@ -377,9 +377,9 @@ public class HttpHdmiVideoStreamService : BackgroundService
|
||||||
endpoints.Add(new HdmiVideoStreamEndpoint
|
endpoints.Add(new HdmiVideoStreamEndpoint
|
||||||
{
|
{
|
||||||
BoardId = board.ID.ToString(),
|
BoardId = board.ID.ToString(),
|
||||||
MjpegUrl = $"http://{Global.localhost}:{_serverPort}/mjpeg?boardId={board.ID}",
|
MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={board.ID}",
|
||||||
VideoUrl = $"http://{Global.localhost}:{_serverPort}/video?boardId={board.ID}",
|
VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={board.ID}",
|
||||||
SnapshotUrl = $"http://{Global.localhost}:{_serverPort}/snapshot?boardId={board.ID}"
|
SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={board.ID}"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return endpoints;
|
return endpoints;
|
||||||
|
@ -395,9 +395,9 @@ public class HttpHdmiVideoStreamService : BackgroundService
|
||||||
return new HdmiVideoStreamEndpoint
|
return new HdmiVideoStreamEndpoint
|
||||||
{
|
{
|
||||||
BoardId = boardId,
|
BoardId = boardId,
|
||||||
MjpegUrl = $"http://{Global.localhost}:{_serverPort}/mjpeg?boardId={boardId}",
|
MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={boardId}",
|
||||||
VideoUrl = $"http://{Global.localhost}:{_serverPort}/video?boardId={boardId}",
|
VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={boardId}",
|
||||||
SnapshotUrl = $"http://{Global.localhost}:{_serverPort}/snapshot?boardId={boardId}"
|
SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={boardId}"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,6 +87,8 @@ public class HttpVideoStreamService : BackgroundService
|
||||||
{
|
{
|
||||||
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
private readonly Database.UserManager _userManager;
|
||||||
|
|
||||||
private HttpListener? _httpListener;
|
private HttpListener? _httpListener;
|
||||||
private readonly int _serverPort = 4321;
|
private readonly int _serverPort = 4321;
|
||||||
|
|
||||||
|
@ -99,13 +101,18 @@ public class HttpVideoStreamService : BackgroundService
|
||||||
private readonly object _usbCameraLock = new object();
|
private readonly object _usbCameraLock = new object();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
public HttpVideoStreamService(Database.UserManager userManager)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化 HttpVideoStreamService
|
/// 初始化 HttpVideoStreamService
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override async Task StartAsync(CancellationToken cancellationToken)
|
public override async Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_httpListener = new HttpListener();
|
_httpListener = new HttpListener();
|
||||||
_httpListener.Prefixes.Add($"http://{Global.localhost}:{_serverPort}/");
|
_httpListener.Prefixes.Add($"http://{Global.LocalHost}:{_serverPort}/");
|
||||||
_httpListener.Start();
|
_httpListener.Start();
|
||||||
logger.Info($"Video Stream Service started on port {_serverPort}");
|
logger.Info($"Video Stream Service started on port {_serverPort}");
|
||||||
|
|
||||||
|
@ -147,14 +154,7 @@ public class HttpVideoStreamService : BackgroundService
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
var db = new Database.AppDataConnection();
|
var boardRet = _userManager.GetBoardByID(Guid.Parse(boardId));
|
||||||
if (db == null)
|
|
||||||
{
|
|
||||||
logger.Error("Failed to create HdmiIn instance");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var boardRet = db.GetBoardByID(Guid.Parse(boardId));
|
|
||||||
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
|
||||||
{
|
{
|
||||||
logger.Error($"Failed to get board with ID {boardId}");
|
logger.Error($"Failed to get board with ID {boardId}");
|
||||||
|
@ -675,9 +675,9 @@ public class HttpVideoStreamService : BackgroundService
|
||||||
return new VideoEndpoint
|
return new VideoEndpoint
|
||||||
{
|
{
|
||||||
BoardId = boardId,
|
BoardId = boardId,
|
||||||
MjpegUrl = $"http://{Global.localhost}:{_serverPort}/mjpeg?boardId={boardId}",
|
MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={boardId}",
|
||||||
VideoUrl = $"http://{Global.localhost}:{_serverPort}/video?boardId={boardId}",
|
VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={boardId}",
|
||||||
SnapshotUrl = $"http://{Global.localhost}:{_serverPort}/snapshot?boardId={boardId}",
|
SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={boardId}",
|
||||||
Resolution = $"{client.FrameWidth}x{client.FrameHeight}",
|
Resolution = $"{client.FrameWidth}x{client.FrameHeight}",
|
||||||
FrameRate = client.FrameRate
|
FrameRate = client.FrameRate
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue