Merge branch 'Test_HIR_Net8' of https://gitea.frp.extimaging.com/XCKJ/irc-netcore-api into Test_HIR_Net8
continuous-integration/drone/push Build is passing Details

Test_HIR_Net8
he 2025-08-26 14:14:53 +08:00
commit 5f7c4dda97
36 changed files with 21098 additions and 126 deletions

View File

@ -0,0 +1,42 @@
using Serilog;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Helper
{
public static class SafeBussinessHelper
{
public static async Task<bool> RunAsync(Func<Task> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
{
try
{
await func();
return true;
}
catch (Exception ex)
{
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
return false;
}
}
public static async Task<(bool Success, T? Result)> RunAsync<T>(Func<Task<T>> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
{
try
{
var result = await func();
return (true, result);
}
catch (Exception ex)
{
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
return (false, default);
}
}
}
}

View File

@ -227,7 +227,11 @@ namespace IRaCIS.Core.SCP.Service
{
foreach (var item in findCmoveInfo.HopitalGroupIdList)
{
await _studyGroupRepository.AddAsync(new SCPStudyHospitalGroup() { SCPStudyId = g.Key.SCPStudyId, HospitalGroupId = item });
if (!_studyGroupRepository.Any(t => t.SCPStudyId == g.Key.SCPStudyId && t.HospitalGroupId == item))
{
await _studyGroupRepository.AddAsync(new SCPStudyHospitalGroup() { SCPStudyId = g.Key.SCPStudyId, HospitalGroupId = item });
}
}
}
else

View File

@ -12,6 +12,7 @@ using IRaCIS.Core.SCP.Service;
using IRaCIS.Core.Infra.EFCore;
using MassTransit;
using Serilog.Sinks.File;
using IRaCIS.Core.Infrastructure.Extention;
namespace IRaCIS.Core.SCP.Service
{
@ -149,6 +150,10 @@ namespace IRaCIS.Core.SCP.Service
Id = studyId,
StudyInstanceUid = studyInstanceUid,
StudyTime = studyTime,
DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
//ModalityForEdit = modalityForEdit,
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
@ -183,6 +188,11 @@ namespace IRaCIS.Core.SCP.Service
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
}
}
else
{
findStudy.DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty);
findStudy.DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty);
}
if (findSerice == null)
@ -200,6 +210,9 @@ namespace IRaCIS.Core.SCP.Service
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
@ -221,7 +234,19 @@ namespace IRaCIS.Core.SCP.Service
++findStudy.SeriesCount;
}
else
{
findSerice.DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty);
findSerice.DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty);
}
var transferSyntaxUID = dataset.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty);
var isEncapsulated = false;
if (transferSyntaxUID.IsNotNullOrEmpty())
{
isEncapsulated = DicomTransferSyntax.Lookup(DicomUID.Parse(transferSyntaxUID)).IsEncapsulated;
}
if (findInstance == null)
{
@ -235,6 +260,12 @@ namespace IRaCIS.Core.SCP.Service
SeriesInstanceUid = findSerice.SeriesInstanceUid,
SopInstanceUid = sopInstanceUid,
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
TransferSyntaxUID = transferSyntaxUID,
MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
IsEncapsulated = isEncapsulated,
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
@ -261,6 +292,14 @@ namespace IRaCIS.Core.SCP.Service
++findStudy.InstanceCount;
++findSerice.InstanceCount;
}
else
{
findInstance.SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty);
findInstance.MediaStorageSOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty);
findInstance.TransferSyntaxUID = transferSyntaxUID;
findInstance.MediaStorageSOPInstanceUID = dataset.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty);
findInstance.IsEncapsulated = isEncapsulated;
}
if (isPatientNeedAdd)
{

View File

@ -81,7 +81,6 @@
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
<PackageReference Include="Hangfire.SqlServer" Version="1.8.20" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
@ -155,6 +154,12 @@
</Content>
</ItemGroup>
<ItemGroup>
<None Update="Resources\GeoLite2-City.mmdb">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties properties_4launchsettings_1json__JsonSchema="" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 MiB

View File

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using ILogger = Microsoft.Extensions.Logging.ILogger;
namespace IRaCIS.Core.Application.Service.BusinessFilter;

View File

@ -10,5 +10,6 @@ global using Microsoft.Extensions.Localization;
global using AutoMapper;
global using IRaCIS.Core.Domain.Share;
global using IRaCIS.Core.Application.BusinessFilter;
global using Serilog;

View File

@ -0,0 +1,190 @@
using FellowOakDicom;
using FellowOakDicom.Media;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Helper
{
public class StudyDIRInfo
{
public Guid SubjectVisitId { get; set; }
// Study
public Guid DicomStudyId { get; set; }
public string PatientId { get; set; }
public string PatientName { get; set; }
public string PatientBirthDate { get; set; }
public string PatientSex { get; set; }
public string StudyInstanceUid { get; set; }
public string StudyId { get; set; }
public string DicomStudyDate { get; set; }
public string DicomStudyTime { get; set; }
public string AccessionNumber { get; set; }
public string StudyDescription { get; set; }
// Series
public string SeriesInstanceUid { get; set; }
public string Modality { get; set; }
public string DicomSeriesDate { get; set; }
public string DicomSeriesTime { get; set; }
public int SeriesNumber { get; set; }
public string SeriesDescription { get; set; }
// Instance
public Guid InstanceId { get; set; }
public string SopInstanceUid { get; set; }
public string SOPClassUID { get; set; }
public int InstanceNumber { get; set; }
public string MediaStorageSOPClassUID { get; set; }
public string MediaStorageSOPInstanceUID { get; set; }
public string TransferSyntaxUID { get; set; }
}
public static class DicomDIRHelper
{
public static async Task GenerateStudyDIRAndUploadAsync(List<StudyDIRInfo> list, Dictionary<string, string> dic, string ossFolder, IOSSService _oSSService)
{
var mappings = new List<string>();
int index = 1;
var dicomDir = new DicomDirectory();
foreach (var item in list.OrderBy(t => t.SeriesNumber).ThenBy(t => t.InstanceNumber))
{
var dicomUid = DicomUID.Enumerate().FirstOrDefault(uid => uid.UID == item.TransferSyntaxUID);
if (dicomUid != null)
{
var ts = DicomTransferSyntax.Query(dicomUid);
var dataset = new DicomDataset(ts)
{
{ DicomTag.PatientID, item.PatientId ?? string.Empty },
{ DicomTag.PatientName, item.PatientName ?? string.Empty },
{ DicomTag.PatientBirthDate, item.PatientBirthDate ?? string.Empty },
{ DicomTag.PatientSex, item.PatientSex ?? string.Empty },
{ DicomTag.StudyInstanceUID, item.StudyInstanceUid ?? string.Empty },
{ DicomTag.StudyID, item.StudyId ?? string.Empty },
{ DicomTag.StudyDate, item.DicomStudyDate ?? string.Empty },
{ DicomTag.StudyTime, item.DicomStudyTime ?? string.Empty },
{ DicomTag.AccessionNumber, item.AccessionNumber ?? string.Empty },
{ DicomTag.StudyDescription, item.StudyDescription ?? string.Empty },
{ DicomTag.SeriesInstanceUID, item.SeriesInstanceUid ?? string.Empty },
{ DicomTag.Modality, item.Modality ?? string.Empty },
{ DicomTag.SeriesDate, item.DicomSeriesDate ?? string.Empty },
{ DicomTag.SeriesTime, item.DicomSeriesTime ?? string.Empty },
{ DicomTag.SeriesNumber, item.SeriesNumber.ToString() ?? string.Empty },
{ DicomTag.SeriesDescription, item.SeriesDescription ?? string.Empty },
{ DicomTag.SOPInstanceUID, item.SopInstanceUid ?? string.Empty },
{ DicomTag.SOPClassUID, item.SOPClassUID ?? string.Empty },
{ DicomTag.InstanceNumber, item.InstanceNumber.ToString() ?? string.Empty },
{ DicomTag.MediaStorageSOPClassUID, item.MediaStorageSOPClassUID ?? string.Empty },
{ DicomTag.MediaStorageSOPInstanceUID, item.MediaStorageSOPInstanceUID ?? string.Empty },
{ DicomTag.TransferSyntaxUID, item.TransferSyntaxUID ?? string.Empty },
};
var dicomFile = new DicomFile(dataset);
// 文件名递增格式IM_00001, IM_00002, ...
string filename = $@"IMAGE/IM_{index:D5}"; // :D5 表示补足5位
mappings.Add($"{filename} => {item.InstanceId}");
dic.Add(item.InstanceId.ToString(), Path.GetFileName(filename));
dicomDir.AddFile(dicomFile, filename);
index++;
}
}
//有实际的文件
if (mappings.Count > 0)
{
#region 写入临时路径
var tempFilePath = Path.GetTempFileName();
// 保存 DICOMDIR 到临时文件 不能直接写入到流种
await dicomDir.SaveAsync(tempFilePath);
using (var memoryStream = new MemoryStream(File.ReadAllBytes(tempFilePath)))
{
// 重置流位置
memoryStream.Position = 0;
await _oSSService.UploadToOSSAsync(memoryStream, ossFolder, "DICOMDIR", false);
}
//清理临时文件
File.Delete(tempFilePath);
#endregion
#region 映射上传
// 将映射写入内存流
var mappingText = string.Join(Environment.NewLine, mappings);
await using var mappingStream = new MemoryStream(Encoding.UTF8.GetBytes(mappingText));
await _oSSService.UploadToOSSAsync(mappingStream, ossFolder, $"Download_{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}", false);
#endregion
}
}
public static StudyDIRInfo ReadDicomDIRInfo(DicomFile dicomFile)
{
var dataset = dicomFile.Dataset;
var info = new StudyDIRInfo
{
PatientId = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
StudyInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty),
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
DicomStudyDate = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty),
DicomStudyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty),
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
StudyDescription = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
SeriesInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SeriesInstanceUID, string.Empty),
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
DicomSeriesDate = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty),
DicomSeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty),
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
SeriesDescription = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
SopInstanceUid = dataset.GetSingleValueOrDefault(DicomTag.SOPInstanceUID, string.Empty),
SOPClassUID = dataset.GetSingleValueOrDefault(DicomTag.SOPClassUID, string.Empty),
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
MediaStorageSOPClassUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPClassUID, string.Empty),
MediaStorageSOPInstanceUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.MediaStorageSOPInstanceUID, string.Empty),
TransferSyntaxUID = dicomFile.FileMetaInfo.GetSingleValueOrDefault(DicomTag.TransferSyntaxUID, string.Empty)
};
return info;
}
}
}

