using DotNext; using LinqToDB; using LinqToDB.Data; using LinqToDB.Mapping; namespace Database; /// /// 用户类,表示用户信息 /// public class User { /// /// 用户的唯一标识符 /// [PrimaryKey] public Guid ID { get; set; } = Guid.NewGuid(); /// /// 用户的名称 /// [NotNull] public required string Name { get; set; } /// /// 用户的电子邮箱 /// [NotNull] public required string EMail { get; set; } /// /// 用户的密码(应该进行哈希处理) /// [NotNull] public required string Password { get; set; } /// /// 用户权限等级 /// [NotNull] public required UserPermission Permission { get; set; } /// /// 绑定的实验板ID,如果未绑定则为空 /// [Nullable] public Guid BoardID { get; set; } /// /// 用户绑定板子的过期时间 /// [Nullable] public DateTime? BoardExpireTime { get; set; } /// /// 用户权限枚举 /// public enum UserPermission { /// /// 管理员权限,可以管理用户和实验板 /// Admin, /// /// 普通用户权限,只能使用实验板 /// Normal, } } /// /// FPGA 板子类,表示板子信息 /// public class Board { /// /// FPGA 板子的唯一标识符 /// [PrimaryKey] public Guid ID { get; set; } = Guid.NewGuid(); /// /// FPGA 板子的名称 /// [NotNull] public required string BoardName { get; set; } /// /// FPGA 板子的IP地址 /// [NotNull] public required string IpAddr { get; set; } /// /// FPGA 板子的通信端口 /// [NotNull] public required int Port { get; set; } /// /// FPGA 板子的当前状态 /// [NotNull] public required BoardStatus Status { get; set; } /// /// 占用该板子的用户的唯一标识符 /// [Nullable] public Guid OccupiedUserID { get; set; } /// /// 占用该板子的用户的用户名 /// [Nullable] public string? OccupiedUserName { get; set; } /// /// FPGA 板子的固件版本号 /// [NotNull] public string FirmVersion { get; set; } = "1.0.0"; /// /// FPGA 板子状态枚举 /// public enum BoardStatus { /// /// 繁忙状态,正在被用户使用 /// Busy, /// /// 可用状态,可以被分配给用户 /// Available, } } /// /// 应用程序数据连接类,用于与数据库交互 /// public class AppDataConnection : DataConnection { private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger(); static readonly string DATABASE_FILEPATH = $"{Environment.CurrentDirectory}/Database.sqlite"; static readonly LinqToDB.DataOptions options = new LinqToDB.DataOptions().UseSQLite($"Data Source={DATABASE_FILEPATH}"); /// /// 初始化应用程序数据连接 /// public AppDataConnection() : base(options) { 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}"); } } /// /// 创建所有数据库表 /// public void CreateAllTables() { logger.Info("正在创建数据库表..."); this.CreateTable(); this.CreateTable(); logger.Info("数据库表创建完成"); } /// /// 删除所有数据库表 /// public void DropAllTables() { logger.Warn("正在删除所有数据库表..."); this.DropTable(); this.DropTable(); logger.Warn("所有数据库表已删除"); } /// /// 添加一个新的用户到数据库 /// /// 用户的名称 /// 用户的电子邮箱地址 /// 用户的密码 /// 插入的记录数 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 = this.Insert(user); logger.Info($"新用户已添加: {name} ({email})"); return result; } /// /// 根据用户名获取用户信息 /// /// 用户名 /// 包含用户信息的结果,如果未找到或出错则返回相应状态 public Result> GetUserByName(string name) { var user = this.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.None); } logger.Debug($"成功获取用户信息: {name}"); return new(user[0]); } /// /// 根据电子邮箱获取用户信息 /// /// 用户的电子邮箱地址 /// 包含用户信息的结果,如果未找到或出错则返回相应状态 public Result> GetUserByEMail(string email) { var user = this.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.None); } logger.Debug($"成功获取用户信息: {email}"); return new(user[0]); } /// /// 验证用户密码 /// /// 用户名 /// 用户密码 /// 如果密码正确返回用户信息,否则返回空 public Result> CheckUserPassword(string name, string password) { var ret = this.GetUserByName(name); if (!ret.IsSuccessful) return new(ret.Error); if (!ret.Value.HasValue) return new(Optional.None); var user = ret.Value.Value; if (user.Password == password) { logger.Info($"用户 {name} 密码验证成功"); return new(user); } else { logger.Warn($"用户 {name} 密码验证失败"); return new(Optional.None); } } /// /// 绑定用户与实验板 /// /// 用户的唯一标识符 /// 实验板的唯一标识符 /// 绑定过期时间 /// 更新的记录数 public int BindUserToBoard(Guid userId, Guid boardId, DateTime expireTime) { // 获取用户信息 var user = this.UserTable.Where(u => u.ID == userId).FirstOrDefault(); if (user == null) { logger.Error($"未找到用户: {userId}"); return 0; } // 更新用户的板子绑定信息 var userResult = this.UserTable .Where(u => u.ID == userId) .Set(u => u.BoardID, boardId) .Set(u => u.BoardExpireTime, expireTime) .Update(); // 更新板子的用户绑定信息 var boardResult = this.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; } /// /// 解除用户与实验板的绑定 /// /// 用户的唯一标识符 /// 更新的记录数 public int UnbindUserFromBoard(Guid userId) { // 获取用户当前绑定的板子ID var user = this.UserTable.Where(u => u.ID == userId).FirstOrDefault(); Guid boardId = user?.BoardID ?? Guid.Empty; // 清空用户的板子绑定信息 var userResult = this.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 = this.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; } /// /// 添加一块新的 FPGA 板子到数据库 /// /// FPGA 板子的名称 /// FPGA 板子的IP地址 /// FPGA 板子的通信端口 /// 插入的记录数 public int AddBoard(string name, string ipAddr, int port) { var board = new Board() { BoardName = name, IpAddr = ipAddr, Port = port, Status = Database.Board.BoardStatus.Available, }; var result = this.Insert(board); logger.Info($"新实验板已添加: {name} ({ipAddr}:{port})"); return result; } /// /// 根据名称删除实验板 /// /// 实验板的名称 /// 删除的记录数 public int DeleteBoardByName(string name) { // 先获取要删除的板子信息 var board = this.BoardTable.Where(b => b.BoardName == name).FirstOrDefault(); if (board == null) { logger.Warn($"未找到名称为 {name} 的实验板"); return 0; } // 如果板子被占用,先解除绑定 if (board.OccupiedUserID != Guid.Empty) { this.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 = this.BoardTable.Where(b => b.BoardName == name).Delete(); logger.Info($"实验板已删除: {name},删除记录数: {result}"); return result; } /// /// 根据ID删除实验板 /// /// 实验板的唯一标识符 /// 删除的记录数 public int DeleteBoardByID(Guid id) { // 先获取要删除的板子信息 var board = this.BoardTable.Where(b => b.ID == id).FirstOrDefault(); if (board == null) { logger.Warn($"未找到ID为 {id} 的实验板"); return 0; } // 如果板子被占用,先解除绑定 if (board.OccupiedUserID != Guid.Empty) { this.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 = this.BoardTable.Where(b => b.ID == id).Delete(); logger.Info($"实验板已删除: {id},删除记录数: {result}"); return result; } /// /// 根据实验板ID获取实验板信息 /// /// 实验板的唯一标识符 /// 包含实验板信息的结果,如果未找到则返回空 public Result> GetBoardByID(Guid id) { var boards = this.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.None); } logger.Debug($"成功获取实验板信息: {id}"); return new(boards[0]); } /// /// 获取所有实验板信息 /// /// 所有实验板的数组 public Board[] GetAllBoard() { var boards = this.BoardTable.ToArray(); logger.Debug($"获取所有实验板,共 {boards.Length} 块"); return boards; } /// /// 获取一块可用的实验板并将其状态设置为繁忙 /// /// 要分配板子的用户ID /// 绑定过期时间 /// 可用的实验板,如果没有可用的板子则返回空 public Optional GetAvailableBoard(Guid userId, DateTime expireTime) { var boards = this.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 = this.UserTable.Where(u => u.ID == userId).FirstOrDefault(); if (user == null) { logger.Error($"未找到用户: {userId}"); return new(null); } // 更新板子状态和用户绑定信息 this.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(); // 更新用户的板子绑定信息 this.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); } } /// /// 用户表 /// public ITable UserTable => this.GetTable(); /// /// FPGA 板子表 /// public ITable BoardTable => this.GetTable(); }