feat: 完成提交作业的后端

This commit is contained in:
2025-08-14 11:38:09 +08:00
parent 24622d30cf
commit e5dac3e731
13 changed files with 808 additions and 203 deletions

View File

@@ -57,7 +57,7 @@ public class AppDataConnection : DataConnection
Name = "Admin",
EMail = "selfconfusion@gmail.com",
Password = "12345678",
Permission = Database.User.UserPermission.Admin,
Permission = Database.UserPermission.Admin,
};
this.Insert(user);
logger.Info("默认管理员用户已创建");

View File

@@ -30,7 +30,7 @@ public class ExamManager
try
{
// 检查实验ID是否已存在
var existingExam = _db.ExamTable.Where(e => e.ID == id).FirstOrDefault();
var existingExam = _db.ExamTable.Where(e => e.ID.ToString() == id).FirstOrDefault();
if (existingExam != null)
{
logger.Error($"实验ID已存在: {id}");
@@ -82,28 +82,28 @@ public class ExamManager
if (name != null)
{
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Name, name).Update();
result += _db.ExamTable.Where(e => e.ID.ToString() == id).Set(e => e.Name, name).Update();
}
if (description != null)
{
result += _db.ExamTable.Where(e => e.ID == id).Set(e => e.Description, description).Update();
result += _db.ExamTable.Where(e => e.ID.ToString() == 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();
result += _db.ExamTable.Where(e => e.ID.ToString() == 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();
result += _db.ExamTable.Where(e => e.ID.ToString() == 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();
result += _db.ExamTable.Where(e => e.ID.ToString() == id).Set(e => e.IsVisibleToUsers, isVisibleToUsers.Value).Update();
}
// 更新时间
_db.ExamTable.Where(e => e.ID == id).Set(e => e.UpdatedTime, DateTime.Now).Update();
_db.ExamTable.Where(e => e.ID.ToString() == id).Set(e => e.UpdatedTime, DateTime.Now).Update();
logger.Info($"实验已更新: {id},更新记录数: {result}");
return new(result);
@@ -133,7 +133,7 @@ public class ExamManager
/// <returns>包含实验信息的结果,如果未找到则返回空</returns>
public Result<Optional<Exam>> GetExamByID(string examId)
{
var exams = _db.ExamTable.Where(exam => exam.ID == examId).ToArray();
var exams = _db.ExamTable.Where(exam => exam.ID.ToString() == examId).ToArray();
if (exams.Length > 1)
{

View File

@@ -114,7 +114,7 @@ public class ResourceManager
/// <param name="mimeType">MIME类型可选将根据文件扩展名自动确定</param>
/// <returns>创建的资源</returns>
public Result<Resource> AddResource(
Guid userId, string resourceType, string resourcePurpose,
Guid userId, string resourceType, ResourcePurpose resourcePurpose,
string resourceName, byte[] data, string? examId = null, string? mimeType = null)
{
try
@@ -130,7 +130,7 @@ public class ResourceManager
// 如果指定了实验ID验证实验是否存在
if (!string.IsNullOrEmpty(examId))
{
var exam = _db.ExamTable.Where(e => e.ID == examId).FirstOrDefault();
var exam = _db.ExamTable.Where(e => e.ID.ToString() == examId).FirstOrDefault();
if (exam == null)
{
logger.Error($"实验不存在: {examId}");
@@ -139,8 +139,8 @@ public class ResourceManager
}
// 验证资源用途
if (resourcePurpose != Resource.ResourcePurposes.Template &&
resourcePurpose != Resource.ResourcePurposes.User)
if (resourcePurpose != ResourcePurpose.Template &&
resourcePurpose != ResourcePurpose.User)
{
logger.Error($"无效的资源用途: {resourcePurpose}");
return new(new Exception($"无效的资源用途: {resourcePurpose}"));
@@ -174,7 +174,7 @@ public class ResourceManager
UserID = userId,
ExamID = examId,
ResourceType = resourceType,
ResourcePurpose = resourcePurpose,
Purpose = resourcePurpose,
ResourceName = resourceName,
Path = duplicateResource == null ?
Path.Combine(resourceType, nowTime.ToString("yyyyMMddHH"), resourceName) :
@@ -184,8 +184,7 @@ public class ResourceManager
UploadTime = nowTime
};
var insertedId = _db.InsertWithIdentity(resource);
resource.ID = Convert.ToInt32(insertedId);
var insertedId = _db.Insert(resource);
var writeRet = WriteBytesToPath(resource.Path, data);
if (writeRet.IsSuccessful && writeRet.Value)
@@ -217,7 +216,11 @@ public class ResourceManager
/// <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)
public Result<(string ID, string Name)[]> GetResourceListByType(
string resourceType,
ResourcePurpose? resourcePurpose = null,
string? examId = null,
Guid? userId = null)
{
try
{
@@ -230,7 +233,7 @@ public class ResourceManager
if (resourcePurpose != null)
{
query = query.Where(r => r.ResourcePurpose == resourcePurpose);
query = query.Where(r => r.Purpose == resourcePurpose);
}
if (userId != null)
@@ -242,10 +245,10 @@ public class ResourceManager
.Select(r => new { r.ID, r.ResourceName })
.ToArray();
var result = resources.Select(r => (r.ID, r.ResourceName)).ToArray();
var result = resources.Select(r => (r.ID.ToString(), r.ResourceName)).ToArray();
logger.Info($"获取资源列表: {resourceType}" +
(examId != null ? $"/{examId}" : "") +
(resourcePurpose != null ? $"/{resourcePurpose}" : "") +
($"/{resourcePurpose.ToString()}") +
(userId != null ? $"/{userId}" : "") +
$",共 {result.Length} 个资源");
return new(result);
@@ -265,7 +268,11 @@ public class ResourceManager
/// <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)
public Result<List<Resource>> GetFullResourceList(
string? examId = null,
string? resourceType = null,
ResourcePurpose? resourcePurpose = null,
Guid? userId = null)
{
try
{
@@ -283,7 +290,7 @@ public class ResourceManager
if (resourcePurpose != null)
{
query = query.Where(r => r.ResourcePurpose == resourcePurpose);
query = query.Where(r => r.Purpose == resourcePurpose);
}
if (userId != null)
@@ -295,7 +302,7 @@ public class ResourceManager
logger.Info($"获取完整资源列表" +
(examId != null ? $" [实验: {examId}]" : "") +
(resourceType != null ? $" [类型: {resourceType}]" : "") +
(resourcePurpose != null ? $" [用途: {resourcePurpose}]" : "") +
($" [用途: {resourcePurpose.ToString()}]") +
(userId != null ? $" [用户: {userId}]" : "") +
$",共 {resources.Count} 个资源");
return new(resources);
@@ -312,26 +319,18 @@ public class ResourceManager
/// </summary>
/// <param name="resourceId">资源ID</param>
/// <returns>资源数据</returns>
public Result<Optional<Resource>> GetResourceById(int resourceId)
public Optional<Resource> GetResourceById(string resourceId)
{
try
{
var resource = _db.ResourceTable.Where(r => r.ID == resourceId).FirstOrDefault();
var resource = _db.ResourceTable.Where(r => r.ID.ToString() == resourceId).FirstOrDefault();
if (resource == null)
{
logger.Info($"未找到资源: {resourceId}");
return new(Optional<Resource>.None);
}
logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})");
return new(resource);
}
catch (Exception ex)
if (resource == null)
{
logger.Error($"获取资源时出错: {ex.Message}");
return new(ex);
logger.Info($"未找到资源: {resourceId}");
return new(null);
}
logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})");
return new(resource);
}
/// <summary>
@@ -339,11 +338,11 @@ public class ResourceManager
/// </summary>
/// <param name="resourceId">资源ID</param>
/// <returns>删除的记录数</returns>
public Result<int> DeleteResource(int resourceId)
public Result<int> DeleteResource(string resourceId)
{
try
{
var result = _db.ResourceTable.Where(r => r.ID == resourceId).Delete();
var result = _db.ResourceTable.Where(r => r.ID.ToString() == resourceId).Delete();
logger.Info($"资源已删除: {resourceId},删除记录数: {result}");
return new(result);
}

View File

@@ -4,6 +4,22 @@ using LinqToDB.Mapping;
namespace Database;
/// <summary>
/// 用户权限枚举
/// </summary>
public enum UserPermission
{
/// <summary>
/// 管理员权限,可以管理用户和实验板
/// </summary>
Admin,
/// <summary>
/// 普通用户权限,只能使用实验板
/// </summary>
Normal,
}
/// <summary>
/// 用户类,表示用户信息
/// </summary>
@@ -50,22 +66,27 @@ public class User
/// </summary>
[Nullable]
public DateTime? BoardExpireTime { get; set; }
}
/// <summary>
/// FPGA 板子状态枚举
/// </summary>
public enum BoardStatus
{
/// <summary>
/// 未启用状态,无法被使用
/// </summary>
Disabled,
/// <summary>
/// 用户权限枚举
/// 繁忙状态,正在被用户使
/// </summary>
public enum UserPermission
{
/// <summary>
/// 管理员权限,可以管理用户和实验板
/// </summary>
Admin,
Busy,
/// <summary>
/// 普通用户权限,只能使用实验板
/// </summary>
Normal,
}
/// <summary>
/// 可用状态,可以被分配给用户
/// </summary>
Available,
}
/// <summary>
@@ -127,26 +148,6 @@ public class Board
[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>
@@ -227,6 +228,60 @@ public class Exam
}
}
/// <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 const string Compression = "compression";
}
public enum ResourcePurpose : int
{
/// <summary>
/// 模板资源,通常由管理员上传,供用户参考
/// </summary>
Template,
/// <summary>
/// 用户上传的资源
/// </summary>
User,
/// <summary>
/// 用户提交的作业
/// </summary>
Homework
}
/// <summary>
/// 资源类,统一管理实验资源、用户比特流等各类资源
/// </summary>
@@ -235,8 +290,8 @@ public class Resource
/// <summary>
/// 资源的唯一标识符
/// </summary>
[PrimaryKey, Identity]
public int ID { get; set; }
[PrimaryKey]
public Guid ID { get; set; } = Guid.NewGuid();
/// <summary>
/// 上传资源的用户ID
@@ -260,7 +315,7 @@ public class Resource
/// 资源用途template模板或 user用户上传
/// </summary>
[NotNull]
public required string ResourcePurpose { get; set; }
public required ResourcePurpose Purpose { get; set; }
/// <summary>
/// 资源名称(包含文件扩展名)
@@ -292,50 +347,4 @@ public class Resource
[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";
}
}

View File

@@ -29,7 +29,7 @@ public class UserManager
Name = name,
EMail = email,
Password = password,
Permission = Database.User.UserPermission.Normal,
Permission = UserPermission.Normal,
};
var result = _db.Insert(user);
logger.Info($"新用户已添加: {name} ({email})");
@@ -142,7 +142,7 @@ public class UserManager
// 更新板子的用户绑定信息
var boardResult = _db.BoardTable
.Where(b => b.ID == boardId)
.Set(b => b.Status, Board.BoardStatus.Busy)
.Set(b => b.Status, BoardStatus.Busy)
.Set(b => b.OccupiedUserID, userId)
.Set(b => b.OccupiedUserName, user.Name)
.Update();
@@ -175,7 +175,7 @@ public class UserManager
{
boardResult = _db.BoardTable
.Where(b => b.ID == boardId)
.Set(b => b.Status, Board.BoardStatus.Available)
.Set(b => b.Status, BoardStatus.Available)
.Set(b => b.OccupiedUserID, Guid.Empty)
.Set(b => b.OccupiedUserName, (string?)null)
.Update();
@@ -236,7 +236,7 @@ public class UserManager
BoardName = name,
IpAddr = AllocateIpAddr(),
MacAddr = AllocateMacAddr(),
Status = Database.Board.BoardStatus.Disabled,
Status = BoardStatus.Disabled,
};
var result = _db.Insert(board);
logger.Info($"新实验板已添加: {name} ({board.IpAddr}:{board.MacAddr})");
@@ -375,7 +375,7 @@ public class UserManager
public Optional<Board> GetAvailableBoard(Guid userId, DateTime expireTime)
{
var boards = _db.BoardTable.Where(
(board) => board.Status == Database.Board.BoardStatus.Available
(board) => board.Status == BoardStatus.Available
).ToArray();
if (boards.Length == 0)
@@ -397,7 +397,7 @@ public class UserManager
// 更新板子状态和用户绑定信息
_db.BoardTable
.Where(target => target.ID == board.ID)
.Set(target => target.Status, Board.BoardStatus.Busy)
.Set(target => target.Status, BoardStatus.Busy)
.Set(target => target.OccupiedUserID, userId)
.Set(target => target.OccupiedUserName, user.Name)
.Update();
@@ -409,7 +409,7 @@ public class UserManager
.Set(u => u.BoardExpireTime, expireTime)
.Update();
board.Status = Database.Board.BoardStatus.Busy;
board.Status = BoardStatus.Busy;
board.OccupiedUserID = userId;
board.OccupiedUserName = user.Name;
@@ -445,7 +445,7 @@ public class UserManager
/// <param name="boardId">[TODO:parameter]</param>
/// <param name="newStatus">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
public int UpdateBoardStatus(Guid boardId, Board.BoardStatus newStatus)
public int UpdateBoardStatus(Guid boardId, BoardStatus newStatus)
{
var result = _db.BoardTable
.Where(b => b.ID == boardId)