View File

@ -1,5 +1,6 @@
using AlibabaCloud.SDK.Sts20150401;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using Amazon;
using Amazon.Runtime;
using Amazon.S3;
@ -9,32 +10,24 @@ using Amazon.SecurityToken.Model;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
using MassTransit;
using MassTransit.Caching.Internals;
using Microsoft.Extensions.Options;
using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Newtonsoft.Json;
using System.Net;
using Minio.Exceptions;
using System.Reactive.Linq;
using System.Runtime.InteropServices;
using System.Web;
namespace IRaCIS.Core.Application.Helper;
#region 绑定和返回模型
[LowerCamelCaseJson]
public class MinIOOptions
public class MinIOOptions : AWSOptions
{
public int Port { get; set; }
public string EndPoint { get; set; }
public bool UseSSL { get; set; }
[JsonProperty("accessKey")]
public string AccessKeyId { get; set; }
[JsonProperty("secretKey")]
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
}
@ -43,19 +36,13 @@ public class AWSOptions
{
public string EndPoint { get; set; }
public bool UseSSL { get; set; }
[JsonProperty("AccessKey")]
public string AccessKeyId { get; set; }
public string RoleArn { get; set; }
[JsonProperty("SecretKey")]
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public int DurationSeconds { get; set; }
public string Region { get; set; }
public string viewEndpoint { get; set; }
}
public class AliyunOSSOptions
@ -77,7 +64,7 @@ public class AliyunOSSOptions
public int DurationSeconds { get; set; }
public string PreviewEndpoint { get; set; }
}
@ -123,6 +110,9 @@ public class AliyunOSSTempToken
public string SecurityToken { get; set; }
public DateTime Expiration { get; set; }
public string PreviewEndpoint { get; set; }
public string DownloadEndPoint => EndPoint.Insert(EndPoint.IndexOf("//") + 2, BucketName + ".");
}
@ -136,7 +126,7 @@ public class AWSTempToken
public string SecretAccessKey { get; set; }
public string BucketName { get; set; }
public string ViewEndpoint { get; set; }
public DateTime Expiration { get; set; }
public DateTime? Expiration { get; set; }
}
public enum ObjectStoreUse
@ -157,13 +147,23 @@ public interface IOSSService
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
public Task<Stream> GetStreamFromOSSAsync(string ossRelativePath);
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
public Task<string> GetSignedUrl(string ossRelativePath);
public Task DeleteFromPrefix(string prefix);
public Task DeleteFromPrefix(string prefix, bool isCache = false);
public Task DeleteObjects(List<string> objectKeys);
List<string> GetRootFolderNames();
public ObjectStoreDTO GetObjectStoreTempToken();
public Task MoveObject(string sourcePath, string destPath, bool overwrite = true);
public Task<long> GetObjectSizeAsync(string sourcePath);
}
@ -194,7 +194,7 @@ public class OSSService : IOSSService
/// <returns></returns>
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
BackBatchGetToken();
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
@ -223,17 +223,11 @@ public class OSSService : IOSSService
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.WithHttpClient(new HttpClient(httpClientHandler))
.Build();
var putObjectArgs = new PutObjectArgs()
@ -290,6 +284,37 @@ public class OSSService : IOSSService
}
//后端批量上传 或者下载不每个文件获取临时token
private void BackBatchGetToken()
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
if (AliyunOSSTempToken == null)
{
GetObjectStoreTempToken();
}
//token 过期了
else if (AliyunOSSTempToken.Expiration.AddSeconds(10) <= DateTime.Now)
{
GetObjectStoreTempToken();
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
if (AWSTempToken == null)
{
GetObjectStoreTempToken();
}
//token 过期了
else if (AWSTempToken.Expiration?.AddSeconds(10) <= DateTime.Now)
{
GetObjectStoreTempToken();
}
}
}
/// <summary>
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
@ -301,7 +326,7 @@ public class OSSService : IOSSService
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
{
GetObjectStoreTempToken();
BackBatchGetToken();
var localFileName = Path.GetFileName(localFilePath);
@ -320,18 +345,11 @@ public class OSSService : IOSSService
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.WithHttpClient(new HttpClient(httpClientHandler))
.Build();
var putObjectArgs = new PutObjectArgs()
@ -378,7 +396,7 @@ public class OSSService : IOSSService
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
{
GetObjectStoreTempToken();
BackBatchGetToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
@ -391,33 +409,23 @@ public class OSSService : IOSSService
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
// 上传文件
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
// 将下载的文件流保存到本地文件
using (var fs = File.OpenWrite(localFilePath))
{
result.Content.CopyTo(fs);
fs.Close();
await result.Content.CopyToAsync(fs);
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.WithHttpClient(new HttpClient(httpClientHandler))
.Build();
var getObjectArgs = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
@ -470,6 +478,96 @@ public class OSSService : IOSSService
}
public async Task<Stream> GetStreamFromOSSAsync(string ossRelativePath)
{
BackBatchGetToken();
ossRelativePath = ossRelativePath.TrimStart('/');
try
{
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
AliyunOSSTempToken.AccessKeyId,
AliyunOSSTempToken.AccessKeySecret,
AliyunOSSTempToken.SecurityToken
);
var result = _ossClient.GetObject(aliConfig.BucketName, ossRelativePath);
// 将OSS返回的流复制到内存流中并返回
var memoryStream = new MemoryStream();
await result.Content.CopyToAsync(memoryStream);
memoryStream.Position = 0; // 重置位置以便读取
return memoryStream;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient()
.WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
.WithSSL(minIOConfig.UseSSL)
.Build();
var memoryStream = new MemoryStream();
var getObjectArgs = new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(ossRelativePath)
.WithCallbackStream(stream => stream.CopyToAsync(memoryStream));
await minioClient.GetObjectAsync(getObjectArgs);
memoryStream.Position = 0;
return memoryStream;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
var credentials = new SessionAWSCredentials(
AWSTempToken.AccessKeyId,
AWSTempToken.SecretAccessKey,
AWSTempToken.SessionToken
);
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var getObjectRequest = new Amazon.S3.Model.GetObjectRequest
{
BucketName = awsConfig.BucketName,
Key = ossRelativePath
};
var response = await amazonS3Client.GetObjectAsync(getObjectRequest);
var memoryStream = new MemoryStream();
await response.ResponseStream.CopyToAsync(memoryStream);
memoryStream.Position = 0;
return memoryStream;
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
catch (Exception ex)
{
throw new BusinessValidationFailedException("oss流获取失败! " + ex.Message);
}
}
public async Task<string> GetSignedUrl(string ossRelativePath)
{
GetObjectStoreTempToken();
@ -565,12 +663,240 @@ public class OSSService : IOSSService
}
/// <summary>
/// 移动OSS文件到新路径
/// </summary>
/// <param name="sourcePath">原文件路径格式bucket/key</param>
/// <param name="destPath">新文件路径格式bucket/key</param>
/// <param name="overwrite">是否覆盖已存在的目标文件默认true</param>
public async Task MoveObject(string sourcePath, string destPath, bool overwrite = true)
{
GetObjectStoreTempToken();
switch (ObjectStoreServiceOptions.ObjectStoreUse)
{
case "AliyunOSS":
#region 阿里云
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var client = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
if (sourcePath.StartsWith("/"))
{
sourcePath = sourcePath.Substring(1);
}
if (destPath.StartsWith("/"))
{
destPath = destPath.Substring(1);
}
var sourceBucket = aliConfig.BucketName;
var sourceKey = sourcePath;
var destBucket = aliConfig.BucketName;
var destKey = destPath;
try
{
// 检查目标是否存在(当不允许覆盖时)
if (!overwrite && client.DoesObjectExist(destBucket, destKey))
{
throw new InvalidOperationException("File Exist");
}
//var copyRequest = new Aliyun.OSS.CopyObjectRequest(sourceBucket, sourceKey, sourceBucket, destKey);
//var result = client.CopyObject(copyRequest);
//// 2. 删除原文件(可选,根据是否需要保留原文件)
//client.DeleteObject(sourceBucket, sourceKey);
// 执行复制
var copyRequestAli = new Aliyun.OSS.CopyObjectRequest(
sourceBucket, sourceKey,
destBucket, destKey);
// 保持原文件元数据
copyRequestAli.NewObjectMetadata = new ObjectMetadata
{
ContentType = client.GetObjectMetadata(sourceBucket, sourceKey).ContentType
};
var result = client.CopyObject(copyRequestAli);
// 删除原文件(仅在复制成功后)
client.DeleteObject(sourceBucket, sourceKey);
}
catch (OssException ex)
{
throw new Exception($"[{ex.ErrorCode}] {ex.Message}", ex);
}
#endregion
break;
case "MinIO":
#region MinIO
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient()
.WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey)
.WithSSL(minIOConfig.UseSSL)
.Build();
// 定义源路径和目标路径
string destinationKey = "b路径/文件名";
try
{
// 1. 复制文件到新路径[2,5](@ref)
using (var memoryStream = new MemoryStream())
{
// 下载源文件流
await minioClient.GetObjectAsync(new GetObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(sourcePath)
.WithCallbackStream(stream => stream.CopyTo(memoryStream)));
memoryStream.Position = 0; // 重置流位置
// 上传到新路径
await minioClient.PutObjectAsync(new PutObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(destinationKey)
.WithStreamData(memoryStream)
.WithObjectSize(memoryStream.Length));
}
// 2. 删除原文件[1,6](@ref)
await minioClient.RemoveObjectAsync(new RemoveObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(sourcePath));
}
catch (MinioException ex)
{
// 处理异常(例如:记录日志或抛出)
throw new Exception();
}
#endregion
break;
case "AWS":
#region AWS
var awsConfig = ObjectStoreServiceOptions.AWS;
var credentials = new SessionAWSCredentials(
AWSTempToken.AccessKeyId,
AWSTempToken.SecretAccessKey,
AWSTempToken.SessionToken
);
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
// 定义原路径和目标路径
// 1. 复制对象到新路径
var copyRequest = new Amazon.S3.Model.CopyObjectRequest
{
SourceBucket = awsConfig.BucketName,
SourceKey = sourcePath,
DestinationBucket = awsConfig.BucketName,
DestinationKey = destPath
};
try
{
// 执行复制操作
await amazonS3Client.CopyObjectAsync(copyRequest);
// 2. 删除原对象
var deleteRequest = new Amazon.S3.Model.DeleteObjectRequest
{
BucketName = awsConfig.BucketName,
Key = sourcePath
};
await amazonS3Client.DeleteObjectAsync(deleteRequest);
}
catch (AmazonS3Exception ex)
{
Console.WriteLine($"ERROR: {ex.Message}");
// 可根据异常类型细化处理(如文件不存在、权限问题等)
}
#endregion
break;
default:
throw new BusinessValidationFailedException("ERROR");
}
}
/// <summary>
/// 获取所有根目录名称
/// </summary>
/// <returns></returns>
public List<string> GetRootFolderNames()
{
GetObjectStoreTempToken();
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint,
AliyunOSSTempToken.AccessKeyId,
AliyunOSSTempToken.AccessKeySecret,
AliyunOSSTempToken.SecurityToken);
List<string> rootFolders = new List<string>();
string nextMarker = null;
try
{
ObjectListing objectListing = null;
do
{
// 列出根目录下的对象和文件夹
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
{
MaxKeys = 1000,
Marker = nextMarker,
Delimiter = "/" // 使用分隔符来模拟文件夹
});
// 遍历 CommonPrefixes 获取根文件夹名称
foreach (var prefix in objectListing.CommonPrefixes)
{
rootFolders.Add(prefix.TrimEnd('/')); // 去掉末尾的斜杠
}
// 设置 NextMarker 以获取下一页的数据
nextMarker = objectListing.NextMarker;
} while (objectListing.IsTruncated);
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return rootFolders;
}
/// <summary>
/// 删除某个目录的文件
/// </summary>
/// <param name="prefix"></param>
/// <returns></returns>
public async Task DeleteFromPrefix(string prefix)
public async Task DeleteFromPrefix(string prefix, bool isCache = false)
{
GetObjectStoreTempToken();
@ -580,6 +906,21 @@ public class OSSService : IOSSService
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
var bucketName = string.Empty;
if (isCache)
{
Uri uri = new Uri(aliConfig.ViewEndpoint);
string host = uri.Host; // 获取 "zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com"
string[] parts = host.Split('.');
bucketName = parts[0];
}
else
{
bucketName = aliConfig.BucketName;
}
try
{
@ -588,7 +929,7 @@ public class OSSService : IOSSService
do
{
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(bucketName)
{
Prefix = prefix,
MaxKeys = 1000,
@ -600,7 +941,7 @@ public class OSSService : IOSSService
// 删除获取到的文件
if (keys.Count > 0)
{
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(bucketName, keys, false));
}
// 设置 NextMarker 以获取下一页的数据
@ -657,7 +998,6 @@ public class OSSService : IOSSService
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
@ -709,6 +1049,155 @@ public class OSSService : IOSSService
}
}
public async Task DeleteObjects(List<string> objectKeys)
{
GetObjectStoreTempToken();
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
if (objectKeys.Count > 0)
{
var result = _ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, objectKeys, false));
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
if (objectKeys.Count > 0)
{
var objArgs = new RemoveObjectsArgs()
.WithBucket(minIOConfig.BucketName)
.WithObjects(objectKeys);
// 删除对象
await minioClient.RemoveObjectsAsync(objArgs);
}
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
if (objectKeys.Count > 0)
{
// 准备删除请求
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
{
BucketName = awsConfig.BucketName,
Objects = objectKeys.Select(t => new KeyVersion() { Key = t }).ToList()
};
// 批量删除对象
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
}
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
public async Task<long> GetObjectSizeAsync(string sourcePath)
{
BackBatchGetToken();
var objectkey = sourcePath.Trim('/');
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
{
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
var key = HttpUtility.UrlDecode(objectkey);
var metadata = _ossClient.GetObjectMetadata(aliConfig.BucketName, key);
long fileSize = metadata?.ContentLength ?? 0; // 文件大小(字节)
return fileSize;
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
{
var minIOConfig = ObjectStoreServiceOptions.MinIO;
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
.Build();
var stat = await minioClient.StatObjectAsync(new Minio.DataModel.Args.StatObjectArgs()
.WithBucket(minIOConfig.BucketName)
.WithObject(objectkey));
return stat.Size; // 文件大小(字节)
}
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
{
var awsConfig = ObjectStoreServiceOptions.AWS;
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
//提供awsEndPoint域名进行访问配置
var clientConfig = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.USEast1,
UseHttp = true,
};
var request = new Amazon.S3.Model.GetObjectMetadataRequest
{
BucketName = awsConfig.BucketName,
Key = objectkey
};
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
var response = await amazonS3Client.GetObjectMetadataAsync(request);
long fileSize = response.ContentLength; // 文件大小(字节)
return fileSize;
}
else
{
throw new BusinessValidationFailedException("未定义的存储介质类型");
}
}
public ObjectStoreDTO GetObjectStoreTempToken()
{
@ -752,6 +1241,7 @@ public class OSSService : IOSSService
BucketName = ossOptions.BucketName,
EndPoint = ossOptions.EndPoint,
ViewEndpoint = ossOptions.ViewEndpoint,
PreviewEndpoint = ossOptions.PreviewEndpoint
};

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace IRaCIS.Core.Application.Helper
{
public static class SafeBussinessHelper
{
public static async Task<bool> RunAsync(Func<Task> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
{
try
{
await func();
return true;
}
catch (Exception ex)
{
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
return false;
}
}
public static async Task<(bool Success, T? Result)> RunAsync<T>(Func<Task<T>> func, [CallerMemberName] string caller = "", string errorMsgTitle = "")
{
try
{
var result = await func();
return (true, result);
}
catch (Exception ex)
{
Log.Logger.Error($"【{errorMsgTitle}失败 - {caller}】: {ex.Message}");
return (false, default);
}
}
}
}

View File

@ -34,36 +34,37 @@
<ItemGroup>
<PackageReference Include="MaxMind.GeoIP2" Version="5.3.0" />
<PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.4" />
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.400.36" />
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.6" />
<PackageReference Include="AWSSDK.SecurityToken" Version="4.0.2" />
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
<PackageReference Include="AWSSDK.S3" Version="3.7.405" />
<PackageReference Include="DocX" Version="3.0.1" />
<PackageReference Include="AWSSDK.S3" Version="4.0.6.8" />
<PackageReference Include="DocX" Version="4.0.25105.5786" />
<PackageReference Include="FreeSpire.Doc" Version="12.2.0" />
<PackageReference Include="ExcelDataReader" Version="3.7.0" />
<PackageReference Include="ExcelDataReader.DataSet" Version="3.7.0" />
<PackageReference Include="DistributedLock.Redis" Version="1.0.3" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.5" />
<PackageReference Include="fo-dicom" Version="5.1.3" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.3" />
<PackageReference Include="fo-dicom.Codecs" Version="5.15.1" />
<PackageReference Include="DistributedLock.Redis" Version="1.1.0" />
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
<PackageReference Include="fo-dicom" Version="5.2.2" />
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.2" />
<PackageReference Include="fo-dicom.Codecs" Version="5.16.4" />
<PackageReference Include="IP2Region.Net" Version="2.0.2" />
<PackageReference Include="MailKit" Version="4.7.1.1" />
<PackageReference Include="MailKit" Version="4.13.0" />
<PackageReference Include="Masa.Contrib.Service.MinimalAPIs" Version="1.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.10" />
<PackageReference Include="MimeKit" Version="4.7.1" />
<PackageReference Include="MiniExcel" Version="1.34.2" />
<PackageReference Include="Minio" Version="6.0.3" />
<PackageReference Include="MimeKit" Version="4.13.0" />
<PackageReference Include="MiniExcel" Version="1.41.3" />
<PackageReference Include="Minio" Version="6.0.5" />
<PackageReference Include="MiniWord" Version="0.9.2" />
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
<TreatAsUsed>true</TreatAsUsed>
</PackageReference>
<PackageReference Include="NPOI" Version="2.7.1" />
<PackageReference Include="NPOI" Version="2.7.4" />
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
<PackageReference Include="RestSharp" Version="112.1.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.11" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="9.0.0" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="1.4.0" />
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
</ItemGroup>
<ItemGroup>

View File

@ -1510,6 +1510,20 @@
<param name="modality"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.GetTrialVisitImageStatInfo(System.Guid)">
<summary>
获取项目影像统计有影像的subject 数量 访视数量
</summary>
<param name="trialId"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.GetExportSubjectVisitImageList(IRaCIS.Core.Application.Contracts.TrialExportImageCommand)">
<summary>
批量勾选访视 进行下载
</summary>
<param name="inCommand"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.ImageAndDoc.DownloadAndUploadService.GetSubejectOrVisitZipInfo(IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Subject},IRaCIS.Core.Application.Contracts.SubejctZipInfoQuery)">
<summary>
受试者级别所有的影像
@ -13870,7 +13884,21 @@
<returns></returns>
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
</member>
<member name="M:IRaCIS.Core.Application.Helper.OSSService.DeleteFromPrefix(System.String)">
<member name="M:IRaCIS.Core.Application.Helper.OSSService.MoveObject(System.String,System.String,System.Boolean)">
<summary>
移动OSS文件到新路径
</summary>
<param name="sourcePath">原文件路径格式bucket/key</param>
<param name="destPath">新文件路径格式bucket/key</param>
<param name="overwrite">是否覆盖已存在的目标文件默认true</param>
</member>
<member name="M:IRaCIS.Core.Application.Helper.OSSService.GetRootFolderNames">
<summary>
获取所有根目录名称
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Helper.OSSService.DeleteFromPrefix(System.String,System.Boolean)">
<summary>
删除某个目录的文件
</summary>
@ -18239,7 +18267,7 @@
</member>
<member name="M:IRaCIS.Application.Services.PatientService.ModifySCPStudyHospitalGroup(IRaCIS.Application.Contracts.SCPStudyHospitalGroupCommand)">
<summary>
修改检查课题组 1是添加 2是删除
修改检查课题组 1是添加 2是删除3是更新
</summary>
<param name="incommand"></param>
<returns></returns>
@ -18408,7 +18436,7 @@
<param name="inQuery"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.PatientService.GetDownloadSubjectVisitStudyInfo(System.Guid,System.Guid)">
<member name="M:IRaCIS.Application.Services.PatientService.GetDownloadSubjectVisitStudyInfo(System.Guid,System.Guid,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.DicomStudy},IRaCIS.Core.Application.Helper.IOSSService)">
<summary>
获取下载的访视检查信息
</summary>

View File

@ -620,4 +620,178 @@ namespace IRaCIS.Core.Application.Contracts
public Guid TaskId { get; set; }
}
public class TrialVisitImageQuery : PageInput
{
[NotDefault]
public Guid TrialId { get; set; }
public Guid? TrialSiteId { get; set; }
public string? SubjectCode { get; set; }
public DateTime? BeginScanDate { get; set; }
public DateTime? EndScanDate { get; set; }
}
public class TrialVisitImageStatView
{
public Guid TrialId { get; set; }
public Guid SubjectVisitId { get; set; }
public Guid TrialSiteId { get; set; }
public string TrialSiteCode { get; set; }
public string SubjectCode { get; set; }
public string VisitName { get; set; }
public decimal VisitNum { get; set; }
public DateTime? EarliestScanDate { get; set; }
public DateTime? LatestScanDate { get; set; }
public int TotalStudyCount { get; set; }
public int TotalImageCount { get; set; }
public long? TotalImageSize { get; set; }
public long? TotalReadingImageSize { get; set; }
public string TotalImageSizeStr => TotalImageSize.HasValue
? $"{TotalImageSize.Value / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string TotalReadingImageSizeStr => TotalReadingImageSize.HasValue
? $"{TotalReadingImageSize.Value / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string ImageTypeStr => $"{(IsHaveDicom ? "DICOM" : "")}{(IsHaveNoneDicom && IsHaveDicom ? " , " : "")}{(IsHaveNoneDicom ? "Non-DICOM" : "")}";
public bool IsHaveDicom { get; set; }
public bool IsHaveNoneDicom { get; set; }
#region 废弃,为了字段排序
//public int TotalStudyCount => DicomStudyCount + NoneDicomStudyCount;
//public int TotalImageCount => DicomImageCount + NoneDicomImageCount;
//public long? TotalImageSize => DicomImageSize + NoneDicomImageSize;
//public int DicomStudyCount { get; set; }
//public int DicomImageCount { get; set; }
//public long? DicomImageSize { get; set; }
//public int NoneDicomStudyCount { get; set; }
//public int NoneDicomImageCount { get; set; }
//public long? NoneDicomImageSize { get; set; }
#endregion
}
public class TrialExportImageCommand
{
[NotDefault]
public Guid TrialId { get; set; }
public List<Guid> SubjectVisitIdList { get; set; }
public bool IsKeyImage { get; set; }
// true 导出阅片null 就是所有影像
public bool? IsExportReading { get; set; }
}
public class TrialKeyImageExportDTO
{
public Guid Id { get; set; }
public string ResearchProgramNo { get; set; }
public string CriterionName { get; set; }
public ArbitrationRule ArbitrationRule { get; set; }
public bool IsGlobalReading { get; set; }
public List<decimal> SubjectCriterionReadingPeriodVisitNumList { get; set; }
public string TrialSiteCode { get; set; }
public string VisitName { get; set; }
public Decimal VisitTaskNum { get; set; }
public string SubjectCode { get; set; }
public Arm ArmEnum { get; set; }
public Guid? JudgeResultTaskId { get; set; }
public bool? IsTrigerJudge { get; set; }
public bool? IsJudgeSelect { get; set; }
public ReadingCategory ReadingCategory { get; set; }
public List<TrialKeyPicturePath> ReadingNoneDicomMarkPathList { get; set; }
public List<TrialKeyPicturePath> QuestionMarkPictureList { get; set; }
public List<TrialKeyPicturePath> TableQuestionRowPictureList { get; set; }
}
public class TrialKeyPicturePath
{
public string PicturePath { get; set; }
public string OtherPicturePath { get; set; }
}
public class TrialImageStatInfo
{
public int SubjectCount { get; set; }
public int SubjectVisitCount { get; set; }
public long? TotalImageSize { get; set; }
public string TotalImageSizeStr => TotalImageSize.HasValue
? $"{TotalImageSize.Value / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string SubjectImageAVGSizeStr => TotalImageSize.HasValue
? $"{TotalImageSize.Value / SubjectCount / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string SubjectVisitImageAVGSizeStr => TotalImageSize.HasValue
? $"{TotalImageSize.Value / SubjectVisitCount / 1024d / 1024d:F3} MB"
: "0.000 MB";
public long? TotalReadingImageSize { get; set; }
public string TotalReadingImageSizeStr => TotalReadingImageSize.HasValue
? $"{TotalReadingImageSize.Value / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string SubjectReadingImageAVGSizeStr => TotalReadingImageSize.HasValue
? $"{TotalReadingImageSize.Value / SubjectCount / 1024d / 1024d:F3} MB"
: "0.000 MB";
public string SubjectVisitReadingImageAVGSizeStr => TotalReadingImageSize.HasValue
? $"{TotalReadingImageSize.Value / SubjectVisitCount / 1024d / 1024d:F3} MB"
: "0.000 MB";
}
}

