913 lines
39 KiB
C#
913 lines
39 KiB
C#
using IRaCIS.Core.Application.Contracts;
|
||
using IRaCIS.Core.Domain.Share;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Microsoft.AspNetCore.Authorization;
|
||
using IRaCIS.Core.Application.Services;
|
||
using EasyCaching.Core;
|
||
using System.Linq.Expressions;
|
||
using IRaCIS.Core.Application.Helper;
|
||
using IRaCIS.Core.Infrastructure;
|
||
using DocumentFormat.OpenXml.Presentation;
|
||
using IRaCIS.Core.Domain.Models;
|
||
using IRaCIS.Core.Application.Filter;
|
||
using Microsoft.AspNetCore.Http;
|
||
using Newtonsoft.Json;
|
||
using System.Threading;
|
||
using Medallion.Threading;
|
||
using System.Reactive.Subjects;
|
||
using DocumentFormat.OpenXml.Drawing.Diagrams;
|
||
using IRaCIS.Core.Application.Service.ImageAndDoc.DTO;
|
||
using ZiggyCreatures.Caching.Fusion;
|
||
|
||
namespace IRaCIS.Core.Application.Service.ImageAndDoc
|
||
{
|
||
[ApiExplorerSettings(GroupName = "Image")]
|
||
public class StudyService : BaseService, IStudyService
|
||
{
|
||
|
||
|
||
|
||
private readonly IRepository<SubjectVisit> _subjectVisitRepository;
|
||
private readonly IRepository<DicomInstance> _dicomInstanceRepository;
|
||
private readonly IRepository<DicomSeries> _dicomSeriesRepository;
|
||
private readonly IRepository<DicomStudy> _dicomstudyRepository;
|
||
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||
|
||
private readonly IRepository<StudyMonitor> _studyMonitorRepository;
|
||
private readonly IDistributedLockProvider _distributedLockProvider;
|
||
|
||
public StudyService(IRepository<SubjectVisit> subjectVisitRepository,
|
||
IRepository<DicomInstance> dicomInstanceRepository,
|
||
IRepository<DicomSeries> dicomSeriesRepository, IRepository<DicomStudy> dicomstudyRepository, IRepository<Dictionary> dictionaryRepository, IRepository<StudyMonitor> studyMonitorRepository, IDistributedLockProvider distributedLockProvider)
|
||
{
|
||
_subjectVisitRepository = subjectVisitRepository;
|
||
_dicomInstanceRepository = dicomInstanceRepository;
|
||
_dicomSeriesRepository = dicomSeriesRepository;
|
||
_dicomstudyRepository = dicomstudyRepository;
|
||
_dictionaryRepository = dictionaryRepository;
|
||
_studyMonitorRepository = studyMonitorRepository;
|
||
_distributedLockProvider = distributedLockProvider;
|
||
}
|
||
|
||
|
||
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<IResponseOutput> 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,
|
||
FileSize = preArchiveStudyCommand.FileSize,
|
||
FileCount = preArchiveStudyCommand.FileCount,
|
||
|
||
};
|
||
|
||
|
||
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
|
||
|
||
return ResponseOutput.Ok(addEntity.Id);
|
||
|
||
}
|
||
|
||
[HttpGet]
|
||
public async Task<IResponseOutput> DicomUploadInProgress(Guid trialId, string studyInstanceUid)
|
||
{
|
||
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<IResponseOutput> AddOrUpdateArchiveStudy(NewArchiveStudyCommand incommand)
|
||
{
|
||
|
||
var @uploadLock = _distributedLockProvider.CreateLock($"UploadDicom");
|
||
|
||
using (await @uploadLock.AcquireAsync())
|
||
{
|
||
if (_fusionCache.GetOrDefault<Guid>(CacheKeys.TrialStudyUidDBLock(incommand.TrialId, incommand.Study.StudyInstanceUid)) != Guid.Empty)
|
||
{
|
||
//---当前已有人正在上传和归档该检查!
|
||
return ResponseOutput.NotOk(StaticData.International("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;
|
||
|
||
//上传
|
||
if (studyMonitor.IsDicomReUpload == false)
|
||
{
|
||
var study = _mapper.Map<DicomStudy>(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<int>(CacheKeys.TrialStudyMaxCode(trialId));
|
||
|
||
int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1;
|
||
|
||
study.Code = currentNextCodeInt;
|
||
|
||
study.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy));
|
||
|
||
await _fusionCache.SetAsync<int>(CacheKeys.TrialStudyMaxCode(trialId), study.Code, TimeSpan.FromMinutes(30));
|
||
|
||
}
|
||
|
||
|
||
study.Id = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString());
|
||
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);
|
||
|
||
if (findStudy != null)
|
||
{
|
||
//直接返回
|
||
return ResponseOutput.Ok();
|
||
}
|
||
|
||
|
||
|
||
//特殊处理逻辑
|
||
study.Modalities = string.Join("、", incommand.Study.SeriesList.Select(t => t.Modality).Distinct());
|
||
SpecialArchiveStudyDeal(study);
|
||
modalitys = study.Modalities;
|
||
|
||
await _dicomstudyRepository.AddAsync(study);
|
||
|
||
|
||
studyMonitor.StudyId = study.Id;
|
||
studyMonitor.StudyCode = study.StudyCode;
|
||
|
||
|
||
foreach (var seriesItem in incommand.Study.SeriesList)
|
||
{
|
||
var series = _mapper.Map<DicomSeries>(seriesItem);
|
||
|
||
series.Id = IdentifierHelper.CreateGuid(seriesItem.StudyInstanceUid, seriesItem.SeriesInstanceUid, incommand.TrialId.ToString());
|
||
series.StudyId = study.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<DicomInstance>(instanceItem);
|
||
|
||
Guid instanceId = IdentifierHelper.CreateGuid(study.StudyInstanceUid, series.SeriesInstanceUid, isntance.SopInstanceUid, study.TrialId.ToString());
|
||
|
||
isntance.Id = instanceId;
|
||
isntance.StudyId = study.Id;
|
||
isntance.SeriesId = series.Id;
|
||
|
||
isntance.TrialId = incommand.TrialId;
|
||
isntance.SubjectId = incommand.SubjectId;
|
||
isntance.SubjectVisitId = incommand.SubjectVisitId;
|
||
|
||
await _dicomInstanceRepository.AddAsync(isntance);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
var studyId = IdentifierHelper.CreateGuid(incommand.Study.StudyInstanceUid, incommand.TrialId.ToString()); ;
|
||
|
||
var study = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == studyId);
|
||
|
||
//重传的时候也要赋值检查Id
|
||
studyMonitor.StudyId = study.Id;
|
||
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;
|
||
|
||
|
||
// 少了整个序列
|
||
|
||
//某个序列下少了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<DicomSeries>(seriesItem);
|
||
|
||
series.Id = seriesId;
|
||
series.StudyId = study.Id;
|
||
|
||
series.TrialId = incommand.TrialId;
|
||
series.SubjectId = incommand.SubjectId;
|
||
series.SubjectVisitId = incommand.SubjectVisitId;
|
||
|
||
|
||
dicomSeries = await _dicomSeriesRepository.AddAsync(series);
|
||
|
||
//新的序列 那么 检查的序列数量+1
|
||
study.SeriesCount += 1;
|
||
}
|
||
else
|
||
{
|
||
//该序列掉了instance
|
||
dicomSeries.InstanceCount += seriesItem.InstanceList.Count;
|
||
}
|
||
|
||
foreach (var instanceItem in seriesItem.InstanceList)
|
||
{
|
||
var insntance = _mapper.Map<DicomInstance>(instanceItem);
|
||
insntance.Id = IdentifierHelper.CreateGuid(insntance.StudyInstanceUid, insntance.SeriesInstanceUid, insntance.SopInstanceUid, trialId.ToString());
|
||
insntance.StudyId = study.Id;
|
||
insntance.SeriesId = dicomSeries.Id;
|
||
|
||
insntance.TrialId = incommand.TrialId;
|
||
insntance.SubjectId = incommand.SubjectId;
|
||
insntance.SubjectVisitId = incommand.SubjectVisitId;
|
||
|
||
await _dicomInstanceRepository.AddAsync(insntance);
|
||
}
|
||
|
||
|
||
// 不管是新的序列 还是 该序列 掉了Instance 重传的时候 检查的instance 数量都会增加
|
||
study.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<PageOutput<UnionStudyViewModel>> GetDicomAndNoneDicomStudyList(StudyQuery studyQuery)
|
||
{
|
||
|
||
var svExpression = QCCommon.GetDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray);
|
||
|
||
var dicomStudyQuery = _repository.Where<DicomStudy>(t => t.TrialId == studyQuery.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(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression)
|
||
.WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.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.Uploader.UserName,
|
||
|
||
UploadTime = t.CreateTime
|
||
|
||
});
|
||
|
||
|
||
//.ProjectTo<UnionStudyViewDodel>(_mapper.ConfigurationProvider);
|
||
var svExpression2 = QCCommon.GetNoneDicomStudySubjectVisitFilter(studyQuery.VisitPlanArray);
|
||
|
||
|
||
var nodeDicomStudyQuery = _repository.Where<NoneDicomStudy>(t => t.TrialId == studyQuery.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(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression2)
|
||
.WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.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<UnionStudyViewDodel>(_mapper.ConfigurationProvider);
|
||
|
||
|
||
var unionQuery = dicomStudyQuery.Union(nodeDicomStudyQuery)
|
||
.WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId)
|
||
.WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId)
|
||
.WhereIf(studyQuery.TrialSiteId != null, t => t.TrialSiteId == studyQuery.TrialSiteId)
|
||
.WhereIf(studyQuery.IsFromPACS != null, t => t.IsFromPACS == studyQuery.IsFromPACS);
|
||
|
||
return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, studyQuery.SortField, studyQuery.Asc);
|
||
}
|
||
|
||
|
||
[HttpPost]
|
||
public async Task<PageOutput<UnionStudyMonitorModel>> GetDicomAndNoneDicomStudyMonitorList(StudyQuery inQuery)
|
||
{
|
||
var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(inQuery.VisitPlanArray);
|
||
var StudyMonitorQuery = _repository.Where<StudyMonitor>(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.Uploader.UserName.Contains(inQuery.Uploader))
|
||
.WhereIf(inQuery.IsSuccess != null, t => t.IsSuccess == inQuery.IsSuccess)
|
||
.WhereIf(!string.IsNullOrWhiteSpace(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode))
|
||
.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.Uploader.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.PageIndex, inQuery.PageSize, string.IsNullOrEmpty(inQuery.SortField) ? "UploadTime" : inQuery.SortField, inQuery.Asc);
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
/// <summary> 指定资源Id,渲染Dicom检查的Jpeg预览图像 </summary>
|
||
/// <param name="studyId"> Dicom检查的Id </param>
|
||
[HttpGet("{studyId:guid}")]
|
||
public async Task<FileContentResult> 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");
|
||
}
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取某个检查的关联检查列表(该受试者在这个想项目下的所有检查)
|
||
/// 点击检查检查列表中的一个检查获取对应的序列列表(调用之前的接口:/series/list/,根据StudyId,获取访视的序列列表)
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
[HttpGet("{subjectVisitId:guid}")]
|
||
[AllowAnonymous]
|
||
public IResponseOutput<List<RelationStudyDTO>> 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 = _repository.Where<DicomStudy>(u => u.SubjectVisitId == subjectVisitId).Select(t => new { t.SubjectId, t.TrialId }).FirstOrDefault().IfNullThrowException();
|
||
|
||
var query = _repository.Where<DicomStudy>(t => t.SubjectVisitId != subjectVisitId && t.TrialId == studyInfo.TrialId && t.SubjectId == studyInfo.SubjectId)
|
||
.ProjectTo<RelationStudyDTO>(_mapper.ConfigurationProvider).ToList();
|
||
|
||
var list = query.OrderBy(u => u.VisitName).ThenBy(s => s.StudyCode).ToList();
|
||
|
||
return ResponseOutput.Ok(list);
|
||
}
|
||
|
||
|
||
/// <summary> 指定资源Id,获取Dicom检查信息 </summary>
|
||
/// <param name="studyId"> Dicom检查的Id </param>
|
||
[HttpGet, Route("{studyId:guid}")]
|
||
[AllowAnonymous]
|
||
public IResponseOutput<DicomStudyDTO> Item(Guid studyId)
|
||
{
|
||
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_repository.Where<DicomStudy>().FirstOrDefault(s => s.Id == studyId)));
|
||
}
|
||
|
||
|
||
|
||
[HttpPost]
|
||
public async Task<IResponseOutput<List<VerifyStudyUploadResult>>> VerifyTaskStudyAllowUploadAsync(VerifyTaskUploadOrReupload verifyInfo)
|
||
{
|
||
|
||
var queryStudy = _repository.Where<VisitTask>(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<VerifyStudyUploadResult>();
|
||
|
||
var trialId = verifyInfo.TrialId;
|
||
|
||
var subjectId = verifyInfo.SubjectId;
|
||
|
||
var visitList = _repository.Where<SubjectVisit>(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<Guid>($"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 = _repository.Where<SystemAnonymization>(t => t.IsEnable).ToList();
|
||
|
||
return ResponseOutput.Ok<List<VerifyStudyUploadResult>>(resultList, new
|
||
{
|
||
AnonymizeFixedList = systemAnonymizationList.Where(t => t.IsFixed).ToList(),
|
||
AnonymizeNotFixedList = systemAnonymizationList.Where(t => t.IsFixed == false).ToList()
|
||
});
|
||
}
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 批量验证 检查是否可以上传 并告知原因
|
||
/// </summary>
|
||
[HttpPost]
|
||
public IResponseOutput<List<VerifyStudyUploadResult>> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyInfo)
|
||
{
|
||
var trialInfo = _repository.Where<Trial>().FirstOrDefault(t => t.Id == verifyInfo.TrialId).IfNullThrowException();
|
||
|
||
var result = new List<VerifyStudyUploadResult>();
|
||
|
||
var visitList = _repository.Where<SubjectVisit>(t => t.SubjectId == verifyInfo.SubjectId).Select(t => new { t.VisitNum, t.EarliestScanDate, t.LatestScanDate, t.Id }).ToList();
|
||
|
||
|
||
verifyInfo.StudyInstanceUidList.ForEach(waitUploadItem =>
|
||
{
|
||
|
||
if (trialInfo.IsVerifyVisitImageDate)
|
||
{
|
||
|
||
//小于当前访视 最近的最晚拍片
|
||
var before = visitList.Where(u => u.VisitNum < verifyInfo.VisitNum).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 > verifyInfo.VisitNum).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(_repository), TimeSpan.FromDays(7)).Result;
|
||
|
||
return ResponseOutput.Ok<List<VerifyStudyUploadResult>>(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 result = new VerifyStudyUploadResult();
|
||
|
||
if (_fusionCache.GetOrDefault<Guid>(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid)) != _userInfo.Id)
|
||
{
|
||
|
||
result.AllowUpload = false;
|
||
|
||
result.AllowReUpload = false;
|
||
result.StudyInstanceUid = studyInstanceUid;
|
||
//---当前有人正在上传归档该检查!
|
||
result.ErrorMesseage = _localizer["Study_UploadArchiving"];
|
||
|
||
return result;
|
||
}
|
||
|
||
if (_repository.Where<Domain.Models.Subject>(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 = _repository.Where<DicomStudy>(t => t.TrialId == trialId && t.Id == expectStudyId).ProjectTo<VerifyStudyDto>(_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 = _repository.Where<DicomSeries>(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;
|
||
}
|
||
|
||
|
||
|
||
|
||
/// <summary>
|
||
/// 获取保存到Dicom文件中的信息
|
||
/// </summary>
|
||
/// <param name="subjectVisitId"></param>
|
||
/// <returns></returns>
|
||
public DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjectVisitId)
|
||
{
|
||
//6表连接 subject trial trialSite sponsor subjectVisit site
|
||
var info = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).ProjectTo<DicomTrialSiteSubjectInfo>(_mapper.ConfigurationProvider).FirstOrDefault().IfNullThrowException();
|
||
|
||
return info;
|
||
}
|
||
|
||
public (List<string> SeriesInstanceUid, List<string> 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);
|
||
}
|
||
|
||
}
|
||
}
|