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 c0882ff60..cfc8e09af 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -1760,7 +1760,7 @@ - + 项目影像后台下载,不打包 @@ -6052,7 +6052,7 @@ - + 计算平均值 diff --git a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs index ef8607b24..d3090eec7 100644 --- a/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs +++ b/IRaCIS.Core.Application/Service/Common/TrialImageDownloadService.cs @@ -7,6 +7,7 @@ using FellowOakDicom; using FellowOakDicom.Imaging; using FellowOakDicom.Imaging.Render; using FellowOakDicom.IO.Buffer; +using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.MassTransit.Command; using IRaCIS.Core.Application.ViewModel; @@ -45,7 +46,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 @@ -187,20 +188,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 +214,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 +308,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 +316,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 +414,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 {