View File

@ -7,6 +7,7 @@ using IRaCIS.Core.Application.Helper;
using IRaCIS.Core.Application.Service.ImageAndDoc.DTO;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Common;
using IRaCIS.Core.Infrastructure;
using MassTransit;
using MassTransit.Initializers;
@ -1154,6 +1155,407 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc
return ResponseOutput.Ok();
}
#region 影像汇总页面
[HttpPost]
public async Task<IResponseOutput<PageOutput<TrialVisitImageStatView>>> GetTrialVisitImageStatList(TrialVisitImageQuery inQuery)
{
var query = _subjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId)
.WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId)
.WhereIf(inQuery.SubjectCode.IsNotNullOrEmpty(), t => t.Subject.Code.Contains(inQuery.SubjectCode))
.WhereIf(inQuery.BeginScanDate != null, t => t.LatestScanDate >= inQuery.BeginScanDate)
.WhereIf(inQuery.EndScanDate != null, t => t.LatestScanDate == inQuery.EndScanDate)
.Select(t => new TrialVisitImageStatView()
{
TrialId = t.TrialId,
SubjectVisitId = t.Id,
SubjectCode = t.Subject.Code,
TrialSiteCode = t.TrialSite.TrialSiteCode,
TrialSiteId = t.TrialSiteId,
VisitName = t.VisitName,
VisitNum = t.VisitNum,
EarliestScanDate = t.EarliestScanDate,
LatestScanDate = t.LatestScanDate,
IsHaveDicom = t.StudyList.Any(),
IsHaveNoneDicom = t.NoneDicomStudyList.Any(),
TotalStudyCount = t.StudyList.Count() + t.NoneDicomStudyList.Count(),
TotalImageCount = t.StudyList.Sum(t => t.InstanceCount) + t.NoneDicomStudyList.Sum(t => t.FileCount),
TotalImageSize = t.StudyList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize) + t.NoneDicomStudyList.SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize),
TotalReadingImageSize = t.StudyList.SelectMany(t => t.InstanceList/*.Where(t => t.IsReading && t.DicomSerie.IsReading)*/).Sum(t => t.FileSize)
+ t.NoneDicomStudyList/*.Where(t => t.IsReading)*/.SelectMany(t => t.NoneDicomFileList/*.Where(t => t.IsReading)*/).Sum(t => t.FileSize),
//DicomStudyCount = t.StudyList.Count(),
//NoneDicomStudyCount = t.NoneDicomStudyList.Count(),
//DicomImageCount = t.StudyList.Sum(t => t.InstanceCount),
//NoneDicomImageCount = t.NoneDicomStudyList.Sum(t => t.FileCount),
//DicomImageSize = t.StudyList.SelectMany(t => t.InstanceList).Sum(t => t.FileSize),
//NoneDicomImageSize = t.NoneDicomStudyList.SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize)
});
var defalutSortArray = new string[] { nameof(TrialVisitImageStatView.TrialSiteCode), nameof(QCCRCVisitViewModel.SubjectCode), nameof(QCCRCVisitViewModel.VisitNum) };
var pagelist = await query.Where(t => t.TotalImageCount > 0).ToPagedListAsync(inQuery, defalutSortArray);
return ResponseOutput.Ok(pagelist);
}
/// <summary>
/// 获取项目影像统计有影像的subject 数量 访视数量
/// </summary>
/// <param name="trialId"></param>
/// <returns></returns>
public async Task<IResponseOutput<TrialImageStatInfo>> GetTrialVisitImageStatInfo(Guid trialId)
{
var subjectImageList = _subjectVisitRepository.Where(t => t.TrialId == trialId)
.Where(t => t.StudyList.Sum(t => t.InstanceCount) > 0 || t.NoneDicomStudyList.Sum(t => t.FileCount) > 0)
.GroupBy(t => t.SubjectId)
.Select(g => new
{
SubjectId = g.Key,
VisitCount = g.Count(),
ReadingImageSize = g.SelectMany(t => t.NoneDicomStudyList/*.Where(t => t.IsReading)*/).SelectMany(t => t.NoneDicomFileList/*.Where(t => t.IsReading)*/).Sum(t => t.FileSize)
+ g.SelectMany(t => t.StudyList).SelectMany(t => t.InstanceList.Where(t => /*t.IsReading &&*/ t.DicomSerie.IsReading)).Sum(t => t.FileSize),
ImageSize = g.SelectMany(t => t.NoneDicomStudyList).SelectMany(t => t.NoneDicomFileList).Sum(t => t.FileSize)
+ g.SelectMany(t => t.StudyList).SelectMany(t => t.InstanceList).Sum(t => t.FileSize)
})
.ToList();
var subjectCount = subjectImageList.Count;
var subjectVisitCount = subjectImageList.Sum(t => t.VisitCount);
var totalImageSize = subjectImageList.Sum(t => t.ImageSize);
var totalReadingImageSize = subjectImageList.Sum(t => t.ReadingImageSize);
return ResponseOutput.Ok(new TrialImageStatInfo { SubjectCount = subjectCount, SubjectVisitCount = subjectVisitCount, TotalImageSize = totalImageSize, TotalReadingImageSize = totalReadingImageSize });
}
/// <summary>
/// 批量勾选访视 进行下载
/// </summary>
/// <param name="inCommand"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> GetExportSubjectVisitImageList(TrialExportImageCommand inCommand)
{
var isExportReading = inCommand.IsExportReading == true;
if (inCommand.IsKeyImage)
{
var downloadInfoList = _visitTaskRepository.Where(t => t.TrialId == inCommand.TrialId && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)
&& t.ReadingTaskState == ReadingTaskState.HaveSigned && t.IsAnalysisCreate == false && (t.TaskState == TaskState.Effect || t.TaskState == TaskState.Freeze))
.Where(t => inCommand.SubjectVisitIdList.Contains((Guid)t.SourceSubjectVisitId))
.Select(t => new TrialKeyImageExportDTO()
{
ResearchProgramNo = t.Trial.ResearchProgramNo,
CriterionName = t.TrialReadingCriterion.CriterionName,
ArbitrationRule = t.TrialReadingCriterion.ArbitrationRule,
IsGlobalReading = t.TrialReadingCriterion.IsGlobalReading,
SubjectCriterionReadingPeriodVisitNumList = t.Subject.ReadModuleList
.Where(u => u.TrialReadingCriterionId == t.TrialReadingCriterionId && u.ReadingSetType == ReadingSetType.ImageReading).Select(c => c.SubjectVisit.VisitNum).ToList(),
TrialSiteCode = t.Subject.TrialSite.TrialSiteCode,
SubjectCode = t.Subject.Code,
ReadingCategory = t.ReadingCategory,
VisitName = t.ReadingCategory == ReadingCategory.Visit ? t.SourceSubjectVisit.VisitName : "",
VisitTaskNum = t.VisitTaskNum,
ArmEnum = t.ArmEnum,
//ReadingNoneDicomMarkPathList = t.ReadingNoneDicomMarkList.Select(c => new TrialKeyPicturePath { PicturePath = c.Path, OtherPicturePath = string.Empty }).ToList(),
QuestionMarkPictureList = t.ReadingTaskQuestionMarkList.Select(c => new TrialKeyPicturePath { PicturePath = c.PicturePath, OtherPicturePath = c.OtherPicturePath }).ToList(),
TableQuestionRowPictureList = t.LesionList.Select(c => new TrialKeyPicturePath { PicturePath = c.PicturePath, OtherPicturePath = c.OtherPicturePath }).ToList(),
Id = t.Id,
//裁判选择结果
JudgeResultTaskId = t.JudgeVisitTask.JudgeResultTaskId,
//是否触发裁判
IsTrigerJudge = t.JudgeVisitTaskId != null,
IsJudgeSelect = null
}).ToList();
foreach (var subjectCriterionGroup in downloadInfoList.GroupBy(t => new { t.SubjectCode, t.CriterionName, t.ArbitrationRule, t.IsGlobalReading }))
{
var arbitrationRule = subjectCriterionGroup.Key.ArbitrationRule;
var isGlobalReading = subjectCriterionGroup.Key.IsGlobalReading;
foreach (var item in subjectCriterionGroup)
{
if (arbitrationRule == ArbitrationRule.Visit)
{
//是否产生裁判
if (item.IsTrigerJudge == true)
{
//裁判做完了
if (item.JudgeResultTaskId != null)
{
//裁判选择了自己,那么设置
if (item.JudgeResultTaskId == item.Id)
{
item.IsJudgeSelect = true;
}
else
{
item.IsJudgeSelect = false;
}
}
}
else
{
//没产生 且两个人做完了 默认R1
if (subjectCriterionGroup.Where(t => t.VisitTaskNum == item.VisitTaskNum && t.SubjectCode == item.SubjectCode).Select(t => t.ArmEnum).Distinct().Count() == 2)
{
if (item.ArmEnum == Arm.DoubleReadingArm1)
{
item.IsJudgeSelect = true;
}
else
{
item.IsJudgeSelect = false;
}
}
}
}
else if (arbitrationRule == ArbitrationRule.Reading)
{
//阅片期访视号
var subjectReadingPeriondVisitNumList = subjectCriterionGroup.FirstOrDefault()?.SubjectCriterionReadingPeriodVisitNumList;
//两个人完成最大得任务号(访视+全局)
var subjectMaxFinishedTaskNum = subjectCriterionGroup.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).DefaultIfEmpty().Max();
var addReadingPeriodNum = isGlobalReading ? ReadingCommon.TaskNumDic[ReadingCategory.Global] : 0;
//可能没有配置阅片期 ,或者配置了 没有完成
var finishedGlobalCount = 0;
var globalFinishedVisitTaskNumList = new List<decimal>();
//没有配置阅片期
if (subjectReadingPeriondVisitNumList == null)
{
finishedGlobalCount = 0;
}
{
globalFinishedVisitTaskNumList = subjectCriterionGroup.Where(t => subjectReadingPeriondVisitNumList.Any(c => (c + addReadingPeriodNum) == t.VisitTaskNum))
.Select(t => new { t.VisitTaskNum, t.ArmEnum }).Distinct()
.GroupBy(t => t.VisitTaskNum).Where(g => g.Count() == 2).Select(g => g.Key).ToList();
finishedGlobalCount = globalFinishedVisitTaskNumList.Count();
}
if (finishedGlobalCount != 0)
{
//最大的完成的全局是否产生裁判
var maxFinishedGlobalNum = globalFinishedVisitTaskNumList.Max();
var globalMaxTask = subjectCriterionGroup.FirstOrDefault(t => t.VisitTaskNum == maxFinishedGlobalNum);
// 触发了裁判
if (globalMaxTask.IsTrigerJudge == true)
{
//最大裁判完成了
var maxJudgeVisitTaskNum = maxFinishedGlobalNum + ReadingCommon.TaskNumDic[ReadingCategory.Judge];
if (globalMaxTask.JudgeResultTaskId != null)
{
var maxJudgeArmEnum = subjectCriterionGroup.Where(t => t.Id == globalMaxTask.JudgeResultTaskId).FirstOrDefault().ArmEnum;
if (item.VisitTaskNum < maxJudgeVisitTaskNum)
{
//触发裁判
item.IsTrigerJudge = true;
if (item.ArmEnum == maxJudgeArmEnum)
{
item.IsJudgeSelect = true;
}
//裁判没选择的人设置为false
else
{
item.IsJudgeSelect = false;
}
}
}
else
{
//最大的裁判未完成
//找到当前未阅最大裁判之前的已完成的最大裁判任务
var finishedGlobalFinishedMaxJudge = subjectCriterionGroup.Where(t => globalFinishedVisitTaskNumList.Contains(t.VisitTaskNum) && t.IsTrigerJudge == true && t.JudgeResultTaskId != null).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault();
//未完成裁判之前 没有已完成的全局裁判
if (finishedGlobalFinishedMaxJudge == null)
{
if (item.VisitTaskNum < maxJudgeVisitTaskNum)
{
item.IsTrigerJudge = true;
//item.IsJudgeSelect = null;
}
}
else
{
var maxFinishedJudgeVisitTaskNum = finishedGlobalFinishedMaxJudge.VisitTaskNum + +ReadingCommon.TaskNumDic[ReadingCategory.Judge];
var maxFinishedJudgeArmEnum = subjectCriterionGroup.Where(t => t.Id == finishedGlobalFinishedMaxJudge.JudgeResultTaskId).FirstOrDefault().ArmEnum;
if (item.VisitTaskNum < maxFinishedJudgeVisitTaskNum)
{
item.IsTrigerJudge = true;
if (item.ArmEnum == maxFinishedJudgeArmEnum)
{
item.IsJudgeSelect = true;
}
//裁判没选择的人设置为false
else
{
item.IsJudgeSelect = false;
}
}
else if (item.VisitTaskNum > maxFinishedJudgeVisitTaskNum && item.VisitTaskNum < maxJudgeVisitTaskNum)
{
//完成裁判 和未完成裁判之间的 裁判选择标记默认是null
item.IsTrigerJudge = true;
}
else
{
//在未完成全局裁判之后的访视 未知 都是null
item.IsTrigerJudge = null;
}
}
}
}
else
{
//最大的全局未产生裁判
if (item.VisitTaskNum <= maxFinishedGlobalNum)
{
item.IsTrigerJudge = false;
if (item.ArmEnum == Arm.DoubleReadingArm1)
{
item.IsJudgeSelect = true;
}
else
{
item.IsJudgeSelect = false;
}
}
}
}
}
}
}
downloadInfoList = downloadInfoList.Where(t => t.ReadingCategory == ReadingCategory.Visit).ToList();
return ResponseOutput.Ok(downloadInfoList);
}
else
{
var downloadInfo = _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new
{
t.ResearchProgramNo,
VisitList = t.SubjectVisitList.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id)).Select(sv => new
{
TrialSiteCode = sv.TrialSite.TrialSiteCode,
SubjectCode = sv.Subject.Code,
VisitName = sv.VisitName,
StudyList = sv.StudyList.Select(u => new
{
u.PatientId,
u.StudyTime,
u.StudyCode,
SeriesList = u.SeriesList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(z => new
{
z.Modality,
InstancePathList = z.DicomInstanceList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(k => new
{
k.Path
})
})
}),
NoneDicomStudyList = sv.NoneDicomStudyList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(nd => new
{
nd.Modality,
nd.StudyCode,
nd.ImageDate,
FileList = nd.NoneDicomFileList/*.Where(t => isExportReading ? t.IsReading : true)*/.Select(file => new
{
file.FileName,
file.Path,
file.FileType
})
})
}).ToList()
}).FirstOrDefault();
return ResponseOutput.Ok(downloadInfo);
}
}
#endregion
#region 之前后端下载废弃

