EI-Image-Viewer-Api/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs

555 lines
24 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using IRaCIS.Core.Application.Filter;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Application.Auth;
using IRaCIS.Core.Infra.EFCore.Common;
using MassTransit;
namespace IRaCIS.Core.Application.Services
{
[ApiExplorerSettings(GroupName = "Trial")]
public class SubjectVisitService : BaseService, ISubjectVisitService
{
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
private readonly IRepository<ClinicalDataTrialSet> _clinicalDataTrialSetRepository;
private readonly IRepository<ReadingClinicalData> _readingClinicalDataRepository;
private readonly IRepository<ReadModule> _readModuleRepository;
private readonly IRepository<Trial> _trialRepository;
private readonly IRepository<ReadingPeriodSet> _readingPeriodSetRepository;
private readonly IRepository<NoneDicomStudy> _noneDicomStudyRepository;
private readonly IRepository<DicomInstance> _dicomInstanceRepository;
private readonly IRepository<VisitTask> _visitTaskRepository;
private readonly IRepository<ReadingTableAnswerRowInfo> _readingTableAnswerRowInfoRepository;
private readonly IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository;
private readonly IRepository<ReadingPeriodPlan> _readingPeriodPlanRepository;
private readonly IRepository<Subject> _subjectRepository;
public SubjectVisitService(IRepository<SubjectVisit> subjectVisitRepository,
IRepository<ClinicalDataTrialSet> clinicalDataTrialSetRepository,
IRepository<ReadingClinicalData> readingClinicalDataRepository,
IRepository<ReadModule> readModuleRepository,
IRepository<Trial> trialRepository,
IRepository<ReadingPeriodSet> readingPeriodSetRepository,
IRepository<NoneDicomStudy> noneDicomStudyRepository,
IRepository<DicomInstance> dicomInstanceRepository,
IRepository<VisitTask> visitTaskRepository,
IRepository<ReadingTableAnswerRowInfo> readingTableAnswerRowInfoRepository,
IRepository<NoneDicomStudyFile> noneDicomStudyFileRepository,
IRepository<ReadingPeriodPlan> readingPeriodPlanRepository,
IRepository<Subject> subjectRepository)
{
_subjectVisitRepository = subjectVisitRepository;
this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository;
this._readingClinicalDataRepository = readingClinicalDataRepository;
this._readModuleRepository = readModuleRepository;
this._trialRepository = trialRepository;
this._readingPeriodSetRepository = readingPeriodSetRepository;
this._noneDicomStudyRepository = noneDicomStudyRepository;
this._dicomInstanceRepository = dicomInstanceRepository;
this._visitTaskRepository = visitTaskRepository;
this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository;
this._noneDicomStudyFileRepository = noneDicomStudyFileRepository;
this._readingPeriodPlanRepository = readingPeriodPlanRepository;
_subjectRepository = subjectRepository;
}
[HttpPost]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
[UnitOfWork]
public async Task<IResponseOutput<string>> AddOrUpdateSV(SubjectVisitCommand svCommand)
{
var verifyExp1 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.VisitNum == svCommand.VisitNum && t.SubjectId == svCommand.SubjectId,
VerifyMsg = "该患者的检查批次计划中已经包含一个具有相同检查批次号的检查批次。"
};
var verifyExp2 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.IsFinalVisit,
VerifyMsg = "该患者已经有检查批次设置为末次检查批次,不允许将当前检查批次设置为末次检查批次。",
IsVerify = svCommand.IsFinalVisit
};
var verifyExp3 = new EntityVerifyExp<SubjectVisit>()
{
VerifyExp = t => t.SubjectId == svCommand.SubjectId && t.VisitName == svCommand.VisitName,
VerifyMsg = "该患者的检查批次计划中已经包含一个具有相同检查批次名称的检查批次。"
};
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("设置末次评估后,不允许添加计划外检查批次。");
}
if (await _repository.AnyAsync<VisitTask>(t => t.SubjectId == svCommand.SubjectId && t.TaskState == TaskState.Effect && t.VisitTaskNum > svCommand.VisitNum && t.SignTime != null && t.TrialReadingCriterion.IsReadingTaskViewInOrder))
{
throw new BusinessValidationFailedException("该患者后续检查批次已有任务完成阅片(有序阅片标准),不允许在此添加,如果确实需要,请回退");
}
}
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)
{
throw new BusinessValidationFailedException("当前检查批次影像提交后不允许修改PD确认状态。");
}
if (svCommand.PDState != dbBeforeEntity.PDState && dbBeforeEntity.RequestBackState == RequestBackStateEnum.PM_AgressBack)
{
throw new BusinessValidationFailedException("当前检查批次为回退的检查批次不允许修改PD确认状态。");
}
if (svCommand.IsLostVisit)
{
if (await _subjectVisitRepository.AnyAsync(t => t.Id == svCommand.Id && t.SubmitState == SubmitStateEnum.ToSubmit))
{
throw new BusinessValidationFailedException("当前检查批次已经有有影像上传,不允许设置为失访。");
}
}
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}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
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}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
public async Task<IResponseOutput> DeleteSV(Guid id)
{
if (await _repository.AnyAsync<DicomStudy>(t => t.SubjectVisitId == id))
{
return ResponseOutput.NotOk("当前检查批次已经有影像上传,不允许删除。");
}
if (await _subjectVisitRepository.AnyAsync(t => t.Id == id && t.InPlan))
{
return ResponseOutput.NotOk("计划内的检查批次不允许删除。");
}
if (await _subjectVisitRepository.AnyAsync(t => t.OutPlanPreviousVisitId == id))
{
return ResponseOutput.NotOk("当前检查批次已经被设置为另一检查批次的上一检查批次,不允许删除。");
}
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>
/// <returns></returns>
[HttpGet, Route("{trialId:guid}/{sujectVisitId:guid}/{isReading}")]
[AllowAnonymous]
public async Task<List<VisitStudyDTO>> GetVisitStudyList(Guid trialId, Guid sujectVisitId, int isReading)
{
var studyList = await _repository.Where<DicomStudy>(t => t.TrialId == trialId && t.SubjectVisitId == sujectVisitId).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 _repository.Where<DicomInstance>(t => studyIds.Contains(t.StudyId))
.Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames }).ToListAsync();
foreach (var t in studyList)
{
t.SeriesList = await _repository.Where<DicomSeries>(s => s.StudyId == t.StudyId)
.WhereIf(isReading == 1, s => s.IsReading).OrderBy(s => s.SeriesNumber).
ThenBy(s => s.SeriesTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
t.SeriesList.ForEach(series =>
{
series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList();
//series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList();
//处理多帧
series.InstancePathList = instanceList.OrderBy(t => t.InstanceNumber).Where(s => s.SeriesId == series.Id)
.SelectMany(u =>
{
if (u.NumberOfFrames > 1)
{
var pathList = new List<string>();
for (int i = 1; i <= u.NumberOfFrames; i++)
{
pathList.Add(u.Path + "?frame=" + (i - 1));
}
return pathList;
}
else
{
return new List<string> { u.Path };
}
})
.ToList();
}
);
//设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计
t.SeriesCount = t.SeriesList.Count();
t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count();
}
return studyList;
//return ResponseOutput.Ok(studyList.Where(t => t.SeriesList.Count > 0).ToList());
}
/// <summary>
/// 获取检查批次下的Dicom 检查信息 分所有的, 阅片的 不阅片 isReading : 0 查询所有 1 查询仅仅阅片的
/// </summary>
/// <param name="indto"></param>
/// <returns></returns>
[HttpPost]
public async Task<List<VisitStudyDTO>> GetReadingVisitStudyList(GetReadingVisitStudyListIndto indto)
{
var result = new List<VisitStudyDTO>();
var thisRowinfo = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId && x.StudyId != null).OrderBy(x => x.ReadingQuestionTrial.ShowOrder).ThenBy(x => x.RowIndex).Select(x => new
{
x.ReadingQuestionTrial.ShowOrder,
x.RowIndex,
x.SeriesId,
x.StudyId,
x.InstanceId,
}).ToListAsync();
var taskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync();
if (taskInfo.ReadingTaskState == ReadingTaskState.HaveSigned)
{
var thisStudyIds = thisRowinfo.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.StudyId).Distinct().ToList();
var thisSeriesIdIds = thisRowinfo.Where(x => x.SeriesId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(x => x.SeriesId).Distinct().ToList();
if (thisRowinfo.Count > 0)
{
var thisVisitTaskStudy = await _repository.Where<DicomStudy>(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 _repository.Where<DicomSeries>(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;
item.InstanceList = thisRowinfo.Where(y => y.InstanceId != null).OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).Select(y => y.InstanceId.Value).Distinct().ToList();
var tempInstanceList = await _repository.Where<DicomInstance>(t => item.InstanceList.Contains(t.Id)).OrderBy(t => t.InstanceNumber)
.Select(t => new TempInstance
{
Id = t.Id,
Path = t.Path,
NumberOfFrames = t.NumberOfFrames,
InstanceNumber = t.InstanceNumber
}).ToListAsync();
tempInstanceList.ForEach(x =>
{
var item = thisRowinfo.FirstOrDefault(y => y.InstanceId == x.Id);
if (item != null)
{
x.ShowOrder = item.ShowOrder;
x.RowIndex = item.RowIndex;
}
});
item.InstancePathList = tempInstanceList.OrderBy(x => x.ShowOrder).ThenBy(x => x.RowIndex).SelectMany(u =>
{
if (u.NumberOfFrames > 1)
{
var pathList = new List<string>();
for (int i = 1; i <= u.NumberOfFrames; i++)
{
pathList.Add(u.Path + "?frame=" + (i - 1));
}
return pathList;
}
else
{
return new List<string> { u.Path };
}
})
.ToList();
item.InstanceCount = item.InstanceList.Count();
thisVisitTaskStudy.SeriesList.Add(item);
thisVisitTaskStudy.SeriesCount = thisVisitTaskStudy.SeriesList.Count();
}
result.Add(thisVisitTaskStudy);
}
}
}
var studyList = await _repository.Where<DicomStudy>(t => t.TrialId == indto.TrialId && t.SubjectVisitId == indto.SujectVisitId).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 _repository.Where<DicomInstance>(t => studyIds.Contains(t.StudyId))
.Select(t => new { t.SeriesId, t.Id, t.InstanceNumber, t.Path, t.NumberOfFrames,t.WindowCenter,t.WindowWidth }).ToListAsync();
List<DicomSeriesDTO> seriesLists = await _repository.Where<DicomSeries>(s => studyIds.Contains(s.StudyId) && s.IsReading).OrderBy(s => s.SeriesNumber).
ThenBy(s => s.SeriesTime)
.ProjectTo<DicomSeriesDTO>(_mapper.ConfigurationProvider).ToListAsync();
foreach (var t in studyList)
{
t.SeriesList = seriesLists.Where(s => s.StudyId == t.StudyId).OrderBy(s => s.SeriesNumber).
ThenBy(s => s.SeriesTime).ToList();
t.SeriesList.ForEach(series =>
{
series.InstanceList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Id).ToList();
//series.InstancePathList = instanceList.Where(t => t.SeriesId == series.Id).OrderBy(t => t.InstanceNumber).Select(k => k.Path).ToList();
//处理多帧
series.InstancePathList = instanceList.Where(s => s.SeriesId == series.Id).OrderBy(t => t.InstanceNumber)
.SelectMany(u =>
{
if (u.NumberOfFrames > 1)
{
var pathList = new List<string>();
for (int i = 1; i <= u.NumberOfFrames; i++)
{
pathList.Add(u.Path + "?frame=" + (i - 1));
}
return pathList;
}
else
{
return new List<string> { u.Path };
}
})
.ToList();
series.WindowWidth = instanceList.FirstOrDefault()?.WindowWidth;
series.WindowCenter = instanceList.FirstOrDefault()?.WindowCenter;
});
//设置为阅片与否 不更改数据库检查 的instance数量 和 SeriesCount 所以这里要实时统计
t.SeriesCount = t.SeriesList.Count();
t.InstanceCount = t.SeriesList.SelectMany(t => t.InstanceList).Count();
}
// 非Dicom
var noDicomList = await _noneDicomStudyRepository.Where(x => x.TrialId == indto.TrialId && x.SubjectVisitId == indto.SujectVisitId).ToListAsync();
List<VisitStudyDTO> noDicomStudyList = noDicomList.Select(x => new VisitStudyDTO()
{
InstanceCount = x.FileCount,
StudyId = x.Id,
Modalities = x.Modality,
SeriesCount = 1,
StudyCode = x.StudyCode,
IsDicom = false,
}).ToList();
foreach (var item in noDicomStudyList)
{
var nodicom = noDicomList.Where(x => x.Id == item.StudyId).FirstOrDefault();
item.SeriesList = new List<DicomSeriesDTO>()
{
new DicomSeriesDTO (){
IsDicom=false,
Id=item.StudyId,
InstanceCount=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).CountAsync(),
Modality=item.Modalities,
StudyId=item.StudyId,
TrialId=nodicom.TrialId,
SiteId=nodicom.SiteId,
SubjectVisitId=nodicom.SubjectVisitId,
SubjectId=nodicom.SubjectId,
SeriesNumber=1,
NoneDicomFileFirstFile=await _noneDicomStudyFileRepository.Where(x=>x.NoneDicomStudyId==item.StudyId).Select(x=>x.Path).FirstOrDefaultAsync(),
}
};
}
if (studyList == null || studyList.Count == 0)
{
studyList = new List<VisitStudyDTO>();
}
studyList.AddRange(noDicomStudyList);
studyList.ForEach(x =>
{
x.SeriesList.ForEach(y =>
{
y.IsBeMark = thisRowinfo.Any(z => z.SeriesId == y.Id);
});
});
result.AddRange(studyList);
result = result.Where(x => x.SeriesCount > 0).ToList();
return result;
//return ResponseOutput.Ok(studyList.Where(t => t.SeriesList.Count > 0).ToList());
}
/// <summary>
/// 设置患者检查批次已执行 也就是将studyUploaded状态置为true 为了那些没有影像 人工设置准备
/// </summary>
/// <param name="subjectVisitId"></param>
/// <returns></returns>
[HttpPut("{trialId:guid}/{subjectVisitId:guid}")]
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
[Obsolete]
public async Task<IResponseOutput> SetSVExecuted(Guid subjectVisitId)
{
await _subjectVisitRepository.UpdatePartialFromQueryAsync(subjectVisitId, u => new SubjectVisit() { VisitExecuted = VisitExecutedEnum.Executed }, true);
return ResponseOutput.Ok();
}
}
}