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; } /// /// 根据文件扩展名获取MIME类型 /// /// 文件扩展名 /// 文件名(可选,用于特殊文件判断) /// MIME类型 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" }; } /// /// 将二进制数据写入指定路径 /// /// 目标文件路径 /// 要写入的二进制数据 /// 写入是否成功 public Result 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); } } /// /// 从指定路径读取二进制数据 /// /// 要读取的文件路径 /// 读取到的二进制数据 public Result 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); } } /// /// 添加资源 /// /// 上传用户ID /// 资源类型 /// 资源用途(template 或 user) /// 资源名称 /// 资源二进制数据 /// 所属实验ID(可选) /// MIME类型(可选,将根据文件扩展名自动确定) /// 创建的资源 public Result 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 duplicateResource; } 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); } } /// /// 获取资源信息列表(返回ID和名称) /// 资源类型 /// 实验ID(可选) /// 资源用途(可选) /// 用户ID(可选) /// /// 资源信息列表 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); } } /// /// 获取完整的资源列表 /// /// 实验ID(可选) /// 资源类型(可选) /// 资源用途(可选) /// 用户ID(可选) /// 完整的资源对象列表 public Result> 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); } } /// /// 根据资源ID获取资源 /// /// 资源ID /// 资源数据 public Result> GetResourceById(int resourceId) { try { var resource = _db.ResourceTable.Where(r => r.ID == resourceId).FirstOrDefault(); if (resource == null) { logger.Info($"未找到资源: {resourceId}"); return new(Optional.None); } logger.Info($"成功获取资源: {resourceId} ({resource.ResourceName})"); return new(resource); } catch (Exception ex) { logger.Error($"获取资源时出错: {ex.Message}"); return new(ex); } } /// /// 删除资源 /// /// 资源ID /// 删除的记录数 public Result 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); } } }