View File

@ -248,7 +248,7 @@ namespace IRaCIS.Application.Contracts
public List<UserAddUserType> UserRoleList { get; set; }
public List<Guid> HospitalGroupIdList { get; set; }
public List<Guid> HospitalGroupIdList { get; set; } = new List<Guid>();
public string BaseUrl { get; set; } = string.Empty;
public string RouteUrl { get; set; } = string.Empty;

View File

@ -668,7 +668,7 @@ namespace IRaCIS.Core.Application.Service
user.IdentityUserHospitalGroupList.AddRange(addIdList.Select(t => new HospitalGroupIdentityUser() { HospitalGroupId = t, IsManager = false, IdentityUserId = user.Id }));
var success = await _userRoleRepository.SaveChangesAsync();
var success = await _identityUserRepository.SaveChangesAsync();
await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, ActionIdentityUserId = _userInfo.IdentityUserId, ActionUserName = _userInfo.UserName, TargetIdentityUserId = model.Id, OptType = UserOptType.UpdateUser }, true);

View File

@ -140,7 +140,8 @@ namespace IRaCIS.Core.Application.Service
CreateMap<IdentityUser, UserRole>();
CreateMap<UserCommand, IdentityUser>();
CreateMap<UserCommand, IdentityUser>()
.ForMember(d => d.UserRoleList, c => c.Ignore());
CreateMap<IdentityUser, UserBasicInfo>()
.ForMember(d => d.IdentityUserId, c => c.MapFrom(t => t.Id))

View File

@ -1148,4 +1148,31 @@ namespace IRaCIS.Application.Contracts
}
public class DownloadDicomStudyDto
{
public string PatientId { get; set; }
public DateTime? StudyTime { get; set; }
public string StudyCode { get; set; }
public string StudyInstanceUid { get; set; }
public string StudyDIRPath { get; set; }
public List<DownloadDicomSeriesDto> SeriesList { get; set; }
}
public class DownloadDicomSeriesDto
{
public string Modality { get; set; }
public List<DownloadDicomInstanceDto> InstanceList { get; set; }
}
public class DownloadDicomInstanceDto
{
public bool IsEncapsulated { get; set; }
public Guid InstanceId { get; set; }
public string FileName { get; set; }
public string Path { get; set; }
public long? FileSize { get; set; }
}
}

View File

@ -62,7 +62,9 @@ namespace IRaCIS.Application.Contracts
public string? ModalitiesInStudy { get; set; }
public List<HospitalGroupInfo> HospitalGroupList { get; set; }
public Guid? SCPStudyId { get; set; }
public List<HospitalGroupInfo> HospitalGroupList { get; set; } = new List<HospitalGroupInfo>();
public List<HospitalGroupInfo> CurrentUserHospitalGroupList { get; set; } = new List<HospitalGroupInfo>();

