From d9a85b6825f1c2bdb4e7f5927a67048d45ea43de Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Tue, 20 Aug 2024 17:58:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B7=BB=E5=8A=A0pacs=20?= =?UTF-8?q?=E5=BD=B1=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../IRaCIS.Core.Application.xml | 5 +- .../Service/ImageAndDoc/StudyService.cs | 2 + .../Service/Visit/DTO/PatientViewModel.cs | 13 +- .../Service/Visit/DTO/VisitPointViewModel.cs | 12 +- .../Service/Visit/PatientService.cs | 235 +++++++++++++++++- 5 files changed, 256 insertions(+), 11 deletions(-) diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 36374a2b0..4d68334dc 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -15239,11 +15239,14 @@ - + 提交 患者检查和访视的绑定 + + + diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs index cdd161e9b..7d7d8cbd1 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -206,6 +206,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc study.TrialId = incommand.TrialId; study.SubjectId = incommand.SubjectId; study.SubjectVisitId = incommand.SubjectVisitId; + study.IsFromPACS = false; //如果因为意外情况,连续点击两次,导致第一次插入了,第二次进来也会插入,在此判断一下 var findStudy = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == study.Id); @@ -280,6 +281,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc studyMonitor.StudyCode = study.StudyCode; //特殊处理逻辑 + study.IsFromPACS = false; study.Modalities = string.Join("、", incommand.Study.SeriesList.Select(t => t.Modality).Union(study.Modalities.Split("、", StringSplitOptions.RemoveEmptyEntries)).Distinct()); SpecialArchiveStudyDeal(study); modalitys = study.Modalities; diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs index 05d8d3fcc..2409b3028 100644 --- a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -256,9 +256,9 @@ namespace IRaCIS.Application.Contracts public string? TrialSiteCode { get; set; } - public string? TrialSiteName { get; set; } + public string? TrialSiteName { get; set; } - public string? TrialSiteAliasName { get; set; } + public string? TrialSiteAliasName { get; set; } } @@ -382,7 +382,7 @@ namespace IRaCIS.Application.Contracts } - public class SubmitVisitStudyBindingCommand + public class VerifyPacsImageCommand { [NotDefault] public Guid TrialId { get; set; } @@ -396,6 +396,13 @@ namespace IRaCIS.Application.Contracts public List SCPStudyIdList { get; set; } } + public class SubmitVisitStudyBindingCommand: VerifyPacsImageCommand + { + + + public List ReUploadSCPStudyIdList { get; set; } + } + public class SubjectVisitSelectQuery { [NotDefault] diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs index 92d44de4b..382023c91 100644 --- a/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs +++ b/IRaCIS.Core.Application/Service/Visit/DTO/VisitPointViewModel.cs @@ -264,7 +264,7 @@ namespace IRaCIS.Core.Application.Contracts public bool IsCriticalSequence { get; set; } = false; - public int SeriesCount =>SeriesList.Count; + public int SeriesCount => SeriesList.Count; public int InstanceCount { get; set; } public bool IsDicom { get; set; } = true; @@ -369,6 +369,16 @@ namespace IRaCIS.Core.Application.Contracts public List SOPInstanceUIDList { get; set; } } + public class VerifySCPStudyUploadResult + { + public Guid SCPStudyId { get; set; } + + public string ErrorMesseage { get; set; } = String.Empty; + + public bool AllowUpload { get; set; } = false; + + public bool AllowReUpload { get; set; } = false; + } public class VerifyStudyUploadResult { diff --git a/IRaCIS.Core.Application/Service/Visit/PatientService.cs b/IRaCIS.Core.Application/Service/Visit/PatientService.cs index 1d98ce922..2953f522f 100644 --- a/IRaCIS.Core.Application/Service/Visit/PatientService.cs +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -54,10 +54,12 @@ namespace IRaCIS.Application.Services private readonly IRepository _scpStudyRepository; private readonly IRepository _subjectRepository; private readonly IRepository _subjectVisitRepository; + private readonly IRepository _dictionaryRepository; private readonly IDistributedLockProvider _distributedLockProvider; - public PatientService(IRepository studyRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) + public PatientService(IRepository studyRepository, IRepository dictionaryRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) { + _dictionaryRepository = dictionaryRepository; _scpStudyRepository = studyRepository; _trialRepository = trialRepository; _patientRepository = patientRepository; @@ -75,7 +77,7 @@ namespace IRaCIS.Application.Services [HttpPost] public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) { - var query = _repository.Where(t=>t.TrialId==inQuery.TrialId) + var query = _repository.Where(t => t.TrialId == inQuery.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.CalledAE.Contains(inQuery.CalledAE)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAEIP), t => t.CallingAEIP.Contains(inQuery.CallingAEIP)) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) @@ -283,23 +285,120 @@ namespace IRaCIS.Application.Services + public async Task> VerifyPacsImage(VerifyPacsImageCommand inCommand) + { + var trialId = inCommand.TrialId; + + var subjectId = inCommand.SubjectId; + + var isVerifyVisitImageDate = await _repository.Where(t => t.Id == inCommand.TrialId).Select(t => t.IsVerifyVisitImageDate).FirstNotNullAsync(); + + var result = new List(); + + var visitList = _repository.Where(t => t.SubjectId == inCommand.SubjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList(); + + var currentVisitNum = visitList.First(t => t.Id == inCommand.SubjectVisitId).VisitNum; + + var scpStudyList = _scpStudyRepository.Where(t => inCommand.SCPStudyIdList.Contains(t.Id)).Select(t => new { StudyDate = t.StudyTime, t.Id }).ToList(); + + foreach (var waitUploadItem in scpStudyList) + { + if (isVerifyVisitImageDate) + { + //小于当前访视 最近的最晚拍片 + var before = visitList.Where(u => u.VisitNum < currentVisitNum).Max(k => k.LatestScanDate); + + if (before != null && waitUploadItem.StudyDate != null && before > waitUploadItem.StudyDate) + { + + // $"当前访视检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能早于前序访视检查时间{before?.ToString("yyyy-MM-dd")},请核对检查数据是否有误", + result.Add(new VerifySCPStudyUploadResult() { ErrorMesseage = _localizer["Study_VisitBeforePrevError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, before?.ToString("yyyy-MM-dd")!] }); + } + + //大于当前访视 最近的最早拍片日期 + var after = visitList.Where(u => u.VisitNum > currentVisitNum).Min(k => k.EarliestScanDate); + + if (after != null && waitUploadItem.StudyDate != null && after < waitUploadItem.StudyDate) + { + // $"当前访视检查时间{waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")}不能晚于该访视之后的检查时间{after?.ToString("yyyy-MM-dd")},请核对检查数据是否有误" + result.Add(new VerifySCPStudyUploadResult() { ErrorMesseage = _localizer["Study_VisitAfterSubseqError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, after?.ToString("yyyy-MM-dd")!]}); + } + } + + var verifyStudyInfo = _repository.Where(t => t.TrialId == trialId && t.Id == waitUploadItem.Id).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + var currentStudyResult = new VerifyStudyUploadResult(); + + //数据库不存在该检查 允许上传 + if (verifyStudyInfo == null) + { + currentStudyResult.AllowUpload = true; + } + //数据库该项目有该检查 看是否支持重传 + else + { + //是同一个受试者 支持重传 + if (verifyStudyInfo.SubjectId == subjectId && verifyStudyInfo.SubjectVisitId == inCommand.SubjectVisitId) + { + currentStudyResult.AllowReUpload = true; + } + //不是同一个受试者 + else + { + //有默认值,其实不用写,这里为了好理解 + currentStudyResult.AllowUpload = false; + + currentStudyResult.AllowReUpload = false; + + //$"此处不可以上传。当前影像检查已经上传给受试者{verifyStudyInfo.SubjectCode}的{verifyStudyInfo.VisitName}" + currentStudyResult.ErrorMesseage = _localizer["Study_ImgAlreadyUploaded", verifyStudyInfo.SubjectCode, verifyStudyInfo.VisitName]; + } + } + } + + + return result; + + + } + + + /// /// 提交 患者检查和访视的绑定 /// /// + /// + /// + /// /// [HttpPost] [UnitOfWork] [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] - public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand) + public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, + [FromServices]IRepository _dicomstudyRepository, + [FromServices] IRepository _dicomSeriesRepository, + [FromServices] IRepository _dicomInstanceRepository) { + //这里要做校验 + 同时验证本地系统里面的影像是否存在pacs推过来的 + + var copyCommand = inCommand.Clone(); + copyCommand.SCPStudyIdList = inCommand.SCPStudyIdList.Union(inCommand.ReUploadSCPStudyIdList).ToList(); + var verifyResult = await VerifyPacsImage(copyCommand); + + var allowUploadList = verifyResult.Where(u => u.AllowUpload == true).Select(t => t.SCPStudyId).ToList(); + var allowReUploadList = verifyResult.Where(u => u.AllowReUpload == true).Select(t => t.SCPStudyId).ToList(); + + if (inCommand.SCPStudyIdList.All(t => allowUploadList.Contains(t)) && inCommand.ReUploadSCPStudyIdList.All(t => allowReUploadList.Contains(t))) + { + throw new BusinessValidationFailedException("对接提示: 前端提交的检查有不能上传的,请刷新页面调用验证接口重试!"); + } + var subjectId = inCommand.SubjectId; var subjectVisitId = inCommand.SubjectVisitId; var trialId = inCommand.TrialId; - - var @lock = _distributedLockProvider.CreateLock($"StudyCode"); using (await @lock.AcquireAsync()) @@ -308,6 +407,7 @@ namespace IRaCIS.Application.Services int currentNextCodeInt = dbStudyCodeIntMax + 1; + //新增的,上传的 foreach (var scpStudyId in inCommand.SCPStudyIdList) { @@ -315,7 +415,6 @@ namespace IRaCIS.Application.Services if (find != null) { - var newStuty = _mapper.Map(find.SCPStudy); await _repository.AddAsync(newStuty); @@ -362,6 +461,93 @@ namespace IRaCIS.Application.Services } + foreach (var scpStudyId in inCommand.ReUploadSCPStudyIdList) + { + + var study = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == scpStudyId); + + var instanceIdList = _dicomInstanceRepository.Where(t => t.Id == scpStudyId).Select(t => t.Id).ToList(); + + var scpStudy = _scpStudyRepository.Where(t => t.Id == scpStudyId).Include(t=>t.SeriesList).ThenInclude(t=>t.SCPInstanceList).FirstOrDefault(); + + //以最后一次为准 + study.IsFromPACS = true; + //特殊处理逻辑 + study.Modalities = string.Join("、", scpStudy.SeriesList.Select(t => t.Modality).Union(study.Modalities.Split("、", StringSplitOptions.RemoveEmptyEntries)).Distinct()); + + SpecialArchiveStudyDeal(study); + + + // 少了整个序列 + + //某个序列下少了instance + foreach (var seriesItem in scpStudy.SeriesList) + { + var seriesId = IdentifierHelper.CreateGuid(seriesItem.StudyInstanceUid, seriesItem.SeriesInstanceUid, trialId.ToString()); + + DicomSeries dicomSeries = await _dicomSeriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId); + + //判断重复 + if (dicomSeries == null) + { + var series = _mapper.Map(seriesItem); + + series.SeqId = Guid.Empty; + series.TrialId = trialId; + series.SubjectId = subjectId; + series.SubjectVisitId = subjectVisitId; + + + dicomSeries = await _dicomSeriesRepository.AddAsync(series); + + + + foreach (var instanceItem in seriesItem.SCPInstanceList) + { + var instance = _mapper.Map(instanceItem); + + instance.SeqId = Guid.Empty; + instance.TrialId = trialId; + instance.SubjectId = subjectId; + instance.SubjectVisitId = subjectVisitId; + + await _dicomInstanceRepository.AddAsync(instance); + } + + //新的序列 那么 检查的序列数量+1 + study.SeriesCount += 1; + + study.InstanceCount += seriesItem.SCPInstanceList.Count; + } + else + { + //该序列掉了instance + dicomSeries.InstanceCount += seriesItem.SCPInstanceList.Count; + + var newInstanceList = seriesItem.SCPInstanceList.Where(t => !instanceIdList.Contains(t.Id)); + + foreach (var instanceItem in newInstanceList) + { + var instance = _mapper.Map(instanceItem); + + instance.SeqId = Guid.Empty; + instance.TrialId = trialId; + instance.SubjectId = subjectId; + instance.SubjectVisitId = subjectVisitId; + + await _dicomInstanceRepository.AddAsync(instance); + } + + study.InstanceCount += newInstanceList.Count(); + + } + } + + await _repository.SaveChangesAsync(); + await _repository.BatchUpdateAsync(t => t.Id == scpStudy.PatientId, u => new SCPPatient() { SubjectId = subjectId }); + await _repository.BatchUpdateAsync(t => t.Id == scpStudyId, u => new SCPStudy() { SubjectVisitId = subjectVisitId }); + } + } @@ -373,6 +559,43 @@ namespace IRaCIS.Application.Services return ResponseOutput.Ok(); } + + private void SpecialArchiveStudyDeal(DicomStudy study) + { + #region 特殊逻辑 + + + if (study.PatientBirthDate.Length == 8) + { + study.PatientBirthDate = $"{study.PatientBirthDate[0]}{study.PatientBirthDate[1]}{study.PatientBirthDate[2]}{study.PatientBirthDate[3]}-{study.PatientBirthDate[4]}{study.PatientBirthDate[5]}-{study.PatientBirthDate[6]}{study.PatientBirthDate[7]}"; + } + + var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList(); + + + var modality = study.Modalities; + + var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty; + + if (modality == "MR") + { + modalityForEdit = "MRI"; + } + + if (modality == "PT") + { + modalityForEdit = "PET"; + } + if (modality == "PT、CT") + { + modalityForEdit = "PET-CT"; + } + + study.ModalityForEdit = modalityForEdit; + #endregion + } + + #endregion