diff --git a/IRaCIS.Core.API/appsettings.Prod_Event_IRC.json b/IRaCIS.Core.API/appsettings.Prod_Event_IRC.json index 9c15f6a86..47936737a 100644 --- a/IRaCIS.Core.API/appsettings.Prod_Event_IRC.json +++ b/IRaCIS.Core.API/appsettings.Prod_Event_IRC.json @@ -7,8 +7,8 @@ } }, "ConnectionStrings": { - "RemoteNew": "Server=10.10.10.49,1433;Database=Prod_IRC_pidr_restore;User ID=sa;Password=xc@123456;TrustServerCertificate=true", - "Hangfire": "Server=10.10.10.49,1433;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + "RemoteNew": "Server=101.132.193.237,1433;Database=Prod_IRC_pidr_restore;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=101.132.193.237,1433;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true" //"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", //"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true" }, diff --git a/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs b/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs index cba05d034..dd3b7c140 100644 --- a/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs +++ b/IRaCIS.Core.Application/Helper/DicomDIRHelper.cs @@ -160,6 +160,82 @@ namespace IRaCIS.Core.Application.Helper } + public static async Task GenerateStudyDIR(List list, Dictionary dic,string dirSavePath) + { + var mappings = new List(); + int index = 1; + + var trialId = Guid.Empty; + + + var studyUid = list.FirstOrDefault()?.StudyInstanceUid; + + var dicomDir = new DicomDirectory(); + + foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber)) + { + var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSytaxUID); + + if (dicomUid != null) + { + var ts = DicomTransferSyntax.Query(dicomUid); + + var dataset = new DicomDataset(ts) + { + { DicomTag.PatientID, item.PatientId ?? string.Empty }, + { DicomTag.PatientName, item.PatientName ?? string.Empty }, + { DicomTag.PatientBirthDate, item.PatientBirthDate ?? string.Empty }, + { DicomTag.PatientSex, item.PatientSex ?? string.Empty }, + + { DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty }, + { DicomTag.StudyID, item.StudyId ?? string.Empty }, + { DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty }, + { DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty }, + { DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty }, + { DicomTag.StudyDescription, item.StudyDescription ?? string.Empty }, + + { DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty }, + { DicomTag.Modality, item.Modality ?? string.Empty }, + { DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty }, + { DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty }, + { DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty }, + { DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty }, + + { DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty }, + { DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty }, + { DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty }, + { DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty }, + { DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty }, + { DicomTag.TransferSyntaxUID, item.TransferSytaxUID ?? string.Empty }, + }; + + var dicomFile = new DicomFile(dataset); + + // 文件名递增格式:IM_00001, IM_00002, ... + string filename = $@"IMAGE\IM_{index:D5}"; // :D5 表示补足5位 + + mappings.Add($"{filename} => {item.InstanceId}"); + + dic.Add(item.InstanceId.ToString(), filename.TrimEnd('/', '\\').Split('/', '\\').Last()); + + dicomDir.AddFile(dicomFile, filename); + + index++; + } + + + } + + //有实际的文件 + if (mappings.Count > 0) + { + // 保存 DICOMDIR 到临时文件 不能直接写入到流种 + await dicomDir.SaveAsync(dirSavePath); + } + + } + + public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile) { diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index fa54d6d24..49858298f 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -1760,7 +1760,7 @@ - + 项目影像后台下载,不打包 diff --git a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs index ef8607b24..eaad74bcb 100644 --- a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs +++ b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs @@ -7,6 +7,8 @@ using FellowOakDicom; using FellowOakDicom.Imaging; using FellowOakDicom.Imaging.Render; using FellowOakDicom.IO.Buffer; +using Hangfire.Common; +using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.ViewModel; @@ -18,7 +20,6 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using MiniExcelLibs; -using NPOI.Util; using Org.BouncyCastle.Utilities.Zlib; using SharpCompress.Common; using System; @@ -33,6 +34,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Web; using static IRaCIS.Core.Application.Service.TestService; +using static IRaCIS.Core.Application.Service.TrialImageDownloadService; using static IRaCIS.Core.Domain.Share.StaticData; using static Microsoft.EntityFrameworkCore.DbLoggerCategory; @@ -45,7 +47,7 @@ namespace IRaCIS.Core.Application.Service /// /// [ApiExplorerSettings(GroupName = "Common")] - public class TrialImageDownloadService(IRepository _trialRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment, + public class TrialImageDownloadService(IRepository _trialRepository, IRepository _subjectVisitRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment, IRepository _studyRepository, IRepository _seriesRepository, IRepository _instanceRepository) : BaseService @@ -164,6 +166,389 @@ namespace IRaCIS.Core.Application.Service public Func Action { get; set; } } + [HttpPost] + [AllowAnonymous] + public async Task DownloadExcelTrialImage(Guid trialId) + { + + + + var trialInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo }).FirstOrDefault(); + + #region 设置目录 + + var rootFolder = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + Directory.CreateDirectory(rootFolder); + + // 获取无效字符(系统定义的) + string invalidChars = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars()); + + // 用正则表达式替换所有非法字符为下划线或空字符 + string pattern = $"[{Regex.Escape(invalidChars)}]"; + + var regexNo = Regex.Replace(trialInfo.ResearchProgramNo, pattern, "_"); + + // 创建一个临时文件夹来存放文件 + string trialFolderPath = Path.Combine(rootFolder, $"{regexNo}");//_{NewId.NextGuid()} + Directory.CreateDirectory(trialFolderPath); + + #endregion + + + var downloadVisits = MiniExcel.Query(Path.Combine(rootFolder, "download.xlsx")).ToList().Where(t => t.SubjectCode.IsNotNullOrEmpty() && t.VisitName.IsNotNullOrEmpty()); + + var downloadJobs = new List(); + + foreach (var downloadVisit in downloadVisits) + { + var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new + { + t.ResearchProgramNo, + t.TrialCode, + + VisitList = t.SubjectVisitList.Where(t => t.VisitName.Trim() == downloadVisit.VisitName.Trim() && t.Subject.Code.Trim() == downloadVisit.SubjectCode.Trim() && t.VisitNum == downloadVisit.VisitNum) + .Select(sv => new + { + SubjectVisitId = sv.Id, + TrialSiteCode = sv.TrialSite.TrialSiteCode, + SubjectCode = sv.Subject.Code, + VisitName = sv.VisitName, + VisitNum = sv.VisitNum, + StudyList = sv.StudyList.Select(u => new + { + StudyId = u.Id, + u.PatientId, + u.StudyTime, + u.StudyCode, + u.StudyInstanceUid, + u.StudyDIRPath, + + SeriesList = u.SeriesList.Where(t => t.IsReading).Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList.Where(t => t.IsReading).Select(k => new + { + InstanceId = k.Id, + k.Path, + k.IsEncapsulated, + k.NumberOfFrames, + }).ToList() + }) + + }).ToList(), + + NoneDicomStudyList = sv.NoneDicomStudyList.Where(t => t.IsReading).Select(nd => new + { + nd.Modality, + nd.StudyCode, + nd.ImageDate, + + FileList = nd.NoneDicomFileList.Where(t => t.IsReading).Select(file => new + { + file.FileName, + file.Path, + file.FileType + }).ToList() + }).ToList() + }).OrderBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ToList() + + }).FirstOrDefault(); + + if (downloadInfo == null) + { + return ResponseOutput.Ok(); + } + + #region 排除已经下载的 + + var logFilePath = Path.Combine(rootFolder, $"{trialId}_{regexNo}_download_log.csv"); + + if (File.Exists(logFilePath)) + { + var existVisits = MiniExcel.Query(logFilePath, configuration: new MiniExcelLibs.Csv.CsvConfiguration() + { + StreamReaderFunc = (stream) => new StreamReader(stream, Encoding.GetEncoding("gb2312")) + }).ToList(); + + if (existVisits.Any(old => old.VisitNum == downloadVisit.VisitNum && old.SubjectCode == downloadVisit.SubjectCode && + old.VisitName.Trim().ToLower() == downloadVisit.VisitName.Trim().ToLower())) + { + continue; + } + + } + + + #endregion + + + + foreach (var visitItem in downloadInfo.VisitList) + { + if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0) + { + continue; + } + + + foreach (var studyInfo in visitItem.StudyList) + { + + var dirDic = new Dictionary(); + + #region 生成DIR + + string studyDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{string.Join('_', studyInfo.SeriesList.Select(t => t.Modality))}"); + + // 创建 影像 文件夹 + Directory.CreateDirectory(studyDicomFolderPath); + + + if (!_instanceRepository.Where(t => t.IsReading && t.DicomSerie.IsReading) + .Where(t => visitItem.SubjectVisitId == t.SubjectVisitId).Any(c => c.TransferSytaxUID == string.Empty)) + { + var list = _subjectVisitRepository.Where(t => t.Id == visitItem.SubjectVisitId).SelectMany(t => t.StudyList) + .SelectMany(t => t.InstanceList.Where(t => t.IsReading && t.DicomSerie.IsReading && t.StudyId == studyInfo.StudyId)) + .Select(t => new StudyDIRInfo() + { + + DicomStudyId = t.DicomStudy.Id, + + PatientId = downloadInfo.TrialCode + "-" + t.DicomStudy.Subject.Code, + PatientName = t.DicomStudy.PatientName, + PatientBirthDate = t.DicomStudy.PatientBirthDate, + PatientSex = t.DicomStudy.PatientSex, + + StudyInstanceUid = t.StudyInstanceUid, + StudyId = t.DicomStudy.StudyId, + DicomStudyDate = t.DicomStudy.DicomStudyDate, + DicomStudyTime = t.DicomStudy.DicomStudyTime, + AccessionNumber = t.DicomStudy.AccessionNumber, + + StudyDescription = t.DicomStudy.Description, + + SeriesInstanceUid = t.DicomSerie.SeriesInstanceUid, + Modality = t.DicomSerie.Modality, + DicomSeriesDate = t.DicomSerie.DicomSeriesDate, + DicomSeriesTime = t.DicomSerie.DicomSeriesTime, + SeriesNumber = t.DicomSerie.SeriesNumber, + SeriesDescription = t.DicomSerie.Description, + + InstanceId = t.Id, + SopInstanceUid = t.SopInstanceUid, + SOPClassUID = t.SOPClassUID, + InstanceNumber = t.InstanceNumber, + MediaStorageSOPClassUID = t.MediaStorageSOPClassUID, + MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID, + TransferSytaxUID = t.TransferSytaxUID, + + }).ToList(); + + var pathInfo = await _subjectVisitRepository.Where(t => t.Id == visitItem.SubjectVisitId).Select(t => new { t.TrialId, t.SubjectId, VisitId = t.Id }).FirstNotNullAsync(); + + foreach (var group in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId })) + { + + var first = group.First(); + + + var dirPath = Path.Combine(studyDicomFolderPath, "DICOMDIR"); + + var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIR(group.ToList(), dirDic, dirPath)); + + + + + } + } + #endregion + + // 遍历 Series + foreach (var seriesInfo in studyInfo.SeriesList) + { + + + + // 遍历 InstancePathList + foreach (var instanceInfo in seriesInfo.InstancePathList) + { + + string destinationFolder = Path.Combine(studyDicomFolderPath, "IMAGE"); + + Directory.CreateDirectory(destinationFolder); + + // 复制文件到相应的文件夹 + string destinationPath = Path.Combine(destinationFolder, dirDic[instanceInfo.InstanceId.ToString()]); + + + downloadJobs.Add(new DownloadJob() + { + Name = $"{visitItem.SubjectCode}_{visitItem.VisitNum}_{visitItem.VisitName.Trim()}_DICOM_{destinationPath}", + + Action = async () => + { + + await using var output = File.Create(destinationPath); + + if (instanceInfo.IsEncapsulated) + { + var isSucess = await TryWriteMergedDicomAsync( + () => _oSSService.GetStreamFromOSSAsync(instanceInfo.Path), + output); + + if (isSucess == false) + { + Log.Logger.Warning($"合并多帧失败: failed: {destinationFolder} {destinationPath}"); + } + } + else + { + await using var input = + await _oSSService.GetStreamFromOSSAsync(instanceInfo.Path); + + await input.CopyToAsync(output); + } + } + }); + + } + } + + + } + + foreach (var noneDicomStudy in visitItem.NoneDicomStudyList) + { + string studyNoneDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}", $"{noneDicomStudy.StudyCode}_{noneDicomStudy.ImageDate.ToString("yyyy-MM-dd")}_{noneDicomStudy.Modality}"); + + Directory.CreateDirectory(studyNoneDicomFolderPath); + + foreach (var file in noneDicomStudy.FileList) + { + string destinationPath = Path.Combine(studyNoneDicomFolderPath, Path.GetFileName(file.FileName)); + + //加入到下载任务里 + downloadJobs.Add(new DownloadJob() { Name = $"{visitItem.SubjectCode}_{visitItem.VisitNum}_{visitItem.VisitName.Trim()}_NoneDICOM_{destinationPath}", Action = () => _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath) }); + + //下载到当前目录 + //await _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath); + } + } + + + //建立压缩包 + string visitFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}"); + + downloadJobs.Add(new DownloadJob() + { + Name = $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}_Zip", + + IsZip = true, + + Action = async () => + { + Log.Logger.Warning($" {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}压缩访视:{visitItem.SubjectCode} {visitItem.VisitName} {visitItem.VisitNum}"); + + string zipPath = visitFolderPath + ".zip"; + + if (File.Exists(zipPath)) + { + File.Delete(zipPath); + } + + ZipFile.CreateFromDirectory( + visitFolderPath, + zipPath, + CompressionLevel.NoCompression, + false); + + Directory.Delete(visitFolderPath, true); + + await Task.CompletedTask; + } + }); + + //记录日志 + downloadJobs.Add(new DownloadJob() + { + Name = $"{visitItem.SubjectCode}_{visitItem.VisitNum}_{visitItem.VisitName.Trim()}_Finished", + Action = () => + { + DownloadLogger.Write( + logFilePath: logFilePath, + subjectCode: visitItem.SubjectCode, + visitNum: visitItem.VisitNum, + visitName: visitItem.VisitName.Trim(), + message: "Success"); + + return Task.CompletedTask; + } + }); + } + + + + + + + + + } + + + + + + #region 异步方式处理 + + int totalCount = downloadJobs.Count; + int downloadedCount = 0; + + Log.Logger.Warning($"开始下载总数: {totalCount}"); + foreach (var job in downloadJobs) + { + try + { + if (job.IsZip == false) + { + await job.Action(); + + } + else + { + _ = Task.Run(async () => + { + await job.Action(); + }); + } + } + catch (Exception ex) + { + Log.Logger.Error($"{job.Name}下载失败: {ex.Message}"); + } + + downloadedCount++; + + // 每处理50个,输出一次进度(或最后一个时也输出) + if (downloadedCount % 50 == 0 || downloadedCount == totalCount) + { + + Log.Logger.Warning($"已下载 {downloadedCount} / {totalCount} 个文件,完成 {(downloadedCount * 100.0 / totalCount):F2}%"); + + } + } + #endregion + + + return ResponseOutput.Ok(); + + + + + } + + /// /// 后端api swagger 下载项目影像 /// @@ -187,20 +572,25 @@ namespace IRaCIS.Core.Application.Service var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.ResearchProgramNo, + t.TrialCode, VisitList = t.SubjectVisitList.Where(t => t.VisitTaskList.Any(t => t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Visit && t.ReadingTaskState != ReadingTaskState.HaveSigned && t.SourceSubjectVisitId != null && t.DoctorUserId != null)) //.Where(t=>subjectCodeList.Contains(t.Subject.Code)) .Select(sv => new { + SubjectVisitId = sv.Id, TrialSiteCode = sv.TrialSite.TrialSiteCode, SubjectCode = sv.Subject.Code, VisitName = sv.VisitName, VisitNum = sv.VisitNum, StudyList = sv.StudyList.Select(u => new { + StudyId = u.Id, u.PatientId, u.StudyTime, u.StudyCode, + u.StudyInstanceUid, + u.StudyDIRPath, SeriesList = u.SeriesList.Where(t => t.IsReading).Select(z => new { @@ -208,6 +598,7 @@ namespace IRaCIS.Core.Application.Service InstancePathList = z.DicomInstanceList.Where(t => t.IsReading).Select(k => new { + InstanceId = k.Id, k.Path, k.IsEncapsulated, k.NumberOfFrames, @@ -301,6 +692,7 @@ namespace IRaCIS.Core.Application.Service var downloadJobs = new List(); + foreach (var visitItem in acturalDownList) { if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0) @@ -308,32 +700,96 @@ namespace IRaCIS.Core.Application.Service continue; } - #region 处理 中心,受试者dicom non-dicom 文件夹层级 - - //var siteFolderPath = Path.Combine(trialFolderPath, visitItem.TrialSiteCode); - //if (!Directory.Exists(siteFolderPath)) - //{ - // Directory.CreateDirectory(siteFolderPath); - //} - - #endregion foreach (var studyInfo in visitItem.StudyList) { + + var dirDic = new Dictionary(); + + #region 生成DIR + + string studyDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{string.Join('_', studyInfo.SeriesList.Select(t => t.Modality))}"); + + // 创建 影像 文件夹 + Directory.CreateDirectory(studyDicomFolderPath); + + + if (!_instanceRepository.Where(t => t.IsReading && t.DicomSerie.IsReading) + .Where(t => visitItem.SubjectVisitId == t.SubjectVisitId).Any(c => c.TransferSytaxUID == string.Empty)) + { + var list = _subjectVisitRepository.Where(t => t.Id == visitItem.SubjectVisitId).SelectMany(t => t.StudyList) + .SelectMany(t => t.InstanceList.Where(t => t.IsReading && t.DicomSerie.IsReading && t.StudyId == studyInfo.StudyId)) + .Select(t => new StudyDIRInfo() + { + + DicomStudyId = t.DicomStudy.Id, + + PatientId = downloadInfo.TrialCode + "-" + t.DicomStudy.Subject.Code, + PatientName = t.DicomStudy.PatientName, + PatientBirthDate = t.DicomStudy.PatientBirthDate, + PatientSex = t.DicomStudy.PatientSex, + + StudyInstanceUid = t.StudyInstanceUid, + StudyId = t.DicomStudy.StudyId, + DicomStudyDate = t.DicomStudy.DicomStudyDate, + DicomStudyTime = t.DicomStudy.DicomStudyTime, + AccessionNumber = t.DicomStudy.AccessionNumber, + + StudyDescription = t.DicomStudy.Description, + + SeriesInstanceUid = t.DicomSerie.SeriesInstanceUid, + Modality = t.DicomSerie.Modality, + DicomSeriesDate = t.DicomSerie.DicomSeriesDate, + DicomSeriesTime = t.DicomSerie.DicomSeriesTime, + SeriesNumber = t.DicomSerie.SeriesNumber, + SeriesDescription = t.DicomSerie.Description, + + InstanceId = t.Id, + SopInstanceUid = t.SopInstanceUid, + SOPClassUID = t.SOPClassUID, + InstanceNumber = t.InstanceNumber, + MediaStorageSOPClassUID = t.MediaStorageSOPClassUID, + MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID, + TransferSytaxUID = t.TransferSytaxUID, + + }).ToList(); + + var pathInfo = await _subjectVisitRepository.Where(t => t.Id == visitItem.SubjectVisitId).Select(t => new { t.TrialId, t.SubjectId, VisitId = t.Id }).FirstNotNullAsync(); + + foreach (var group in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId })) + { + + var first = group.First(); + + + var dirPath = Path.Combine(studyDicomFolderPath, "DICOMDIR"); + + var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIR(group.ToList(), dirDic, dirPath)); + + + + + } + } + #endregion + // 遍历 Series foreach (var seriesInfo in studyInfo.SeriesList) { - string studyDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}"); - // 创建 影像 文件夹 - Directory.CreateDirectory(studyDicomFolderPath); + // 遍历 InstancePathList foreach (var instanceInfo in seriesInfo.InstancePathList) { + + string destinationFolder = Path.Combine(studyDicomFolderPath, "IMAGE"); + + Directory.CreateDirectory(destinationFolder); + // 复制文件到相应的文件夹 - string destinationPath = Path.Combine(studyDicomFolderPath, Path.GetFileName(instanceInfo.Path)); + string destinationPath = Path.Combine(destinationFolder, dirDic[instanceInfo.InstanceId.ToString()]); downloadJobs.Add(new DownloadJob() @@ -342,13 +798,19 @@ namespace IRaCIS.Core.Application.Service Action = async () => { + await using var output = File.Create(destinationPath); if (instanceInfo.IsEncapsulated) { - await TryWriteMergedDicomAsync( - () => _oSSService.GetStreamFromOSSAsync(instanceInfo.Path), - output); + var isSucess = await TryWriteMergedDicomAsync( + () => _oSSService.GetStreamFromOSSAsync(instanceInfo.Path), + output); + + if (isSucess == false) + { + Log.Logger.Warning($"合并多帧失败: failed: {destinationFolder} {destinationPath}"); + } } else {