diff --git a/IRaCIS.Core.Application/Helper/OSSService.cs b/IRaCIS.Core.Application/Helper/OSSService.cs index 5ddf2c30a..c3431aa33 100644 --- a/IRaCIS.Core.Application/Helper/OSSService.cs +++ b/IRaCIS.Core.Application/Helper/OSSService.cs @@ -96,8 +96,8 @@ namespace IRaCIS.Core.Application.Helper public interface IOSSService { - public Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName); - public Task UploadToOSSAsync(string localFilePath, string oosFolderPath); + public Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true); + public Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true); public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath); @@ -124,12 +124,12 @@ namespace IRaCIS.Core.Application.Helper /// /// /// + /// /// - public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName) + public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true) { - - - var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}"; + var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}"; + //var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}"; //var ossRelativePath = oosFolderPath + "/" + fileRealName; using (var memoryStream = new MemoryStream()) @@ -207,11 +207,12 @@ namespace IRaCIS.Core.Application.Helper /// /// /// - public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath) + public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true) { var localFileName = Path.GetFileName(localFilePath); - var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}"; + var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}"; + //var ossRelativePath = oosFolderPath + "/" + localFileName; @@ -266,6 +267,7 @@ namespace IRaCIS.Core.Application.Helper } + public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath) { diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index b47136377..f27ff3667 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -84,16 +84,17 @@ - + oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + - + oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder @@ -852,6 +853,24 @@ 后台托管服务的方式运行 + + + 打包和匿名化影像 默认是匿名化打包,也可以不匿名化打包 + + + + + + + + + 后台任务调用,前端忽略该接口 + + + + + + 上传临床数据 diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs new file mode 100644 index 000000000..30d4d0a49 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -0,0 +1,232 @@ +using FellowOakDicom; +using IRaCIS.Core.Application.Helper; +using MassTransit; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using System.IO.Compression; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.Service.ImageAndDoc +{ + public interface IDownloadAndUploadService + { + Task PackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, bool isAnonymize = true); + } + [ApiExplorerSettings(GroupName = "Trial")] + public class DownloadAndUploadService : BaseService, IDownloadAndUploadService + { + + private readonly IRepository _systemAnonymizationRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IOSSService _oSSService; + public DownloadAndUploadService(IRepository systemAnonymizationRepository, IRepository subjectVisitRepository, IOSSService oSSService) + { + _systemAnonymizationRepository = systemAnonymizationRepository; + + _subjectVisitRepository = subjectVisitRepository; + + _oSSService = oSSService; + } + + + /// + /// 打包和匿名化影像 默认是匿名化打包,也可以不匿名化打包 + /// + /// + /// + /// + /// + + public async Task RequestPackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, bool isAnonymize = true) + { + var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); + + if (subjectVisit.PackState == PackState.WaitPack) + { + HangfireJobHelper.NotImmediatelyOnceOnlyJob(t => t.PackageAndAnonymizImage(trialId, subjectVisitId, isAnonymize), TimeSpan.FromSeconds(1)); + + subjectVisit.PackState = PackState.Packing; + + await _subjectVisitRepository.SaveChangesAsync(); + + } + + return ResponseOutput.Ok(subjectVisit.VisitImageZipPath); + + } + + + /// + /// 后台任务调用,前端忽略该接口 + /// + /// + /// + /// + /// + public async Task PackageAndAnonymizImage(Guid trialId, Guid subjectVisitId, bool isAnonymize = true) + { + + var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == subjectVisitId); + + try + { + var addOrUpdateFixedFieldList = new List(); + + var ircFieldList = new List(); + + if (isAnonymize) + { + var systemAnonymizationList = _systemAnonymizationRepository.Where(t => t.IsEnable).ToList(); + + addOrUpdateFixedFieldList = systemAnonymizationList.Where(t => t.IsFixed).ToList(); + + ircFieldList = systemAnonymizationList.Where(t => t.IsFixed == false).ToList(); + } + + var subjectAndVisitInfo = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { SubjectCode = t.Subject.Code, t.Trial.TrialCode, t.VisitNum }).FirstOrDefault(); + + var query = from sv in _subjectVisitRepository.Where(t => t.Id == subjectVisitId) + + select new + { + SubjectCode = sv.Subject.Code, + VisitName = sv.VisitName, + StudyList = sv.StudyList.Select(u => new + { + u.PatientId, + u.StudyTime, + u.StudyCode, + + SeriesList = u.SeriesList.Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList.Select(k => new + { + k.Path + }) + }) + + }) + }; + + var info = query.FirstOrDefault(); + + if (info != null) + { + // 创建一个临时文件夹来存放文件 + string tempFolderPath = Path.Combine(Directory.GetCurrentDirectory(), $"DownloadTemp_{NewId.NextGuid()}"); + Directory.CreateDirectory(tempFolderPath); + + // 遍历查询结果 + foreach (var studyInfo in info.StudyList) + { + // 遍历 Series + foreach (var seriesInfo in studyInfo.SeriesList) + { + string studyFolderPath = Path.Combine(tempFolderPath, $"{info.SubjectCode}_{info.VisitName}", $"{studyInfo.StudyCode}_{studyInfo.StudyTime?.ToString("yyyy-MM-dd")}_{seriesInfo.Modality}"); + + // 创建 影像 文件夹 + Directory.CreateDirectory(studyFolderPath); + + // 遍历 InstancePathList + foreach (var instanceInfo in seriesInfo.InstancePathList) + { + // 复制文件到相应的文件夹 + string destinationPath = Path.Combine(studyFolderPath, Path.GetFileName(instanceInfo.Path)); + + //下载到当前目录 + await _oSSService.DownLoadFromOSSAsync(instanceInfo.Path, destinationPath); + + #region 匿名化逻辑 + + + if (isAnonymize) + { + DicomFile dicomFile = await DicomFile.OpenAsync(destinationPath, Encoding.Default); + + DicomDataset dataset = dicomFile.Dataset; + + foreach (var item in addOrUpdateFixedFieldList) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + dataset.AddOrUpdate(dicomTag, item.ReplaceValue); + } + + foreach (var item in ircFieldList) + { + + var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16)); + + if (dicomTag == DicomTag.ClinicalTrialProtocolID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSiteID) + { + //dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialSiteCode); + + } + if (dicomTag == DicomTag.ClinicalTrialSubjectID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.SubjectCode); + + } + if (dicomTag == DicomTag.ClinicalTrialTimePointID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.VisitNum.ToString()); + + } + if (dicomTag == DicomTag.PatientID) + { + dataset.AddOrUpdate(dicomTag, subjectAndVisitInfo.TrialCode + "_" + subjectAndVisitInfo.SubjectCode); + + } + + } + } + #endregion + } + } + } + + var zipPath = Path.Combine(Directory.GetCurrentDirectory(), $"{info.SubjectCode}_{info.VisitName}_ImageStudy.zip"); + + ZipFile.CreateFromDirectory(tempFolderPath, zipPath); + + //上传到Oss + var relativePath = await _oSSService.UploadToOSSAsync(zipPath, $"download_zip", false); + + + //subjectVisit.PackState = PackState.Packed; + //subjectVisit.VisitImageZipPath = relativePath; + //await _subjectVisitRepository.SaveChangesAsync(); + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() { PackState = PackState.Packed, VisitImageZipPath = relativePath }); + + + //清理文件夹 + Directory.Delete(tempFolderPath, true); + File.Delete(zipPath); + + } + } + catch (Exception ex) + { + await _subjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.Id == subjectVisitId, u => new SubjectVisit() { PackState = PackState.WaitPack }); + + } + + + + + + } + + } +} diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index ecf16ca86..18629eff1 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -1470,6 +1470,10 @@ namespace IRaCIS.Core.Application.Contracts public bool IsLostVisit { get; set; } + public string VisitImageZipPath { get; set; } + + public PackState PackState { get; set; } + //public Guid? ClinicalDataSignUserId { get; set; } } diff --git a/IRaCIS.Core.Application/Service/QC/QCListService.cs b/IRaCIS.Core.Application/Service/QC/QCListService.cs index d2d7dc7b3..8e9c0e9d3 100644 --- a/IRaCIS.Core.Application/Service/QC/QCListService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCListService.cs @@ -57,7 +57,7 @@ namespace IRaCIS.Core.Application.Image.QA /// /// [HttpPost] - public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetCRCVisitList(CRCVisitSearchDTO visitSearchDTO) + public async Task>> GetCRCVisitList(CRCVisitSearchDTO visitSearchDTO) { var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); @@ -83,7 +83,7 @@ namespace IRaCIS.Core.Application.Image.QA var config = await _repository.Where(t => t.Id == visitSearchDTO.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); config.IsHaveSubjectClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.Subject ) && x.UploadRole == UploadRole.CRC); config.IsHaveVisitClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && x.UploadRole == UploadRole.CRC); - return (pageList, config); + return ResponseOutput.Ok (pageList, config); } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs index d7f800cfe..042b65eab 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs @@ -342,7 +342,8 @@ namespace IRaCIS.Core.Application.Contracts public class GetTrialReadingInfoOutDto { - public string TrialModalitys { get; set;} + public string TrialModalitys { get; set;} + /// /// 项目ID @@ -501,6 +502,11 @@ namespace IRaCIS.Core.Application.Contracts public string CriterionModalitys { get; set; } + public ReadingImageDownload? ImageDownloadEnum { get; set; } + + public ReadingImageUpload? ImageUploadEnum { get; set; } + + } public class ReadingCriterionPageDto diff --git a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs index d0cd03c75..c245110fc 100644 --- a/IRaCIS.Core.Domain/Visit/SubjectVisit.cs +++ b/IRaCIS.Core.Domain/Visit/SubjectVisit.cs @@ -240,6 +240,29 @@ namespace IRaCIS.Core.Domain.Models public List SubjectCriteriaEvaluationVisitFilterList { get; set; } public ReadingStatusEnum ReadingStatus { get; set; } + public string VisitImageZipPath { get; set; } = string.Empty; + public PackState PackState { get; set; } + } + /// + /// 影像下载打包状态 + /// + public enum PackState + { + + /// + /// 待打包 + /// + WaitPack = 0, + + /// + /// 打包中 + /// + Packing = 1, + + /// + /// 打包完成 + /// + Packed = 2 } }