irc-netcore-api/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs

927 lines
39 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.Contracts;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Application.Services;
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 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(
IRepository<SubjectVisit> _subjectVisitRepository,
IRepository<DicomInstance> _dicomInstanceRepository,
IRepository<DicomSeries> _dicomSeriesRepository,
IRepository<DicomStudy> _dicomstudyRepository,
IRepository<Dictionary> _dictionaryRepository,
IRepository<Trial> _trialRepository,
IRepository<VisitTask> _visitTaskRepository,
IRepository<DicomStudy> _dicomStudyRepository,
IRepository<Subject> _subjectRepository,
IRepository<StudyMonitor> _studyMonitorRepository,
IRepository<SystemAnonymization> _systemAnonymizationRepository,
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
IDistributedLockProvider _distributedLockProvider) : 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<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,
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, Guid? visitTaskId)
{
if (visitTaskId != null)
{
var cacheValue = _fusionCache.GetOrDefault<Guid>(CacheKeys.TrialTaskStudyUidUploading(trialId, visitTaskId.Value, studyInstanceUid));
if (cacheValue != Guid.Empty && cacheValue != _userInfo.Id)
{
//---当前已有人正在上传和归档该检查!
return ResponseOutput.NotOk(StaticData.International("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<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;
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??"";
if (incommand.Study.SeriesList.SelectMany(t => t.InstanceList).Count() == 0)
{
await _studyMonitorRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
//上传
if (studyMonitor.IsDicomReUpload == false)
{
//如果因为意外情况,连续点击两次,导致第一次插入了,第二次进来也会插入,在此判断一下
if (findStudy != null)
{
await _studyMonitorRepository.SaveChangesAsync();
//直接返回
return ResponseOutput.Ok();
}
var addStudy = _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;
addStudy.Code = currentNextCodeInt;
addStudy.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy));
await _fusionCache.SetAsync<int>(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<DicomSeries>(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<DicomInstance>(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<DicomSeries>(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<DicomInstance>(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<PageOutput<UnionStudyViewModel>> 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<UnionStudyViewDodel>(_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<UnionStudyViewDodel>(_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<PageOutput<UnionStudyMonitorModel>> 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))
.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);
}
/// <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 = _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<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>(_dicomStudyRepository.Where().FirstOrDefault(s => s.Id == studyId)));
}
[HttpPost]
public async Task<IResponseOutput<List<VerifyStudyUploadResult>>> 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<VerifyStudyUploadResult>();
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<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 = _systemAnonymizationRepository.Where(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 = _trialRepository.Where().FirstOrDefault(t => t.Id == verifyInfo.TrialId).IfNullThrowException();
var result = new List<VerifyStudyUploadResult>();
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<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 cacheUserId = _fusionCache.TryGet<Guid>(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<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 = _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;
}
/// <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);
}
}
}