using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service.ImageAndDoc.DTO; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; using Medallion.Threading; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using ZiggyCreatures.Caching.Fusion; namespace IRaCIS.Core.Application.Service.ImageAndDoc { [ApiExplorerSettings(GroupName = "Image")] public class StudyService(IRepository _subjectVisitRepository, IRepository _dicomInstanceRepository, IRepository _dicomSeriesRepository, IRepository _dicomstudyRepository, IRepository _dictionaryRepository, IRepository _trialRepository, IRepository _visitTaskRepository, IRepository _dicomStudyRepository, IRepository _subjectRepository, IRepository _studyMonitorRepository, IRepository _systemAnonymizationRepository, IRepository _noneDicomStudyRepository, IDistributedLockProvider _distributedLockProvider, IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache) : BaseService, IStudyService { 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 } [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] public async Task PreArchiveDicomStudy(PreArchiveDicomStudyCommand preArchiveStudyCommand) { var studyMonitor = new StudyMonitor() { TrialId = preArchiveStudyCommand.TrialId, SubjectId = preArchiveStudyCommand.SubjectId, SubjectVisitId = preArchiveStudyCommand.SubjectVisitId, IsSuccess = false, UploadStartTime = DateTime.Now, IsDicom = true, IP = _userInfo.IP, IsDicomReUpload = preArchiveStudyCommand.IsDicomReUpload, FileCount = preArchiveStudyCommand.FileCount, }; var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true); return ResponseOutput.Ok(addEntity.Id); } [HttpGet] public async Task DicomUploadInProgress(Guid trialId, string studyInstanceUid, Guid? visitTaskId) { if (visitTaskId != null) { var cacheValue = _fusionCache.GetOrDefault(CacheKeys.TrialTaskStudyUidUploading(trialId, visitTaskId.Value, studyInstanceUid)); if (cacheValue != Guid.Empty && cacheValue != _userInfo.Id) { //---当前已有人正在上传和归档该检查! return ResponseOutput.NotOk(I18n.T("UploadDownLoad_ArchiveInProgress")); } else { await _fusionCache.SetAsync(CacheKeys.TrialTaskStudyUidUploading(trialId, visitTaskId.Value, studyInstanceUid), _userInfo.Id, TimeSpan.FromSeconds(15)); } } else { await _fusionCache.SetAsync(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid), _userInfo.Id, TimeSpan.FromSeconds(15)); } return ResponseOutput.Ok(); } [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] public async Task AddOrUpdateArchiveStudy(NewArchiveStudyCommand incommand) { var @uploadLock = _distributedLockProvider.CreateLock($"UploadDicom"); using (await @uploadLock.AcquireAsync()) { if (_fusionCache.GetOrDefault(CacheKeys.TrialStudyUidDBLock(incommand.TrialId, incommand.Study.StudyInstanceUid)) != Guid.Empty) { //---当前已有人正在上传和归档该检查! return ResponseOutput.NotOk(I18n.T("UploadDownLoad_ArchiveInProgress")); } else { //在事务未完成前 防止前端重复提交 await _fusionCache.SetAsync(CacheKeys.TrialStudyUidDBLock(incommand.TrialId, incommand.Study.StudyInstanceUid), _userInfo.Id, TimeSpan.FromMinutes(3)); } } var modalitys = string.Empty; try { var trialId = incommand.TrialId; var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == incommand.StudyMonitorId); studyMonitor.UploadFinishedTime = DateTime.Now; studyMonitor.ArchiveFinishedTime = DateTime.Now; studyMonitor.FailedFileCount = incommand.FailedFileCount; studyMonitor.IsSuccess = incommand.FailedFileCount == 0; studyMonitor.RecordPath = incommand.RecordPath; studyMonitor.FileSize = incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize); var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString()); var findStudy = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == studyId); studyMonitor.StudyId = studyId; studyMonitor.StudyCode = findStudy?.StudyCode ?? ""; studyMonitor.IsDicomReUpload = findStudy != null; if (incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Count() == 0) { await _studyMonitorRepository.SaveChangesAsync(); return ResponseOutput.Ok(); } //上传 if (studyMonitor.IsDicomReUpload == false) { var addStudy = _mapper.Map(incommand.Study); var @lock = _distributedLockProvider.CreateLock($"StudyCode"); using (await @lock.AcquireAsync()) { //查询数据库获取最大的Code 没有记录则为0 var dbStudyCodeIntMax = _dicomstudyRepository.Where(s => s.TrialId == trialId).Select(t => t.Code).DefaultIfEmpty().Max(); //获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增 var cacheMaxCodeInt = await _fusionCache.GetOrDefaultAsync(CacheKeys.TrialStudyMaxCode(trialId)); int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1; addStudy.Code = currentNextCodeInt; addStudy.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); await _fusionCache.SetAsync(CacheKeys.TrialStudyMaxCode(trialId), addStudy.Code, TimeSpan.FromMinutes(30)); } addStudy.Id = studyId; addStudy.TrialId = incommand.TrialId; addStudy.SubjectId = incommand.SubjectId; addStudy.SubjectVisitId = incommand.SubjectVisitId; addStudy.IsFromPACS = false; //特殊处理逻辑 addStudy.Modalities = string.Join("、", incommand.Study.SeriesList.Select(t => t.Modality).Distinct()); SpecialArchiveStudyDeal(addStudy); modalitys = addStudy.Modalities; await _dicomstudyRepository.AddAsync(addStudy); studyMonitor.StudyCode = addStudy.StudyCode; foreach (var seriesItem in incommand.Study.SeriesList) { var series = _mapper.Map(seriesItem); series.Id = IdentifierHelper.CreateGuid(seriesItem.StudyInstanceUid, seriesItem.SeriesInstanceUid, incommand.TrialId.ToString()); series.StudyId = addStudy.Id; series.TrialId = incommand.TrialId; series.SubjectId = incommand.SubjectId; series.SubjectVisitId = incommand.SubjectVisitId; //前端传递的数量不准,上传的时候,把失败的也加进来了,以实际数组的数字为准 series.InstanceCount = seriesItem.InstanceList.Count; await _dicomSeriesRepository.AddAsync(series); foreach (var instanceItem in seriesItem.InstanceList) { var isntance = _mapper.Map(instanceItem); Guid instanceId = IdentifierHelper.CreateGuid(addStudy.StudyInstanceUid, series.SeriesInstanceUid, isntance.SopInstanceUid, addStudy.TrialId.ToString()); isntance.Id = instanceId; isntance.StudyId = addStudy.Id; isntance.SeriesId = series.Id; isntance.TrialId = incommand.TrialId; isntance.SubjectId = incommand.SubjectId; isntance.SubjectVisitId = incommand.SubjectVisitId; await _dicomInstanceRepository.AddAsync(isntance); } } } else { //特殊处理逻辑 findStudy.IsFromPACS = false; findStudy.Modalities = string.Join("、", incommand.Study.SeriesList.Select(t => t.Modality).Union(findStudy.Modalities.Split("、", StringSplitOptions.RemoveEmptyEntries)).Distinct()); SpecialArchiveStudyDeal(findStudy); modalitys = findStudy.Modalities; // 少了整个序列 //某个序列下少了instance foreach (var seriesItem in incommand.Study.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.Id = seriesId; series.StudyId = findStudy.Id; series.TrialId = incommand.TrialId; series.SubjectId = incommand.SubjectId; series.SubjectVisitId = incommand.SubjectVisitId; dicomSeries = await _dicomSeriesRepository.AddAsync(series); //新的序列 那么 检查的序列数量+1 findStudy.SeriesCount += 1; } else { //该序列掉了instance dicomSeries.InstanceCount += seriesItem.InstanceList.Count; } foreach (var instanceItem in seriesItem.InstanceList) { var insntance = _mapper.Map(instanceItem); insntance.Id = IdentifierHelper.CreateGuid(insntance.StudyInstanceUid, insntance.SeriesInstanceUid, insntance.SopInstanceUid, trialId.ToString()); insntance.StudyId = findStudy.Id; insntance.SeriesId = dicomSeries.Id; insntance.TrialId = incommand.TrialId; insntance.SubjectId = incommand.SubjectId; insntance.SubjectVisitId = incommand.SubjectVisitId; await _dicomInstanceRepository.AddAsync(insntance); } // 不管是新的序列 还是 该序列 掉了Instance 重传的时候 检查的instance 数量都会增加 findStudy.InstanceCount += seriesItem.InstanceList.Count; } } var @lock2 = _distributedLockProvider.CreateLock($"StudyCommit"); using (await @lock2.AcquireAsync()) { await _dicomInstanceRepository.SaveChangesAsync(); } } catch (Exception ex) { return ResponseOutput.NotOk(ex.Message); } finally { await _fusionCache.RemoveAsync(CacheKeys.TrialStudyUidUploading(incommand.TrialId, incommand.Study.StudyInstanceUid)); await _fusionCache.RemoveAsync(CacheKeys.TrialStudyUidDBLock(incommand.TrialId, incommand.Study.StudyInstanceUid)); } return ResponseOutput.Ok(modalitys); } [HttpPost] public async Task> GetDicomAndNoneDicomStudyList(StudyQuery inQuery) { var svExpression = QCCommon.GetDicomStudySubjectVisitFilter(inQuery.VisitPlanArray); var dicomStudyQuery = _dicomStudyRepository.Where(t => t.TrialId == inQuery.TrialId) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.Subject.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) .Select(t => new UnionStudyViewModel() { TrialId = t.TrialId, TrialSiteId = t.Subject.TrialSiteId, SubjectId = t.SubjectId, SubjectVisitId = t.SubjectVisitId, VisitName = t.SubjectVisit.VisitName, VisitNum = t.SubjectVisit.VisitNum, IsDicom = true, IsFromPACS = t.IsFromPACS, SubjectCode = t.Subject.Code, Id = t.Id, Bodypart = t.BodyPartExamined, Modalities = t.Modalities, Count = t.SeriesCount, StudyCode = t.StudyCode, //DicomStudyCode = t.StudyCode, //NoneDicomCode = 0, StudyTime = t.StudyTime, TrialSiteAliasName = t.Subject.TrialSite.TrialSiteAliasName, TrialSiteCode = t.Subject.TrialSite.TrialSiteCode, Uploader = t.CreateUser.UserName, UploadTime = t.CreateTime }); //.ProjectTo(_mapper.ConfigurationProvider); var svExpression2 = QCCommon.GetNoneDicomStudySubjectVisitFilter(inQuery.VisitPlanArray); var nodeDicomStudyQuery = _noneDicomStudyRepository.Where(t => t.TrialId == inQuery.TrialId) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.Subject.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression2) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) .Select(t => new UnionStudyViewModel() { TrialId = t.TrialId, TrialSiteId = t.Subject.TrialSiteId, SubjectId = t.SubjectId, SubjectVisitId = t.SubjectVisitId, VisitName = t.SubjectVisit.VisitName, VisitNum = t.SubjectVisit.VisitNum, IsDicom = false, IsFromPACS = false, SubjectCode = t.Subject.Code, Id = t.Id, Bodypart = t.BodyPart, Modalities = t.Modality, Count = t.NoneDicomFileList.Count(), StudyCode = t.StudyCode, //NoneDicomCode = t.Code, //DicomStudyCode = string.Empty, StudyTime = t.ImageDate, TrialSiteAliasName = t.Subject.TrialSite.TrialSiteAliasName, TrialSiteCode = t.Subject.TrialSite.TrialSiteCode, Uploader = t.CreateUser.UserName, UploadTime = t.CreateTime }); //.ProjectTo(_mapper.ConfigurationProvider); var unionQuery = dicomStudyQuery.Union(nodeDicomStudyQuery) .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) .WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectId == inQuery.SubjectVisitId) .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) .WhereIf(inQuery.IsFromPACS != null, t => t.IsFromPACS == inQuery.IsFromPACS); return await unionQuery.ToPagedListAsync(inQuery); } [HttpPost] public async Task> GetDicomAndNoneDicomStudyMonitorList(StudyQuery inQuery) { var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(inQuery.VisitPlanArray); var StudyMonitorQuery = _studyMonitorRepository.Where(t => t.TrialId == inQuery.TrialId, ignoreQueryFilters: true) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.Subject.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) .WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectId == inQuery.SubjectVisitId) .WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId) .WhereIf(inQuery.IsDicom != null, t => t.IsDicom == inQuery.IsDicom) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Uploader), t => t.CreateUser.UserName.Contains(inQuery.Uploader)) .WhereIf(inQuery.IsSuccess != null, t => t.IsSuccess == inQuery.IsSuccess) .WhereIf(!string.IsNullOrWhiteSpace(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode)) .WhereIf(inQuery.UploadStartTime != null, t => t.UploadStartTime >= inQuery.UploadStartTime) .WhereIf(inQuery.UploadFinishedTime != null, t => t.UploadFinishedTime <= inQuery.UploadFinishedTime) .Select(t => new UnionStudyMonitorModel() { TrialId = t.TrialId, TrialSiteId = t.Subject.TrialSiteId, SubjectId = t.SubjectId, SubjectVisitId = t.SubjectVisitId, VisitName = t.SubjectVisit.VisitName, VisitNum = t.SubjectVisit.VisitNum, SubjectCode = t.Subject.Code, TrialSiteAliasName = t.Subject.TrialSite.TrialSiteAliasName, TrialSiteCode = t.Subject.TrialSite.TrialSiteCode, Uploader = t.CreateUser.UserName, UploadTime = t.CreateTime, IsSuccess = t.IsSuccess, Note = t.Note, IP = t.IP, FileCount = t.FileCount, FileSize = t.FileSize, UploadFinishedTime = t.UploadFinishedTime, UploadStartTime = t.UploadStartTime, ArchiveFinishedTime = t.ArchiveFinishedTime, RecordPath = t.RecordPath, IsDicomReUpload = t.IsDicomReUpload, StudyId = t.Id, IsDicom = t.IsDicom, StudyCode = t.StudyCode }); return await StudyMonitorQuery.ToPagedListAsync(inQuery); } /// 指定资源Id,渲染Dicom检查的Jpeg预览图像 /// Dicom检查的Id [HttpGet("{studyId:guid}")] public async Task Preview(Guid studyId) { string path = String.Empty; path = (await _dicomInstanceRepository.Where(s => s.StudyId == studyId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException(); using (var sw = ImageHelper.RenderPreviewJpeg(path)) { var bytes = new byte[sw.Length]; sw.Read(bytes, 0, bytes.Length); sw.Close(); return new FileContentResult(bytes, "image/jpeg"); } } /// /// 获取某个检查的关联检查列表(该受试者在这个想项目下的所有检查) /// 点击检查检查列表中的一个检查获取对应的序列列表(调用之前的接口:/series/list/,根据StudyId,获取访视的序列列表) /// /// [HttpGet("{subjectVisitId:guid}")] [AllowAnonymous] public IResponseOutput> GetAllRelationStudyList(Guid subjectVisitId) { #region 废弃 //var studylist = _studyRepository.Where(u => u.SubjectVisitId == subjectVisitId && u.IsDeleted == false).Select(t => new { StudyId = t.Id, t.SubjectId, t.TrialId }).ToList(); //var subjectId = studylist.FirstOrDefault().SubjectId; //var trialId = studylist.FirstOrDefault().TrialId; //var studyIds = studylist.Select(t => t.StudyId).ToList(); //var query = from studyItem in _studyRepository.Where(u => u.SubjectId == subjectId // && u.TrialId == trialId && u.IsDeleted == false && // !studyIds.Contains(u.Id) // /* && u.Status != (int)StudyStatus.Abandon*/) // join visitItem in _subjectVisitRepository.AsQueryable() // on studyItem.SubjectVisitId equals visitItem.Id // select new RelationStudyDTO // { // StudyId = studyItem.Id, // StudyCode = studyItem.StudyCode, // VisitName = visitItem.VisitName, // Modalities = studyItem.Modalities, // Description = studyItem.Description, // SeriesCount = studyItem.SeriesCount // }; #endregion var studyInfo = _dicomStudyRepository.Where(u => u.SubjectVisitId == subjectVisitId).Select(t => new { t.SubjectId, t.TrialId }).FirstOrDefault().IfNullThrowException(); var query = _dicomStudyRepository.Where(t => t.SubjectVisitId != subjectVisitId && t.TrialId == studyInfo.TrialId && t.SubjectId == studyInfo.SubjectId) .ProjectTo(_mapper.ConfigurationProvider).ToList(); var list = query.OrderBy(u => u.VisitName).ThenBy(s => s.StudyCode).ToList(); return ResponseOutput.Ok(list); } /// 指定资源Id,获取Dicom检查信息 /// Dicom检查的Id [HttpGet, Route("{studyId:guid}")] [AllowAnonymous] public IResponseOutput Item(Guid studyId) { return ResponseOutput.Ok(_mapper.Map(_dicomStudyRepository.Where().FirstOrDefault(s => s.Id == studyId))); } [HttpPost] public async Task>> VerifyTaskStudyAllowUploadAsync(VerifyTaskUploadOrReupload verifyInfo) { var queryStudy = _visitTaskRepository.Where(t => t.SubjectId == verifyInfo.SubjectId && t.SourceSubjectVisitId != null && t.DoctorUserId == _userInfo.Id).Select(u => new { VisitTaskId = u.Id, SourceSubjectVisitId = u.SourceSubjectVisitId, OrginalStudyList = u.SourceSubjectVisit.StudyList.Select(t => new StudyBasicInfo() { Id = t.Id, StudyInstanceUid = t.StudyInstanceUid }).ToList(), UploadStudyList = u.TaskStudyList.Select(t => new { Id = t.Id, StudyInstanceUid = t.StudyInstanceUid, SeriesList = t.SeriesList.Select(t => new UploadedSeries() { SeriesId = t.Id, SeriesInstanceUid = t.SeriesInstanceUid, SOPInstanceUIDList = t.InstanceList.Select(c => c.SopInstanceUid).ToList() }).ToList() }).ToList() }); var studyList = await queryStudy.ToListAsync(); var resultList = new List(); var trialId = verifyInfo.TrialId; var subjectId = verifyInfo.SubjectId; var visitList = _subjectVisitRepository.Where(t => t.SubjectId == verifyInfo.SubjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList(); foreach (var waitUploadItem in verifyInfo.StudyInstanceUidList) { var studyInstanceUid = waitUploadItem.StudyInstanceUid; var result = new VerifyStudyUploadResult(); if (_fusionCache.GetOrDefault($"StudyUid_{trialId}_{studyInstanceUid}") != _userInfo.Id) { result.AllowUpload = false; result.AllowReUpload = false; result.StudyInstanceUid = studyInstanceUid; //---当前有人正在上传归档该检查! result.ErrorMesseage = _localizer["Study_UploadArchiving"]; resultList.Add(result); continue; } var findOriginStudy = studyList.FirstOrDefault(c => c.OrginalStudyList.Any(t => t.StudyInstanceUid == studyInstanceUid)); if (findOriginStudy == null) { throw new BusinessValidationFailedException("该检查不属于该受试者,请核查"); } else { if (findOriginStudy.UploadStudyList.Any(t => t.StudyInstanceUid == studyInstanceUid)) { result.AllowReUpload = true; result.AllowUpload = false; result.UploadedSeriesList = findOriginStudy.UploadStudyList.Where(t => t.StudyInstanceUid == studyInstanceUid).SelectMany(t => t.SeriesList).ToList(); } else { result.AllowUpload = true; result.AllowReUpload = false; } } result.StudyInstanceUid = studyInstanceUid; var @lock = _distributedLockProvider.CreateLock($"StudyUpload"); using (@lock.Acquire()) { if (result.AllowReUpload || result.AllowUpload) { await _fusionCache.SetAsync($"StudyUid_{trialId}_{studyInstanceUid}", _userInfo.Id, TimeSpan.FromSeconds(30)); } else { await _fusionCache.RemoveAsync(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid)); } } resultList.Add(result); } var systemAnonymizationList = _systemAnonymizationRepository.Where(t => t.IsEnable).ToList(); return ResponseOutput.Ok>(resultList, new { AnonymizeFixedList = systemAnonymizationList.Where(t => t.IsFixed).ToList(), AnonymizeNotFixedList = systemAnonymizationList.Where(t => t.IsFixed == false).ToList() }); } /// /// 批量验证 检查是否可以上传 并告知原因 /// [HttpPost] public IResponseOutput> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo) { var trialInfo = _trialRepository.Where().FirstOrDefault(t => t.Id == verifyInfo.TrialId).IfNullThrowException(); var result = new List(); var visitList = _subjectVisitRepository.Where(t => t.SubjectId == verifyInfo.SubjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList(); var currentVisitNum = visitList.Where(t => t.Id == verifyInfo.SubjectVisitId).First().VisitNum; verifyInfo.StudyInstanceUidList.ForEach(waitUploadItem => { if (trialInfo.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 VerifyStudyUploadResult() { ErrorMesseage = _localizer["Study_VisitBeforePrevError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, before?.ToString("yyyy-MM-dd")!], StudyInstanceUid = waitUploadItem.StudyInstanceUid }); return; } //大于当前访视 最近的最早拍片日期 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 VerifyStudyUploadResult() { ErrorMesseage = _localizer["Study_VisitAfterSubseqError", waitUploadItem.StudyDate?.ToString("yyyy-MM-dd")!, after?.ToString("yyyy-MM-dd")!], StudyInstanceUid = waitUploadItem.StudyInstanceUid }); return; } } var temp = VerifyStudyUpload(waitUploadItem.StudyInstanceUid, verifyInfo.TrialId, verifyInfo.SubjectVisitId, verifyInfo.SubjectId); result.Add(temp); }); // 写入dicom 固定的信息,以及组织路径的信息 以及匿名化的信息 var otherData = GetSaveToDicomInfo(verifyInfo.SubjectVisitId); var anonymizeList = _fusionCache.GetOrSetAsync(CacheKeys.SystemAnonymization, _ => CacheHelper.GetSystemAnonymizationListAsync(_systemAnonymizationRepository), TimeSpan.FromDays(7)).Result; return ResponseOutput.Ok>(result, new { DicomStoreInfo = otherData, AnonymizeFixedList = anonymizeList.Where(t => t.IsFixed).ToList(), AnonymizeNotFixedList = anonymizeList.Where(t => t.IsFixed == false).ToList() }); } private VerifyStudyUploadResult VerifyStudyUpload(string studyInstanceUid, Guid trialId, Guid currentSubjectVisitId, Guid SubjectId) { var cacheUserId = _fusionCache.TryGet(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid)); var result = new VerifyStudyUploadResult(); if (cacheUserId.GetValueOrDefault() != _userInfo.Id && cacheUserId.HasValue) { result.AllowUpload = false; result.AllowReUpload = false; result.StudyInstanceUid = studyInstanceUid; //---当前有人正在上传归档该检查! result.ErrorMesseage = _localizer["Study_UploadArchiving"]; return result; } if (_subjectRepository.Where(t => t.Id == SubjectId).Select(t => t.Status).FirstOrDefault() == SubjectStatus.EndOfVisit) { result.AllowUpload = false; result.AllowReUpload = false; result.StudyInstanceUid = studyInstanceUid; //---受试者访视结束,不允许上传! result.ErrorMesseage = _localizer["Study_VisitEndedNotAllowed"]; return result; } Guid expectStudyId = IdentifierHelper.CreateGuid(studyInstanceUid.Trim(), trialId.ToString()); var verifyStudyInfo = _dicomStudyRepository.Where(t => t.TrialId == trialId && t.Id == expectStudyId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); result.StudyInfo = verifyStudyInfo; //数据库不存在该检查 允许上传 if (verifyStudyInfo == null) { result.AllowUpload = true; } //数据库该项目有该检查 看是否支持重传 else { //是同一个受试者 支持重传 if (verifyStudyInfo.SubjectId == SubjectId && verifyStudyInfo.SubjectVisitId == currentSubjectVisitId) { result.AllowReUpload = true; result.UploadedSeriesList = _dicomSeriesRepository.Where(t => t.StudyId == verifyStudyInfo.Id).Select(t => new UploadedSeries() { SeriesId = t.Id, SeriesInstanceUid = t.SeriesInstanceUid, SOPInstanceUIDList = t.DicomInstanceList.Select(c => c.SopInstanceUid).ToList() }).ToList(); } //不是同一个受试者 else { //有默认值,其实不用写,这里为了好理解 result.AllowUpload = false; result.AllowReUpload = false; //$"此处不可以上传。当前影像检查已经上传给受试者{verifyStudyInfo.SubjectCode}的{verifyStudyInfo.VisitName}" result.ErrorMesseage = _localizer["Study_ImgAlreadyUploaded", verifyStudyInfo.SubjectCode, verifyStudyInfo.VisitName]; } } result.StudyInstanceUid = studyInstanceUid; var @lock = _distributedLockProvider.CreateLock($"StudyUpload"); using (@lock.Acquire()) { if (result.AllowReUpload || result.AllowUpload) { _fusionCache.Set(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid), _userInfo.Id, TimeSpan.FromSeconds(30)); } else { _fusionCache.Remove(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid)); } } return result; } /// /// 获取保存到Dicom文件中的信息 /// /// /// public DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId) { //6表连接 subject trial trialSite sponsor subjectVisit site var info = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault().IfNullThrowException(); return info; } public (List SeriesInstanceUid, List SopInstanceUid) GetHasUploadSeriesAndInstance(Guid studyId) { var seriesInstanceUidList = _dicomSeriesRepository.Where(t => t.StudyId == studyId).Select(t => t.SeriesInstanceUid).ToList(); var sopInstanceUidList = _dicomInstanceRepository.Where(t => t.StudyId == studyId).Select(t => t.SopInstanceUid).ToList(); return (seriesInstanceUidList, sopInstanceUidList); } } }