Merge branch 'Test_IRC_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_IRC_Net8
continuous-integration/drone/push Build is running
Details
continuous-integration/drone/push Build is running
Details
commit
a2966e761c
|
|
@ -7,8 +7,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=10.10.10.49,1433;Database=Prod_IRC_pidr_restore;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=10.10.10.49,1433;Database=irc_Hangfire_bak;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",
|
//"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"
|
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,82 @@ namespace IRaCIS.Core.Application.Helper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static async Task GenerateStudyDIR(List<StudyDIRInfo> list, Dictionary<string, string> dic,string dirSavePath)
|
||||||
|
{
|
||||||
|
var mappings = new List<string>();
|
||||||
|
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)
|
public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1760,7 +1760,7 @@
|
||||||
<param name="_trialRepository"></param>
|
<param name="_trialRepository"></param>
|
||||||
<param name="_oSSService"></param>
|
<param name="_oSSService"></param>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.Application.Service.TrialImageDownloadService.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Application.Helper.IOSSService,Microsoft.AspNetCore.Hosting.IWebHostEnvironment,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomSeries},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomInstance})">
|
<member name="M:IRaCIS.Core.Application.Service.TrialImageDownloadService.#ctor(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Trial},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.SubjectVisit},IRaCIS.Core.Application.Helper.IOSSService,Microsoft.AspNetCore.Hosting.IWebHostEnvironment,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomSeries},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomInstance})">
|
||||||
<summary>
|
<summary>
|
||||||
项目影像后台下载,不打包
|
项目影像后台下载,不打包
|
||||||
</summary>
|
</summary>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ using FellowOakDicom;
|
||||||
using FellowOakDicom.Imaging;
|
using FellowOakDicom.Imaging;
|
||||||
using FellowOakDicom.Imaging.Render;
|
using FellowOakDicom.Imaging.Render;
|
||||||
using FellowOakDicom.IO.Buffer;
|
using FellowOakDicom.IO.Buffer;
|
||||||
|
using Hangfire.Common;
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.MassTransit.Command;
|
using IRaCIS.Core.Application.MassTransit.Command;
|
||||||
using IRaCIS.Core.Application.ViewModel;
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
|
|
@ -18,7 +20,6 @@ using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using MiniExcelLibs;
|
using MiniExcelLibs;
|
||||||
using NPOI.Util;
|
|
||||||
using Org.BouncyCastle.Utilities.Zlib;
|
using Org.BouncyCastle.Utilities.Zlib;
|
||||||
using SharpCompress.Common;
|
using SharpCompress.Common;
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -33,6 +34,7 @@ using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using static IRaCIS.Core.Application.Service.TestService;
|
using static IRaCIS.Core.Application.Service.TestService;
|
||||||
|
using static IRaCIS.Core.Application.Service.TrialImageDownloadService;
|
||||||
using static IRaCIS.Core.Domain.Share.StaticData;
|
using static IRaCIS.Core.Domain.Share.StaticData;
|
||||||
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
|
||||||
|
|
||||||
|
|
@ -45,7 +47,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
/// <param name="_trialRepository"></param>
|
/// <param name="_trialRepository"></param>
|
||||||
/// <param name="_oSSService"></param>
|
/// <param name="_oSSService"></param>
|
||||||
[ApiExplorerSettings(GroupName = "Common")]
|
[ApiExplorerSettings(GroupName = "Common")]
|
||||||
public class TrialImageDownloadService(IRepository<Trial> _trialRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment,
|
public class TrialImageDownloadService(IRepository<Trial> _trialRepository, IRepository<SubjectVisit> _subjectVisitRepository, IOSSService _oSSService, IWebHostEnvironment _hostEnvironment,
|
||||||
IRepository<DicomStudy> _studyRepository,
|
IRepository<DicomStudy> _studyRepository,
|
||||||
IRepository<DicomSeries> _seriesRepository,
|
IRepository<DicomSeries> _seriesRepository,
|
||||||
IRepository<DicomInstance> _instanceRepository) : BaseService
|
IRepository<DicomInstance> _instanceRepository) : BaseService
|
||||||
|
|
@ -164,6 +166,389 @@ namespace IRaCIS.Core.Application.Service
|
||||||
public Func<Task> Action { get; set; }
|
public Func<Task> Action { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IResponseOutput> 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<SubjectVisitExcel>(Path.Combine(rootFolder, "download.xlsx")).ToList().Where(t => t.SubjectCode.IsNotNullOrEmpty() && t.VisitName.IsNotNullOrEmpty());
|
||||||
|
|
||||||
|
var downloadJobs = new List<DownloadJob>();
|
||||||
|
|
||||||
|
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<SubjectVisitExcel>(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<string, string>();
|
||||||
|
|
||||||
|
#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();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 后端api swagger 下载项目影像
|
/// 后端api swagger 下载项目影像
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -187,20 +572,25 @@ namespace IRaCIS.Core.Application.Service
|
||||||
var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new
|
var downloadInfo = _trialRepository.Where(t => t.Id == trialId).Select(t => new
|
||||||
{
|
{
|
||||||
t.ResearchProgramNo,
|
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))
|
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))
|
//.Where(t=>subjectCodeList.Contains(t.Subject.Code))
|
||||||
.Select(sv => new
|
.Select(sv => new
|
||||||
{
|
{
|
||||||
|
SubjectVisitId = sv.Id,
|
||||||
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
TrialSiteCode = sv.TrialSite.TrialSiteCode,
|
||||||
SubjectCode = sv.Subject.Code,
|
SubjectCode = sv.Subject.Code,
|
||||||
VisitName = sv.VisitName,
|
VisitName = sv.VisitName,
|
||||||
VisitNum = sv.VisitNum,
|
VisitNum = sv.VisitNum,
|
||||||
StudyList = sv.StudyList.Select(u => new
|
StudyList = sv.StudyList.Select(u => new
|
||||||
{
|
{
|
||||||
|
StudyId = u.Id,
|
||||||
u.PatientId,
|
u.PatientId,
|
||||||
u.StudyTime,
|
u.StudyTime,
|
||||||
u.StudyCode,
|
u.StudyCode,
|
||||||
|
u.StudyInstanceUid,
|
||||||
|
u.StudyDIRPath,
|
||||||
|
|
||||||
SeriesList = u.SeriesList.Where(t => t.IsReading).Select(z => new
|
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
|
InstancePathList = z.DicomInstanceList.Where(t => t.IsReading).Select(k => new
|
||||||
{
|
{
|
||||||
|
InstanceId = k.Id,
|
||||||
k.Path,
|
k.Path,
|
||||||
k.IsEncapsulated,
|
k.IsEncapsulated,
|
||||||
k.NumberOfFrames,
|
k.NumberOfFrames,
|
||||||
|
|
@ -301,6 +692,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
var downloadJobs = new List<DownloadJob>();
|
var downloadJobs = new List<DownloadJob>();
|
||||||
|
|
||||||
|
|
||||||
foreach (var visitItem in acturalDownList)
|
foreach (var visitItem in acturalDownList)
|
||||||
{
|
{
|
||||||
if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0)
|
if (visitItem.StudyList.Count() == 0 && visitItem.NoneDicomStudyList.Count() == 0)
|
||||||
|
|
@ -308,32 +700,96 @@ namespace IRaCIS.Core.Application.Service
|
||||||
continue;
|
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)
|
foreach (var studyInfo in visitItem.StudyList)
|
||||||
{
|
{
|
||||||
// 遍历 Series
|
|
||||||
foreach (var seriesInfo in studyInfo.SeriesList)
|
var dirDic = new Dictionary<string, string>();
|
||||||
{
|
|
||||||
string studyDicomFolderPath = Path.Combine(trialFolderPath, $"{visitItem.SubjectCode}_{visitItem.VisitName.Trim()}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}");
|
#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);
|
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
|
// 遍历 InstancePathList
|
||||||
foreach (var instanceInfo in seriesInfo.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()
|
downloadJobs.Add(new DownloadJob()
|
||||||
|
|
@ -342,13 +798,19 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
Action = async () =>
|
Action = async () =>
|
||||||
{
|
{
|
||||||
|
|
||||||
await using var output = File.Create(destinationPath);
|
await using var output = File.Create(destinationPath);
|
||||||
|
|
||||||
if (instanceInfo.IsEncapsulated)
|
if (instanceInfo.IsEncapsulated)
|
||||||
{
|
{
|
||||||
await TryWriteMergedDicomAsync(
|
var isSucess = await TryWriteMergedDicomAsync(
|
||||||
() => _oSSService.GetStreamFromOSSAsync(instanceInfo.Path),
|
() => _oSSService.GetStreamFromOSSAsync(instanceInfo.Path),
|
||||||
output);
|
output);
|
||||||
|
|
||||||
|
if (isSucess == false)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"合并多帧失败: failed: {destinationFolder} {destinationPath}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue