影像汇总页面增加
continuous-integration/drone/push Build is passing Details

Test_HIR_Net8
hang 2025-08-26 10:43:55 +08:00
parent 25425eab93
commit b4e2af4c63
3 changed files with 584 additions and 0 deletions

View File

@ -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<Guid> 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<decimal> 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<TrialKeyPicturePath> ReadingNoneDicomMarkPathList { get; set; }
public List<TrialKeyPicturePath> QuestionMarkPictureList { get; set; }
public List<TrialKeyPicturePath> 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";
}
}

View File

@ -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<IResponseOutput<PageOutput<TrialVisitImageStatView>>> 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);
}
/// <summary>
/// 获取项目影像统计有影像的subject 数量 访视数量
/// </summary>
/// <param name="trialId"></param>
/// <returns></returns>
public async Task<IResponseOutput<TrialImageStatInfo>> 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 });
}
/// <summary>
/// 批量勾选访视 进行下载
/// </summary>
/// <param name="inCommand"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> 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<decimal>();
//没有配置阅片期
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 之前后端下载废弃

View File

@ -9,6 +9,14 @@ namespace IRaCIS.Core.Domain.Models;
public class VisitTask : BaseFullAuditEntity
{
#region 导航属性
//[JsonIgnore]
//public List<ReadingNoneDicomMark> ReadingNoneDicomMarkList { get; set; }
[JsonIgnore]
public List<ReadingTaskQuestionMark> ReadingTaskQuestionMarkList { get; set; }
[JsonIgnore]
public List<TaskStudy> TaskStudyList { get; set; }