Files
irc-netcore-api/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs
T
hang 13397b0bc8
continuous-integration/drone/push Build is passing
返回影像字段
2026-04-30 10:57:15 +08:00

962 lines
44 KiB
C#

using DocumentFormat.OpenXml.EMMA;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Application.Filter;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.Service.Reading.Dto;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Org.BouncyCastle.Asn1.Tsp;
namespace IRaCIS.Core.Application.Services
{
[ApiExplorerSettings(GroupName = "Trial")]
public class SubjectVisitService(IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<Trial> _trialRepository,
IRepository<DicomStudy> _dicomStudyRepository,
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository,
//IRepository<ReadingCustomTag> _readingCustomTagRepository,
IRepository<ReadingTaskQuestionMark> _readingTaskQuestionMark,
IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository,
IRepository<Subject> _subjectRepository,
IRepository<DicomInstance> _dicomInstanceRepository,
IRepository<TaskStudy> _taskStudyRepository,
IRepository<TaskSeries> _taskSeriesRepository,
IRepository<Segmentation> _segmentationRepository,
IRepository<TaskInstance> _taskInstanceRepository,
IServiceProvider _serviceProvider,
IRepository<DicomSeries> _dicomSeriesRepository,
IRepository<ReadingQuestionCriterionTrial> _trialReadingCriterionRepository, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer) : BaseService, ISubjectVisitService
{
[HttpPost]
[TrialGlobalLimit("AfterStopCannNotOpt")]
[UnitOfWork]
//[Authorize(Policy = IRaCISPolicy.CRC)]
public async Task<IResponseOutput<string>> AddOrUpdateSV(SubjectVisitCommand svCommand)
{
var verifyExp1 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.VisitNum == svCommand.VisitNum && t.SubjectId == svCommand.SubjectId,
//---该受试者的访视计划中已经包含一个具有相同访视号的访视。
VerifyMsg = _localizer["Visit_DuplicateVisitNo"]
};
var verifyExp2 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit,
//---该受试者已经有访视设置为末次访视,不允许将当前访视设置为末次访视。
VerifyMsg = _localizer["Visit_LastVisitNoChange"],
IsVerify = svCommand.IsFinalVisit
};
var verifyExp3 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.VisitName == svCommand.VisitName,
//---该受试者的访视计划中已经包含一个具有相同访视名称的访视。
VerifyMsg = _localizer["Visit_DuplicateVisitName"]
};
svCommand.BlindName = "B" + ((int)(svCommand.VisitNum * 10)).ToString("D3");
svCommand.VisitExecuted = svCommand.IsLostVisit ? VisitExecutedEnum.Executed : svCommand.VisitExecuted;
SubjectVisit? dbBeforeEntity = null;
//Add
if (svCommand.Id == null)
{
//设置末次评估后,不允许添加计划外访视
if (svCommand.InPlan == false)
{
if (await _subjectVisitRepository.AnyAsync(t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit))
{
//---设置末次评估后,不允许添加计划外访视。
throw new BusinessValidationFailedException(_localizer["Visit_NoExtraVisitAfterLast"]);
}
if (await _visitTaskRepository.AnyAsync(t => t.SubjectId == svCommand.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum > svCommand.VisitNum && t.SignTime != null && t.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder))
{
//---该受试者后续访视已有任务完成阅片(有序阅片标准),不允许在此添加,如果确实需要,请回退
throw new BusinessValidationFailedException(_localizer["Visit_FinishedTasksNoAdd"]);
}
}
dbBeforeEntity = await _subjectVisitRepository.InsertFromDTOAsync(svCommand, false, verifyExp1, verifyExp2, verifyExp3);
//var cRCClinicalDataIds = await _clinicalDataTrialSetRepository.Where(x => x.TrialId == svCommand.TrialId && x.UploadRole == UploadRole.CRC && x.IsConfirm && x.ClinicalDataLevel == ClinicalLevel.SubjectVisit)
//.Select(x => x.Id).ToListAsync();
//List<ReadingClinicalData> readingClinicals = cRCClinicalDataIds.Select(x => new ReadingClinicalData()
//{
// ClinicalDataTrialSetId = x,
// IsVisit = true,
// SubjectId = svCommand.SubjectId,
// ReadingId = dbBeforeEntity.Id,
// TrialId = svCommand.TrialId
//}).ToList();
//await _readingClinicalDataRepository.AddRangeAsync(readingClinicals);
}
else
{
dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, false, false, verifyExp1, verifyExp2, verifyExp3);
if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.SubmitState == SubmitStateEnum.Submitted)
{
//---当前访视影像提交后,不允许修改PD确认状态。
throw new BusinessValidationFailedException(_localizer["Visit_NoPDStatusChangeAfterSubmission"]);
}
if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.RequestBackState == RequestBackStateEnum.PM_AgressBack)
{
//---当前访视影像提交后,不允许修改PD确认状态。
throw new BusinessValidationFailedException(_localizer["Visit_NoPDStatusChangeAfterSubmission"]);
}
if (svCommand.IsLostVisit)
{
if (await _subjectVisitRepository.AnyAsync(t => t.Id == svCommand.Id && t.SubmitState == SubmitStateEnum.ToSubmit))
{
//---当前访视已经有影像上传,不允许设置为失访。
throw new BusinessValidationFailedException(_localizer["Visit_UploadedNoLost"]);
}
}
dbBeforeEntity = await _subjectVisitRepository.UpdateFromDTOAsync(svCommand, true, false, verifyExp1, verifyExp2, verifyExp3);
}
//更新受试者 访视基准日期 是否入组确认
if (svCommand.SubjectFirstGiveMedicineTime != null && svCommand.IsBaseLine)
{
await _subjectRepository.UpdatePartialFromQueryAsync(svCommand.SubjectId, t => new Subject()
{
FirstGiveMedicineTime = svCommand.SubjectFirstGiveMedicineTime
});
}
await _subjectVisitRepository.SaveChangesAsync();
return ResponseOutput.Ok(dbBeforeEntity.Id.ToString());
}
[HttpPut("{trialId:guid}/{subjectVisitId:guid}/{isUrgent:bool}")]
[TrialGlobalLimit("AfterStopCannNotOpt")]
//[Authorize(Policy = IRaCISPolicy.PM_IQC)]
public async Task<IResponseOutput> SetSubjectVisitUrgent(Guid subjectVisitId, bool isUrgent)
{
await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { IsUrgent = isUrgent }, true);
return ResponseOutput.Ok();
}
[HttpDelete, Route("{trialId:guid}/{id:guid}")]
[TrialGlobalLimit("AfterStopCannNotOpt")]
//[Authorize(Policy = IRaCISPolicy.CRC)]
public async Task<IResponseOutput> DeleteSV(Guid id)
{
if (await _dicomStudyRepository.AnyAsync(t => t.SubjectVisitId == id))
{
//---当前访视已经有影像上传,不允许删除。
return ResponseOutput.NotOk(_localizer["Visit_UploadedNoDelete"]);
}
if (await _subjectVisitRepository.AnyAsync(t => t.Id == id && t.InPlan))
{
//---计划内的访视不允许删除。
return ResponseOutput.NotOk(_localizer["Visit_PlanVisitNoDelete"]);
}
if (await _subjectVisitRepository.AnyAsync(t => t.OutPlanPreviousVisitId == id))
{
//---当前访视已经被设置为另一访视的上一访视,不允许删除。
return ResponseOutput.NotOk(_localizer["Visit_PreviousVisitNoDelete"]);
}
await _subjectVisitRepository.DeleteFromQueryAsync(s => s.Id == id, true);
return ResponseOutput.Ok();
}
/// <summary>
/// 获取访视下的Dicom 检查信息 分所有的, 阅片的 不阅片 isReading : 0 查询所有 1 查询仅仅阅片的
/// </summary>
/// <param name="trialId"></param>
/// <param name="sujectVisitId"></param>
/// <param name="isReading"></param>
/// <param name="visitTaskId"></param>
/// <returns></returns>
[HttpGet, Route("{trialId:guid}/{sujectVisitId:guid}/{isReading}")]
[AllowAnonymous]
public async Task<List<VisitStudyDTO>> GetVisitStudyList(Guid trialId, Guid sujectVisitId, int isReading, [FromQuery] Guid? visitTaskId)
{
var qcAuditState = await _subjectVisitRepository.Where(s => s.Id == sujectVisitId).Select(t => t.AuditState).FirstOrDefaultAsync();
//质控通过以后,过滤删除的 质控之前的不过滤
var isQCFinished = qcAuditState == AuditStateEnum.QCPassed;
var isImageFilter = false;
var criterionModalitys = "";
var criterionType = CriterionType.NoCriterion;
var isVisitTask = false;
bool? isImageSegmentLabel = null;
if (visitTaskId != null && visitTaskId != Guid.Empty)
{
isVisitTask = true;
var info = await _visitTaskRepository.Where(t => t.Id == visitTaskId)
.Select(t => new { t.TrialReadingCriterion.IsImageFilter, t.TrialReadingCriterion.CriterionModalitys, t.TrialReadingCriterion.CriterionType }).FirstNotNullAsync();
isImageFilter = info.IsImageFilter;
criterionModalitys = info.CriterionModalitys;
criterionType = info.CriterionType;
if (criterionType == CriterionType.OCT || criterionType == CriterionType.IVUS)
{
isImageSegmentLabel = true;
}
}
var studyList = await _dicomStudyRepository.Where(t => t.TrialId == trialId && t.SubjectVisitId == sujectVisitId).IgnoreQueryFilters()
.Where(t => isImageFilter ? ("|" + criterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|") : true)
.WhereIf(isReading == 1 || isQCFinished, s => s.IsDeleted == false)
//预览靶段标注上传的影像 影像后处理 上传了新的影像 还要原始dsa
.WhereIf(isImageSegmentLabel == true && isVisitTask && (criterionType == CriterionType.OCT || criterionType == CriterionType.IVUS), t => t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" || t.ModalityForEdit == "OCT")
.Select(k => new VisitStudyDTO()
{
InstanceCount = k.InstanceCount,
Modalities = k.Modalities,
//SeriesCount = k.SeriesCount,
StudyCode = k.StudyCode,
StudyId = k.Id
}).ToListAsync();
var studyIds = studyList.Select(t => t.StudyId).ToList();
var instanceList = await _dicomInstanceRepository.Where(t => studyIds.Contains(t.StudyId)).IgnoreQueryFilters()
.WhereIf(isReading == 1, s => s.IsReading && s.IsDeleted == false)
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames, t.HtmlPath, t.IsReading, t.IsDeleted, t.FileSize, t.ImagePositionPatient, t.ImageOrientationPatient }).ToListAsync();
foreach (var t in studyList)
{
t.SeriesList = await _dicomSeriesRepository.Where(s => s.StudyId == t.StudyId).IgnoreQueryFilters()
.WhereIf(isReading == 1, s => s.IsReading && s.IsDeleted == false)
.WhereIf(isQCFinished, t => t.IsDeleted == false)
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
t.SeriesList.ForEach(series =>
{
var instances = instanceList.Where(x => x.SeriesId == series.Id);
// ⭐ DICOM 空间排序(带兜底)
var sorted = DicomSortHelper.SortSlices(
instances,
x => x.ImagePositionPatient,
x => x.ImageOrientationPatient,
x => x.InstanceNumber
);
series.InstanceInfoList = sorted.Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
IsReading = k.IsReading,
IsDeleted = k.IsDeleted,
FileSize = k.FileSize,
}).ToList();
series.InstanceCount = series.InstanceInfoList.Count();
}
);
//设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计
//t.SeriesCount = t.SeriesList.Count;
t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceInfoList).Count();
}
//预览靶段标注上传的影像
if (isImageSegmentLabel == true && isVisitTask)
{
//var taskStudyList = await _taskStudyRepository.Where(t => t.SubjectVisitId == sujectVisitId)
// .WhereIf(isImageFilter, t => ("|" + criterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|"))
// .ProjectTo<VisitStudyDTO>(_mapper.ConfigurationProvider).ToListAsync();
//foreach (var study in taskStudyList)
//{
// study.SeriesList = study.SeriesList.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ToList();
// study.InstanceCount = study.SeriesList.SelectMany(t => t.InstanceInfoList).Count();
//}
//studyList.AddRange(taskStudyList);
}
return studyList.Where(x => x.SeriesList.Count() > 0).ToList();
}
/// <summary>
/// 获取Series信息
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<DicomSeriesDTO> GetDicomSeriesInfo(GetDicomSeriesInfoInDto inDto)
{
DicomSeriesDTO? series = await _dicomSeriesRepository.Where(s => s.Id == inDto.SeriesId).ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
if (series != null)
{
var instanceList = await _dicomInstanceRepository.Where(t => t.SeriesId == inDto.SeriesId)
.Select(t => new { t.SeriesId, t.StudyId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames, t.WindowCenter, t.WindowWidth, t.HtmlPath, t.SliceLocation, t.FileSize }).ToListAsync();
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
FileSize = k.FileSize
}).ToList();
}
else
{
series = await _taskSeriesRepository.Where(s => s.Id == inDto.SeriesId).ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).FirstNotNullAsync();
var instanceList = await _taskInstanceRepository.Where(t => t.SeriesId == inDto.SeriesId)
.Select(t => new { t.SeriesId, t.StudyId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames, t.WindowCenter, t.WindowWidth, t.HtmlPath, t.SliceLocation, t.FileSize }).ToListAsync();
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
FileSize = k.FileSize
}).ToList();
}
return series;
}
/// <summary>
/// 获取PTAndCtSeries
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<GetPTAndCtSeriesOutDto>> GetPTAndCtSeries(GetPTAndCtSeriesInDto inDto)
{
var pastResultList = await _serviceProvider.GetService<IReadingImageTaskService>().GetReadingPastResultList(new GetReadingPastResultListInDto()
{
VisitTaskId = inDto.VisitTaskId
});
var visitTaskIds = pastResultList.Select(x => x.VisitTaskId).ToList();
var visitSeriesList = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId))
.Where(x => x.PTSeriesId != null && x.CTSeriesId != null)
.Select(x => new
{
x.PTSeriesId,
x.CTSeriesId,
x.VisitTaskId,
x.OtherStudyId,
x.VisitTask.SourceSubjectVisitId,
x.VisitTask.SourceSubjectVisit.IsBaseLine,
x.VisitTask.VisitTaskNum,
x.VisitTask.TaskBlindName,
x.UpdateTime,
}).ToListAsync();
List<GetPTAndCtSeriesOutDto> result = new List<GetPTAndCtSeriesOutDto>();
List<Guid> ctseriesIds = visitSeriesList.Select(x => x.CTSeriesId.Value).ToList();
List<Guid> ptseriesIds = visitSeriesList.Select(x => x.PTSeriesId.Value).ToList();
foreach (var item in visitTaskIds)
{
var visitSeries = visitSeriesList.Where(x => x.VisitTaskId == item).OrderByDescending(x => x.UpdateTime).FirstOrDefault();
if (visitSeries != null)
{
result.Add(new GetPTAndCtSeriesOutDto()
{
PTSeriesId = visitSeries.PTSeriesId!.Value,
CTSeriesId = visitSeries.CTSeriesId!.Value,
TaskBlindName = visitSeries.TaskBlindName,
VisitTaskId = item,
SubjectVisitId = visitSeries.SourceSubjectVisitId!.Value,
IsBaseLineTask = visitSeries.IsBaseLine,
});
}
}
List<DicomSeriesDTO> ctseriesLists = await _dicomSeriesRepository.Where(s => ctseriesIds.Contains(s.Id)).ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
List<DicomSeriesDTO> ptseriesLists = await _dicomSeriesRepository.Where(s => ptseriesIds.Contains(s.Id)).ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
var seriesIds = ctseriesIds.Union(ptseriesIds).ToList(); //并集
var instanceList = await _dicomInstanceRepository.Where(t => seriesIds.Contains(t.SeriesId))
.Select(t => new { t.SeriesId, t.StudyId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames, t.WindowCenter, t.WindowWidth, t.HtmlPath, t.FileSize }).ToListAsync();
var studyIds = instanceList.Select(x => x.StudyId).Distinct().ToList();
var studyList = await _dicomStudyRepository.Where(t => studyIds.Contains(t.Id))
.Select(k => new StudyInfo()
{
InstanceCount = k.InstanceCount,
Modalities = k.Modalities,
SeriesCount = k.SeriesCount,
StudyCode = k.StudyCode,
StudyId = k.Id,
SubjectVisitId = k.SubjectVisitId,
}).ToListAsync();
AddinstanceList(ctseriesLists);
AddinstanceList(ptseriesLists);
void AddinstanceList(List<DicomSeriesDTO> list)
{
list.ForEach(series =>
{
series.InstanceInfoList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
FileSize = k.FileSize
}).ToList();
//series.WindowWidth = instanceList.FirstOrDefault()!.WindowWidth;
//series.WindowCenter = instanceList.FirstOrDefault()!.WindowCenter;
});
}
foreach (var item in result)
{
item.StudyId = ptseriesLists.Where(x => x.Id == item.PTSeriesId).Select(x => x.StudyId).FirstOrDefault();
item.StudyInfoList = studyList.Where(x => x.SubjectVisitId == item.SubjectVisitId).ToList();
foreach (var study in item.StudyInfoList)
{
study.PTSeriesList = ptseriesLists.Where(x => x.SubjectVisitId == item.SubjectVisitId).ToList();
study.CTSeriesList = ctseriesLists.Where(x => x.SubjectVisitId == item.SubjectVisitId).ToList();
}
}
return result;
}
/// <summary>
/// 获取访视下的Dicom 检查信息 分所有的, 阅片的 不阅片 isReading : 0 查询所有 1 查询仅仅阅片的
/// </summary>
/// <param name="indto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<VisitStudyDTO>> GetReadingVisitStudyList(GetReadingVisitStudyListIndto indto)
{
var studyList = new List<VisitStudyDTO>();
var taskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).Select(t => new
{
t.TrialReadingCriterionId,
t.TrialReadingCriterion.CriterionType,
t.TrialReadingCriterion.IsImageFilter,
t.TrialReadingCriterion.CriterionModalitys,
t.ReadingTaskState,
//是否有了后处理的dicom检查
IsHaveTaskStudy = t.TaskStudyList.Any(),
//是否有了后处理的NoneDicom检查
IsHaveTaskNoneDicomStudyFile = t.SourceSubjectVisit.NoneDicomStudyList.SelectMany(c => c.TaskNoneDicomFileList.Where(t => t.VisitTaskId == indto.VisitTaskId)).Any(),
//是否有了标注的dicom检查
//IsMarkDicomStudy = t.SourceSubjectVisit.TaskStudyList.Any(),
//是否有了标注的NoneDicom检查
IsMarkNoneDicomStudy = t.SourceSubjectVisit.NoneDicomStudyList.SelectMany(c => c.ImageLabelNoneDicomFileList).Any()
}).FirstNotNullAsync();
#region dicom 检查查询
//如果是手动生成的标准,需要过滤检查和序列数据
var isManualGenerate = await _trialReadingCriterionRepository.AnyAsync(t => t.Id == taskInfo.TrialReadingCriterionId && t.IsAutoCreate == false);
var dicomStudyList = await _dicomStudyRepository.Where(t => t.TrialId == indto.TrialId && t.SubjectVisitId == indto.SujectVisitId)
.WhereIf(taskInfo.IsImageFilter == true, t => ("|" + taskInfo.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|"))
.WhereIf(isManualGenerate, t => t.SubjectCriteriaEvaluationVisitStudyFilterList.Any(t => t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.IsConfirmed && t.IsReading))
//影像后处理 上传了新的影像 还要原始dsa
.WhereIf(taskInfo.IsHaveTaskStudy && taskInfo.CriterionType == CriterionType.OCT,
t => t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA")
.WhereIf(taskInfo.CriterionType == CriterionType.IVUS,
t => t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" || t.ModalityForEdit == "IVUS")
.WhereIf(taskInfo.IsHaveTaskStudy == false && taskInfo.CriterionType == CriterionType.OCT,
t => t.ModalityForEdit == "XA" || t.ModalityForEdit == "DSA" || t.ModalityForEdit == "OCT")
//其他 不应该看原始影像
.WhereIf(taskInfo.IsHaveTaskStudy && (taskInfo.CriterionType != CriterionType.OCT && taskInfo.CriterionType != CriterionType.IVUS), t => false)
.Select(k => new VisitStudyDTO()
{
InstanceCount = k.InstanceCount,
StudyName = k.StudyName,
Modalities = k.Modalities,
//SeriesCount = k.SeriesCount,
StudyCode = k.StudyCode,
StudyId = k.Id,
BodyPartForEdit = k.BodyPartForEdit,
BodyPartForEditOther = k.BodyPartForEditOther,
PatientSex=k.PatientSex,
PatientWeight=k.PatientWeight,
RadionuclideTotalDose=k.RadionuclideTotalDose,
RadionuclideHalfLife=k.RadionuclideHalfLife,
RadiopharmaceuticalStartTime=k.RadiopharmaceuticalStartTime,
AcquisitionTime= k.AcquisitionTime,
}).ToListAsync();
var studyIds = dicomStudyList.Select(t => t.StudyId).ToList();
if (studyIds.Count > 0)
{
var instanceList = await _dicomInstanceRepository.Where(t => studyIds.Contains(t.StudyId) && t.IsReading)
.Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames, t.WindowCenter, t.WindowWidth, t.HtmlPath, t.IsReading, t.FileSize, t.ImagePositionPatient, t.ImageOrientationPatient,t.PhotometricInterpretation }).ToListAsync();
List<DicomSeriesDTO> seriesLists = await _dicomSeriesRepository.Where(s => studyIds.Contains(s.StudyId))
.WhereIf(isManualGenerate == false, t => t.IsReading)
.WhereIf(isManualGenerate, t => t.SubjectCriteriaEvaluationVisitStudyFilterList.Any(t => t.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId && t.IsConfirmed && t.IsReading))
.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
foreach (var t in dicomStudyList)
{
t.SeriesList = seriesLists.Where(s => s.StudyId == t.StudyId).OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ToList();
t.SeriesList.ForEach(series =>
{
var instances = instanceList.Where(x => x.SeriesId == series.Id);
// ⭐ DICOM 空间排序(带兜底)
var sorted = DicomSortHelper.SortSlices(
instances,
x => x.ImagePositionPatient,
x => x.ImageOrientationPatient,
x => x.InstanceNumber
);
series.InstanceInfoList = sorted.Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
FileSize = k.FileSize,
PhotometricInterpretation=k.PhotometricInterpretation
}).ToList();
// 设置阅片 不阅片,数字要要重新统计
series.InstanceCount = series.InstanceInfoList.Count;
//series.WindowWidth = instanceList.FirstOrDefault()!.WindowWidth;
//series.WindowCenter = instanceList.FirstOrDefault()!.WindowCenter;
});
//设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计
//t.SeriesCount = t.SeriesList.Count;
t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceInfoList).Count();
}
studyList.AddRange(dicomStudyList);
}
//影像后处理 上传了新的影像
if (taskInfo.IsHaveTaskStudy /*|| taskInfo.IsMarkDicomStudy*/ || taskInfo.IsMarkNoneDicomStudy)
{
var taskStudyList = await _taskStudyRepository.Where(t => t.TrialId == indto.TrialId)
.WhereIf(taskInfo.IsHaveTaskStudy, t => t.VisitTaskId == indto.VisitTaskId)
.WhereIf(taskInfo.IsHaveTaskStudy == false && (/*taskInfo.IsMarkDicomStudy ||*/ taskInfo.IsMarkNoneDicomStudy), t => t.SubjectVisitId == indto.SujectVisitId)
.WhereIf(taskInfo.IsImageFilter == true, t => ("|" + taskInfo.CriterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|"))
.ProjectTo<VisitStudyDTO>(_mapper.ConfigurationProvider).ToListAsync();
foreach (var study in taskStudyList)
{
study.SeriesList = study.SeriesList.OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).ToList();
study.SeriesList.ForEach(series =>
{
// ⭐ DICOM 空间排序(带兜底)
var sorted = DicomSortHelper.SortSlices(
series.InstanceInfoList,
x => x.ImagePositionPatient,
x => x.ImageOrientationPatient,
x => x.InstanceNumber
);
series.InstanceInfoList = sorted;
});
study.InstanceCount = study.SeriesList.SelectMany(t => t.InstanceInfoList).Count();
}
studyList.AddRange(taskStudyList);
}
#endregion
#region dicom 关键序列处理
//已经签名的任务,加关键序列
if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned)
{
var rowInfoList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).OrderBy(x => x.ReadingQuestionTrial.ShowOrder).ThenBy(x => x.RowIndex).Select(x => new StudyInstanceInfo()
{
ShowOrder = x.ReadingQuestionTrial.ShowOrder,
RowIndex = x.RowIndex,
SeriesId = x.SeriesId,
StudyId = x.StudyId,
InstanceId = x.InstanceId,
NumberOfFrames = x.NumberOfFrames,
CreateTime = x.CreateTime,
}).ToListAsync();
// 这个表的数据已经移动到 _readingTaskQuestionMark 了
//var customoList = await _readingCustomTagRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).Select(x => new StudyInstanceInfo()
//{
// ShowOrder = 0,
// RowIndex = 0m,
// SeriesId = x.SeriesId,
// StudyId = x.StudyId,
// InstanceId = x.InstanceId,
// NumberOfFrames = x.NumberOfFrames,
// CreateTime = x.CreateTime,
//}).ToListAsync();
//rowInfoList.AddRange(customoList);
var questionMarkList = await _readingTaskQuestionMark.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).Select(x => new StudyInstanceInfo()
{
ShowOrder = x.ReadingQuestionTrial == null ? 0 : x.ReadingQuestionTrial.ShowOrder,
RowIndex = 0m,
SeriesId = x.SeriesId,
StudyId = x.StudyId,
InstanceId = x.InstanceId,
NumberOfFrames = x.NumberOfFrames,
CreateTime = x.CreateTime,
}).ToListAsync();
rowInfoList.AddRange(questionMarkList);
var thisStudyIds = rowInfoList.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.StudyId).Distinct().ToList();
var thisSeriesIdIds = rowInfoList.Where(x => x.SeriesId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.SeriesId).Distinct().ToList();
if (rowInfoList.Count > 0)
{
var thisVisitTaskStudy = await _dicomStudyRepository.Where(t => thisStudyIds.Contains(t.Id)).Select(k => new VisitStudyDTO()
{
InstanceCount = k.InstanceCount,
//SeriesCount = k.SeriesCount,
StudyId = k.Id,
IsCriticalSequence = true,
}).FirstOrDefaultAsync();
if (thisVisitTaskStudy != null)
{
thisVisitTaskStudy.StudyId = default(Guid);
var item = await _dicomSeriesRepository.Where(s => thisSeriesIdIds.Contains(s.Id)).OrderBy(s => s.SeriesNumber).
ThenBy(s => s.SeriesTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
if (item != null)
{
item.SeriesInstanceUid = string.Empty;
var markInstanceIdList = rowInfoList.Where(y => y.InstanceId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(y => y.InstanceId!.Value).Distinct().ToList();
item.InstanceInfoList = await _dicomInstanceRepository.Where(t => markInstanceIdList.Contains(t.Id)).OrderBy(s => s.DicomSerie.SeriesNumber).ThenBy(s => s.DicomSerie.SeriesTime).ThenBy(x => x.InstanceTime).Select(k =>
new InstanceBasicInfo()
{
Id = k.Id,
NumberOfFrames = k.NumberOfFrames,
HtmlPath = k.HtmlPath,
Path = k.Path,
InstanceNumber = k.InstanceNumber,
StudyId = k.StudyId,
SeriesId = k.SeriesId,
FileSize = k.FileSize,
PhotometricInterpretation=k.PhotometricInterpretation,
}).ToListAsync();
item.InstanceInfoList.ForEach(x =>
{
x.RowDate = rowInfoList.FirstOrDefault(y => y.InstanceId == x.Id)?.CreateTime ?? DateTime.Now;
var keyFramesList = rowInfoList.Where(y => y.InstanceId == x.Id && y.NumberOfFrames != 0 && y.NumberOfFrames != null).Select(y => y.NumberOfFrames).Distinct().ToList();
if (keyFramesList.Count() > 0)
{
x.KeyFramesList = keyFramesList;
}
var item = rowInfoList.FirstOrDefault(y => y.InstanceId == x.Id);
if (item != null)
{
x.ShowOrder = item.ShowOrder;
x.RowIndex = item.RowIndex;
}
});
item.InstanceInfoList = item.InstanceInfoList.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).ThenBy(x => x.RowDate).ToList();
item.InstanceCount = item.InstanceInfoList.Count;
item.Description = "Key Series";
var modalityList = await _dicomSeriesRepository.Where(s => thisSeriesIdIds.Contains(s.Id)).OrderBy(s => s.SeriesNumber).ThenBy(s => s.SeriesTime).Select(x => x.Modality).Distinct().ToListAsync(); ;
item.Modality = string.Join(",", modalityList);
thisVisitTaskStudy.SeriesList.Add(item);
//thisVisitTaskStudy.SeriesCount = thisVisitTaskStudy.SeriesList.Count;
}
studyList.Insert(0, thisVisitTaskStudy);
}
}
studyList.ForEach(x =>
{
x.SeriesList.ForEach(y =>
{
y.IsBeMark = rowInfoList.Any(z => z.SeriesId == y.Id);
});
});
}
#endregion
#region Dicom 检查查询
var noDicomList = await _noneDicomStudyRepository.Where(x => x.TrialId == indto.TrialId && x.SubjectVisitId == indto.SujectVisitId)
//iVUS 存在空检查
.WhereIf(taskInfo.CriterionType != CriterionType.IVUS && taskInfo.CriterionType != CriterionType.OCT, x => x.NoneDicomFileList.Any(t => !t.FileType.Contains(StaticData.FileType.Zip)))
.Where(t => t.IsReading)
.WhereIf(taskInfo.IsImageFilter, t => ("|" + taskInfo.CriterionModalitys + "|").Contains("|" + t.Modality + "|"))
.ToListAsync();
List<VisitStudyDTO> noDicomStudyList = noDicomList.Select(x => new VisitStudyDTO()
{
InstanceCount = x.FileCount,
StudyId = x.Id,
StudyName = x.StudyName,
Modalities = x.Modality,
//SeriesCount = 1,
StudyCode = x.StudyCode,
IsDicom = false,
BodyPartForEdit = x.BodyPart,
BodyPartForEditOther = x.BodyPartForEditOther
}).ToList();
//var isExistTaskNoneDicomFile = _noneDicomStudyFileRepository.Any(t => t.VisitTaskId == indto.VisitTaskId);
foreach (var item in noDicomStudyList)
{
var nodicom = noDicomList.Where(x => x.Id == item.StudyId).First();
var instanceCount = await _noneDicomStudyFileRepository.Where(t => t.IsReading)
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile == false && taskInfo.IsMarkNoneDicomStudy == true, x => x.ImageLabelNoneDicomStudyId == item.StudyId)
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile && taskInfo.IsMarkNoneDicomStudy, x => x.OriginNoneDicomStudyId == item.StudyId && x.VisitTaskId == indto.VisitTaskId)
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile == false && taskInfo.IsMarkNoneDicomStudy == false, x => x.NoneDicomStudyId == item.StudyId)
.Where(t => !t.FileType.Contains(StaticData.FileType.Zip))
.CountAsync();
if (instanceCount == 0)
{
item.SeriesList = new List<DicomSeriesDTO>();
//item.SeriesCount = 0;
}
else
{
item.SeriesList = new List<DicomSeriesDTO>()
{
new DicomSeriesDTO (){
IsDicom=false,
Id=item.StudyId,
InstanceCount=instanceCount,
Modality=item.Modalities,
StudyId=item.StudyId,
TrialId=nodicom.TrialId,
SubjectVisitId=nodicom.SubjectVisitId,
SubjectId=nodicom.SubjectId,
SeriesNumber=1,
NoneDicomFileFirstFile=await _noneDicomStudyFileRepository
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile == false && taskInfo.IsMarkNoneDicomStudy == true, x => x.ImageLabelNoneDicomStudyId == item.StudyId)
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile && taskInfo.IsMarkNoneDicomStudy, x => x.OriginNoneDicomStudyId == item.StudyId && x.VisitTaskId==indto. VisitTaskId)
.WhereIf(taskInfo.IsHaveTaskNoneDicomStudyFile == false && taskInfo.IsMarkNoneDicomStudy == false, x => x.NoneDicomStudyId == item.StudyId)
.Where(x=> !x.FileType.Contains(StaticData.FileType.Zip)).Select(x=>x.Path).FirstOrDefaultAsync(),
}
};
}
}
studyList.AddRange(noDicomStudyList);
#endregion
#region 过滤空序列得检查 过滤空instance得序列
foreach (var study in studyList)
{
study.SeriesList = study.SeriesList.Where(t => t.InstanceCount > 0).ToList();
}
var result = studyList.Where(x => x.SeriesCount > 0).ToList();
#endregion
#region 处理分割
var segmentatSeriesList =await _segmentationRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.SEGUrl != string.Empty).Select(x => x.SeriesId).ToListAsync();
result.ForEach(x =>
{
x.SeriesList.ForEach(y =>
{
y.IsBeSegment = segmentatSeriesList.Any(z => z == y.Id);
});
});
#endregion
return result;
}
/// <summary>
/// 设置受试者访视已执行 也就是将studyUploaded状态置为true 为了那些没有影像 人工设置准备
/// </summary>
/// <param name="subjectVisitId"></param>
/// <returns></returns>
[HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
[TrialGlobalLimit("AfterStopCannNotOpt")]
[Obsolete]
public async Task<IResponseOutput> SetSVExecuted(Guid subjectVisitId)
{
await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Executed }, true);
return ResponseOutput.Ok();
}
}
}