diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs index ab5eff287..dab1621e7 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs @@ -620,4 +620,178 @@ namespace IRaCIS.Core.Application.Contracts public Guid TaskId { get; set; } } + + public class TrialVisitImageQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid? TrialSiteId { get; set; } + + public string? SubjectCode { get; set; } + + public DateTime? BeginScanDate { get; set; } + public DateTime? EndScanDate { get; set; } + } + public class TrialVisitImageStatView + { + public Guid TrialId { get; set; } + + public Guid SubjectVisitId { get; set; } + + public Guid TrialSiteId { get; set; } + + public string TrialSiteCode { get; set; } + + public string SubjectCode { get; set; } + + public string VisitName { get; set; } + + public decimal VisitNum { get; set; } + + public DateTime? EarliestScanDate { get; set; } + public DateTime? LatestScanDate { get; set; } + + + + public int TotalStudyCount { get; set; } + + public int TotalImageCount { get; set; } + + public long? TotalImageSize { get; set; } + + public long? TotalReadingImageSize { get; set; } + + public string TotalImageSizeStr => TotalImageSize.HasValue + ? $"{TotalImageSize.Value / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + public string TotalReadingImageSizeStr => TotalReadingImageSize.HasValue + ? $"{TotalReadingImageSize.Value / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + public string ImageTypeStr => $"{(IsHaveDicom ? "DICOM" : "")}{(IsHaveNoneDicom && IsHaveDicom ? " , " : "")}{(IsHaveNoneDicom ? "Non-DICOM" : "")}"; + + public bool IsHaveDicom { get; set; } + + public bool IsHaveNoneDicom { get; set; } + + #region 废弃,为了字段排序 + //public int TotalStudyCount => DicomStudyCount + NoneDicomStudyCount; + + //public int TotalImageCount => DicomImageCount + NoneDicomImageCount; + + //public long? TotalImageSize => DicomImageSize + NoneDicomImageSize; + + //public int DicomStudyCount { get; set; } + //public int DicomImageCount { get; set; } + //public long? DicomImageSize { get; set; } + + + //public int NoneDicomStudyCount { get; set; } + //public int NoneDicomImageCount { get; set; } + //public long? NoneDicomImageSize { get; set; } + + #endregion + + + } + + public class TrialExportImageCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + public List SubjectVisitIdList { get; set; } + + + public bool IsKeyImage { get; set; } + + // true 导出阅片,null 就是所有影像 + public bool? IsExportReading { get; set; } + } + public class TrialKeyImageExportDTO + { + public Guid Id { get; set; } + public string ResearchProgramNo { get; set; } + public string CriterionName { get; set; } + public ArbitrationRule ArbitrationRule { get; set; } + + public bool IsGlobalReading { get; set; } + + public List SubjectCriterionReadingPeriodVisitNumList { get; set; } + + public string TrialSiteCode { get; set; } + + public string VisitName { get; set; } + + public Decimal VisitTaskNum { get; set; } + + public string SubjectCode { get; set; } + + public Arm ArmEnum { get; set; } + + public Guid? JudgeResultTaskId { get; set; } + + public bool? IsTrigerJudge { get; set; } + public bool? IsJudgeSelect { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + public List ReadingNoneDicomMarkPathList { get; set; } + public List QuestionMarkPictureList { get; set; } + + public List TableQuestionRowPictureList { get; set; } + } + public class TrialKeyPicturePath + { + public string PicturePath { get; set; } + + public string OtherPicturePath { get; set; } + } + public class TrialImageStatInfo + { + public int SubjectCount { get; set; } + + public int SubjectVisitCount { get; set; } + + public long? TotalImageSize { get; set; } + + + public string TotalImageSizeStr => TotalImageSize.HasValue + ? $"{TotalImageSize.Value / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + + public string SubjectImageAVGSizeStr => TotalImageSize.HasValue + ? $"{TotalImageSize.Value / SubjectCount / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + + public string SubjectVisitImageAVGSizeStr => TotalImageSize.HasValue + ? $"{TotalImageSize.Value / SubjectVisitCount / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + public long? TotalReadingImageSize { get; set; } + + + public string TotalReadingImageSizeStr => TotalReadingImageSize.HasValue + ? $"{TotalReadingImageSize.Value / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + + public string SubjectReadingImageAVGSizeStr => TotalReadingImageSize.HasValue + ? $"{TotalReadingImageSize.Value / SubjectCount / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + + public string SubjectVisitReadingImageAVGSizeStr => TotalReadingImageSize.HasValue + ? $"{TotalReadingImageSize.Value / SubjectVisitCount / 1024d / 1024d:F3} MB" + : "0.000 MB"; + + } + + + } diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs index 5a6bc7e5a..d289047c0 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -7,6 +7,7 @@ using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service.ImageAndDoc.DTO; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore.Common; using IRaCIS.Core.Infrastructure; using MassTransit; using MassTransit.Initializers; @@ -1154,6 +1155,407 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc return ResponseOutput.Ok(); } + #region 影像汇总页面 + [HttpPost] + public async Task>> GetTrialVisitImageStatList(TrialVisitImageQuery inQuery) + { + var query = _subjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectCode.IsNotNullOrEmpty(), t => t.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(inQuery.BeginScanDate != null, t => t.LatestScanDate >= inQuery.BeginScanDate) + .WhereIf(inQuery.EndScanDate != null, t => t.LatestScanDate == inQuery.EndScanDate) + .Select(t => new TrialVisitImageStatView() + { + TrialId = t.TrialId, + SubjectVisitId = t.Id, + SubjectCode = t.Subject.Code, + TrialSiteCode = t.TrialSite.TrialSiteCode, + TrialSiteId = t.TrialSiteId, + VisitName = t.VisitName, + VisitNum = t.VisitNum, + EarliestScanDate = t.EarliestScanDate, + LatestScanDate = t.LatestScanDate, + + IsHaveDicom = t.StudyList.Any(), + + IsHaveNoneDicom = t.NoneDicomStudyList.Any(), + + + TotalStudyCount = t.StudyList.Count() + t.NoneDicomStudyList.Count(), + + TotalImageCount = t.StudyList.Sum(t => t.InstanceCount) + t.NoneDicomStudyList.Sum(t => t.FileCount), + + TotalImageSize = t.StudyList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize) + t.NoneDicomStudyList.SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize), + + TotalReadingImageSize = t.StudyList.SelectMany(t => t.InstanceList/*.Where(t => t.IsReading && t.DicomSerie.IsReading)*/).Sum(t => t.FileSize) + + t.NoneDicomStudyList/*.Where(t => t.IsReading)*/.SelectMany(t => t.NoneDicomFileList/*.Where(t => t.IsReading)*/).Sum(t => t.FileSize), + + + //DicomStudyCount = t.StudyList.Count(), + //NoneDicomStudyCount = t.NoneDicomStudyList.Count(), + + //DicomImageCount = t.StudyList.Sum(t => t.InstanceCount), + //NoneDicomImageCount = t.NoneDicomStudyList.Sum(t => t.FileCount), + + //DicomImageSize = t.StudyList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize), + //NoneDicomImageSize = t.NoneDicomStudyList.SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize) + }); + var defalutSortArray = new string[] { nameof(TrialVisitImageStatView.TrialSiteCode), nameof(QCCRCVisitViewModel.SubjectCode), nameof(QCCRCVisitViewModel.VisitNum) }; + + var pagelist = await query.Where(t => t.TotalImageCount > 0).ToPagedListAsync(inQuery, defalutSortArray); + + return ResponseOutput.Ok(pagelist); + } + + /// + /// 获取项目影像统计,有影像的subject 数量 访视数量 + /// + /// + /// + public async Task> GetTrialVisitImageStatInfo(Guid trialId) + { + var subjectImageList = _subjectVisitRepository.Where(t => t.TrialId == trialId) + .Where(t => t.StudyList.Sum(t => t.InstanceCount) > 0 || t.NoneDicomStudyList.Sum(t => t.FileCount) > 0) + .GroupBy(t => t.SubjectId) + .Select(g => new + { + SubjectId = g.Key, + VisitCount = g.Count(), + ReadingImageSize = g.SelectMany(t => t.NoneDicomStudyList/*.Where(t => t.IsReading)*/).SelectMany(t => t.NoneDicomFileList/*.Where(t => t.IsReading)*/).Sum(t => t.FileSize) + + + g.SelectMany(t => t.StudyList).SelectMany(t => t.InstanceList.Where(t => /*t.IsReading &&*/ t.DicomSerie.IsReading)).Sum(t => t.FileSize), + + ImageSize = g.SelectMany(t => t.NoneDicomStudyList).SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize) + + + g.SelectMany(t => t.StudyList).SelectMany(t => t.InstanceList).Sum(t => t.FileSize) + }) + .ToList(); + + var subjectCount = subjectImageList.Count; + + var subjectVisitCount = subjectImageList.Sum(t => t.VisitCount); + + var totalImageSize = subjectImageList.Sum(t => t.ImageSize); + + var totalReadingImageSize = subjectImageList.Sum(t => t.ReadingImageSize); + + return ResponseOutput.Ok(new TrialImageStatInfo { SubjectCount = subjectCount, SubjectVisitCount = subjectVisitCount, TotalImageSize = totalImageSize, TotalReadingImageSize = totalReadingImageSize }); + + + } + + /// + /// 批量勾选访视 进行下载 + /// + /// + /// + [HttpPost] + public async Task GetExportSubjectVisitImageList(TrialExportImageCommand inCommand) + { + + var isExportReading = inCommand.IsExportReading == true; + + if (inCommand.IsKeyImage) + { + var downloadInfoList = _visitTaskRepository.Where(t => t.TrialId == inCommand.TrialId && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global) + && t.ReadingTaskState == ReadingTaskState.HaveSigned && t.IsAnalysisCreate == false && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze)) + .Where(t => inCommand.SubjectVisitIdList.Contains((Guid)t.SourceSubjectVisitId)) + .Select(t => new TrialKeyImageExportDTO() + { + ResearchProgramNo = t.Trial.ResearchProgramNo, + CriterionName = t.TrialReadingCriterion.CriterionName, + ArbitrationRule = t.TrialReadingCriterion.ArbitrationRule, + IsGlobalReading = t.TrialReadingCriterion.IsGlobalReading, + SubjectCriterionReadingPeriodVisitNumList = t.Subject.ReadModuleList + .Where(u => u.TrialReadingCriterionId == t.TrialReadingCriterionId && u.ReadingSetType == ReadingSetType.ImageReading).Select(c => c.SubjectVisit.VisitNum).ToList(), + + TrialSiteCode = t.Subject.TrialSite.TrialSiteCode, + SubjectCode = t.Subject.Code, + + ReadingCategory = t.ReadingCategory, + + VisitName = t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.VisitName : "", + + VisitTaskNum = t.VisitTaskNum, + + ArmEnum = t.ArmEnum, + + //ReadingNoneDicomMarkPathList = t.ReadingNoneDicomMarkList.Select(c => new TrialKeyPicturePath { PicturePath = c.Path, OtherPicturePath = string.Empty }).ToList(), + + QuestionMarkPictureList = t.ReadingTaskQuestionMarkList.Select(c => new TrialKeyPicturePath { PicturePath = c.PicturePath, OtherPicturePath = c.OtherPicturePath }).ToList(), + + TableQuestionRowPictureList = t.LesionList.Select(c => new TrialKeyPicturePath { PicturePath = c.PicturePath, OtherPicturePath = c.OtherPicturePath }).ToList(), + + Id = t.Id, + //裁判选择结果 + JudgeResultTaskId = t.JudgeVisitTask.JudgeResultTaskId, + + //是否触发裁判 + IsTrigerJudge = t.JudgeVisitTaskId != null, + + IsJudgeSelect = null + + + }).ToList(); + + foreach (var subjectCriterionGroup in downloadInfoList.GroupBy(t => new { t.SubjectCode, t.CriterionName, t.ArbitrationRule, t.IsGlobalReading })) + { + var arbitrationRule = subjectCriterionGroup.Key.ArbitrationRule; + var isGlobalReading = subjectCriterionGroup.Key.IsGlobalReading; + + foreach (var item in subjectCriterionGroup) + { + if (arbitrationRule == ArbitrationRule.Visit) + { + //是否产生裁判 + + if (item.IsTrigerJudge == true) + { + //裁判做完了 + if (item.JudgeResultTaskId != null) + { + //裁判选择了自己,那么设置 + if (item.JudgeResultTaskId == item.Id) + { + item.IsJudgeSelect = true; + } + else + { + item.IsJudgeSelect = false; + } + } + + } + else + { + //没产生 且两个人做完了 默认R1 + + if (subjectCriterionGroup.Where(t => t.VisitTaskNum == item.VisitTaskNum && t.SubjectCode == item.SubjectCode).Select(t => t.ArmEnum).Distinct().Count() == 2) + { + if (item.ArmEnum == Arm.DoubleReadingArm1) + { + item.IsJudgeSelect = true; + } + else + { + item.IsJudgeSelect = false; + } + } + + + } + + } + else if (arbitrationRule == ArbitrationRule.Reading) + { + + //阅片期访视号 + var subjectReadingPeriondVisitNumList = subjectCriterionGroup.FirstOrDefault()?.SubjectCriterionReadingPeriodVisitNumList; + + //两个人完成最大得任务号(访视+全局) + var subjectMaxFinishedTaskNum = subjectCriterionGroup.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max(); + + var addReadingPeriodNum = isGlobalReading ? ReadingCommon.TaskNumDic[ReadingCategory.Global] : 0; + + //可能没有配置阅片期 ,或者配置了 没有完成 + var finishedGlobalCount = 0; + + var globalFinishedVisitTaskNumList = new List(); + + //没有配置阅片期 + if (subjectReadingPeriondVisitNumList == null) + { + finishedGlobalCount = 0; + } + { + globalFinishedVisitTaskNumList = subjectCriterionGroup.Where(t => subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum)) + .Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct() + .GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).ToList(); + + finishedGlobalCount = globalFinishedVisitTaskNumList.Count(); + + } + + if (finishedGlobalCount != 0) + { + //最大的完成的全局是否产生裁判 + + var maxFinishedGlobalNum = globalFinishedVisitTaskNumList.Max(); + + var globalMaxTask = subjectCriterionGroup.FirstOrDefault(t => t.VisitTaskNum == maxFinishedGlobalNum); + + // 触发了裁判 + if (globalMaxTask.IsTrigerJudge == true) + { + //最大裁判完成了 + + var maxJudgeVisitTaskNum = maxFinishedGlobalNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge]; + + if (globalMaxTask.JudgeResultTaskId != null) + { + + var maxJudgeArmEnum = subjectCriterionGroup.Where(t => t.Id == globalMaxTask.JudgeResultTaskId).FirstOrDefault().ArmEnum; + + if (item.VisitTaskNum < maxJudgeVisitTaskNum) + { + //触发裁判 + item.IsTrigerJudge = true; + + if (item.ArmEnum == maxJudgeArmEnum) + { + item.IsJudgeSelect = true; + } + //裁判没选择的人设置为false + else + { + item.IsJudgeSelect = false; + } + } + } + else + { + //最大的裁判未完成 + + //找到当前未阅最大裁判之前的已完成的最大裁判任务 + + var finishedGlobalFinishedMaxJudge = subjectCriterionGroup.Where(t => globalFinishedVisitTaskNumList.Contains(t.VisitTaskNum) && t.IsTrigerJudge == true && t.JudgeResultTaskId != null).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault(); + + + //未完成裁判之前 没有已完成的全局裁判 + if (finishedGlobalFinishedMaxJudge == null) + { + if (item.VisitTaskNum < maxJudgeVisitTaskNum) + { + item.IsTrigerJudge = true; + //item.IsJudgeSelect = null; + } + } + else + { + + + var maxFinishedJudgeVisitTaskNum = finishedGlobalFinishedMaxJudge.VisitTaskNum + +ReadingCommon.TaskNumDic[ReadingCategory.Judge]; + + var maxFinishedJudgeArmEnum = subjectCriterionGroup.Where(t => t.Id == finishedGlobalFinishedMaxJudge.JudgeResultTaskId).FirstOrDefault().ArmEnum; + + if (item.VisitTaskNum < maxFinishedJudgeVisitTaskNum) + { + item.IsTrigerJudge = true; + + if (item.ArmEnum == maxFinishedJudgeArmEnum) + { + item.IsJudgeSelect = true; + } + //裁判没选择的人设置为false + else + { + item.IsJudgeSelect = false; + } + } + else if (item.VisitTaskNum > maxFinishedJudgeVisitTaskNum && item.VisitTaskNum < maxJudgeVisitTaskNum) + { + //完成裁判 和未完成裁判之间的 裁判选择标记默认是null + + item.IsTrigerJudge = true; + } + else + { + //在未完成全局裁判之后的访视 未知 都是null + + item.IsTrigerJudge = null; + } + } + + + } + } + else + { + //最大的全局未产生裁判 + + if (item.VisitTaskNum <= maxFinishedGlobalNum) + { + item.IsTrigerJudge = false; + + if (item.ArmEnum == Arm.DoubleReadingArm1) + { + item.IsJudgeSelect = true; + + } + else + { + item.IsJudgeSelect = false; + } + } + } + } + + + } + + } + + } + + downloadInfoList = downloadInfoList.Where(t => t.ReadingCategory == ReadingCategory.Visit).ToList(); + return ResponseOutput.Ok(downloadInfoList); + } + else + { + + var downloadInfo = _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new + { + t.ResearchProgramNo, + + VisitList = t.SubjectVisitList.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id)).Select(sv => new + { + TrialSiteCode = sv.TrialSite.TrialSiteCode, + SubjectCode = sv.Subject.Code, + VisitName = sv.VisitName, + StudyList = sv.StudyList.Select(u => new + { + u.PatientId, + u.StudyTime, + u.StudyCode, + + SeriesList = u.SeriesList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(k => new + { + k.Path + }) + }) + + }), + + NoneDicomStudyList = sv.NoneDicomStudyList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(nd => new + { + nd.Modality, + nd.StudyCode, + nd.ImageDate, + + FileList = nd.NoneDicomFileList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(file => new + { + file.FileName, + file.Path, + file.FileType + }) + }) + }).ToList() + + }).FirstOrDefault(); + + + return ResponseOutput.Ok(downloadInfo); + } + + + + } + + #endregion #region 之前后端下载废弃 diff --git a/IRaCIS.Core.Domain/Allocation/VisitTask.cs b/IRaCIS.Core.Domain/Allocation/VisitTask.cs index 601410610..d64d31d55 100644 --- a/IRaCIS.Core.Domain/Allocation/VisitTask.cs +++ b/IRaCIS.Core.Domain/Allocation/VisitTask.cs @@ -9,6 +9,14 @@ namespace IRaCIS.Core.Domain.Models; public class VisitTask : BaseFullAuditEntity { #region 导航属性 + + //[JsonIgnore] + //public List ReadingNoneDicomMarkList { get; set; } + + [JsonIgnore] + + public List ReadingTaskQuestionMarkList { get; set; } + [JsonIgnore] public List TaskStudyList { get; set; }