Files
irc-netcore-api/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs
T
2026-06-04 13:19:38 +08:00

1448 lines
60 KiB
C#

using FellowOakDicom;
using FellowOakDicom.Imaging;
using FellowOakDicom.IO.Buffer;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service.ImageAndDoc.DTO;
using IRaCIS.Core.Infrastructure;
using Medallion.Threading;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using System.Data;
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<SCPStudy> _scpStudyRepository,
IRepository<FileUploadRecord> _fileUploadRecordRepository,
IRepository<Subject> _subjectRepository,
IRepository<StudyMonitor> _studyMonitorRepository,
IRepository<SystemAnonymization> _systemAnonymizationRepository,
IRepository<NoneDicomStudy> _noneDicomStudyRepository,
IDistributedLockProvider _distributedLockProvider, IOSSService _oSSService,
IMapper _mapper, IUserInfo _userInfo, IStringLocalizer _localizer, IFusionCache _fusionCache, IWebHostEnvironment _hostEnvironment) : 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
}
private static async Task<bool> TryWriteMergedDicomAsync(Func<Task<Stream>> sourceFactory, Stream output)
{
try
{
#region 方式一 有的必须在内存中,不能用这种
//await using var source = await sourceFactory();
//// 如果你是从 stream 打开
//var dicomFile = await DicomFile.OpenAsync(source);
#endregion
#region 方式二
await using var source = await sourceFactory();
// 【关键修复】将 OSS 流缓冲到 MemoryStream
using var bufferedStream = new MemoryStream();
if (source.CanSeek)
{
source.Position = 0;
}
else
{
// 完全复制到内存流
await source.CopyToAsync(bufferedStream);
bufferedStream.Position = 0; // 重置位置
}
// 如果你是从 stream 打开
var dicomFile = await DicomFile.OpenAsync(source.CanSeek ? source : bufferedStream);
#endregion
//获取像素是否为封装形式
var syntax = dicomFile.Dataset.InternalTransferSyntax;
//对于封装像素的文件做转换
if (syntax.IsEncapsulated)
{
// 获取 Pixel Data 标签
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
// 创建一个新的片段序列
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
// 获取每帧数据并封装为单独的片段
for (int n = 0; n < pixelData.NumberOfFrames; n++)
{
var frameData = pixelData.GetFrame(n);
newFragments.Fragments.Add(new MemoryByteBuffer(frameData.Data));
}
//var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
//var originOffsetTable = frag?.OffsetTable;
//newFragments.OffsetTable.AddRange(originOffsetTable?.ToArray());
// 替换原有的片段序列
dicomFile.Dataset.AddOrUpdate(newFragments);
}
await dicomFile.SaveAsync(output);
return true;
}
catch (Exception ex)
{
// 只记录,不传播
Log.Logger.Warning($"TryWriteMergedDicomAsync failed: {ex.Message}");
return false;
}
}
private static async Task<DicomFile> TryWriteSplitDicomAsync(Stream input)
{
var dicomFile = await DicomFile.OpenAsync(input);
var numberOfFrames = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 1);
//多帧处理逻辑
if (numberOfFrames > 1)
{
//一定要有像素数据才处理
var pixelData = DicomPixelData.Create(dicomFile.Dataset);
if (pixelData != null)
{
try
{
var syntax = pixelData.Syntax;
// 每个 fragment 固定大小 (64KB 示例,可以自己调整)
int fragmentSize = 20 * 1024;
var frag = dicomFile.Dataset.GetDicomItem<DicomOtherByteFragment>(DicomTag.PixelData);
int fragmentCount = frag?.Fragments?.Count() ?? 0;
var originOffsetTable = frag?.OffsetTable; //有可能没有表,需要自己重建
var bot = new List<uint>();
uint botOffset = 0;
//需要拆成固定片段的
if (syntax.IsEncapsulated && fragmentCount == pixelData.NumberOfFrames && numberOfFrames > 1)
{
var newFragments = new DicomOtherByteFragment(DicomTag.PixelData);
for (int n = 0; n < pixelData.NumberOfFrames; n++)
{
var frameData = pixelData.GetFrame(n);
var data = frameData.Data;
// 当前 frame 起始 offset
bot.Add(botOffset);
int offset = 0;
while (offset < data.Length)
{
int size = Math.Min(fragmentSize, data.Length - offset);
var buffer = new byte[size];
Buffer.BlockCopy(data, offset, buffer, 0, size);
newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
// 一个 fragment:
// 8-byte item header
// + fragment payload
botOffset += (uint)(8 + size);
offset += size;
}
}
#region 最终使用
//for (int n = 0; n < pixelData.NumberOfFrames; n++)
//{
// var frameData = pixelData.GetFrame(n); // 获取完整一帧
// var data = frameData.Data;
// int offset = 0;
// bot.Add(botOffset);
// botOffset += (uint)data.Length;
// while (offset < data.Length)
// {
// botOffset += 8;
// int size = Math.Min(fragmentSize, data.Length - offset);
// var buffer = new byte[size];
// Buffer.BlockCopy(data, offset, buffer, 0, size);
// newFragments.Fragments.Add(new MemoryByteBuffer(buffer));
// offset += size;
// }
//}
//保留原始偏移表
//if (originOffsetTable.Count == pixelData.NumberOfFrames)
//{
// newFragments.OffsetTable.AddRange(originOffsetTable.ToArray());
//}
//else
{
newFragments.OffsetTable.AddRange(bot.ToArray());
Log.Logger.Warning(bot.ToArray().ToJsonStr());
}
#endregion
dicomFile.Dataset.AddOrUpdate(newFragments);
}
//传递过来的就是拆分的,但是是没有偏移表的,我需要自己创建偏移表,不然生成缩略图失败
else if (syntax.IsEncapsulated && fragmentCount > pixelData.NumberOfFrames && originOffsetTable.Count == 0)
{
uint offset = 0;
bot.Add(offset);
var fistSize = frag.Fragments.FirstOrDefault()?.Size ?? 0;
//和上一个大小不一样
var isDiffrentBefore = false;
uint count = 0;
// 假设你知道每帧对应的 fragment 数量
foreach (var frameFragments in frag.Fragments)
{
count++;
if (frameFragments.Size == fistSize)
{
isDiffrentBefore = false;
// 累加这一帧所有 fragment 的大小
offset += (uint)frameFragments.Size;
continue;
}
else
{
offset += (uint)frameFragments.Size;
isDiffrentBefore = true;
}
if (isDiffrentBefore)
{
//每个Fragment 也占用字节
offset += 8 * count;
bot.Add(offset);
count = 0;
}
}
bot.RemoveAt(bot.Count - 1);
// 设置到新的 PixelData
frag.OffsetTable.AddRange(bot.ToArray());
}
}
catch (Exception ex)
{
// 只记录,不传播
Log.Logger.Warning($"拆分 failed: {ex.Message}");
}
}
}
return dicomFile;
}
/// <summary>
/// 标注遮盖影像 路径后面加了.MaskImage 就是遮盖的新路径
/// </summary>
/// <returns></returns>
public async Task<IResponseOutput> StudyMaskImage(StudyMaskImageCommand inCommand)
{
if (inCommand.SeriesId == null && inCommand.InstanceIdList == null)
{
return ResponseOutput.NotOk("SeriesId and InstanceIdList can not both be null");
}
var idPathList = new List<DownloadDicomInstanceDto>();
if (inCommand.SeriesId == null && inCommand.InstanceIdList != null)
{
idPathList = await _dicomInstanceRepository.Where(t => inCommand.InstanceIdList.Contains(t.Id)).ProjectTo<DownloadDicomInstanceDto>(_mapper.ConfigurationProvider).ToListAsync();
}
else
{
idPathList = await _dicomInstanceRepository.Where(t => t.SeriesId == inCommand.SeriesId).ProjectTo<DownloadDicomInstanceDto>(_mapper.ConfigurationProvider).ToListAsync();
}
var errorList = new List<InstanceIdPath>();
var okList = new List<InstanceIdPath>();
var batchId = Guid.NewGuid();
foreach (var item in idPathList)
{
var path = item.Path;
try
{
await using var inputStream = new MemoryStream();
if (item.IsEncapsulated)
{
var ok = await TryWriteMergedDicomAsync(() => _oSSService.GetStreamFromOSSAsync(path), inputStream);
}
else
{
await (await _oSSService.GetStreamFromOSSAsync(path)).CopyToAsync(inputStream);
}
var outPutStream = await DicomPixelMasker.MaskAsync(inputStream, inCommand.MaskRegionList);
var needUploadDicomFile = await TryWriteSplitDicomAsync(outPutStream);
var prefix = path.Substring(1, path.LastIndexOf('/') - 1);
//每次都用一个新的名字
var maskFileName = string.Empty;
if (path.Contains(".MaskDicom_"))
{
//清理缓存的里面的遮盖图,多次遮盖同一张图时,清除缓存很重要
await _oSSService.DeleteFromPrefix(path, true); //清理缓存的里面的遮盖图,多次遮盖同一张图时,清除缓存很重要
//本身就是遮盖的图,那么就要要替换guid
var length = Guid.Empty.ToString().Length + ".MaskDicom_".Length;
var restorePath = item.Path[..^length];
maskFileName = $"{Path.GetFileName(restorePath)}.MaskDicom_{batchId}";
}
else
{
//没有遮盖的,直接加上.批次_MaskDicom
maskFileName = $"{Path.GetFileName(path)}.MaskDicom_{batchId}";
}
// 直接写入内存
await using var uploadStream = new MemoryStream();
await needUploadDicomFile.SaveAsync(uploadStream);
uploadStream.Position = 0;
await _oSSService.UploadToOSSAsync(uploadStream, prefix, maskFileName, false);
var newPath = $"/{prefix}/{maskFileName}";
okList.Add(new InstanceIdPath() { Id = item.InstanceId, Path = newPath });
await _dicomInstanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.InstanceId, u => new DicomInstance() { Path = newPath, IsMasked = true });
}
catch (Exception ex)
{
errorList.Add(new InstanceIdPath() { Id = item.InstanceId, Path = path });
Log.Logger.Error(ex, $"StudyMaskImage Error for InstanceIdList Path:{path} {ex.Message}");
}
}
await _dicomInstanceRepository.SaveChangesAsync();
return ResponseOutput.Ok(new { OkList = okList, ErrorList = errorList });
}
/// <summary>
/// 撤销遮盖的影像,可以单张,也可以整个序列
/// </summary>
/// <param name="inCommand"></param>
/// <returns></returns>
public async Task<IResponseOutput> StudyUndoMaskImage(StudyUndoMaskImageCommand inCommand)
{
if (inCommand.SeriesId == null && inCommand.InstanceIdList == null)
{
return ResponseOutput.NotOk("SeriesId and InstanceIdList can not both be null");
}
var idPathList = new List<InstanceIdPath>();
if (inCommand.SeriesId == null && inCommand.InstanceIdList != null)
{
idPathList = await _dicomInstanceRepository.Where(t => inCommand.InstanceIdList.Contains(t.Id)).Select(t => new InstanceIdPath { Id = t.Id, Path = t.Path }).ToListAsync();
}
else
{
idPathList = await _dicomInstanceRepository.Where(t => t.SeriesId == inCommand.SeriesId).Select(t => new InstanceIdPath { Id = t.Id, Path = t.Path }).ToListAsync();
}
var okList = new List<InstanceIdPath>();
foreach (var item in idPathList)
{
if (item.Path.Contains(".MaskDicom_"))
{
await _oSSService.DeleteFromPrefix(item.Path, true);
var length = Guid.Empty.ToString().Length + ".MaskDicom_".Length;
var newPath = item.Path[..^length];
okList.Add(new InstanceIdPath() { Id = item.Id, Path = newPath });
await _dicomInstanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, u => new DicomInstance() { Path = newPath, IsMasked = false });
}
//if (item.Path.EndsWith("."))
//{
// await _oSSService.DeleteFromPrefix(item.Path, true);
// var newPath = item.Path[..^1];
// okList.Add(new InstanceIdPath() { Id = item.Id, Path = newPath });
// await _dicomInstanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Id, u => new DicomInstance() { Path = newPath, IsMasked = false });
//}
}
await _dicomInstanceRepository.SaveChangesAsync();
return ResponseOutput.Ok(new { OkList = okList });
}
/// <summary>
/// 获取患者基本信息
/// </summary>
/// <param name="inDto"></param>
/// <returns></returns>
[HttpPost]
public async Task<PatientInfoDto> GetPatientInfo(GetPatientInfoInDto inDto)
{
var study = await _dicomStudyRepository.Where(s => s.Id == inDto.StudyId).ProjectTo<PatientInfoDto>(_mapper.ConfigurationProvider).FirstNotNullAsync();
return study;
}
/// <summary>
/// 编辑患者基本信息
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> EditPatientInfo(EditPatientInfoCommand command)
{
await _dicomStudyRepository.UpdatePartialFromQueryAsync(command.Id, x => new DicomStudy
{
PatientSex = command.PatientSex,
PatientWeight = command.PatientWeight,
RadionuclideTotalDose = command.RadionuclideTotalDose,
RadionuclideHalfLife = command.RadionuclideHalfLife,
RadiopharmaceuticalStartTime = command.RadiopharmaceuticalStartTime,
AcquisitionTime = command.AcquisitionTime,
});
await _dicomStudyRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
/// <summary>
/// 修正患者基本信息
/// </summary>
/// <param name="command"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> AmendmentPatientInfo(EditPatientInfoCommand command)
{
await _dicomStudyRepository.UpdatePartialFromQueryAsync(command.Id, x => new DicomStudy
{
PatientSex = command.PatientSex,
PatientWeight = command.PatientWeight,
RadionuclideTotalDose = command.RadionuclideTotalDose,
RadionuclideHalfLife = command.RadionuclideHalfLife,
RadiopharmaceuticalStartTime = command.RadiopharmaceuticalStartTime,
AcquisitionTime = command.AcquisitionTime,
Reason = command.Reason,
});
await _dicomStudyRepository.SaveChangesAsync();
return ResponseOutput.Ok();
}
[TrialGlobalLimit("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.UserRoleId)
{
//---当前已有人正在上传和归档该检查!
return ResponseOutput.NotOk(I18n.T("UploadDownLoad_ArchiveInProgress"));
}
else
{
await _fusionCache.SetAsync(CacheKeys.TrialTaskStudyUidUploading(trialId, visitTaskId.Value, studyInstanceUid), _userInfo.UserRoleId, TimeSpan.FromSeconds(15));
}
}
else
{
await _fusionCache.SetAsync(CacheKeys.TrialStudyUidUploading(trialId, studyInstanceUid), _userInfo.UserRoleId, TimeSpan.FromSeconds(15));
}
return ResponseOutput.Ok();
}
[TrialGlobalLimit("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(I18n.T("UploadDownLoad_ArchiveInProgress"));
}
else
{
//在事务未完成前 防止前端重复提交
await _fusionCache.SetAsync(CacheKeys.TrialStudyUidDBLock(incommand.TrialId, incommand.Study.StudyInstanceUid), _userInfo.UserRoleId, 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<DicomStudy>(incommand.Study);
var @lock = _distributedLockProvider.CreateLock($"StudyCode");
using (await @lock.AcquireAsync())
{
//查询数据库获取最大的Code 没有记录则为0
var dbStudyCodeIntMax = _trialRepository.Where(s => s.Id == trialId, ignoreQueryFilters: true).Select(t => t.MaxDicomCode).DefaultIfEmpty().Max();
//获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增
var cacheMaxCodeInt = await _fusionCache.GetOrDefaultAsync<int>(CacheKeys.TrialStudyMaxCode(trialId));
int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1;
addStudy.Code = currentNextCodeInt;
await _trialRepository.BatchUpdateNoTrackingAsync(t => t.Id == trialId, u => new Trial() { MaxDicomCode = addStudy.Code });
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);
}
}
findStudy = addStudy;
}
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;
if (incommand.Study.RadionuclideTotalDose != string.Empty
|| incommand.Study.RadionuclideHalfLife != string.Empty
|| incommand.Study.RadiopharmaceuticalStartTime != string.Empty
)
{
await _dicomStudyRepository.UpdatePartialFromQueryAsync(t => t.Id == findStudy.Id, u => new DicomStudy()
{
RadionuclideTotalDose = incommand.Study.RadionuclideTotalDose.IsNotNullOrEmpty() ? incommand.Study.RadionuclideTotalDose : findStudy.RadionuclideTotalDose,
RadionuclideHalfLife = incommand.Study.RadionuclideHalfLife.IsNotNullOrEmpty() ? incommand.Study.RadionuclideHalfLife : findStudy.RadionuclideHalfLife,
RadiopharmaceuticalStartTime = incommand.Study.RadiopharmaceuticalStartTime.IsNotNullOrEmpty() ? incommand.Study.RadiopharmaceuticalStartTime : findStudy.RadiopharmaceuticalStartTime
});
}
}
}
var @lock2 = _distributedLockProvider.CreateLock($"StudyCommit");
using (await @lock2.AcquireAsync())
{
await _dicomInstanceRepository.SaveChangesAsync();
}
await _fileUploadRecordRepository.BatchUpdateNoTrackingAsync(t => t.UploadBatchId == incommand.UploadBatchId, u => new FileUploadRecord() { StudyCode = findStudy.StudyCode });
}
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.UserRoleId))
//.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.BodyPartForEdit,
BodyPartForEditOther = t.BodyPartForEditOther,
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.CreateUserRole.IdentityUser.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.UserRoleId))
//.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,
BodyPartForEditOther = t.BodyPartForEditOther,
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.CreateUserRole.IdentityUser.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.UserRoleId))
//.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.CreateUserRole.IdentityUser.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.CreateUserRole.IdentityUser.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, bool? isReading)
{
#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 list = _dicomStudyRepository.Where(t => t.SubjectVisitId != subjectVisitId && t.TrialId == studyInfo.TrialId && t.SubjectId == studyInfo.SubjectId)
.Select(t => new RelationStudyDTO()
{
StudyId = t.Id,
StudyCode = t.StudyCode,
VisitName = t.SubjectVisit.VisitName,
Modalities = t.Modalities,
Description = t.Description,
SeriesCount = t.SeriesList.Where(t => isReading == true ? t.IsReading == true : true).Count()
}).ToList();
list = list.Where(t => t.SeriesCount > 0).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, bool? isPacs)
{
if (isPacs == true)
{
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_scpStudyRepository.Where().FirstOrDefault(s => s.Id == studyId)));
}
else
{
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.UserRoleId).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.UserRoleId)
{
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.UserRoleId, 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.UserRoleId && 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, false, true).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.UserRoleId, 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);
}
}
}