View File

@ -27,7 +27,6 @@ using MailKit.Search;
using DocumentFormat.OpenXml.Office2010.Excel;
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
using IRaCIS.Core.Application.Helper;
using NPOI.SS.Formula.Functions;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Text;
@ -36,7 +35,6 @@ using Azure;
using System.IO.Compression;
using static IRaCIS.Core.Domain.Share.StaticData;
using FellowOakDicom;
using DocumentFormat.OpenXml.Office2010.Drawing;
using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider;
using DocumentFormat.OpenXml.InkML;
using Microsoft.AspNetCore.Hosting;
@ -46,16 +44,11 @@ using FellowOakDicom.Network;
using Microsoft.Extensions.Logging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
using DocumentFormat.OpenXml.Vml.Office;
using IRaCIS.Core.Infra.EFCore.Migrations;
using System.Dynamic;
using System.Threading.Channels;
using NPOI.HSSF.Record.Chart;
using IRaCIS.Core.Application.Helper.OtherTool;
using IRaCIS.Core.Infrastructure.Extention;
using DocumentFormat.OpenXml.Drawing.Charts;
using DocumentFormat.OpenXml.Drawing.Diagrams;
namespace IRaCIS.Application.Services
@ -918,9 +911,25 @@ namespace IRaCIS.Application.Services
[HttpDelete]
public async Task<IResponseOutput> DeletePatientStudy(Guid patiendId, Guid scpStudyId,
[FromServices] IRepository<SCPSeries> _SeriesRepository,
[FromServices] IRepository<SCPInstance> _instanceRepository)
[FromServices] IRepository<SCPInstance> _instanceRepository,
[FromServices] IRepository<SCPStudyHospitalGroup> _scpStudyHospitalGroupRepository)
{
var existList = _scpStudyHospitalGroupRepository.Where(t => t.SCPStudyId == scpStudyId).Select(t => t.HospitalGroupId).ToList();
var isAdminOrOAOrGA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA
|| _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.GA;
// abc 课题组人属于AB 课题组,删除仅仅删除这个标签 直接返回
if (!isAdminOrOAOrGA && existList.Count > _userInfo.HospitalGroupList.Count)
{
await _scpStudyHospitalGroupRepository.BatchDeleteNoTrackingAsync(t => t.SCPStudyId == scpStudyId && _userInfo.HospitalGroupIdList.Contains(t.HospitalGroupId));
return ResponseOutput.Ok();
}
if (_studySubjectVisitRepository.Any(t => t.SCPStudyId == scpStudyId && t.StudyId != null))
{
@ -928,11 +937,15 @@ namespace IRaCIS.Application.Services
}
else
{
await _subjectPatientRepository.DeleteFromQueryAsync(t => t.PatientId == patiendId);
await _studySubjectVisitRepository.DeleteFromQueryAsync(t => t.SCPStudyId == scpStudyId);
}
await _scpStudyHospitalGroupRepository.BatchDeleteNoTrackingAsync(t => t.SCPStudyId == scpStudyId);
await _studyRepository.BatchDeleteNoTrackingAsync(t => t.Id == scpStudyId);
await _SeriesRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == scpStudyId);
await _instanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == scpStudyId);
@ -2911,8 +2924,73 @@ namespace IRaCIS.Application.Services
/// <param name="trialId"></param>
/// <param name="subjectVisitId"></param>
/// <returns></returns>
public async Task<IResponseOutput> GetDownloadSubjectVisitStudyInfo(Guid trialId, Guid subjectVisitId)
public async Task<IResponseOutput> GetDownloadSubjectVisitStudyInfo(Guid trialId, Guid subjectVisitId,
[FromServices] IRepository<DicomStudy> _dicomStudyRepository, [FromServices] IOSSService _oSSService)
{
var dirDic = new Dictionary<string, string>();
#region DIR处理导出文件名并将对应关系上传到OSS里面存储
//有传输语法值的导出 才生成DIR
if (_subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.StudyList.SelectMany(t => t.InstanceList)).All(c => c.TransferSyntaxUID != string.Empty))
{
var list = _subjectVisitRepository.Where(t => t.Id == subjectVisitId).SelectMany(t => t.StudyList)
.SelectMany(t => t.InstanceList)
.Select(t => new StudyDIRInfo()
{
DicomStudyId = t.DicomStudy.Id,
PatientId = t.DicomStudy.PatientId,
PatientName = t.DicomStudy.PatientName,
PatientBirthDate = t.DicomStudy.PatientBirthDate,
PatientSex = t.DicomStudy.PatientSex,
StudyInstanceUid = t.StudyInstanceUid,
StudyId = t.DicomStudy.StudyId,
DicomStudyDate = t.DicomStudy.DicomStudyDate,
DicomStudyTime = t.DicomStudy.DicomStudyTime,
AccessionNumber = t.DicomStudy.AccessionNumber,
StudyDescription = t.DicomStudy.Description,
SeriesInstanceUid = t.DicomSerie.SeriesInstanceUid,
Modality = t.DicomSerie.Modality,
DicomSeriesDate = t.DicomSerie.DicomSeriesDate,
DicomSeriesTime = t.DicomSerie.DicomSeriesTime,
SeriesNumber = t.DicomSerie.SeriesNumber,
SeriesDescription = t.DicomSerie.Description,
InstanceId = t.Id,
SopInstanceUid = t.SopInstanceUid,
SOPClassUID = t.SOPClassUID,
InstanceNumber = t.InstanceNumber,
MediaStorageSOPClassUID = t.MediaStorageSOPClassUID,
MediaStorageSOPInstanceUID = t.MediaStorageSOPInstanceUID,
TransferSyntaxUID = t.TransferSyntaxUID,
}).ToList();
var pathInfo = await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.SubjectId, VisitId = t.Id }).FirstNotNullAsync();
foreach (var item in list.GroupBy(t => new { t.StudyInstanceUid, t.DicomStudyId }))
{
var ossFolder = $"{pathInfo.TrialId}/Image/{pathInfo.SubjectId}/{pathInfo.VisitId}/{item.Key.StudyInstanceUid}";
var isSucess = await SafeBussinessHelper.RunAsync(async () => await DicomDIRHelper.GenerateStudyDIRAndUploadAsync(item.ToList(), dirDic, ossFolder, _oSSService));
if (isSucess)
{
await _dicomStudyRepository.BatchUpdateNoTrackingAsync(t => t.Id == item.Key.DicomStudyId, u => new DicomStudy() { StudyDIRPath = $"/{ossFolder}/DICOMDIR" });
}
}
}
#endregion
var query = from sv in _subjectVisitRepository.Where(t => t.Id == subjectVisitId)
select new
@ -2923,23 +3001,27 @@ namespace IRaCIS.Application.Services
VisitName = sv.VisitName,
StudyList = sv.StudyList
.Select(u => new
.Select(u => new DownloadDicomStudyDto()
{
u.PatientIdStr,
u.StudyTime,
u.StudyCode,
PatientId = u.PatientIdStr,
StudyTime = u.StudyTime,
StudyCode = u.StudyCode,
StudyInstanceUid = u.StudyInstanceUid,
StudyDIRPath = u.StudyDIRPath,
SeriesList = u.SeriesList.Select(z => new
SeriesList = u.SeriesList.Select(z => new DownloadDicomSeriesDto()
{
z.Modality,
Modality = z.Modality,
InstanceList = z.DicomInstanceList.Select(k => new
InstanceList = z.DicomInstanceList.Select(k => new DownloadDicomInstanceDto()
{
k.Path,
k.FileSize
})
})
IsEncapsulated = k.IsEncapsulated,
InstanceId = k.Id,
FileName = string.Empty,
Path = k.Path,
FileSize = k.FileSize
}).ToList()
}).ToList()
}).ToList(),
@ -2948,6 +3030,15 @@ namespace IRaCIS.Application.Services
var result = query.FirstOrDefault();
foreach (var item in result.StudyList.SelectMany(t => t.SeriesList).SelectMany(t => t.InstanceList))
{
var key = item.InstanceId.ToString();
if (dirDic.ContainsKey(key))
{
item.FileName = dirDic[key];
}
}
var preDownloadInfo = new SubejctVisitDownload()
{
Id = NewId.NextSequentialGuid(),
@ -3099,13 +3190,12 @@ namespace IRaCIS.Application.Services
return request;
}
public static void DebugStudyResponse(DicomCFindResponse response, ILogger _logger)
public static void DebugStudyResponse(DicomCFindResponse response)
{
if (response.Status == DicomStatus.Pending)
{
// print the results
_logger.LogDebug($"Patient {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty)}, {(response.Dataset.TryGetString(DicomTag.ModalitiesInStudy, out var dummy) ? dummy : string.Empty)}-Study from {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, new DateTime())} with UID {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)} ");
Log.Logger.Debug($"Patient {response.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty)}, {(response.Dataset.TryGetString(DicomTag.ModalitiesInStudy, out var dummy) ? dummy : string.Empty)}-Study from {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyDate, new DateTime())} with UID {response.Dataset.GetSingleValueOrDefault(DicomTag.StudyInstanceUID, string.Empty)} ");
}
if (response.Status == DicomStatus.Success)
{
@ -3381,7 +3471,7 @@ namespace IRaCIS.Application.Services
var resultInstanceUidList = result.Select(t => t.StudyInstanceUID).ToList();
var existStudyIdList = _studyRepository.Where(t => resultInstanceUidList.Contains(t.StudyInstanceUid))
.Select(t => new { t.StudyInstanceUid, HospitalGroupList = t.HospitalGroupList.Select(t => new HospitalGroupInfo { Id = t.HospitalGroupId, Name = t.HospitalGroup.Name, Code = t.HospitalGroup.Code }).ToList() }).ToList();
.Select(t => new { SCPStudyId = t.Id, t.StudyInstanceUid, HospitalGroupList = t.HospitalGroupList.Select(t => new HospitalGroupInfo { Id = t.HospitalGroupId, Name = t.HospitalGroup.Name, Code = t.HospitalGroup.Code }).ToList() }).ToList();
var allHospitalGroupList = _hospitalGroupRepository.Where(t => t.IsEnable).Select(t => new HospitalGroupInfo() { Name = t.Name, Id = t.Id, Code = t.Code }).ToList();
@ -3390,8 +3480,9 @@ namespace IRaCIS.Application.Services
foreach (var item in result)
{
item.SCPStudyId = existStudyIdList.FirstOrDefault(t => t.StudyInstanceUid == item.StudyInstanceUID)?.SCPStudyId;
item.IsStudyExist = existStudyIdList.Any(t => t.StudyInstanceUid == item.StudyInstanceUID);
item.HospitalGroupList = existStudyIdList.FirstOrDefault(t => t.StudyInstanceUid == item.StudyInstanceUID)?.HospitalGroupList;
item.HospitalGroupList = existStudyIdList.FirstOrDefault(t => t.StudyInstanceUid == item.StudyInstanceUID)?.HospitalGroupList ?? new List<HospitalGroupInfo>();
if (isAdminOrOA)
{
@ -3532,6 +3623,9 @@ namespace IRaCIS.Application.Services
var latestInstanceList = await _scpInstanceRepository.Where(t => inCommand.StudyInstanceUIDList.Contains(t.StudyInstanceUid))
.GroupBy(t => t.StudyInstanceUid).Select(g => g.OrderByDescending(t => t.CreateTime).FirstOrDefault()).ToListAsync();
await _cmoveStudyRepository.AddAsync(new CmoveStudy() { CallingAE = hirClient.CalledAE, CalledAE = find.CalledAE, StudyInstanceUIDList = inCommand.StudyInstanceUIDList, HopitalGroupIdList = inCommand.HospitalGroupIdList }, true);
var task = Task.Run(async () =>
{
var client = DicomClientFactory.Create(find.IP, find.Port, false, hirClient.CalledAE, find.CalledAE);
@ -3575,8 +3669,6 @@ namespace IRaCIS.Application.Services
if (cmoveRequestList.Count > 0)
{
await _cmoveStudyRepository.AddAsync(new CmoveStudy() { CallingAE = hirClient.CalledAE, CalledAE = find.CalledAE, StudyInstanceUIDList = cmoveStudyUIdList, HopitalGroupIdList = inCommand.HospitalGroupIdList }, true);
await client.AddRequestsAsync(cmoveRequestList);
await client.SendAsync();
}

View File

@ -9,6 +9,14 @@ namespace IRaCIS.Core.Domain.Models;
public class VisitTask : BaseFullAuditEntity
{
#region 导航属性
//[JsonIgnore]
//public List<ReadingNoneDicomMark> ReadingNoneDicomMarkList { get; set; }
[JsonIgnore]
public List<ReadingTaskQuestionMark> ReadingTaskQuestionMarkList { get; set; }
[JsonIgnore]
public List<TaskStudy> TaskStudyList { get; set; }

View File

@ -75,4 +75,20 @@ public class DicomInstance : BaseFullAuditEntity, IEntitySeqId
public string WindowWidth { get; set; } = null!;
#region DIR 增加
public string SOPClassUID { get; set; }
public string MediaStorageSOPClassUID { get; set; }
public string TransferSyntaxUID { get; set; }
public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated { get; set; }
#endregion
}

View File

@ -68,5 +68,13 @@ public class DicomSeries : BaseFullDeleteAuditEntity, IEntitySeqId
public string TriggerTime { get; set; } = null!;
public Guid? VisitTaskId { get; set; }
#region DIR 增加
public string DicomSeriesDate { get; set; }
public string DicomSeriesTime { get; set; }
#endregion
}

View File

@ -10,6 +10,9 @@ public class DicomStudy : BaseFullDeleteAuditEntity, IEntitySeqId
[JsonIgnore]
public List<DicomSeries> SeriesList { get; set; }
[JsonIgnore]
public List<DicomInstance> InstanceList { get; set; }
[JsonIgnore]
[ForeignKey("SubjectId")]
public Subject Subject { get; set; }
@ -95,4 +98,15 @@ public class DicomStudy : BaseFullDeleteAuditEntity, IEntitySeqId
public string PatientIdStr { get; set; } = string.Empty;
#region DIR 增加字段
public string DicomStudyDate { get; set; }
public string DicomStudyTime { get; set; }
public string StudyDIRPath { get; set; }
#endregion
}

View File

@ -40,4 +40,19 @@ public class SCPInstance : BaseFullAuditEntity, IEntitySeqId
public string Path { get; set; } = string.Empty;
public long? FileSize { get; set; }
#region DIR 增加
public string SOPClassUID { get; set; }
public string MediaStorageSOPClassUID { get; set; }
public string TransferSyntaxUID { get; set; }
public string MediaStorageSOPInstanceUID { get; set; }
public bool IsEncapsulated { get; set; }
#endregion
}

View File

@ -39,4 +39,12 @@ public class SCPSeries : BaseFullDeleteAuditEntity, IEntitySeqId
[StringLength(1000)]
public string ImageResizePath { get; set; } = string.Empty;
#region DIR 增加
public string DicomSeriesDate { get; set; }
public string DicomSeriesTime { get; set; }
#endregion
}

View File

@ -75,6 +75,16 @@ public class SCPStudy : BaseFullDeleteAuditEntity, IEntitySeqId
//public Guid? SubjectVisitId { get; set; }
#endregion
#region DIR 增加字段
public string DicomStudyDate { get; set; }
public string DicomStudyTime { get; set; }
public string StudyDIRPath { get; set; }
#endregion
}
public class SCPStudyHospitalGroup : BaseFullAuditEntity

View File

@ -61,7 +61,7 @@ namespace IRaCIS.Core.Domain.Models
[Comment("后台 - 系统真实账户表")]
[Table("IdentityUser")]
public class IdentityUser : BaseFullAuditEntityNoNavigat
public class IdentityUser : BaseFullAuditEntity
{
[JsonIgnore]

View File

@ -5,11 +5,11 @@ namespace IRaCIS.Core.Domain.Models;
[Comment("后台 - 系统账户")]
[Table("User")]
public class UserRole : BaseFullAuditEntityNoNavigat
public class UserRole : BaseFullAuditEntity
{
#region 导航属性
//[ForeignKey("IdentityUserId")]
[ForeignKey("IdentityUserId")]
[JsonIgnore]
public IdentityUser IdentityUser { get; set; }

View File

@ -13,6 +13,8 @@ public class DicomStudyConfigration : IEntityTypeConfiguration<DicomStudy>
{
builder.HasKey(e => e.SeqId);
builder.HasMany(s => s.InstanceList).WithOne(se => se.DicomStudy).HasForeignKey(se => se.StudyId).HasPrincipalKey(st => st.Id);
builder.HasMany(s => s.SeriesList).WithOne(se => se.DicomStudy).HasForeignKey(se => se.StudyId).HasPrincipalKey(st => st.Id);
builder.HasMany(s => s.DicomStudyMonitorList).WithOne(sm => sm.DicomStudy).HasForeignKey(sm => sm.StudyId).HasPrincipalKey(se => se.Id);
@ -40,7 +42,7 @@ public class DicomInstanceConfigration : IEntityTypeConfiguration<DicomInstance>
{
builder.HasKey(e => e.SeqId);
builder.HasOne(e => e.DicomStudy).WithMany().HasForeignKey(t => t.StudyId).HasPrincipalKey(st => st.Id);
builder.HasOne(e => e.DicomStudy).WithMany(c => c.InstanceList).HasForeignKey(t => t.StudyId).HasPrincipalKey(st => st.Id);
builder.HasMany(s => s.ReadingTableAnswerRowInfoList).WithOne(di => di.Instance).HasForeignKey(t => t.InstanceId).HasPrincipalKey(se => se.Id);

View File

@ -72,18 +72,17 @@ namespace IRaCIS.Core.Infra.EFCore.EntityConfigration
}
}
//public class IdentityUserConfigration : IEntityTypeConfiguration<IdentityUser>
//{
// //当一个实体,针对同一个类,有两个一对一导航属性,但是是不同的外键,一个外键是一对一,一个是一对多,那么需要显示配置一对多的关系
// public void Configure(EntityTypeBuilder<IdentityUser> builder)
// {
// builder.HasMany(s => s.UserRoleList)
// .WithOne(t=>t.IdentityUser)
// .HasForeignKey(s => s.IdentityUserId);
public class IdentityUserConfigration : IEntityTypeConfiguration<IdentityUser>
{
//当一个实体,针对同一个类,有两个一对一导航属性,但是是不同的外键,一个外键是一对一,一个是一对多,那么需要显示配置一对多的关系
public void Configure(EntityTypeBuilder<IdentityUser> builder)
{
builder.HasMany(s => s.UserRoleList)
.WithOne(t => t.IdentityUser)
.HasForeignKey(s => s.IdentityUserId);
// }
//}
}
}
public class TrialIdentityUserConfigration : IEntityTypeConfiguration<TrialIdentityUser>

View File

@ -31,12 +31,12 @@
<PackageReference Include="Hangfire.Core" Version="1.8.18" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.19" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.19">
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.19">
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.10">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,256 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace IRaCIS.Core.Infra.EFCore.Migrations
{
/// <inheritdoc />
public partial class dicomDir : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "DicomStudyDate",
table: "SCPStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomStudyTime",
table: "SCPStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "StudyDIRPath",
table: "SCPStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomSeriesDate",
table: "SCPSeries",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomSeriesTime",
table: "SCPSeries",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<bool>(
name: "IsEncapsulated",
table: "SCPInstance",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "MediaStorageSOPClassUID",
table: "SCPInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "MediaStorageSOPInstanceUID",
table: "SCPInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "SOPClassUID",
table: "SCPInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TransferSyntaxUID",
table: "SCPInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomStudyDate",
table: "DicomStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomStudyTime",
table: "DicomStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "StudyDIRPath",
table: "DicomStudy",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomSeriesDate",
table: "DicomSeries",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "DicomSeriesTime",
table: "DicomSeries",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<bool>(
name: "IsEncapsulated",
table: "DicomInstance",
type: "bit",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<string>(
name: "MediaStorageSOPClassUID",
table: "DicomInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "MediaStorageSOPInstanceUID",
table: "DicomInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "SOPClassUID",
table: "DicomInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<string>(
name: "TransferSyntaxUID",
table: "DicomInstance",
type: "nvarchar(400)",
maxLength: 400,
nullable: false,
defaultValue: "");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "DicomStudyDate",
table: "SCPStudy");
migrationBuilder.DropColumn(
name: "DicomStudyTime",
table: "SCPStudy");
migrationBuilder.DropColumn(
name: "StudyDIRPath",
table: "SCPStudy");
migrationBuilder.DropColumn(
name: "DicomSeriesDate",
table: "SCPSeries");
migrationBuilder.DropColumn(
name: "DicomSeriesTime",
table: "SCPSeries");
migrationBuilder.DropColumn(
name: "IsEncapsulated",
table: "SCPInstance");
migrationBuilder.DropColumn(
name: "MediaStorageSOPClassUID",
table: "SCPInstance");
migrationBuilder.DropColumn(
name: "MediaStorageSOPInstanceUID",
table: "SCPInstance");
migrationBuilder.DropColumn(
name: "SOPClassUID",
table: "SCPInstance");
migrationBuilder.DropColumn(
name: "TransferSyntaxUID",
table: "SCPInstance");
migrationBuilder.DropColumn(
name: "DicomStudyDate",
table: "DicomStudy");
migrationBuilder.DropColumn(
name: "DicomStudyTime",
table: "DicomStudy");
migrationBuilder.DropColumn(
name: "StudyDIRPath",
table: "DicomStudy");
migrationBuilder.DropColumn(
name: "DicomSeriesDate",
table: "DicomSeries");
migrationBuilder.DropColumn(
name: "DicomSeriesTime",
table: "DicomSeries");
migrationBuilder.DropColumn(
name: "IsEncapsulated",
table: "DicomInstance");
migrationBuilder.DropColumn(
name: "MediaStorageSOPClassUID",
table: "DicomInstance");
migrationBuilder.DropColumn(
name: "MediaStorageSOPInstanceUID",
table: "DicomInstance");
migrationBuilder.DropColumn(
name: "SOPClassUID",
table: "DicomInstance");
migrationBuilder.DropColumn(
name: "TransferSyntaxUID",
table: "DicomInstance");
}
}
}

View File

@ -936,6 +936,19 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<DateTime?>("InstanceTime")
.HasColumnType("datetime2");
b.Property<bool>("IsEncapsulated")
.HasColumnType("bit");
b.Property<string>("MediaStorageSOPClassUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("MediaStorageSOPInstanceUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("NumberOfFrames")
.HasColumnType("int");
@ -949,6 +962,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("SOPClassUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("SeriesId")
.HasColumnType("uniqueidentifier");
@ -984,6 +1002,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<Guid>("SubjectVisitId")
.HasColumnType("uniqueidentifier");
b.Property<string>("TransferSyntaxUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("TrialId")
.HasColumnType("uniqueidentifier");
@ -1060,6 +1083,16 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomSeriesDate")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomSeriesTime")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
@ -1218,6 +1251,16 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomStudyDate")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomStudyTime")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
@ -1287,6 +1330,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("StudyDIRPath")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("StudyId")
.IsRequired()
.HasMaxLength(400)
@ -7675,6 +7723,19 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<DateTime?>("InstanceTime")
.HasColumnType("datetime2");
b.Property<bool>("IsEncapsulated")
.HasColumnType("bit");
b.Property<string>("MediaStorageSOPClassUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("MediaStorageSOPInstanceUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<int>("NumberOfFrames")
.HasColumnType("int");
@ -7688,6 +7749,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("SOPClassUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("SeriesId")
.HasColumnType("uniqueidentifier");
@ -7717,6 +7783,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("TransferSyntaxUID")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<DateTime>("UpdateTime")
.HasColumnType("datetime2");
@ -7851,6 +7922,16 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomSeriesDate")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomSeriesTime")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
@ -8000,6 +8081,16 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomStudyDate")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("DicomStudyTime")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<Guid>("Id")
.HasColumnType("uniqueidentifier");
@ -8058,6 +8149,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
b.Property<int>("SeriesCount")
.HasColumnType("int");
b.Property<string>("StudyDIRPath")
.IsRequired()
.HasMaxLength(400)
.HasColumnType("nvarchar(400)");
b.Property<string>("StudyId")
.IsRequired()
.HasMaxLength(400)
@ -14699,7 +14795,7 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
.IsRequired();
b.HasOne("IRaCIS.Core.Domain.Models.DicomStudy", "DicomStudy")
.WithMany()
.WithMany("InstanceList")
.HasForeignKey("StudyId")
.HasPrincipalKey("Id")
.OnDelete(DeleteBehavior.Cascade)
@ -18493,6 +18589,8 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
{
b.Navigation("DicomStudyMonitorList");
b.Navigation("InstanceList");
b.Navigation("ReadingClinicalDataList");
b.Navigation("ReadingConsistentClinicalDataList");

View File

@ -18,7 +18,7 @@
<PackageReference Include="SharpCompress" Version="0.38.0" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.6" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.6.7" />
</ItemGroup>