添加项目文件。

This commit is contained in:
DK
2022-03-28 15:27:40 +08:00
parent b620fcb0cf
commit dc78fd1a09
898 changed files with 173053 additions and 0 deletions
@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public sealed class DicomArchiveResult
{
public int ReceivedFileCount { get; set; }
public ICollection<string> ErrorFiles { get; set; }
public ICollection<DicomStudyBasicDTO> ArchivedDicomStudies { get; set; }
public Guid ReuploadNewStudyId { get; set; } = Guid.Empty;
public DicomArchiveResult()
{
ReceivedFileCount = 0;
ErrorFiles = new List<string>();
ArchivedDicomStudies = new List<DicomStudyBasicDTO>();
}
}
}
@@ -0,0 +1,58 @@
namespace IRaCIS.Core.Application.Contracts
{
public class DicomInstanceDTO
{
public Guid Id { get; set;}
public Guid StudyId { get; set; }
public Guid SeriesId { get; set; }
public string StudyInstanceUid { get; set; } = string.Empty;
public string SeriesInstanceUid { get; set; } = string.Empty;
public string SopInstanceUid { get; set; } = string.Empty;
public int InstanceNumber { get; set; }
public DateTime InstanceTime { get; set; }
public bool CPIStatus { get; set; }
public int ImageRows { get; set; }
public int ImageColumns { get; set; }
public int SliceLocation { get; set; }
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
//public Guid CreateUserId { get; set; }
//public DateTime CreateTime { get; set; }
//public Guid UpdateUserId { get; set; }
//public DateTime UpdateTime { get; set; }
}
public class DicomTrialSiteSubjectInfo
{
public string TrialSiteCode { get; set; } = string.Empty;
public string SiteCode { get; set; } = string.Empty;
public string SiteName { get; set; } = string.Empty;
public string SubjectCode { get; set; } = string.Empty;
public int? SubjectAge { get; set; }
public string SubjectSex { get; set; } = string.Empty;
public string TrialCode { get; set; } = string.Empty;
public string ResearchProgramNo { get; set; } = string.Empty;
public string TrialIndication { get; set; } = string.Empty;
public decimal VisitNum { get; set; }
public string SVUPDES { get; set; } = string.Empty;
public string VisitName { get; set; } = string.Empty;
public string Sponsor { get; set; } = string.Empty;
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
//public string SubjectName => LastName + " / " + FirstName;
//public string FirstName { get; set; } = string.Empty;
//public string LastName { get; set; } = string.Empty;
//public bool IsDoubleReview { get; set; }
}
}
@@ -0,0 +1,34 @@
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class DicomSeriesDTO
{
public Guid Id { get; set; }
public Guid StudyId { get; set; }
public string StudyInstanceUid { get; set; } = String.Empty;
public string SeriesInstanceUid { get; set; } = String.Empty;
public int SeriesNumber { get; set; }
public DateTime SeriesTime { get; set; }
public string Modality { get; set; } = String.Empty;
public string Description { get; set; }=String.Empty;
public int InstanceCount { get; set; }
public string SliceThickness { get; set; } = String.Empty;
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
public Guid CreateUserId { get; set; }
public DateTime CreateTime { get; set; }
public Guid UpdateUserId { get; set; }
public DateTime UpdateTime { get; set; }
public List<Guid> InstanceList { get; set; } = new List<Guid>();
}
public class DicomSeriesWithLabelDTO : DicomSeriesDTO
{
public bool HasLabel { get; set; } = false;
public bool KeySeries { get; set; } = false;
}
}
@@ -0,0 +1,91 @@
namespace IRaCIS.Core.Application.Contracts
{
public class DicomStudyBasicDTO
{
public Guid Id { get; set; }
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
public string StudyCode { get; set; } = string.Empty;
public DateTime StudyTime { get; set; }
}
public class DicomStudyDTO
{
public Guid Id { get; set; }
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
public string StudyCode { get; set; } = string.Empty;
public int Status { get; set; } = 1;
public string StudyInstanceUid { get; set; } = string.Empty;
public DateTime StudyTime { get; set; }
public string Modalities { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public int SeriesCount { get; set; } = 0;
public int InstanceCount { get; set; } = 0;
public bool SoftDelete { get; set; } = false;
public string InstitutionName { get; set; } = string.Empty;
public string PatientId { get; set; } = string.Empty;
public string PatientName { get; set; } = string.Empty;
public string PatientAge { get; set; } = string.Empty;
public string PatientSex { get; set; } = string.Empty;
public Guid UpdateUserId { get; set; }
public DateTime UpdateTime { get; set; }
public Guid CreateUserId { get; set; }
public DateTime CreateTime { get; set; }
}
public class RelationVisitDTO
{
public string VisitName { get; set; } = string.Empty;
public string TpCode { get; set; } = string.Empty;
public Guid StudyId { get; set; }
}
public class RelationStudyDTO
{
public string VisitName { get; set; } = string.Empty;
public string StudyCode { get; set; } = string.Empty;
public Guid StudyId { get; set; }
public int SeriesCount { get; set; }
public string Modalities { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
public class UploadViewInitDto
{
public Guid SubjectVisitId { get; set; }
public Guid SubjectId { get; set; }
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
//public string SubjectName { get; set; }
public string SubjectCode { get; set; } = string.Empty;
public string TrialSiteCode { get; set; } = string.Empty;
public decimal VisitNum { get; set; }
public string VisitName { get; set; } = string.Empty;
}
}
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class ImageLabelDTO
{
public Guid Id { get; set; } = Guid.Empty;
public string TpCode { get; set; } = string.Empty;
public Guid StudyId { get; set; } = Guid.Empty;
public Guid SeriesId { get; set; } = Guid.Empty;
public Guid InstanceId { get; set; } = Guid.Empty;
public string LabelValue { get; set; } = string.Empty;
}
public class ImageLabelInfo
{
public Guid StudyId { get; set; } = Guid.Empty;
public Guid SeriesId { get; set; } = Guid.Empty;
public Guid InstanceId { get; set; } = Guid.Empty;
public string LabelValue { get; set; } = string.Empty;
}
public class ImageLabelCommand
{
public string TpCode { get; set; } = string.Empty;
public List<ImageLabelInfo> ImageLabelList { get; set; } = new List<ImageLabelInfo>();
}
}
@@ -0,0 +1,23 @@
using System;
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class ImageShareCommand
{
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid? StudyId { get; set; }
public DateTime? ExpireTime { get; set; }
public string Password { get; set; } = string.Empty;
}
public class ResourceInfo
{
public Guid StudyId { get; set; }
public string Token { get; set; } = string.Empty;
}
}
@@ -0,0 +1,30 @@
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class StudyDTFDTO
{
public Guid Id { get; set; }
public Guid StudyId { get; set; }
public string FileName { get; set; } = string.Empty;
public string Path { get; set; } = string.Empty;
public Guid CreateUserId { get; set; }
public string UserName { get; set; } = string.Empty;
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTime CreateTime { get; set; }
}
public class StudyDTFAddOrUpdateCommand
{
public Guid? Id { get; set; }
public Guid StudyId { get; set; }
public string FileName { get; set; } = string.Empty;
public string Path { get; set; } = string.Empty;
}
}
@@ -0,0 +1,46 @@
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class StudyReviewerDTO
{
public Guid Id { get; set; }
public Guid StudyId { get; set; }
public Guid ReviewerId { get; set; }
}
public class StudyDistributeInfo
{
public Guid StudyId { get; set; }
public int Status { get; set; }
//public int ReviewerCount { get; set; }
}
public class StudyReviewerCommand
{
public List<StudyDistributeInfo> StudyList { get; set; } = new List<StudyDistributeInfo>();
//public List<Guid> StudyIdList { get; set; }
public Guid ReviewerId { get; set; }
//public int WorkloadType { get; set; }
public Guid TrialId { get; set; }
//public bool IsDoubleReview { get; set; }
}
public class StudyReviewerEditCommand
{
public Guid TrialId { get; set; }
public Guid StudyId { get; set; }
public bool IsDoubleReview { get; set; }
public Guid? ReviewerId1 { get; set; }
public Guid? ReviewerId2 { get; set; }
public Guid? ReviewerIdForAD { get; set; }
}
public class ReviewerDistributionDTO
{
public Guid ReviewerId { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public string ReviewerCode { get; set; } = string.Empty;
}
}
@@ -0,0 +1,27 @@
using System;
namespace IRaCIS.Core.Application.Contracts.Dicom.DTO
{
public class StudyStatusDetailDTO
{
public Guid Id { get; set; }
public Guid StudyId { get; set; }
public int Status { get; set; }
public string OptUserName { get; set; }=string.Empty;
public DateTime OptTime { get; set; }
public string Note { get; set; } = string.Empty;
}
public class StudyStatusDetailCommand
{
public Guid StudyId { get; set; }
public int Status { get; set; }
public DateTime? DeadlineTime { get; set; }
public string Note { get; set; } = string.Empty;
//QA不通过的时候传递参数
public string QAComment { get; set; } = string.Empty;
}
}
@@ -0,0 +1,61 @@
//--------------------------------------------------------------------
// 此代码由T4模板自动生成 byzhouhang 20210918
// 生成时间 2022-03-03 15:28:20
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace IRaCIS.Core.Application.ViewModel
{
/// <summary> SystemAnonymizationView 列表视图模型 </summary>
public class SystemAnonymizationView: SystemAnonymizationAddOrEdit
{
public Guid CreateUserId { get; set; }
public DateTime UpdateTime { get; set; }
public Guid UpdateUserId { get; set; }
public DateTime CreateTime { get; set; }
}
///<summary>SystemAnonymizationQuery 列表查询参数模型</summary>
public class SystemAnonymizationQuery:PageInput
{
public string Group { get; set; } = string.Empty;
public bool? IsAdd { get; set; }
public string Element { get; set; } = string.Empty;
public string TagDescription { get; set; } = string.Empty;
public string ValueRepresentation { get; set; } = string.Empty;
}
///<summary> SystemAnonymizationAddOrEdit 列表查询参数模型</summary>
public class SystemAnonymizationAddOrEdit
{
public Guid? Id { get; set; }
public string Group { get; set; } = String.Empty;
public string Element { get; set; } = String.Empty;
public string TagDescription { get; set; } = String.Empty;
public string TagDescriptionCN { get; set; } = String.Empty;
public string ReplaceValue { get; set; } = String.Empty;
public string ValueRepresentation { get; set; } = String.Empty;
public bool IsAdd { get; set; }
public bool IsEnable { get; set; }
public bool IsFixed { get; set; }
}
}
@@ -0,0 +1,109 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Contracts
{
public class UnionStudyBaseModel
{
public Guid TrialId { get; set; }
public Guid SiteId { get; set; }
public Guid SubjectId { get; set; }
public Guid SubjectVisitId { get; set; }
public string SubjectCode { get; set; }
public string VisitName { get; set; } = string.Empty;
public decimal VisitNum { get; set; }
public string TrialSiteCode { get; set; } = string.Empty;
public string TrialSiteAliasName { get; set; } = string.Empty;
public string Uploader { get; set; } = string.Empty;
public DateTime UploadTime { get; set; }
public string StudyCode => IsDicom ? DicomStudyCode : "NST" + NoneDicomCode.ToString("D5");
[JsonIgnore]
public string DicomStudyCode { get; set; } = string.Empty;
[JsonIgnore]
public int NoneDicomCode { get; set; }
public bool IsDicom { get; set; }
}
public class UnionStudyMonitorModel : UnionStudyBaseModel
{
public Guid StudyId { get; set; }
public DateTime UploadStartTime { get; set; }
public DateTime UploadFinishedTime { get; set; }
public decimal FileSize { get; set; }
public string IP { get; set; }
public bool IsDicomReUpload { get; set; }
public bool IsDicom { get; set; }
public int FileCount { get; set; }
}
public class UnionStudyViewModel:UnionStudyBaseModel
{
public Guid Id { get; set; }
public int? Count { get; set; }
public string Modalities { get; set; } = string.Empty;
public string Bodypart { get; set; } = string.Empty;
public DateTime? StudyTime { get; set; }
}
public class StudyQuery:PageInput
{
[NotDefault]
public Guid TrialId { get; set; }
public Guid? SubjectId { get; set; }
public Guid? SiteId { get; set; }
public Guid? SubjectVisitId { get; set; }
public string SubjectInfo { get; set; } = String.Empty;
public string VisitPlanInfo { get; set; } = String.Empty;
}
}
@@ -0,0 +1,447 @@
using Dicom;
using Dicom.Imaging.Codec;
using EasyCaching.Core;
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Share;
using Microsoft.Extensions.Hosting;
using System.Text;
using IRaCIS.Core.Application.Contracts;
namespace IRaCIS.Core.Application.Services
{
public class DicomArchiveService : IDicomArchiveService
{
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IRepository<DicomSeries> _seriesRepository;
private readonly IRepository<DicomInstance> _instanceRepository;
private readonly IEasyCachingProvider _provider;
private readonly DicomFileStoreHelper _dicomFileStoreHelper;
private static object lockCodeGenerate = new object();
public DicomArchiveService(IRepository<DicomStudy> studyRepository,
IRepository<DicomSeries> seriesRepository,
IRepository<DicomInstance> instanceRepository,
IHostEnvironment hostEnvironment,
DicomFileStoreHelper dicomFileStoreHelper,
IEasyCachingProvider provider)
{
_dicomFileStoreHelper = dicomFileStoreHelper;
_studyRepository = studyRepository;
_seriesRepository = seriesRepository;
_instanceRepository = instanceRepository;
_provider = provider;
}
public async Task<bool> DicomDBDataSaveChange()
{
var success = await _studyRepository.SaveChangesAsync();
return success;
}
public async Task<Guid> ArchiveDicomStreamAsync(Stream dicomStream,
DicomTrialSiteSubjectInfo addtionalInfo, List<string> seriesInstanceUidList, List<string> instanceUidList)
{
DicomFile dicomFile = await DicomFile.OpenAsync(dicomStream, Encoding.Default);
DicomDataset dataset = dicomFile.Dataset;
//如果数据库存在该instance 记录 那么就不处理
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
if (instanceUidList.Any(t => t == sopInstanceUid))
{
return IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString());
}
var anonymize_AddFixedFiledList = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddFixedFiled).Value;
var anonymize_AddIRCInfoFiled = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_AddIRCInfoFiled).Value;
var anonymize_FixedField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_FixedField).Value;
var anonymize_IRCInfoField = _provider.Get<List<SystemAnonymization>>(StaticData.Anonymize_IRCInfoField).Value;
foreach (var item in anonymize_AddFixedFiledList.Union(anonymize_FixedField))
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
dataset.AddOrUpdate(dicomTag, item.ReplaceValue);
}
foreach (var item in anonymize_AddIRCInfoFiled.Union(anonymize_IRCInfoField))
{
var dicomTag = new DicomTag(Convert.ToUInt16(item.Group, 16), Convert.ToUInt16(item.Element, 16));
if (dicomTag == DicomTag.ClinicalTrialProtocolID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode);
}
if (dicomTag == DicomTag.ClinicalTrialSiteID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialSiteCode);
}
if (dicomTag == DicomTag.ClinicalTrialSubjectID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.SubjectCode);
}
if (dicomTag == DicomTag.ClinicalTrialTimePointID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.VisitNum.ToString());
}
if (dicomTag == DicomTag.PatientID)
{
dataset.AddOrUpdate(dicomTag, addtionalInfo.TrialCode+"_"+ addtionalInfo.SubjectCode);
}
}
#region
////按照配置文件 匿名化
//foreach (var anonymizeItem in SystemConfig.AnonymizeTagList)
//{
// if (anonymizeItem.Enable)
// {
// ushort group = Convert.ToUInt16(anonymizeItem.Group, 16);
// ushort element = Convert.ToUInt16(anonymizeItem.Element, 16);
// dataset.AddOrUpdate(new DicomTag(group, element), anonymizeItem.ReplaceValue);
// }
//}
//if (AppSettings.AddClinicalInfo) //是否需要写入临床信息
//{
// //Dicom 文件中写入临床信息
// dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolID, addtionalInfo.TrialCode); //Trial
// dataset.AddOrUpdate(DicomTag.ClinicalTrialProtocolName, addtionalInfo.TrialIndication); //indication
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSponsorName, addtionalInfo.Sponsor);//sponsor
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteID, addtionalInfo.SiteCode); //SiteId
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSiteName, addtionalInfo.SiteName);//SiteName
// dataset.AddOrUpdate(DicomTag.ClinicalTrialSubjectID, addtionalInfo.SubjectCode + " " + addtionalInfo.SubjectSex);//SubjectId
// dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointID, addtionalInfo.VisitNum.ToString()); // TimePoint
// dataset.AddOrUpdate(DicomTag.ClinicalTrialTimePointDescription, addtionalInfo.VisitName + " " + addtionalInfo.SVUPDES);
//}
//DicomStudy dicomStudy = null;
//DicomSeries dicomSeries = null;
//DicomInstance dicomInstance = null;
//lock (lockTest)
//{
// dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
// dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
// dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries);
// if (isStudyNeedAdd) _studyRepository.Add(dicomStudy);
// if (isSeriesNeedAdd) _seriesRepository.Add(dicomSeries);
// _instanceRepository.Add(dicomInstance);
//}
#endregion
DicomStudy dicomStudy = CreateDicomStudy(dataset, addtionalInfo, out bool isStudyNeedAdd);
DicomSeries dicomSeries = CreateDicomSeries(dataset, dicomStudy, out bool isSeriesNeedAdd);
DicomInstance dicomInstance = CreateDicomInstance(dataset, dicomStudy, dicomSeries);
if (isStudyNeedAdd) await _studyRepository.AddAsync(dicomStudy);
if (isSeriesNeedAdd) await _seriesRepository.AddAsync(dicomSeries);
await _instanceRepository.AddAsync(dicomInstance);
string filePath = _dicomFileStoreHelper.CreateInstanceFilePath(dicomStudy, dicomSeries.Id, dicomInstance.Id);
var samplesPerPixel = dataset.GetSingleValueOrDefault(DicomTag.SamplesPerPixel, string.Empty);
var photometricInterpretation = dataset.GetSingleValueOrDefault(DicomTag.PhotometricInterpretation, string.Empty);
if (samplesPerPixel == "1" && (photometricInterpretation.ToUpper() == "MONOCHROME2" || photometricInterpretation.ToUpper() == "MONOCHROME1"))//MONOCHROME2
{
if (dataset.InternalTransferSyntax.IsEncapsulated)
{
await dicomFile.SaveAsync(filePath);
}
else
{
await dicomFile.Clone(DicomTransferSyntax.JPEGLSLossless).SaveAsync(filePath);
}
}
else
{
if (dataset.InternalTransferSyntax.IsEncapsulated) await dicomFile.SaveAsync(filePath);
else await dicomFile.Clone(DicomTransferSyntax.RLELossless).SaveAsync(filePath); //RLELossless
}
return dicomInstance.StudyId;
}
private DicomStudy CreateDicomStudy(DicomDataset dataset, DicomTrialSiteSubjectInfo addtionalInfo, out bool isStudyNeedAdd)
{
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid, addtionalInfo.TrialId.ToString());
// 每个线程都查询数据库最大的,和缓存中最大的,取最大值为基数生成Code
//var id = Thread.CurrentThread.ManagedThreadId.ToString("00");
//虽然每个文件都会进来,但是只要查询过,就会跟踪,不会再次查询数据库 这里线程并发会有问题,得加锁,不然生成Code 出错
DicomStudy dicomStudy = _studyRepository.ImageFind(studyId, typeof(DicomStudy));
if (dicomStudy != null)
{
isStudyNeedAdd = false;
return dicomStudy;
}
//_logger.LogWarning($"Thread {id} ,studyUid{studyInstanceUid} 生成StudyId{studyId}");
isStudyNeedAdd = true;
dicomStudy = new DicomStudy
{
Id = studyId,
StudyInstanceUid = studyInstanceUid,
StudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, DateTime.Now).TimeOfDay),//dataset.GetDateTime(DicomTag.StudyDate, DicomTag.StudyTime),
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
//需要特殊处理
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
SiteId = addtionalInfo.SiteId,
TrialId = addtionalInfo.TrialId,
SubjectId = addtionalInfo.SubjectId,
SubjectVisitId = addtionalInfo.SubjectVisitId,
//IsDoubleReview = addtionalInfo.IsDoubleReview,
SeriesCount = 0,
InstanceCount = 0
};
if (dicomStudy.PatientBirthDate.Length == 8)
{
dicomStudy.PatientBirthDate = $"{dicomStudy.PatientBirthDate[0]}{dicomStudy.PatientBirthDate[1]}{dicomStudy.PatientBirthDate[2]}{dicomStudy.PatientBirthDate[3]}-{dicomStudy.PatientBirthDate[4]}{dicomStudy.PatientBirthDate[5]}-{dicomStudy.PatientBirthDate[6]}{dicomStudy.PatientBirthDate[7]}";
}
lock (lockCodeGenerate)
{
//查询数据库获取最大的Code 没有记录则为0
var dbStudyCodeIntMax = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.Code).DefaultIfEmpty().Max();
//获取缓存中的值 并发的时候,需要记录,已被占用的值 这样其他线程在此占用的最大的值上递增
var cacheMaxCodeInt = _provider.Get<int>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value;
int currentNextCodeInt = cacheMaxCodeInt > dbStudyCodeIntMax ? cacheMaxCodeInt + 1 : dbStudyCodeIntMax + 1;
dicomStudy.Code = currentNextCodeInt;
dicomStudy.StudyCode = "ST" + currentNextCodeInt.ToString("D5");
_provider.Set<int>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.Code, TimeSpan.FromMinutes(30));
}
#region Setting Code old
//var studyCode = _studyRepository.Where(s => s.TrialId == addtionalInfo.TrialId).Select(t => t.StudyCode).OrderByDescending(c => c).FirstOrDefault();
//var cacheMaxCode = _provider.Get<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}").Value;
//if (studyCode == null && string.IsNullOrEmpty(cacheMaxCode))
//{
// dicomStudy.StudyCode = "ST" + 1.ToString().PadLeft(5, '0');
// _logger.LogWarning($"Thread {id} DB{studyCode} 生成{ dicomStudy.StudyCode}");
//}
//else
//{
// int dbNum = 0;
// int cacheNum = 0;
// if (studyCode != null)
// {
// int.TryParse(studyCode.Substring(studyCode.Length - 5, 5), out dbNum);
// }
// if (!string.IsNullOrEmpty(cacheMaxCode))
// {
// int.TryParse(cacheMaxCode.Substring(cacheMaxCode.Length - 5, 5), out cacheNum);
// }
// dbNum = cacheNum > dbNum ? cacheNum : dbNum;
// dicomStudy.StudyCode = "ST" + (++dbNum).ToString().PadLeft(5, '0');
// _logger.LogWarning($" Thread {id} DB{studyCode} cache:{cacheNum} 生成{ dicomStudy.StudyCode}");
//}
//_provider.Set<string>($"{addtionalInfo.TrialId }_{ StaticData.StudyMaxCode}", dicomStudy.StudyCode, TimeSpan.FromMinutes(30));
#endregion
return dicomStudy;
}
private DicomSeries CreateDicomSeries(DicomDataset dataset, DicomStudy dicomStudy, out bool isSeriesNeedAdd)
{
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
Guid seriesId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, seriesInstanceUid, dicomStudy.TrialId.ToString());
//有几个序列会查询几次
DicomSeries dicomSeries = _seriesRepository.ImageFind(seriesId, typeof(DicomSeries));
if (dicomSeries != null)
{
isSeriesNeedAdd = false;
return dicomSeries;
}
else
{
//var id = Thread.CurrentThread.ManagedThreadId.ToString("00");
isSeriesNeedAdd = true;
dicomSeries = new DicomSeries
{
Id = seriesId,
StudyId = dicomStudy.Id,
StudyInstanceUid = dicomStudy.StudyInstanceUid,
SeriesInstanceUid = seriesInstanceUid,
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay), // dataset.GetDateTime(DicomTag.SeriesDate, DicomTag.SeriesTime),
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
SiteId = dicomStudy.SiteId,
TrialId = dicomStudy.TrialId,
SubjectId = dicomStudy.SubjectId,
SubjectVisitId = dicomStudy.SubjectVisitId,
InstanceCount = 0
};
++dicomStudy.SeriesCount;
//_logger.LogWarning($"线程:{id},sericeId:{seriesId},count:{SeriesDic.Keys.Count}");
return dicomSeries;
}
}
private DicomInstance CreateDicomInstance(DicomDataset dataset, DicomStudy dicomStudy, DicomSeries dicomSeries)
{
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
Guid instanceId = IdentifierHelper.CreateGuid(dicomStudy.StudyInstanceUid, dicomSeries.SeriesInstanceUid, sopInstanceUid, dicomStudy.TrialId.ToString());
DicomInstance dicomInstance = new DicomInstance
{
Id = instanceId,
StudyId = dicomStudy.Id,
SeriesId = dicomSeries.Id,
SiteId = dicomStudy.SiteId,
TrialId = dicomStudy.TrialId,
SubjectId = dicomStudy.SubjectId,
SubjectVisitId = dicomStudy.SubjectVisitId,
StudyInstanceUid = dicomStudy.StudyInstanceUid,
SeriesInstanceUid = dicomSeries.SeriesInstanceUid,
SopInstanceUid = sopInstanceUid,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, DateTime.Now).TimeOfDay),
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
CPIStatus = false,
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
};
++dicomStudy.InstanceCount;
++dicomSeries.InstanceCount;
return dicomInstance;
}
}
}
@@ -0,0 +1,81 @@
using Microsoft.Extensions.Hosting;
using System;
using System.IO;
using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.Application
{
public class DicomFileStoreHelper
{
private readonly IWebHostEnvironment _hostEnvironment;
private static string _fileStorePath = string.Empty;
private readonly ILogger<DicomFileStoreHelper> _logger;
public DicomFileStoreHelper(IWebHostEnvironment hostEnvironment, ILogger<DicomFileStoreHelper> logger)
{
_logger = logger;
_hostEnvironment = hostEnvironment;
var rootPath = Directory.GetParent(_hostEnvironment.ContentRootPath.TrimEnd('\\')).IfNullThrowException().FullName;
//上传根路径
_fileStorePath = Path.Combine(rootPath, StaticData.TrialDataFolder);
}
public string GetInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, string instanceId)
{
return Path.Combine(_fileStorePath, dicomStudy.TrialId.ToString(),
dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.SubjectVisitId.ToString(), StaticData.DicomFolder, dicomStudy.Id.ToString(), instanceId.ToString() + ".dcm");
}
public string CreateInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, Guid instanceId)
{
//加入访视层级 和Data
var path = Path.Combine(_fileStorePath, dicomStudy.TrialId.ToString(),
dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.SubjectVisitId.ToString(), StaticData.DicomFolder, dicomStudy.Id.ToString());
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
return Path.Combine(path, instanceId.ToString() + ".dcm");
}
public string GetSubjectVisitPath(Guid trialId,Guid siteId,Guid subjectId, Guid subjectVisitId)
{
return Path.Combine(_fileStorePath, trialId.ToString(),
siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.DicomFolder);
}
//public static string CreateInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, Guid instanceId)
//{
// string path = Path.Combine(_fileStorePath, "Dicom", dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(),
// dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString());
// if (!Directory.Exists(path)) Directory.CreateDirectory(path);
// return Path.Combine(path, instanceId.ToString() + ".dcm");
//}
//public static string GetInstanceFilePath(DicomStudy dicomStudy, Guid seriesId, Guid instanceId)
//{
// return Path.Combine(_fileStorePath, "Dicom", dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(),
// dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString(), instanceId.ToString() + ".dcm");
//}
//public static void RemoveStudyDirectory(DicomStudy dicomStudy)
//{
// string path = Path.Combine(_fileStorePath, "Dicom", dicomStudy.CreateTime.Year.ToString(), dicomStudy.TrialId.ToString(),
// dicomStudy.SiteId.ToString(), dicomStudy.SubjectId.ToString(), dicomStudy.Id.ToString());
// if (Directory.Exists(path)) Directory.Delete(path, true);
//}
}
}
@@ -0,0 +1,37 @@
using FellowOakDicom.Imaging;
using SixLabors.ImageSharp.Formats.Jpeg;
namespace IRaCIS.Core.Application.Dicom
{
public static class DicomRenderingHelper
{
public static Stream RenderPreviewJpeg(string filePath)
{
string jpegPath = filePath + ".preview.jpg";
if (!File.Exists(jpegPath))
{
using (Stream stream = new FileStream(jpegPath, FileMode.Create))
{
DicomImage image = new DicomImage(filePath);
//image.ShowOverlays = false;
//image.Scale = Math.Min(Math.Min(128.0 / image.Width, 128.0 / image.Height), 1.0);
//image.RenderImage().AsClonedBitmap().Save(stream, ImageFormat.Jpeg);
var sharpimage = image.RenderImage().AsSharpImage();
sharpimage.Save(stream, new JpegEncoder());
}
}
return new FileStream(jpegPath, FileMode.Open);
}
public static void RemovePreviewJpeg(string filePath)
{
string jpegPath = filePath + ".preview.jpg";
if (File.Exists(jpegPath)) File.Delete(jpegPath);
}
}
}
@@ -0,0 +1,24 @@
using System.Security.Cryptography;
using System.Text;
namespace IRaCIS.Core.Application.Services
{
static class IdentifierHelper
{
//private static MD5 md5 = new MD5CryptoServiceProvider
//
private static MD5 md5 = MD5.Create();
private static object lockObj =new object();
public static Guid CreateGuid(params string[] parts)
{
lock (lockObj)
{
return new Guid(md5.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(parts))));
}
}
}
}
@@ -0,0 +1,133 @@
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using IRaCIS.Core.Application.Auth;
namespace IRaCIS.Core.Application.Services
{
[AllowAnonymous, ApiExplorerSettings(GroupName = "Image")]
public class ImageShareService : BaseService, IImageShareService
{
private readonly IRepository<ImageShare> _imageShareRepository;
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IConfiguration _configuration;
private readonly ITokenService _tokenService;
public ImageShareService(IRepository<ImageShare> imageShareRepository, IRepository<DicomStudy> studyRepository, IConfiguration configuration, ITokenService tokenService)
{
_imageShareRepository = imageShareRepository;
_studyRepository = studyRepository;
_configuration = configuration;
_tokenService = tokenService;
}
[HttpPost]
public async Task<IResponseOutput> CreateImageShare(ImageShareCommand imageShareCommand)
{
if (imageShareCommand.StudyId == null)
{
#region 访线
//var subjectVisit1 = _subjectVisitRepository.FirstOrDefault(t =>
// t.TrialId == imageShareCommand.TrialId && t.SubjectId == imageShareCommand.SubjectId &&
// t.VisitNum == 1);
//if (subjectVisit1 == null)
//{
// return ResponseOutput.NotOk("当前无影像数据,无法分享!");
//}
//imageShareCommand.StudyId =
// _studyRepository.GetAll().First(t => t.SubjectVisitId == subjectVisit1.Id&&t.Status != (int)StudyStatus.Abandon).Id;
#endregion
var studyIds = await _studyRepository.AsQueryable()
.Where(t => t.TrialId == imageShareCommand.TrialId && t.SubjectId == imageShareCommand.SubjectId &&
t.SiteId == imageShareCommand.SiteId)
.Select(u => u.Id).ToListAsync();
if (!studyIds.Any())
{
return ResponseOutput.NotOk("There is no image in the current study and cannot be shared! ");
}
imageShareCommand.StudyId = studyIds.First();
}
//验证码 4位
int verificationPassWord = new Random().Next(1000, 10000);
imageShareCommand.Password = verificationPassWord.ToString();
//配置文件读取过期时间
var days = int.Parse(_configuration.GetSection("imageShare:ExpireDays").Value);
imageShareCommand.ExpireTime = DateTime.Now.AddDays(days);
var imageShare = _mapper.Map<ImageShare>(imageShareCommand);
await _imageShareRepository.AddAsync(imageShare);
var success = await _imageShareRepository.SaveChangesAsync();
return ResponseOutput.Result(success, new { ResourceId = imageShare.Id, Password = verificationPassWord.ToString() });
}
[HttpGet, Route("{resourceId:guid}/{password}")]
public async Task<IResponseOutput> VerifyShareImage(Guid resourceId, string password)
{
var pWord = password.Trim();
var imageShare = await _imageShareRepository.FirstOrDefaultAsync(t => t.Id == resourceId);
if (imageShare == null)
{
return ResponseOutput.NotOk("The resource does not exist! ");
}
if (pWord != imageShare.Password.Trim())
{
return ResponseOutput.NotOk("Shared password error!");
}
if (DateTime.Now > imageShare.ExpireTime)
{
return ResponseOutput.NotOk("Resource sharing has expired!");
}
var resource = new ResourceInfo()
{
StudyId = imageShare.StudyId,
Token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo()
{
Id = Guid.Empty,
IsReviewer = false,
IsAdmin = false,
RealName = "Share001",
UserName = "Share001",
Sex = 0,
//UserType = "ShareType",
UserTypeEnum = UserTypeEnum.ShareImage,
Code = "ShareCode001",
}))
};
return ResponseOutput.Ok(resource);
}
}
}
@@ -0,0 +1,109 @@
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Dicom;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.Application.Services
{
[ApiExplorerSettings(GroupName = "Image")]
[AllowAnonymous]
public class InstanceService : BaseService, IInstanceService
{
private readonly IRepository<DicomInstance> _instanceRepository;
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IRepository<KeyInstance> _keyInstanceRepository;
private readonly DicomFileStoreHelper _dicomFileStoreHelper;
public InstanceService(IRepository<DicomInstance> instanceRepository, IRepository<DicomStudy> studyRepository,
IRepository<KeyInstance> keyInstanceRepository, DicomFileStoreHelper dicomFileStoreHelper)
{
_dicomFileStoreHelper = dicomFileStoreHelper;
_instanceRepository = instanceRepository;
_studyRepository = studyRepository;
_keyInstanceRepository = keyInstanceRepository;
}
/// <summary> 指定资源Id,获取Dicom序列所属的实例信息列表 </summary>
/// <param name="seriesId"> Dicom序列的Id </param>
[HttpGet("{seriesId:guid}")]
public async Task<IEnumerable<DicomInstanceDTO>> List(Guid seriesId)
{
return await _instanceRepository.Where(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber).
ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime)
.ProjectTo<DicomInstanceDTO>(_mapper.ConfigurationProvider).ToListAsync();
}
/// <summary> 指定资源Id,获取Dicom序列所属的实例Id列表 </summary>
/// <param name="seriesId"> Dicom序列的Id </param>
/// <param name="tpCode"></param>
/// <param name="key"></param>
[HttpGet, Route("{seriesId:guid}/{tpCode?}/{key?}")]
public IEnumerable<Guid> List(Guid seriesId, string tpCode, bool? key)
{
if (key != null && key.HasValue && key.Value)
{
return _keyInstanceRepository.Where(s => s.TpCode == tpCode).Select(t => t.InstanceId).Distinct();
}
else
return _instanceRepository.Where(s => s.SeriesId == seriesId).OrderBy(s => s.InstanceNumber).Select(t => t.Id);
}
[AllowAnonymous]
[HttpGet, Route("{instanceId:guid}")]
public async Task<FileContentResult> Preview(Guid instanceId)
{
var path = string.Empty;
DicomInstance dicomInstance = await _instanceRepository.FirstOrDefaultAsync(s => s.Id == instanceId).IfNullThrowException();
DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException();
path = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString());
using (var sw = DicomRenderingHelper.RenderPreviewJpeg(path))
{
var bytes = new byte[sw.Length];
sw.Read(bytes, 0, bytes.Length);
sw.Close();
return new FileContentResult(bytes, "image/jpeg");
}
}
[AllowAnonymous]
[HttpGet, Route("{instanceId:guid}")]
public async Task<FileContentResult> Content(Guid instanceId)
{
var filePath = string.Empty;
DicomInstance dicomInstance = await _instanceRepository.FirstOrDefaultAsync(s => s.Id == instanceId).IfNullThrowException();
DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException();
if (dicomInstance.Anonymize) //被匿名化
{
filePath = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id + ".Anonymize");
}
else filePath = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString());
using (var sw = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var bytes = new byte[sw.Length];
sw.Read(bytes, 0, bytes.Length);
sw.Close();
return new FileContentResult(bytes, "application/octet-stream");
}
}
}
}
@@ -0,0 +1,24 @@
namespace IRaCIS.Core.Application.Contracts.Dicom
{
public interface IDicomArchiveService
{
Task<Guid> ArchiveDicomStreamAsync(Stream dicomStream, DicomTrialSiteSubjectInfo addtionalInfo, List<string> seriesInstanceUidList, List<string> instanceUidList);
//ICollection<DicomStudyDTO> GetArchivedStudyList(List<Guid> archivedStudyIds);
Task<bool> DicomDBDataSaveChange();
//[EasyCachingAble(Expiration = 6000)]
//IEnumerable<DicomSeriesDTO> GetSeriesList(Guid studyId);
//IEnumerable<DicomSeriesWithLabelDTO> GetSeriesWithLabelList(Guid studyId,string tpCode);
////[EasyCachingAble(Expiration = 6000)]
//string GetSeriesPreview(Guid seriesId);
}
}
@@ -0,0 +1,10 @@
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
namespace IRaCIS.Core.Application.Services
{
public interface IImageShareService
{
Task<IResponseOutput> CreateImageShare(ImageShareCommand imageShareCommand);
Task<IResponseOutput> VerifyShareImage(Guid resourceId, string password);
}
}
@@ -0,0 +1,13 @@
using IRaCIS.Core.Application.Contracts;
using Microsoft.AspNetCore.Mvc;
namespace IRaCIS.Core.Application.Services
{
public interface IInstanceService
{
Task<FileContentResult> Content(Guid instanceId);
Task<IEnumerable<DicomInstanceDTO>> List(Guid seriesId);
IEnumerable<Guid> List(Guid seriesId, string tpCode, bool? key);
Task<FileContentResult> Preview(Guid instanceId);
}
}
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.Application.Contracts.Image
{
public interface IStudyDTFService
{
IResponseOutput AddStudyDTF(StudyDTFAddOrUpdateCommand studyDtfAddOrUpdate);
IResponseOutput DeleteStudyDTF(Guid studyDTFId );
List<StudyDTFDTO> GetStudyDtfdtos(Guid triaId, string studyInstanceUid);
}
}
@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using System.Threading.Tasks;
using IRaCIS.Core.Infrastructure.Extention;
using IRaCIS.Core.Application.Contracts;
namespace IRaCIS.Application.Interfaces
{
public interface IStudyService
{
//IResponseOutput ForwardStudy(Guid studyId);
DicomTrialSiteSubjectInfo GetSaveToDicomInfo(Guid subjctVisitId);
void GetHasUploadSeriesAndInstance(Guid studyId, ref List<string> seriesInstanceUidList, ref List<string> instanceUidList);
void UploadOrReUploadNeedTodo(ArchiveStudyCommand archiveStudyCommand, List<Guid> archiveStudyIds, ref DicomArchiveResult reusult, StudyMonitor monitor);
//PageOutput<StudyDTO> GetStudyList(StudyQueryDTO queryDto);
//Task<IResponseOutput> DicomAnonymize(Guid studyId, string optuserName);
//List<StudyStatusDetailDTO> GetStudyStatusDetailList(Guid studyId);
//IEnumerable<RelationVisitDTO> GetRelationVisitList(decimal visitNum, string tpCode);
//List<SubjectVisitStudyDTO> GetSubjectVisitStudyList(Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId);
//PageOutput<DistributeReviewerStudyStatusDTO> GetDistributeStudyList(StudyStatusQueryDTO studyStatusQueryDto);
//IEnumerable<RelationStudyDTO> GetAllRelationStudyList(Guid studyId);
/// <summary> 根据项目Id,医生Id,获取受试者访视点(不分页)</summary>
//IEnumerable<StudyStatDTO> GetStudyStatList(Guid trialId, Guid subjectId, Guid siteId);
//bool UpdateReUploadNewStudyCode(Guid studyId, Guid newStudyId);
//void ReUploadSameStudy(Guid subjectVisitId, Guid studyId);
//VerifyStudyUploadResult VerifyStudyAllowUpload(string studyInstanceUid, Guid trialId, Guid? studyId);
//List< VerifyStudyUploadResult> VerifyStudyAllowUpload(VerifyUploadOrReupload verifyList);
//void ClearStudyInstanceAndSeriseData(Guid studyId);
//IResponseOutput DeleteStudy(Guid id);
//Guid AddSubjectVisit(Guid subjectId, Guid siteId, Guid visitStageId, DateTime? SVSTDTC, DateTime? SVENDTC);
//void UpdateSubjectLatestInfo(Guid subjectVisitId);
//void UpdateSubjectVisit(Guid subjectVisitId, Guid studyId);
//bool DistributeStudy(StudyReviewerCommand studyReviewer);
//IResponseOutput EditStudyReviewer(StudyReviewerEditCommand studyReviewerEditCommand);
//void UploadStudyDeal(Guid studyId);
/// <summary>
/// 更新Study 表中的Status,并且往 StudyStatusDetail详情表中插入一条状态变化记录
/// </summary>
/// <param name="studyStatusDetailDTO"></param>
//List<ReviewerDistributionDTO> GetReviewerListByTrialId(Guid trialId);
//IResponseOutput UpdateStudyStatus(StudyStatusDetailCommand studyStatusDetailDTO);
//string GetStudyPreview(Guid studyId);
//DicomStudyDTO GetStudyItem(Guid studyId);
////bool SaveImageLabel(ImageLabelDTO label);
//bool SaveImageLabelList(ImageLabelCommand imageLabelCommand);
//IEnumerable<ImageLabelDTO> GetImageLabel(string tpCode);
//IResponseOutput DealNonDicomFile(Dictionary<string,string> filePathDic, ArchiveStudyCommand archiveStudyCommand);
//bool ReUploadDifferentStudy(Guid subjectVisitId, Guid abandonStudyId, Guid newStudyId);
//List<QANoticeDTO> GetQANoticeList(Guid studyId);
}
}
@@ -0,0 +1,24 @@
//--------------------------------------------------------------------
// 此代码由T4模板自动生成 byzhouhang 20210918
// 生成时间 2022-03-03 15:28:27
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Application.ViewModel;
namespace IRaCIS.Core.Application.Interfaces
{
/// <summary>
/// ISystemAnonymizationService
/// </summary>
public interface ISystemAnonymizationService
{
Task<PageOutput<SystemAnonymizationView>> GetSystemAnonymizationList(SystemAnonymizationQuery querySystemAnonymization);
Task<IResponseOutput> AddOrUpdateSystemAnonymization(SystemAnonymizationAddOrEdit addOrEditSystemAnonymization);
Task<IResponseOutput> DeleteSystemAnonymization(Guid systemAnonymizationId);
}
}
@@ -0,0 +1,118 @@
using IRaCIS.Core.Application.Contracts.Dicom;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Infra.EFCore;
using IRaCIS.Core.Domain.Models;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Dicom;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.AspNetCore.Authorization;
namespace IRaCIS.Core.Application.Services
{
[ApiExplorerSettings(GroupName = "Image")]
[AllowAnonymous]
public class SeriesService : BaseService
{
private readonly IRepository<ImageLabel> _imageLabelRepository;
private readonly IRepository<DicomSeries> _seriesRepository;
private readonly IRepository<DicomInstance> _instanceRepository;
private readonly IRepository<DicomStudy> _studyRepository;
private readonly IRepository<KeyInstance> _keyInstanceRepository;
private readonly DicomFileStoreHelper _dicomFileStoreHelper;
public SeriesService(IRepository<DicomInstance> instanceRepository, IRepository<DicomStudy> studyRepository,
IRepository<KeyInstance> keyInstanceRepository, DicomFileStoreHelper dicomFileStoreHelper, IRepository<DicomSeries> seriesRepository, IRepository<ImageLabel> imageLabelRepository)
{
_seriesRepository = seriesRepository;
_dicomFileStoreHelper = dicomFileStoreHelper;
_instanceRepository = instanceRepository;
_studyRepository = studyRepository;
_keyInstanceRepository = keyInstanceRepository;
_imageLabelRepository = imageLabelRepository;
}
//医生读片那一块有耦合,关键序列 这里暂时留存
/// <summary> 指定资源Id,获取Dicom检查所属序列信息列表 </summary>
/// <param name="studyId"> Dicom检查的Id </param>
/// <param name="tpCode"></param>
[HttpGet, Route("{studyId:guid}/{tpCode?}")]
public async Task<IResponseOutput<List<DicomSeriesWithLabelDTO>>> List(Guid studyId, string? tpCode)
{
var seriesList = await _seriesRepository.Where(s => s.StudyId == studyId).OrderBy(s => s.SeriesNumber).
ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime)
.ProjectTo<DicomSeriesWithLabelDTO>(_mapper.ConfigurationProvider).ToListAsync();
bool hasKeyInstance = false;
var SeriesIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.SeriesId).Distinct().ToList();
var instanceIdList = _imageLabelRepository.Where(u => u.TpCode == tpCode).Select(s => s.InstanceId).Distinct().ToList();
foreach (var item in seriesList)
{
if (SeriesIdList.Contains(item.Id))
{
item.HasLabel = true;
hasKeyInstance = true;
}
else item.HasLabel = false;
}
if (hasKeyInstance)
{
seriesList.Add(new DicomSeriesWithLabelDTO
{
KeySeries = true,
Id = SeriesIdList[0],
InstanceCount = instanceIdList.Count,
HasLabel = true,
Modality = seriesList[0].Modality,
Description = "Key Series"
});
}
var idList = await _instanceRepository.Where(s => s.StudyId == studyId).ToListAsync();//.GroupBy(u => u.SeriesId);
foreach (var item in seriesList)
{
if (item.KeySeries)
{
item.InstanceList = instanceIdList;
}
else
{
item.InstanceList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber)
.ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime).Select(u => u.Id).ToList();
}
}
return ResponseOutput.Ok(seriesList);
}
/// <summary> 指定资源Id,渲染Dicom序列的Jpeg预览图像 </summary>
/// <param name="seriesId"> Dicom序列的Id </param>
[HttpGet, Route("{seriesId:guid}")]
[AllowAnonymous]
public async Task<FileContentResult> Preview(Guid seriesId)
{
string path = string.Empty;
DicomInstance dicomInstance = await _instanceRepository.FirstOrDefaultAsync(s => s.SeriesId == seriesId).IfNullThrowException();
DicomStudy dicomStudy = await _studyRepository.FirstOrDefaultAsync(s => s.Id == dicomInstance.StudyId).IfNullThrowException();
path = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString());
using (var sw = DicomRenderingHelper.RenderPreviewJpeg(path))
{
var bytes = new byte[sw.Length];
sw.Read(bytes, 0, bytes.Length);
sw.Close();
return new FileContentResult(bytes, "image/jpeg");
}
}
}
}
@@ -0,0 +1,108 @@
//using IRaCIS.Core.Application.Contracts.Dicom.DTO;
//using IRaCIS.Core.Application.Contracts.Image;
//using IRaCIS.Core.Infrastructure.Extention;
//using IRaCIS.Core.Infra.EFCore;
//using IRaCIS.Core.Domain.Models;
//using Microsoft.AspNetCore.Authorization;
//using Microsoft.AspNetCore.Mvc;
//namespace IRaCIS.Core.Application.Services
//{
// [ ApiExplorerSettings(GroupName = "Image")]
// public class StudyDTFService : BaseService, IStudyDTFService
// {
// private readonly IRepository<StudyDTF> _studyDtfRepository;
// private readonly IRepository<User> _userRepository;
// public StudyDTFService(IRepository<StudyDTF> studyDtfRepository, IRepository<User> userRepository)
// {
// _studyDtfRepository = studyDtfRepository;
// _userRepository = userRepository;
// }
// public IResponseOutput AddStudyDTF(StudyDTFAddOrUpdateCommand studyDtfAddOrUpdate)
// {
// var studyDtf = _mapper.Map<StudyDTF>(studyDtfAddOrUpdate);
// _studyDtfRepository.Add(studyDtf);
// var success = _studyDtfRepository.SaveChanges();
// return ResponseOutput.Result(success);
// }
// //[NonDynamicMethod]
// //[AllowAnonymous]
// //public async Task<IResponseOutput> UpdateStudyDTF(StudyDTFAddOrUpdateCommand studyDtfAddOrUpdate)
// //{
// // studyDtfAddOrUpdate.Id.IfNullThrowException();
// // var studyDTF = await _studyDtfRepository.FindAsync( studyDtfAddOrUpdate.Id.Value );
// // studyDTF.Path = "9999";
// // //var success= await _efStudyDtfRepository.UpdateAsync(studyDTF,true);
// // var success = await _studyDtfRepository.SaveChangesAsync();
// // //_efStudyDtfRepository.UpdateAsync();
// // return ResponseOutput.Ok(success);
// //}
// [HttpDelete("{trialId:guid}/{studyDTFId:guid}")]
// public IResponseOutput DeleteStudyDTF(Guid studyDTFId)
// {
// return ResponseOutput.Result(_studyDtfRepository.Delete(t => t.Id == studyDTFId));
// }
// [HttpGet("{trialId:guid}/{studyInstanceUid}")]
// public List<StudyDTFDTO> GetStudyDtfdtos(Guid trialId, string studyInstanceUid)
// {
// var studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
// var query = from studyDtf in _studyDtfRepository.Where(t => t.StudyId == studyId)
// join user in _userRepository.AsQueryable() on studyDtf.CreateUserId equals user.Id
// select new StudyDTFDTO()
// {
// CreateTime = studyDtf.CreateTime,
// CreateUserId = studyDtf.CreateUserId,
// FileName = studyDtf.FileName,
// Id = studyDtf.Id,
// FirstName = user.FirstName,
// LastName = user.LastName,
// UserName = user.UserName,
// Path = studyDtf.Path
// };
// return query.ToList();
// }
// [HttpGet("{trialId:guid}/{studyId:guid}")]
// public List<StudyDTFDTO> GetStudyDtfList(Guid trialId, Guid studyId)
// {
// //var studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
// var query = from studyDtf in _studyDtfRepository.Where(t => t.StudyId == studyId)
// join user in _userRepository.AsQueryable() on studyDtf.CreateUserId equals user.Id
// select new StudyDTFDTO()
// {
// CreateTime = studyDtf.CreateTime,
// CreateUserId = studyDtf.CreateUserId,
// FileName = studyDtf.FileName,
// Id = studyDtf.Id,
// FirstName = user.FirstName,
// LastName = user.LastName,
// UserName = user.UserName,
// Path = studyDtf.Path
// };
// return query.ToList();
// }
// }
//}
@@ -0,0 +1,417 @@
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Domain.Share;
using Microsoft.AspNetCore.Mvc;
using IRaCIS.Core.Application.Dicom;
using Microsoft.AspNetCore.Authorization;
using IRaCIS.Core.Application.Services;
using EasyCaching.Core;
namespace IRaCIS.Core.Application.Service.ImageAndDoc
{
[ApiExplorerSettings(GroupName = "Image")]
public class StudyService : BaseService
{
private readonly DicomFileStoreHelper _dicomFileStoreHelper;
private readonly IEasyCachingProvider _provider;
public StudyService(DicomFileStoreHelper dicomFileStoreHelper, IEasyCachingProvider provider)
{
_dicomFileStoreHelper = dicomFileStoreHelper;
_provider = provider;
}
[HttpPost]
public async Task<PageOutput<UnionStudyViewModel>> GetDicomAndNoneDicomStudyList(StudyQuery studyQuery)
{
var dicomStudyQuery = _repository.Where<DicomStudy>(t => t.TrialId == studyQuery.TrialId)
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.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(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo))
.Select(t => new UnionStudyViewModel()
{
TrialId = t.TrialId,
SiteId = t.SiteId,
SubjectId = t.SubjectId,
SubjectVisitId = t.SubjectVisitId,
VisitName = t.SubjectVisit.VisitName,
VisitNum = t.SubjectVisit.VisitNum,
IsDicom = true,
SubjectCode = t.Subject.Code,
Id = t.Id,
Bodypart = t.BodyPartExamined,
Modalities = t.Modalities,
Count = t.SeriesCount,
DicomStudyCode = t.StudyCode,
NoneDicomCode = 0,
StudyTime = t.StudyTime,
TrialSiteAliasName = t.TrialSite.TrialSiteAliasName,
TrialSiteCode = t.TrialSite.TrialSiteCode,
Uploader = t.Uploader.LastName + " / " + t.Uploader.LastName,
UploadTime = t.CreateTime
});
//.ProjectTo<UnionStudyViewDodel>(_mapper.ConfigurationProvider);
var nodeDicomStudyQuery = _repository.Where<NoneDicomStudy>(t => t.TrialId == studyQuery.TrialId)
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.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(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo))
.Select(t => new UnionStudyViewModel()
{
TrialId = t.TrialId,
SiteId = t.SiteId,
SubjectId = t.SubjectId,
SubjectVisitId = t.SubjectVisitId,
VisitName = t.SubjectVisit.VisitName,
VisitNum = t.SubjectVisit.VisitNum,
IsDicom = false,
SubjectCode = t.Subject.Code,
Id = t.Id,
Bodypart = t.BodyPart,
Modalities = t.Modality,
Count = t.NoneDicomFileList.Count(),
NoneDicomCode = t.Code,
DicomStudyCode = string.Empty,
StudyTime = t.ImageDate,
TrialSiteAliasName = t.TrialSite.TrialSiteAliasName,
TrialSiteCode = t.TrialSite.TrialSiteCode,
Uploader = t.CreateUser.LastName + " / " + t.CreateUser.LastName,
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.SiteId != null, t => t.SiteId == studyQuery.SiteId);
return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, studyQuery.SortField, studyQuery.Asc);
}
[HttpPost]
public async Task<PageOutput<UnionStudyMonitorModel>> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery)
{
var dicomStudyQuery = _repository.Where<StudyMonitor>(t => t.TrialId == studyQuery.TrialId && t.IsDicom)
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.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(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo))
.Select(t => new UnionStudyMonitorModel()
{
TrialId = t.TrialId,
SiteId = t.SiteId,
SubjectId = t.SubjectId,
SubjectVisitId = t.SubjectVisitId,
VisitName = t.SubjectVisit.VisitName,
VisitNum = t.SubjectVisit.VisitNum,
SubjectCode = t.Subject.Code,
TrialSiteAliasName = t.TrialSite.TrialSiteAliasName,
TrialSiteCode = t.TrialSite.TrialSiteCode,
Uploader = t.Uploader.LastName + " / " + t.Uploader.LastName,
UploadTime = t.CreateTime,
IP = t.IP,
FileCount = t.FileCount,
FileSize = t.FileSize,
UploadFinishedTime = t.UploadFinishedTime,
UploadStartTime = t.UploadStartTime,
IsDicomReUpload = t.IsDicomReUpload,
StudyId = t.Id,
IsDicom = t.IsDicom,
DicomStudyCode = t.DicomStudy.StudyCode,
NoneDicomCode = 0,
});
//.ProjectTo<UnionStudyViewDodel>(_mapper.ConfigurationProvider);
var nodeDicomStudyQuery = _repository.Where<StudyMonitor>(t => t.TrialId == studyQuery.TrialId && t.IsDicom == false)
.WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator, t => t.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(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo))
.Select(t => new UnionStudyMonitorModel()
{
TrialId = t.TrialId,
SiteId = t.SiteId,
SubjectId = t.SubjectId,
SubjectVisitId = t.SubjectVisitId,
VisitName = t.SubjectVisit.VisitName,
VisitNum = t.SubjectVisit.VisitNum,
SubjectCode = t.Subject.Code,
TrialSiteAliasName = t.TrialSite.TrialSiteAliasName,
TrialSiteCode = t.TrialSite.TrialSiteCode,
Uploader = t.Uploader.LastName + " / " + t.Uploader.LastName,
UploadTime = t.CreateTime,
IP = t.IP,
FileCount = t.FileCount,
FileSize = t.FileSize,
UploadFinishedTime = t.UploadFinishedTime,
UploadStartTime = t.UploadStartTime,
IsDicomReUpload = t.IsDicomReUpload,
StudyId = t.Id,
IsDicom = t.IsDicom,
DicomStudyCode = string.Empty,
NoneDicomCode = t.NoneDicomStudy.Code,
});
//.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.SiteId != null, t => t.SiteId == studyQuery.SiteId);
return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, string.IsNullOrEmpty(studyQuery.SortField) ? "UploadTime" : studyQuery.SortField, studyQuery.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;
DicomInstance dicomInstance = await _repository.FirstOrDefaultAsync<DicomInstance>(s => s.StudyId == studyId);
if (dicomInstance != null)
{
DicomStudy dicomStudy = await _repository.FirstOrDefaultAsync<DicomStudy>(s => s.Id == dicomInstance.StudyId);
if (dicomStudy != null)
{
path = _dicomFileStoreHelper.GetInstanceFilePath(dicomStudy, dicomInstance.SeriesId, dicomInstance.Id.ToString());
}
}
using (var sw = DicomRenderingHelper.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}")]
public IResponseOutput<DicomStudyDTO> Item(Guid studyId)
{
return ResponseOutput.Ok(_mapper.Map<DicomStudyDTO>(_repository.Where<DicomStudy>().FirstOrDefault(s => s.Id == studyId)));
}
/// <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.EarliestScanDate);
if (before != null && before > waitUploadItem.StudyDate)
{
result.Add(new VerifyStudyUploadResult() { ErrorMesseage = "当前访视检查时间不能小于该访视之前检查的时间,请核对检查数据是否有误", StudyInstanceUid = waitUploadItem.StudyInstanceUid });
return;
}
//大于当前访视 最近的最晚拍片日期
var after = visitList.Where(u => u.VisitNum > verifyInfo.VisitNum).Min(k => k.LatestScanDate);
if (after != null && after < waitUploadItem.StudyDate)
{
result.Add(new VerifyStudyUploadResult() { ErrorMesseage = "当前访视检查时间不能大于该访视之后的检查时间,请核对检查数据是否有误", StudyInstanceUid = waitUploadItem.StudyInstanceUid });
return;
}
}
var temp = VerifyStudyUpload(waitUploadItem.StudyInstanceUid, verifyInfo.TrialId, verifyInfo.SubjectVisitId, verifyInfo.SubjectId);
result.Add(temp);
});
return ResponseOutput.Ok(result);
}
private VerifyStudyUploadResult VerifyStudyUpload(string studyInstanceUid, Guid trialId, Guid currentSubjectVisitId, Guid SubjectId)
{
var result = new VerifyStudyUploadResult();
if (_provider.Exists("StudyUid_" + studyInstanceUid))
{
result.AllowUpload = false;
result.AllowReUpload = false;
result.StudyInstanceUid = studyInstanceUid;
result.ErrorMesseage = "当前有人正在上传归档该检查!";
return result;
}
if (_repository.Where<Subject>(t => t.Id == SubjectId).Select(t => t.Status).FirstOrDefault() == SubjectStatus.EndOfVisit)
{
result.AllowUpload = false;
result.AllowReUpload = false;
result.StudyInstanceUid = studyInstanceUid;
result.ErrorMesseage = "受试者访视结束,不允许上传!";
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;
}
//不是同一个受试者
else
{
//有默认值,其实不用写,这里为了好理解
result.AllowUpload = false;
result.AllowReUpload = false;
result.ErrorMesseage = $"{ verifyStudyInfo.StudyTime }: This DICOM images have been uploaded and allocate to the {verifyStudyInfo.VisitName} of the subject {verifyStudyInfo.SubjectCode} (Study ID: {verifyStudyInfo.StudyCode}), which cannot continue to upload and assign it to others.";
}
}
result.StudyInstanceUid = studyInstanceUid;
return result;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
//--------------------------------------------------------------------
// 此代码由T4模板自动生成 byzhouhang 20210918
// 生成时间 2022-03-03 15:28:32
// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。
//--------------------------------------------------------------------
using IRaCIS.Core.Application.Interfaces;
using IRaCIS.Core.Application.MediatR.Handlers;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Infra.EFCore;
using MediatR;
using Microsoft.AspNetCore.Mvc;
namespace IRaCIS.Core.Application.Service
{
/// <summary>
/// SystemAnonymizationService
/// </summary>
[ApiExplorerSettings(GroupName = "Image")]
public class SystemAnonymizationService : BaseService, ISystemAnonymizationService
{
private readonly IMediator _mediator;
private readonly IRepository<SystemAnonymization> systemAnonymizationRepository;
public SystemAnonymizationService(IMediator mediator, IRepository<SystemAnonymization> systemAnonymizationRepository)
{
_mediator = mediator;
this.systemAnonymizationRepository = systemAnonymizationRepository;
}
[HttpPost]
public async Task<PageOutput<SystemAnonymizationView>> GetSystemAnonymizationList(SystemAnonymizationQuery querySystemAnonymization)
{
var systemAnonymizationQueryable = systemAnonymizationRepository
.WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.Group), t => t.Group.Contains(querySystemAnonymization.Group))
.WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.Element), t => t.Element.Contains(querySystemAnonymization.Element))
.WhereIf(querySystemAnonymization.IsAdd != null, t => t.IsAdd == querySystemAnonymization.IsAdd)
.WhereIf(!string.IsNullOrEmpty(querySystemAnonymization.ValueRepresentation), t => t.ValueRepresentation.Contains(querySystemAnonymization.ValueRepresentation))
.ProjectTo<SystemAnonymizationView>(_mapper.ConfigurationProvider);
return await systemAnonymizationQueryable.ToPagedListAsync(querySystemAnonymization.PageIndex, querySystemAnonymization.PageSize, querySystemAnonymization.SortField, querySystemAnonymization.Asc);
}
public async Task<IResponseOutput> AddOrUpdateSystemAnonymization(SystemAnonymizationAddOrEdit addOrEditSystemAnonymization)
{
var entity = await _repository.InsertOrUpdateAsync<SystemAnonymization, SystemAnonymizationAddOrEdit>(addOrEditSystemAnonymization, true);
await _mediator.Send(new AnonymizeCacheRequest());
return ResponseOutput.Ok(entity.Id.ToString());
}
[HttpDelete("{systemAnonymizationId:guid}")]
public async Task<IResponseOutput> DeleteSystemAnonymization(Guid systemAnonymizationId)
{
var success = await systemAnonymizationRepository.DeleteFromQueryAsync(t => t.Id == systemAnonymizationId);
return ResponseOutput.Result(success);
}
}
}
@@ -0,0 +1,116 @@
using AutoMapper;
using IRaCIS.Application.Contracts;
using IRaCIS.Core.Application.Contracts;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Application.Contracts.DTO;
using IRaCIS.Core.Application.ViewModel;
using IRaCIS.Core.Domain.Models;
namespace IRaCIS.Core.Application.Service
{
public class ImageAndDocConfig : Profile
{
public ImageAndDocConfig()
{
CreateMap<Report, ReportDTO>();
CreateMap<ImageLabel, ImageLabelDTO>();
CreateMap<DicomSeries, DicomSeriesWithLabelDTO>();
//影像上传 检查
CreateMap<DicomStudy, VerifyStudyDto>()
.ForMember(d => d.SubjectId, u => u.MapFrom(s => s.Subject.Id))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName))
.ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code))
.ForMember(d => d.FirstName, u => u.MapFrom(s => s.Subject.FirstName))
.ForMember(d => d.LastName, u => u.MapFrom(s => s.Subject.LastName));
CreateMap<DicomStudy, StudyDTO>().IncludeMembers(t => t.Subject, u => u.SubjectVisit)
.ForMember(d => d.UploaderFirstName, u => u.MapFrom(s => s.Site.SiteName))
.ForMember(d => d.SiteName, u => u.MapFrom(s => s.Site.SiteName))
.ForMember(d => d.UploaderFirstName, u => u.MapFrom(s => s.Uploader.FirstName))
.ForMember(d => d.UploaderLastName, u => u.MapFrom(s => s.Uploader.LastName))
.ForMember(d => d.StudyStatus, u => u.MapFrom(s => s.Status))
.ForMember(d => d.UploadedTime, u => u.MapFrom(s => s.CreateTime))
.ForMember(d => d.DTFCount, u => u.MapFrom(s => s.StudyDTFList.Count()));
CreateMap<Subject, StudyDTO>();
CreateMap<SubjectVisit, StudyDTO>();
CreateMap<DicomStudy, RelationStudyDTO>()
.ForMember(d => d.StudyId, u => u.MapFrom(s => s.Id))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName));
CreateMap<DicomStudy, DicomStudyBasicDTO>();
CreateMap<DicomSeries, QASeriesInfoDto>()
.ForMember(o => o.StudyCode, t => t.MapFrom(u => u.DicomStudy.StudyCode))
.ForMember(o => o.InstanceList, t => t.MapFrom(u => u.DicomInstanceList.Select(t=>t.Id).ToArray()))
;
CreateMap<DicomStudy, QAStudyInfoDTO>()
.ForMember(o => o.UploadedTime, t => t.MapFrom(u => u.CreateTime))
.ForMember(o => o.Uploader, t => t.MapFrom(u => u.Uploader.LastName + " / " + u.Uploader.FirstName))
.ForMember(o => o.StudyId, t => t.MapFrom(u => u.Id));
CreateMap<StudyCommand, DicomStudy>();
//CreateMap<ImageQACommand, ImageQA>();
CreateMap<ImageShareCommand, ImageShare>();
CreateMap<StudyDTFAddOrUpdateCommand, StudyDTF>();
CreateMap<StudyStatusDetail, StudyStatusDetailDTO>();
CreateMap<DicomInstance, DicomInstanceDTO>();
CreateMap<DicomStudy, DicomStudyDTO>();
CreateMap<DicomSeries, DicomSeriesDTO>();
CreateMap<SystemAnonymization, SystemAnonymizationAddOrEdit>().ReverseMap();
CreateMap<SystemAnonymization, SystemAnonymizationView>();
CreateMap<DicomStudy, UnionStudyViewModel>()
.ForMember(d => d.DicomStudyCode, u => u.MapFrom(s => s.StudyCode))
//.ForMember(d => d.Modality, u => u.MapFrom(s => s.Modalities))
.ForMember(d => d.Bodypart, u => u.MapFrom(s => s.BodyPartExamined))
.ForMember(d => d.StudyTime, u => u.MapFrom(s => s.StudyTime))
.ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode))
.ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName))
.ForMember(d => d.IsDicom, u => u.MapFrom(s => true))
.ForMember(d => d.Count, u => u.MapFrom(s => s.SeriesCount))
.ForMember(d => d.VisitNum, u => u.MapFrom(s => s.SubjectVisit.VisitNum))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName));
CreateMap<NoneDicomStudy, UnionStudyViewModel>()
.ForMember(d => d.NoneDicomCode, u => u.MapFrom(s => s.Code))
//.ForMember(d => d.Modality, u => u.MapFrom(s => s.Modalities))
.ForMember(d => d.Bodypart, u => u.MapFrom(s => s.BodyPart))
.ForMember(d => d.StudyTime, u => u.MapFrom(s => s.ImageDate))
.ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode))
.ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName))
.ForMember(d => d.IsDicom, u => u.MapFrom(s => false))
.ForMember(d => d.Count, u => u.MapFrom(s => s.NoneDicomFileList.Count()))
.ForMember(d => d.VisitNum, u => u.MapFrom(s => s.SubjectVisit.VisitNum))
.ForMember(d => d.VisitName, u => u.MapFrom(s => s.SubjectVisit.VisitName));
}
}
}