From 9f66a932fcdde4d6fd8cded40988b3c4335bbd00 Mon Sep 17 00:00:00 2001 From: hang <872297557@qq.com> Date: Wed, 3 Jul 2024 14:00:29 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0pacs=20=E7=9B=B4=E8=BF=9E=20?= =?UTF-8?q?=20scp=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BusinessFilter/ModelActionFilter .cs | 37 + IRC.Core.SCP/BusinessFilter/ModelBinding.cs | 28 + .../BusinessFilter/ProjectExceptionFilter.cs | 61 + .../BusinessFilter/UnifiedApiResultFilter.cs | 120 ++ IRC.Core.SCP/Helper/ImageHelper.cs | 62 + IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs | 57 + IRC.Core.SCP/HostConfig/EFSetup.cs | 44 + .../HostConfig/NewtonsoftJsonSetup.cs | 59 + .../HostConfig/NullToEmptyStringResolver.cs | 36 + .../NullToEmptyStringValueProvider.cs | 42 + IRC.Core.SCP/IRC.Core.SCP.csproj | 38 + IRC.Core.SCP/Program.cs | 208 +++ IRC.Core.SCP/Properties/launchSettings.json | 31 + IRC.Core.SCP/Service/BaseService.cs | 118 ++ IRC.Core.SCP/Service/CStoreSCPService.cs | 368 +++++ IRC.Core.SCP/Service/DicomArchiveService.cs | 333 +++++ .../Service/Interface/IDicomArchiveService.cs | 11 + IRC.Core.SCP/Service/OSSService.cs | 427 ++++++ IRC.Core.SCP/appsettings.IRC_Prod_SCP.json | 63 + IRC.Core.SCP/appsettings.IRC_Test_SCP.json | 79 + IRC.Core.SCP/appsettings.IRC_US_SCP.json | 76 + IRC.Core.SCP/appsettings.IRC_Uat_SCP.json | 76 + IRC.Core.SCP/appsettings.json | 9 + IRaCIS.Core.API.sln | 6 + IRaCIS.Core.API/IRaCIS.Core.API.csproj | 9 +- .../Service/Common/ExploreRecommendService.cs | 2 +- .../Service/Visit/DTO/PatientViewModel.cs | 1083 ++++++++++++++ .../Service/Visit/PatientService.cs | 1323 +++++++++++++++++ .../IRaCIS.Core.Infrastructure.csproj | 10 +- 29 files changed, 4805 insertions(+), 11 deletions(-) create mode 100644 IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs create mode 100644 IRC.Core.SCP/BusinessFilter/ModelBinding.cs create mode 100644 IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs create mode 100644 IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs create mode 100644 IRC.Core.SCP/Helper/ImageHelper.cs create mode 100644 IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/EFSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs create mode 100644 IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs create mode 100644 IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs create mode 100644 IRC.Core.SCP/IRC.Core.SCP.csproj create mode 100644 IRC.Core.SCP/Program.cs create mode 100644 IRC.Core.SCP/Properties/launchSettings.json create mode 100644 IRC.Core.SCP/Service/BaseService.cs create mode 100644 IRC.Core.SCP/Service/CStoreSCPService.cs create mode 100644 IRC.Core.SCP/Service/DicomArchiveService.cs create mode 100644 IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs create mode 100644 IRC.Core.SCP/Service/OSSService.cs create mode 100644 IRC.Core.SCP/appsettings.IRC_Prod_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_Test_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_US_SCP.json create mode 100644 IRC.Core.SCP/appsettings.IRC_Uat_SCP.json create mode 100644 IRC.Core.SCP/appsettings.json create mode 100644 IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs create mode 100644 IRaCIS.Core.Application/Service/Visit/PatientService.cs diff --git a/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs b/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs new file mode 100644 index 000000000..2f0ec4873 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ModelActionFilter .cs @@ -0,0 +1,37 @@ +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Localization; +using Newtonsoft.Json; + + +namespace IRaCIS.Core.SCP.Filter +{ + + + public class ModelActionFilter : ActionFilterAttribute, IActionFilter + { + public IStringLocalizer _localizer; + public ModelActionFilter(IStringLocalizer localizer) + { + _localizer = localizer; + } + + public override void OnActionExecuting(ActionExecutingContext context) + { + if (!context.ModelState.IsValid) + { + + var validationErrors = context.ModelState + .Keys + .SelectMany(k => context.ModelState[k]!.Errors) + .Select(e => e.ErrorMessage) + .ToArray(); + + //---提供给接口的参数无效。 + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors))); + } + } + } + +} diff --git a/IRC.Core.SCP/BusinessFilter/ModelBinding.cs b/IRC.Core.SCP/BusinessFilter/ModelBinding.cs new file mode 100644 index 000000000..56e18b418 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ModelBinding.cs @@ -0,0 +1,28 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.ModelBinding; + +namespace IRaCIS.Core.SCP.Filter +{ + #region snippet_DisableFormValueModelBindingAttribute + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter + { + public void OnResourceExecuting(ResourceExecutingContext context) + { + + + var factories = context.ValueProviderFactories; + //factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + context.HttpContext.Request.EnableBuffering(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { + } + } + #endregion +} diff --git a/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs b/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs new file mode 100644 index 000000000..0952bc457 --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/ProjectExceptionFilter.cs @@ -0,0 +1,61 @@ +using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; + +namespace IRaCIS.Core.SCP.Filter +{ + public class ProjectExceptionFilter : Attribute, IExceptionFilter + { + private readonly ILogger _logger; + + public IStringLocalizer _localizer; + + public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) + { + _logger = logger; + _localizer = localizer; + } + public void OnException(ExceptionContext context) + { + //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 + + if (!context.ExceptionHandled) + { + if (context.Exception.GetType().Name == "DbUpdateConcurrencyException") + { + //---并发更新,当前不允许该操作 + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message)); + } + + if (context.Exception.GetType() == typeof(BusinessValidationFailedException)) + { + var error = context.Exception as BusinessValidationFailedException; + + context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code)); + } + else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException)) + { + context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist)); + } + else + { + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/) + : (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException)); + } + + + _logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace)); + + + } + else + { + //继续 + } + context.ExceptionHandled = true;//标记当前异常已经被处理过了 + } + } +} diff --git a/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs b/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs new file mode 100644 index 000000000..7609ae4bc --- /dev/null +++ b/IRC.Core.SCP/BusinessFilter/UnifiedApiResultFilter.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; + +namespace IRaCIS.Application.Services.BusinessFilter +{ + /// + /// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 + /// by zhouhang 2021.09.12 周末 + /// + public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter + { + /// + /// 异步版本 + /// + /// + /// + /// + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + + if (context.Result is ObjectResult objectResult) + { + var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; + + //是200 并且没有包装 那么包装结果 + if (statusCode == 200 && !(objectResult.Value is IResponseOutput)) + { + //if (objectResult.Value == null) + //{ + // var apiResponse = ResponseOutput.DBNotExist(); + + // objectResult.Value = apiResponse; + // objectResult.DeclaredType = apiResponse.GetType(); + //} + //else + //{ + + var type = objectResult.Value?.GetType(); + + + if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>))) + { + + //报错 + //var tuple = (object, object))objectResult.Value; + + //var (val1, val2) = ((dynamic, dynamic))objectResult.Value; + //var apiResponse = ResponseOutput.Ok(val1, val2); + + //OK + var tuple = (dynamic)objectResult.Value; + var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2); + + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + else + { + var apiResponse = ResponseOutput.Ok(objectResult.Value); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + + //} + + } + //如果不是200 是IResponseOutput 不处理 + else if (statusCode != 200 && (objectResult.Value is IResponseOutput)) + { + } + + else if(statusCode != 200&&!(objectResult.Value is IResponseOutput)) + { + //---程序错误,请联系开发人员。 + var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError")); + + objectResult.Value = apiResponse; + objectResult.DeclaredType = apiResponse.GetType(); + } + + } + + await next.Invoke(); + + } + + public static bool IsTupleType(Type type, bool checkBaseTypes = false) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + + if (type == typeof(Tuple)) + return true; + + while (type != null) + { + if (type.IsGenericType) + { + var genType = type.GetGenericTypeDefinition(); + if (genType == typeof(Tuple<>) + || genType == typeof(Tuple<,>) + || genType == typeof(Tuple<,>)) + return true; + } + + if (!checkBaseTypes) + break; + + type = type.BaseType; + } + + return false; + } + } +} diff --git a/IRC.Core.SCP/Helper/ImageHelper.cs b/IRC.Core.SCP/Helper/ImageHelper.cs new file mode 100644 index 000000000..50f0ab4f6 --- /dev/null +++ b/IRC.Core.SCP/Helper/ImageHelper.cs @@ -0,0 +1,62 @@ + +using FellowOakDicom.Imaging; +using SixLabors.ImageSharp.Formats.Jpeg; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Processing; +using System.Security.Cryptography; +using System.Text; + +namespace IRaCIS.Core.Application.Helper; + +public static class ImageHelper +{ + + // 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决) + public static void ResizeSave(string filePath, string? fileStorePath) + { + + fileStorePath = fileStorePath ?? filePath + ".preview.jpeg"; + + using (var image = SixLabors.ImageSharp.Image.Load(filePath)) + { + + image.Mutate(x => x.Resize(500, 500)); + + image.Save(fileStorePath); + + } + } + + + + 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); + + 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); + } + + +} + + + diff --git a/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs b/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs new file mode 100644 index 000000000..183eb235b --- /dev/null +++ b/IRC.Core.SCP/HostConfig/AutofacModuleSetup.cs @@ -0,0 +1,57 @@ +using Autofac; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Http; +using Panda.DynamicWebApi; +using System; +using System.Linq; +using System.Reflection; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Application.Services; +using AutoMapper; +using IRaCIS.Core.SCP.Service; + +namespace IRaCIS.Core.SCP +{ + // ReSharper disable once IdentifierTypo + public class AutofacModuleSetup : Autofac.Module + { + protected override void Load(ContainerBuilder containerBuilder) + { + + #region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义 + + containerBuilder.RegisterGeneric(typeof(Repository<>)) + .As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储 + + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + #endregion + + #region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html + + //获取所有控制器类型并使用属性注入 + containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly) + .Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type)) + .PropertiesAutowired(); + + #endregion + + + + Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.SCP.dll"); + containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service")) + .PropertiesAutowired().AsImplementedInterfaces(); + + + containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + + + + + + } + } +} \ No newline at end of file diff --git a/IRC.Core.SCP/HostConfig/EFSetup.cs b/IRC.Core.SCP/HostConfig/EFSetup.cs new file mode 100644 index 000000000..b53095cbd --- /dev/null +++ b/IRC.Core.SCP/HostConfig/EFSetup.cs @@ -0,0 +1,44 @@ +using IRaCIS.Core.Infra.EFCore; +using Medallion.Threading; +using Medallion.Threading.SqlServer; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace IRaCIS.Core.SCP +{ + public static class EFSetup + { + public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration) + { + //services.AddScoped(); + + //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext + //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 + services.AddDbContext(options => + { + options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, + contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + + options.EnableSensitiveDataLogging(); + + options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + + options.UseProjectables(); + + + + }); + + //注意区分 easy caching 也有 IDistributedLockProvider + services.AddSingleton(sp => + { + //var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); + + return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value); + }); + + //services.AddAssemblyTriggers(typeof(SubjectVisitImageDateTrigger).Assembly); + } + } +} diff --git a/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs b/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs new file mode 100644 index 000000000..00d5ae329 --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NewtonsoftJsonSetup.cs @@ -0,0 +1,59 @@ + + +using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using System; + +namespace IRaCIS.Core.SCP +{ + public static class NewtonsoftJsonSetup + { + public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services) + { + services.AddHttpContextAccessor(); + services.AddScoped(); + + builder.AddNewtonsoftJson(options => + { + //options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; + // 忽略循环引用 + options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + //options.SerializerSettings.TypeNameHandling = TypeNameHandling.All; + + //处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0 + options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver(); + // 设置时间格式 + options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + + options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + + //options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ; + + //options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService()); + + + + }) + .AddControllersAsServices()//动态webApi属性注入需要 + .ConfigureApiBehaviorOptions(o => + { + o.SuppressModelStateInvalidFilter = true; //自己写验证 + + }); + + + Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings(); + JsonConvert.DefaultSettings = new Func(() => + { + //日期类型默认格式化处理 + setting.DateFormatString = "yyyy-MM-dd HH:mm:ss"; + setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return setting; + }); + + + } + } +} diff --git a/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs b/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs new file mode 100644 index 000000000..f4bd469a6 --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NullToEmptyStringResolver.cs @@ -0,0 +1,36 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace IRaCIS.Core.SCP +{ + public class NullToEmptyStringResolver : DefaultContractResolver + { + + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList properties = base.CreateProperties(type, memberSerialization); + + var list= type.GetProperties() + .Select(p => + { + var jp = base.CreateProperty(p, memberSerialization); + jp.ValueProvider = new NullToEmptyStringValueProvider(p); + return jp; + }).ToList(); + + var uu = list.Select(t => t.PropertyName).ToList(); + + //获取复杂对象属性 + properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList(); + + list.AddRange(properties); + return list; + } + + + } +} diff --git a/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs b/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs new file mode 100644 index 000000000..10e7e613d --- /dev/null +++ b/IRC.Core.SCP/HostConfig/NullToEmptyStringValueProvider.cs @@ -0,0 +1,42 @@ +using System; +using System.Reflection; +using Newtonsoft.Json.Serialization; + +namespace IRaCIS.Core.SCP +{ + + public class NullToEmptyStringValueProvider : IValueProvider + { + PropertyInfo _MemberInfo; + public NullToEmptyStringValueProvider(PropertyInfo memberInfo) + { + _MemberInfo = memberInfo; + } + public object GetValue(object target) + { + object result = _MemberInfo.GetValue(target); + if (_MemberInfo.PropertyType == typeof(string) && result == null) result = ""; + else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { }; + //else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0; + else if (_MemberInfo.PropertyType == typeof(Nullable) && result == null) result = 0.00M; + + return result; + } + public void SetValue(object target, object value) + { + + if(_MemberInfo.PropertyType == typeof(string)) + { + //去掉前后空格 + _MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim()); + + } + else + { + _MemberInfo.SetValue(target, value); + } + + } + } + +} diff --git a/IRC.Core.SCP/IRC.Core.SCP.csproj b/IRC.Core.SCP/IRC.Core.SCP.csproj new file mode 100644 index 000000000..9407aa2ba --- /dev/null +++ b/IRC.Core.SCP/IRC.Core.SCP.csproj @@ -0,0 +1,38 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + + true + + + + + + + + + + + + + + + + diff --git a/IRC.Core.SCP/Program.cs b/IRC.Core.SCP/Program.cs new file mode 100644 index 000000000..5ad53894d --- /dev/null +++ b/IRC.Core.SCP/Program.cs @@ -0,0 +1,208 @@ + +using Autofac; +using Autofac.Extensions.DependencyInjection; +using AutoMapper.EquivalencyExpression; +using FellowOakDicom; +using FellowOakDicom.Imaging; +using FellowOakDicom.Imaging.NativeCodec; +using FellowOakDicom.Network; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.SCP; +using IRaCIS.Core.SCP.Filter; +using IRaCIS.Core.SCP.Service; +using MassTransit; +using MassTransit.NewIdProviders; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.Extensions.DependencyInjection; +using Panda.DynamicWebApi; +using Serilog; +using Serilog.Events; +using System.Runtime.InteropServices; + + +//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) +var config = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + +var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + EnvironmentName = enviromentName +}); + +#region 兼容windows 服务命令行的方式 + +int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port")); + +if (urlsIndex > -1) +{ + var port = args[urlsIndex].Substring("--port=".Length); + Console.WriteLine(port); + builder.WebHost.UseUrls($"http://0.0.0.0:{port}"); +} + +#endregion + +#region 主机配置 + +NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); + +builder.Configuration.AddJsonFile("appsettings.json", false, true) + .AddJsonFile($"appsettings.{enviromentName}.json", false, true); +builder.Host + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .ConfigureContainer(containerBuilder => + { + containerBuilder.RegisterModule(); + }) + .UseSerilog(); +#endregion + +#region 配置服务 +var _configuration = builder.Configuration; + +//健康检查 +builder.Services.AddHealthChecks(); + +//本地化 +builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); + + +// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() +builder.Services.AddControllers(options => +{ + //options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + options.Filters.Add(); + + +}) + .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 + + +builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); +builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); +builder.Services.AddOptions().Configure(_configuration.GetSection("DicomSCPServiceConfig")); + + +//动态WebApi + UnifiedApiResultFilter 省掉控制器代码 +//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响 +builder.Services + .AddDynamicWebApi(dynamicWebApiOption => + { + //默认是 api + dynamicWebApiOption.DefaultApiPrefix = ""; + //首字母小写 + dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1); + //删除 Service后缀 + dynamicWebApiOption.RemoveControllerPostfixes.Add("Service"); + + }); + +//AutoMapper +builder.Services.AddAutoMapper(automapper => +{ + + automapper.AddCollectionMappers(); + + +}, typeof(BaseService).Assembly); + +//EF ORM QueryWithNoLock +builder.Services.AddEFSetup(_configuration); + + +//转发头设置 获取真实IP +builder.Services.Configure(options => +{ + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; +}); + +//Dicom影像渲染图片 跨平台 +//builder.Services.AddDicomSetup(); +new DicomSetupBuilder() + .RegisterServices(s => + s.AddFellowOakDicom() + .AddTranscoderManager() + //.AddTranscoderManager() + .AddImageManager()) + .SkipValidation() + .Build(); + + + +#endregion + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +//if (app.Environment.IsDevelopment()) +//{ +app.UseSwagger(); +app.UseSwaggerUI(); +//} + +app.UseAuthorization(); + +app.MapControllers(); + +#region 日志 + +Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + // Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条 + .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) + .MinimumLevel.Override("Hangfire", LogEventLevel.Warning) + .MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning) + .Enrich.WithClientIp() + + .Enrich.FromLogContext() + + //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 + .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + + .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day, + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}") + .CreateLogger(); + + +#endregion + + +#region 运行环境 部署平台 + +Log.Logger.Warning($"当前环境:{enviromentName}"); + +if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) +{ + Log.Logger.Warning($"当前部署平台环境:windows"); +} +else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) +{ + Log.Logger.Warning($"当前部署平台环境:linux"); +} +else +{ + Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD"); +} + +#endregion + + +var server = DicomServerFactory.Create(_configuration.GetSection("DicomSCPServiceConfig").GetValue("ServerPort"), userState: app.Services); + + +app.Run(); diff --git a/IRC.Core.SCP/Properties/launchSettings.json b/IRC.Core.SCP/Properties/launchSettings.json new file mode 100644 index 000000000..ec782f999 --- /dev/null +++ b/IRC.Core.SCP/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:36358", + "sslPort": 0 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5243", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/IRC.Core.SCP/Service/BaseService.cs b/IRC.Core.SCP/Service/BaseService.cs new file mode 100644 index 000000000..a10e9bf9c --- /dev/null +++ b/IRC.Core.SCP/Service/BaseService.cs @@ -0,0 +1,118 @@ +using AutoMapper; +using IRaCIS.Application.Services.BusinessFilter; +using IRaCIS.Core.Infra.EFCore; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Localization; +using Panda.DynamicWebApi; +using Panda.DynamicWebApi.Attributes; +using System.Diagnostics.CodeAnalysis; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Models; + +namespace IRaCIS.Core.SCP.Service +{ + +#pragma warning disable CS8618 + + + #region 非泛型版本 + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseService : IBaseService, IDynamicWebApi + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public IWebHostEnvironment _hostEnvironment { get; set; } + + + + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + } + + + public interface IBaseService + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + [MemberNotNull(nameof(_hostEnvironment))] + public IWebHostEnvironment _hostEnvironment { get; set; } + + } + #endregion + + + #region 泛型版本测试 + + + public interface IBaseServiceTest where T : Entity + { + [MemberNotNull(nameof(_mapper))] + public IMapper _mapper { get; set; } + + [MemberNotNull(nameof(_userInfo))] + public IUserInfo _userInfo { get; set; } + + [MemberNotNull(nameof(_repository))] + public IRepository _repository { get; set; } + + [MemberNotNull(nameof(_localizer))] + public IStringLocalizer _localizer { get; set; } + + + + } + + + [Authorize, DynamicWebApi, UnifiedApiResultFilter] + public class BaseServiceTest : IBaseServiceTest, IDynamicWebApi where T : Entity + { + public IMapper _mapper { get; set; } + + public IUserInfo _userInfo { get; set; } + + public IRepository _repository { get; set; } + + public IStringLocalizer _localizer { get; set; } + + public static IResponseOutput Null404NotFound(TEntity? businessObject) where TEntity : class + { + return new ResponseOutput() + .NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist); + } + + + } + + + #endregion + + + + + + + +} diff --git a/IRC.Core.SCP/Service/CStoreSCPService.cs b/IRC.Core.SCP/Service/CStoreSCPService.cs new file mode 100644 index 000000000..a81e11ec1 --- /dev/null +++ b/IRC.Core.SCP/Service/CStoreSCPService.cs @@ -0,0 +1,368 @@ +using FellowOakDicom.Network; +using FellowOakDicom; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using IRaCIS.Core.SCP.Service; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infra.EFCore; +using Medallion.Threading; +using IRaCIS.Core.Domain.Share; +using Serilog; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; +using Microsoft.Extensions.Options; +using System.Data; +using FellowOakDicom.Imaging; +using SharpCompress.Common; +using SixLabors.ImageSharp.Formats.Jpeg; +using IRaCIS.Core.Infrastructure; + +namespace IRaCIS.Core.SCP.Service +{ + /// + /// 后台托管服务的方式运行 + /// + //public class CStoreSCPHostedService : IHostedService + //{ + // private readonly ILogger _logger; + // private readonly IDicomServerFactory _dicomServerFactory; + // private IDicomServer? _server; + + // public CStoreSCPHostedService(ILogger logger, IDicomServerFactory dicomServerFactory) + // { + // _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + // _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory)); + // } + + // public async Task StartAsync(CancellationToken cancellationToken) + // { + // _logger.LogInformation("Starting DICOM server"); + // _server = _dicomServerFactory.Create(104); + // _logger.LogInformation("DICOM server is running"); + // } + + // public Task StopAsync(CancellationToken cancellationToken) + // { + // if (_server != null) + // { + // _server.Stop(); + // _server.Dispose(); + // _server = null; + // } + // return Task.CompletedTask; + // } + //} + public class DicomSCPServiceOption + { + public List CalledAEList { get; set; } + + public string ServerPort { get; set; } + } + + + + + public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider + { + private IServiceProvider _serviceProvider { get; set; } + + private List _SCPStudyIdList { get; set; } = new List(); + + private SCPImageUpload _upload { get; set; } + + + private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[] + { + DicomTransferSyntax.ExplicitVRLittleEndian, + DicomTransferSyntax.ExplicitVRBigEndian, + DicomTransferSyntax.ImplicitVRLittleEndian + }; + + private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[] + { + // Lossless + DicomTransferSyntax.JPEGLSLossless, + DicomTransferSyntax.JPEG2000Lossless, + DicomTransferSyntax.JPEGProcess14SV1, + DicomTransferSyntax.JPEGProcess14, + DicomTransferSyntax.RLELossless, + // Lossy + DicomTransferSyntax.JPEGLSNearLossless, + DicomTransferSyntax.JPEG2000Lossy, + DicomTransferSyntax.JPEGProcess1, + DicomTransferSyntax.JPEGProcess2_4, + // Uncompressed + DicomTransferSyntax.ExplicitVRLittleEndian, + DicomTransferSyntax.ExplicitVRBigEndian, + DicomTransferSyntax.ImplicitVRLittleEndian + }; + + + public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies) + : base(stream, fallbackEncoding, log, dependencies) + { + } + + + + + public Task OnReceiveAssociationRequestAsync(DicomAssociation association) + { + + _upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost }; + + + Log.Logger.Warning($"接收到来自{association.CallingAE}的连接"); + + _serviceProvider = (IServiceProvider)this.UserState; + + var option = _serviceProvider.GetService>().CurrentValue; + + + + var calledAEList = option.CalledAEList; + + if (!calledAEList.Contains(association.CalledAE)) + + //if (association.CalledAE != "STORESCP") + { + + Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接"); + + return SendAssociationRejectAsync( + DicomRejectResult.Permanent, + DicomRejectSource.ServiceUser, + DicomRejectReason.CalledAENotRecognized); + } + + foreach (var pc in association.PresentationContexts) + { + if (pc.AbstractSyntax == DicomUID.Verification) + { + pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes); + } + else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None) + { + pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes); + } + } + + return SendAssociationAcceptAsync(association); + } + + + public async Task OnReceiveAssociationReleaseRequestAsync() + { + await DataMaintenanceAsaync(); + + //记录监控 + + var _SCPImageUploadRepository = _serviceProvider.GetService>(); + + _upload.EndTime = DateTime.Now; + _upload.StudyCount = _SCPStudyIdList.Count; + + await _SCPImageUploadRepository.AddAsync(_upload,true); + + await SendAssociationReleaseResponseAsync(); + } + + + private async Task DataMaintenanceAsaync() + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查"); + + //var patientStudyService = _serviceProvider.GetService(); + + //await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList); + + //处理检查Modality + var _dictionaryRepository = _serviceProvider.GetService>(); + var _seriesRepository = _serviceProvider.GetService>(); + var _studyRepository = _serviceProvider.GetService>(); + + var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList(); + var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList(); + + foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId)) + { + var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList()); + + //特殊逻辑 + var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty; + + if (modality == "MR") + { + modalityForEdit = "MRI"; + } + + if (modality == "PT") + { + modalityForEdit = "PET"; + } + if (modality == "PT、CT" || modality == "CT、PT") + { + modalityForEdit = "PET-CT"; + } + + await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit }); + + } + + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束"); + } + + public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason) + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}"); + /* nothing to do here */ + } + + + public async void OnConnectionClosed(Exception exception) + { + /* nothing to do here */ + + //奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致 + if (exception == null) + { + var _studyRepository = _serviceProvider.GetService>(); + //将检查设置为传输结束 + await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true }); + + await _studyRepository.SaveChangesAndClearAllTrackingAsync(); + } + + Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}"); + } + + + + + public async Task OnCStoreRequestAsync(DicomCStoreRequest request) + { + + string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID); + string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID); + string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID); + + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + + var ossService = _serviceProvider.GetService(); + var dicomArchiveService = _serviceProvider.GetService(); + var _seriesRepository = _serviceProvider.GetService>(); + + var _distributedLockProvider = _serviceProvider.GetService(); + + var storeRelativePath = string.Empty; + var ossFolderPath = $"Dicom/{studyInstanceUid}"; + + + long fileSize = 0; + try + { + + using (MemoryStream ms = new MemoryStream()) + { + await request.File.SaveAsync(ms); + + //irc 从路径最后一截取Guid + storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false); + + fileSize = ms.Length; + } + + Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 "); + + } + catch (Exception ec) + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}"); + } + + + + var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}"); + + using (await @lock.AcquireAsync()) + { + try + { + var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, storeRelativePath, Association.CallingAE, Association.CalledAE); + + if (!_SCPStudyIdList.Contains(scpStudyId)) + { + _SCPStudyIdList.Add(scpStudyId); + } + + var series = await _seriesRepository.FindAsync(seriesId); + + //没有缩略图 + if (series != null && string.IsNullOrEmpty(series.ImageResizePath)) + { + + // 生成缩略图 + using (var memoryStream = new MemoryStream()) + { + DicomImage image = new DicomImage(request.Dataset); + + var sharpimage = image.RenderImage().AsSharpImage(); + sharpimage.Save(memoryStream, new JpegEncoder()); + + // 上传缩略图到 OSS + + var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false); + + Console.WriteLine(seriesPath + " Id: " + seriesId); + + series.ImageResizePath = seriesPath; + + + + //await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath }); + + + } + } + + await _seriesRepository.SaveChangesAsync(); + } + catch (Exception ex) + { + + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}"); + + } + + } + + + + //监控信息设置 + _upload.FileCount++; + _upload.FileSize= _upload.FileSize+ fileSize; + return new DicomCStoreResponse(request, DicomStatus.Success); + } + + + public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e) + { + // let library handle logging and error response + return Task.CompletedTask; + } + + + public Task OnCEchoRequestAsync(DicomCEchoRequest request) + { + return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success)); + } + + } +} diff --git a/IRC.Core.SCP/Service/DicomArchiveService.cs b/IRC.Core.SCP/Service/DicomArchiveService.cs new file mode 100644 index 000000000..439741952 --- /dev/null +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -0,0 +1,333 @@ +using IRaCIS.Core.Domain.Share; +using System.Text; +using Microsoft.AspNetCore.Hosting; +using IRaCIS.Core.Infrastructure; +using Medallion.Threading; +using FellowOakDicom; +using FellowOakDicom.Imaging.Codec; +using System.Data; +using IRaCIS.Core.Domain.Models; +using FellowOakDicom.Network; +using IRaCIS.Core.SCP.Service; +using IRaCIS.Core.Infra.EFCore; +using MassTransit; + +namespace IRaCIS.Core.SCP.Service +{ + public class DicomArchiveService : BaseService, IDicomArchiveService + { + private readonly IRepository _patientRepository; + private readonly IRepository _studyRepository; + private readonly IRepository _seriesRepository; + private readonly IRepository _instanceRepository; + private readonly IRepository _dictionaryRepository; + private readonly IDistributedLockProvider _distributedLockProvider; + + + private List _instanceIdList = new List(); + + public DicomArchiveService(IRepository patientRepository, IRepository studyRepository, + IRepository seriesRepository, + IRepository instanceRepository, + IRepository dictionaryRepository, + IDistributedLockProvider distributedLockProvider) + { + _distributedLockProvider = distributedLockProvider; + _studyRepository = studyRepository; + _patientRepository = patientRepository; + _seriesRepository = seriesRepository; + _instanceRepository = instanceRepository; + _dictionaryRepository = dictionaryRepository; + + } + + + + + /// + /// 单个文件接收 归档 + /// + /// + /// + /// + public async Task ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE) + { + string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID); + string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID); + string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID); + + string patientIdStr = dataset.GetString(DicomTag.PatientID); + + //Guid patientId= IdentifierHelper.CreateGuid(patientIdStr); + Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid); + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid); + + var isStudyNeedAdd = false; + var isSeriesNeedAdd = false; + var isInstanceNeedAdd = false; + var isPatientNeedAdd = false; + + //var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}"); + + //using (@lock.Acquire()) + { + var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr); + var findStudy = await _studyRepository.FindAsync(studyId); + var findSerice = await _seriesRepository.FindAsync(seriesId); + var findInstance = await _instanceRepository.FindAsync(instanceId); + + DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.StudyTime).TimeOfDay); + + //先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0 + if (findPatient == null && findStudy==null) + { + isPatientNeedAdd = true; + + + findPatient = new SCPPatient() + { + Id = NewId.NextSequentialGuid(), + PatientIdStr = 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), + PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty), + + EarliestStudyTime = studyTime, + LatestStudyTime = studyTime, + LatestPushTime = DateTime.Now, + }; + + if (findPatient.PatientBirthDate.Length == 8) + { + var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}"; + + var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}"; + + int year = 0; + + var canParse = int.TryParse(yearStr, out year); + + if (canParse && year > 1900) + { + findPatient.PatientBirthDate = birthDateStr; + + } + else + { + findPatient.PatientBirthDate = string.Empty; + } + + } + } + else + { + if (studyTime < findPatient.EarliestStudyTime) + { + findPatient.EarliestStudyTime = studyTime; + } + if (studyTime > findPatient.LatestStudyTime) + { + findPatient.LatestStudyTime = studyTime; + } + + findPatient.LatestPushTime = DateTime.Now; + } + + if (findStudy == null) + { + isStudyNeedAdd = true; + findStudy = new SCPStudy + { + CalledAE = calledAE, + CallingAE = callingAE, + + PatientId = findPatient.Id, + Id = studyId, + StudyInstanceUid = studyInstanceUid, + StudyTime = studyTime, + Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty), + //ModalityForEdit = modalityForEdit, + Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty), + InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty), + PatientIdStr = 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), + + + + //IsDoubleReview = addtionalInfo.IsDoubleReview, + SeriesCount = 0, + InstanceCount = 0 + }; + + + if (findStudy.PatientBirthDate.Length == 8) + { + 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]}"; + } + } + + + if (findSerice == null) + { + isSeriesNeedAdd = true; + + findSerice = new SCPSeries + { + Id = seriesId, + StudyId = findStudy.Id, + + StudyInstanceUid = findStudy.StudyInstanceUid, + SeriesInstanceUid = seriesInstanceUid, + SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1), + //SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay), + //SeriesTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.SeriesDate) + dataset.GetSingleValue(DicomTag.SeriesTime), out DateTime dt) ? dt : null, + SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.SeriesTime).TimeOfDay), + 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.AcquisitionTime, string.Empty), + AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty), + TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty), + + + InstanceCount = 0 + }; + + ++findStudy.SeriesCount; + } + + + if (findInstance == null) + { + isInstanceNeedAdd = true; + findInstance = new SCPInstance + { + Id = instanceId, + StudyId = findStudy.Id, + SeriesId = findSerice.Id, + StudyInstanceUid = findStudy.StudyInstanceUid, + SeriesInstanceUid = findSerice.SeriesInstanceUid, + + SopInstanceUid = sopInstanceUid, + InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1), + InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue(DicomTag.ContentTime).TimeOfDay), + //InstanceTime = DateTime.TryParse(dataset.GetSingleValue(DicomTag.ContentDate) + dataset.GetSingleValue(DicomTag.ContentTime), out DateTime dt) ? dt : null, + //InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)), + //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), + + Path = fileRelativePath + }; + + ++findStudy.InstanceCount; + ++findSerice.InstanceCount; + } + + if (isPatientNeedAdd) + { + var ss = await _patientRepository.AddAsync(findPatient); + } + if (isStudyNeedAdd) + { + var dd = await _studyRepository.AddAsync(findStudy); + } + else + { + await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false }); + } + + if (isSeriesNeedAdd) + { + await _seriesRepository.AddAsync(findSerice); + } + if (isInstanceNeedAdd) + { + await _instanceRepository.AddAsync(findInstance); + } + else + { + await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath }); + } + + //await _studyRepository.SaveChangesAsync(); + + return findStudy.Id; + } + + } + + + // 从DICOM文件中获取使用的字符集 + private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag) + { + + // 获取DICOM文件的特定元素,通常用于指示使用的字符集 + var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty); + + var dicomEncoding = DicomEncoding.GetEncoding(charset); + + + var dicomStringElement = dataset.GetDicomItem(dicomTag); + + var bytes = dicomStringElement.Buffer.Data; + + + return dicomEncoding.GetString(bytes); + + + //// 从DICOM文件中获取使用的字符集 + //string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm"; + //DicomFile dicomFile = DicomFile.Open(filePath); + + //// 获取DICOM文件的特定元素,通常用于指示使用的字符集 + //var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty); + + //var dicomEncoding = DicomEncoding.GetEncoding(charset); + + //var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty); + + //var dicomStringElement = dicomFile.Dataset.GetDicomItem(DicomTag.PatientName); + + //var bytes = dicomStringElement.Buffer.Data; + + //var aa= dicomEncoding.GetString(bytes); + + + } + } +} diff --git a/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs new file mode 100644 index 000000000..970b3e146 --- /dev/null +++ b/IRC.Core.SCP/Service/Interface/IDicomArchiveService.cs @@ -0,0 +1,11 @@ +using FellowOakDicom; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; + +namespace IRaCIS.Core.SCP.Service +{ + public interface IDicomArchiveService + { + Task ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE); + + } +} diff --git a/IRC.Core.SCP/Service/OSSService.cs b/IRC.Core.SCP/Service/OSSService.cs new file mode 100644 index 000000000..cfbe4b3f8 --- /dev/null +++ b/IRC.Core.SCP/Service/OSSService.cs @@ -0,0 +1,427 @@ +using Aliyun.OSS; +using IRaCIS.Core.Infrastructure; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using Minio.DataModel.Args; +using Minio; +using SharpCompress.Common; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Security.AccessControl; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.SCP +{ + public class MinIOOptions : AWSOptions + { + public int port { get; set; } + + } + + public class AWSOptions + { + public string endPoint { get; set; } + public bool useSSL { get; set; } + public string accessKey { get; set; } + public string secretKey { get; set; } + public string bucketName { get; set; } + public string viewEndpoint { get; set; } + } + + public class AliyunOSSOptions + { + public string regionId { get; set; } + public string accessKeyId { get; set; } + public string accessKeySecret { get; set; } + public string endPoint { get; set; } + public string bucketName { get; set; } + + public string roleArn { get; set; } + + public string region { get; set; } + + public string viewEndpoint { get; set; } + + } + + public class ObjectStoreServiceOptions + { + public string ObjectStoreUse { get; set; } + public AliyunOSSOptions AliyunOSS { get; set; } + public MinIOOptions MinIO { get; set; } + + public AWSOptions AWS { get; set; } + + } + + public class ObjectStoreDTO + { + public string ObjectStoreUse { get; set; } + + public AliyunOSSOptions AliyunOSS { get; set; } + + public MinIOOptions MinIO { get; set; } + + public AWSOptions AWS { get; set; } + + } + + public class AliyunOSSTempToken + { + public string AccessKeyId { get; set; } + public string AccessKeySecret { get; set; } + public string SecurityToken { get; set; } + public string Expiration { get; set; } + + public string Region { get; set; } + public string BucketName { get; set; } + public string ViewEndpoint { get; set; } + } + + + + public enum ObjectStoreUse + { + AliyunOSS = 0, + MinIO = 1, + AWS = 2, + } + + public interface IOSSService + { + public Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName,bool isFileNameAddGuid=true); + public Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true); + + public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath); + + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + public Task GetSignedUrl(string ossRelativePath); + + } + + + public class OSSService : IOSSService + { + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + + public OSSService(IOptionsMonitor options) + { + ObjectStoreServiceOptions = options.CurrentValue; + } + + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true) + { + + + var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}": $"{oosFolderPath}/{fileRealName}"; + //var ossRelativePath = oosFolderPath + "/" + fileRealName; + + try + { + using (var memoryStream = new MemoryStream()) + { + fileStream.Seek(0, SeekOrigin.Begin); + + fileStream.CopyTo(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + + + // 上传文件 + var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); ; + } + + + + + return "/" + ossRelativePath; + + } + + + + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true) + { + var localFileName = Path.GetFileName(localFilePath); + + var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}"; + + //var ossRelativePath = oosFolderPath + "/" + localFileName; + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 上传文件 + var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFileName(localFilePath); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFileName(localFilePath); + + await minioClient.PutObjectAsync(putObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + return "/" + ossRelativePath; + + } + + public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath) + { + + ossRelativePath = ossRelativePath.TrimStart('/'); + try + { + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 上传文件 + var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath); + + // 将下载的文件流保存到本地文件 + using (var fs = File.OpenWrite(localFilePath)) + { + result.Content.CopyTo(fs); + fs.Close(); + } + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var getObjectArgs = new GetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFile(localFilePath); + + await minioClient.GetObjectAsync(getObjectArgs); + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + var getObjectArgs = new GetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithFile(localFilePath); + + await minioClient.GetObjectAsync(getObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("oss下载失败!" + ex.Message); + } + + + + + + } + + public async Task GetSignedUrl(string ossRelativePath) + { + ossRelativePath = ossRelativePath.TrimStart('/'); + try + { + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + // 生成签名URL。 + var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get) + { + // 设置签名URL过期时间,默认值为3600秒。 + Expiration = DateTime.Now.AddHours(1), + }; + var uri = _ossClient.GeneratePresignedUri(req); + + return uri.PathAndQuery; + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + { + var minIOConfig = ObjectStoreServiceOptions.MinIO; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + //var reqParams = new Dictionary(StringComparer.Ordinal) + // { + // { "response-content-type", "application/json" } + // }; + + var args = new PresignedGetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithExpiry(3600) + /*.WithHeaders(reqParams)*/; + + var presignedUrl = await minioClient.PresignedGetObjectAsync(args); + + Uri uri = new Uri(presignedUrl); + + string relativePath = uri.PathAndQuery; + + + return relativePath; + + } + else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") + { + var minIOConfig = ObjectStoreServiceOptions.AWS; + + var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}") + .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) + .Build(); + + return string.Empty; + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message); + } + + } + } + + +} diff --git a/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json b/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json new file mode 100644 index 000000000..32ee00af6 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Prod_SCP.json @@ -0,0 +1,63 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endpoint": "http://192.168.3.68", + "port": "8001", + "useSSL": false, + "accessKey": "IDFkwEpWej0b4DtiuThL", + "secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h", + "bucketName": "test" + } + }, + + "ConnectionStrings": { + //"RemoteNew": "Server=47.117.165.18,1434;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + //"Hangfire": "Server=47.117.165.18,1434;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + "RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true", + "Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true" + }, + + "BasicSystemConfig": { + + "OpenUserComplexPassword": true, + + "OpenSignDocumentBeforeWork": true, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": true, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "study@extimaging.com", + "FromName": "研究单位阅片系统", + "AuthorizationCode": "zhanying123", + "SiteUrl": "https://study.extimaging.com/login" + } + +} diff --git a/IRC.Core.SCP/appsettings.IRC_Test_SCP.json b/IRC.Core.SCP/appsettings.IRC_Test_SCP.json new file mode 100644 index 000000000..5e93530c7 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Test_SCP.json @@ -0,0 +1,79 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endPoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-test-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "106.14.89.110", + "port": "9001", + "useSSL": false, + "accessKey": "fbStsVYCIPKHQneeqMwD", + "secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy", + "bucketName": "hir-test", + "viewEndpoint": "http://106.14.89.110:9001/hir-test/" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + + "ConnectionStrings": { + "RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "test-study@extimaging.com", + "FromName": "Test_Study", + "AuthorizationCode": "zhanying123", + + "SiteUrl": "http://study.test.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.IRC_US_SCP.json b/IRC.Core.SCP/appsettings.IRC_US_SCP.json new file mode 100644 index 000000000..7821a16b8 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_US_SCP.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "MinIO", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-sir-test-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "44.218.11.19", + "port": "9001", + "useSSL": false, + "accessKey": "lH8DkKskLuDqPaiubuSQ", + "secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4", + "bucketName": "hir-us", + "viewEndpoint": "http://hir.us.extimaging.com/oss/hir-us" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + "ConnectionStrings": { + "RemoteNew": "Server=44.218.11.19,1435;Database=US_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=44.218.11.19,1435;Database=US_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "uat-study@extimaging.com", + "FromName": "Uat_Study", + "AuthorizationCode": "zhanying123", + "SiteUrl": "http://study.uat.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json b/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json new file mode 100644 index 000000000..f50eedf66 --- /dev/null +++ b/IRC.Core.SCP/appsettings.IRC_Uat_SCP.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "MinIO", + "AliyunOSS": { + "regionId": "cn-shanghai", + "endpoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ", + "accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio", + "bucketName": "zy-irc-uat-store", + "roleArn": "acs:ram::1899121822495495:role/oss-upload", + "viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com", + "region": "oss-cn-shanghai" + }, + + "MinIO": { + "endPoint": "47.117.164.182", + "port": "9001", + "useSSL": false, + "accessKey": "b9Ul0e98xPzt6PwRXA1Q", + "secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ", + "bucketName": "hir-uat", + "viewEndpoint": "http://hir.uat.extimaging.com/oss/hir-uat" + }, + + "AWS": { + "endPoint": "s3.us-east-1.amazonaws.com", + "useSSL": false, + "accessKey": "AKIAZQ3DRSOHFPJJ6FEU", + "secretKey": "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", + "bucketName": "ei-irc-test-store", + "viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/" + } + }, + "ConnectionStrings": { + "RemoteNew": "Server=47.117.164.182,1434;Database=Uat_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=47.117.164.182,1434;Database=Uat_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + + "LoginFailLockMinutes": 30 + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "uat-study@extimaging.com", + "FromName": "Uat_Study", + "AuthorizationCode": "zhanying123", + "SiteUrl": "http://study.uat.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.json b/IRC.Core.SCP/appsettings.json new file mode 100644 index 000000000..10f68b8c8 --- /dev/null +++ b/IRC.Core.SCP/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/IRaCIS.Core.API.sln b/IRaCIS.Core.API.sln index d5afa55fa..9511e66e3 100644 --- a/IRaCIS.Core.API.sln +++ b/IRaCIS.Core.API.sln @@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +53,10 @@ Global {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU {07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.csproj b/IRaCIS.Core.API/IRaCIS.Core.API.csproj index f6e1614ef..a434c6d2b 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.csproj +++ b/IRaCIS.Core.API/IRaCIS.Core.API.csproj @@ -64,22 +64,21 @@ - true - + - + - + - + diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs index 76d181268..b67c4f25f 100644 --- a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -31,7 +31,7 @@ namespace IRaCIS.Core.Application.Service var exploreRecommendQueryable = - _exploreRecommendRepository + _exploreRecommendRepository.Where().IgnoreQueryFilters() .WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title)) .WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName)) .WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl)) diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs new file mode 100644 index 000000000..72b1ee836 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -0,0 +1,1083 @@ +using IRaCIS.Core.Application.ViewModel; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Web; + +namespace IRaCIS.Application.Contracts +{ + public class SystemHospitalOption + { + public string HospitalName { get; set; } + public string HospitalAliasName { get; set; } + public string Country { get; set; } + public string City { get; set; } + public string Province { get; set; } + public string Address { get; set; } + public string Phone { get; set; } + + public bool IsCanConnectInternet { get; set; } + + public string HospitalCode { get; set; } + public string HospitalLogoPath { get; set; } + public int TrialKeepCount { get; set; } + + } + + + public class NewTrialView : PatientJoinTrialInitView + { + public Guid Id { get; set; } + //授权年限 + public int AuthorizationDuration { get; set; } + + public DateTime? AuthorizationDate { get; set; } + + public string AuthorizationEncrypt { get; set; } + + public string CriterionTypes { get; set; } + public List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + + public int? UnSubmitCount { get; set; } + + public int? UnReadCount { get; set; } + } + + public class NewTrialQuery : PageInput + { + public string? ExperimentName { get; set; } + public string? ResearchProgramNo { get; set; } + public TrialType? TrialType { get; set; } + + public string? SponsorName { get; set; } + + public string? TrialCode { get; set; } + } + + public class TrialInfoDTO : AddOrUpdateTrialCommand + { + [JsonIgnore] + public List DictionaryList { get; set; } = new List(); + + public new List ModalityIds => DictionaryList.Where(t => t.ParentCode == StaticData.Modality).OrderBy(t => t.ShowOrder).Select(t => t.Id).ToList(); + + public string CriterionTypes { get; set; } = string.Empty; + + + public new List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + + public string AuthorizationEncrypt { get; set; } + + public TrialAuthorizationInfo AuthorizationInfo { get; set; } + + public string TrialStatusStr { get; set; } + + public DateTime CreateTime { get; set; } + } + + public class AddOrUpdateTrialCommand + { + + public Guid? Id { get; set; } + public string TrialCode { get; set; } = string.Empty; + + public TrialType TrialType { get; set; } + + + //研究方案号 + public string ResearchProgramNo { get; set; } = string.Empty; + + //实验名称 + public string ExperimentName { get; set; } = string.Empty; + + // 负责人PI + public string HeadPI { get; set; } = string.Empty; + + public string CRO { get; set; } = string.Empty; + + public string Sponsor { get; set; } = string.Empty; + + //药物名称 + public string MedicineName { get; set; } = string.Empty; + + + //临床分期 + public Guid? PhaseId { get; set; } + + //适应症 + public string Indication { get; set; } = string.Empty; + + //检查技术 + public List ModalityIds { get; set; } = new List(); + + //阅片标准 + public List CriterionTypeList { get; set; } + + //联系人 + public string ContactUser { get; set; } = string.Empty; + //联系电话 + public string ContactPhone { get; set; } = string.Empty; + + //授权年限 + public int AuthorizationDuration { get; set; } + + public DateTime? AuthorizationDate { get; set; } + + } + + public class PatientJoinTrialInitQuery : PageInput + { + [NotDefault] + public Guid PatientId { get; set; } + + public string? Filter { get; set; } + + } + + public class PatientJoinedTrialQuery + { + [NotDefault] + public Guid PatientId { get; set; } + } + + public class PatientJoinedTrialView : PatientJoinTrialInitView + { + public Guid SubjectId { get; set; } + public string Code { get; set; } + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + } + + public class PatientJoinTrialSelectView + { + public Guid TrialId { get; set; } + public string ExperimentName { get; set; } + public string ResearchProgramNo { get; set; } + public string Sponsor { get; set; } + } + + + public class TrialCacheInfo + { + public Guid TrialId { get; set; } + public Guid CreateUserId { get; set; } + public string TrialCode { get; set; } + + public DateTime? AuthorizationDate { get; set; } + public string TrialStatusStr { get; set; } + + public string AuthorizationEncrypt { get; set; } + + public string CriterionTypes { get; set; } + public List CriterionTypeList => CriterionTypes.Split('|', StringSplitOptions.RemoveEmptyEntries) + .Select(s => Enum.Parse(typeof(CriterionType), s)).Cast().ToList(); + } + public class TrialAuthorizationInfo + { + public Guid TrialId { get; set; } + public Guid CreateUserId { get; set; } + public string TrialCode { get; set; } + + public string HospitalName { get; set; } + + public string HospitalCode { get; set; } + + public int PurchaseDuration { get; set; } + + + + public List CriterionTypeList { get; set; } + + public DateTime? AuthorizationDeadLineDate { get; set; } + + public DateTime? ActiveDeadLineDate { get; set; } + + public DateTime? ActiveTime { get; set; } + + } + public class PatientJoinTrialInitView + { + public Guid TrialId { get; set; } + + public string TrialCode { get; set; } + public string ExperimentName { get; set; } + public string ResearchProgramNo { get; set; } + public TrialType TrialType { get; set; } + + public string Sponsor { get; set; } + + public string TrialStatusStr { get; set; } + + public DateTime CreateTime { get; set; } + } + + + public class PatientTrialQuery : PageInput + { + public string? PatientIdStr { get; set; } + public string? PatientName { get; set; } + public List CalledAEList { get; set; } = new List(); + + public string? CallingAE { get; set; } + + public string? ExperimentName { get; set; } = string.Empty; + + public DateTime? BeginPushTime { get; set; } + public DateTime? EndPushTime { get; set; } + + } + + public class PatientTrialView : PatientQueryView + { + + public int? StudyCount { get; set; } + + public List TrialList { get; set; } + } + + public class PatientTrialStatInfo + { + public int? VisitCount { get; set; } + + public string ExperimentName { get; set; } + } + + public class PatientQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public Guid? SubjectId { get; set; } + public string? PatientIdStr { get; set; } + public string? PatientName { get; set; } + public List CalledAEList { get; set; } = new List(); + + public string? CallingAE { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + } + + public class PatientQueryView + { + public Guid PatientId { get; set; } + public string PatientIdStr { 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 string PatientBirthDate { get; set; } = string.Empty; + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + public DateTime LatestPushTime { get; set; } + + public List CallingAEList { get; set; } = new List(); + + public List CalledAEList { get; set; } = new List(); + + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + } + + public class UpdateSubjectVisitStudyBindingCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid SCPStudyId { get; set; } + + [NotDefault] + public Guid SubjectVisitId { get; set; } + + public bool IsAdd { get; set; } + + + } + + public class DeleteSubejctPatientCommand + { + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid PatientId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + } + + + public class VisitStudyVerifyTimeQuery + { + public Guid SCPStudyId { get; set; } + + public Guid SubjectId { get; set; } + public Guid TrialId { get; set; } + } + + public class VerifyTrialSubjectCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public string SubjectCode { get; set; } = string.Empty; + } + public class AddSubjectPatientCommand + { + [NotDefault] + public Guid TrialId { get; set; } + public string SubjectCode { get; set; } = string.Empty; + + public Guid? SubjectId { get; set; } + + public List PatientIdList { get; set; } + } + + public class VerifyVisitStudyDTO + { + public Guid SubjectVisitId { get; set; } + public decimal VisitNum { get; set; } + public DateTime? StudyTime { get; set; } + public Guid SCPStudyId { get; set; } + } + public class AddSubjectPatientStudyVisitCommand + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid SubjectVisitId { get; set; } + [NotDefault] + public Guid SCPStudyId { get; set; } + + //public string VisitName { get; set; } + + } + + public class SubmitVisitStudyBindingCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + [NotDefault] + public Guid SubjectId { get; set; } + + public List SubjectVisitIdList { get; set; } + } + + public class SubjectVisitSelectQuery + { + [NotDefault] + public Guid TrialId { get; set; } + [NotDefault] + public Guid SubjectId { get; set; } + + public Guid? SCPStudyId { get; set; } + } + + public class SubjectVisitSelectDto + { + public Guid Id { get; set; } + + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public string VisitNum { get; set; } + + public string VisitName { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public DateTime? VisitMaxStudyTime { get; set; } + + public DateTime? VisitMinStudyTime { get; set; } + } + public class AddOrUpdateSubjectVisitCommand + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public decimal VisitNum { get; set; } + + public string VisitName { get; set; } + } + public class SubjectSelectQuery + { + public string? SubjectCode { get; set; } + + + public Guid? SubjectId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + } + + public class SubjectPatientSelectQuery + { + public string? SubjectCode { get; set; } + public string? PatientIdStr { get; set; } + + public Guid? SubjectId { get; set; } + + public Guid? PatientId { get; set; } + [NotDefault] + public Guid TrialId { get; set; } + } + public class SubjectSelectDto + { + public Guid SubejctId { get; set; } + + public string SubjectCode { get; set; } + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + + public DateTime? BirthDate { get; set; } + + public SubjectStatus Status { get; set; } + + public List PatientList { get; set; } + } + + public class PatienBasicInfo + { + public Guid PatientId { get; set; } + public string PatientIdStr { get; set; } + } + + public class SubjectPatientSelectDto + { + public Guid Id { get; set; } + public Guid PatientId { get; set; } + public Guid TrialId { get; set; } + public Guid SubejctId { get; set; } + public string? PatientIdStr { get; set; } + public string SubjectCode { get; set; } + } + + public class PatientStudyInfoQuery : PageInput + { + [NotDefault] + public Guid PatientId { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + public string? Modalities { get; set; } + } + + public class PatientStudyQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public List PatientIdList { get; set; } + + + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + } + + + public class PatientVisitTaskQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + + public Guid? SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public string? SubjectShortName { get; set; } + + public string TaskName { get; set; } = String.Empty; + + public Guid? DoctorUserId { get; set; } + + public ReadingCategory? ReadingCategory { get; set; } + + public ReadingTaskState? ReadingTaskState { get; set; } + + public TaskState? TaskState { get; set; } + + + public string? TaskCode { get; set; } + + public Arm? ArmEnum { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + + public DateTime? BeginSignTime { get; set; } + public DateTime? EndSignTime { get; set; } + + public DateTime? BeginTaskCreateTime { get; set; } + public DateTime? EndTaskCreateTime { get; set; } + + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + } + + public class PatientVisitTaskDTO + { + public List PatientList { get; set; } + + public Guid Id { get; set; } + public Guid TrialId { get; set; } + + public string TaskCode { get; set; } + + public string TaskName { get; set; } + public string TaskBlindName { get; set; } + + public decimal VisitTaskNum { get; set; } + + public Guid? SourceSubjectVisitId { get; set; } + + public ReadingCategory ReadingCategory { get; set; } + + + public TaskState TaskState { get; set; } + + public DateTime? SignTime { get; set; } + + public DateTime? AllocateTime { get; set; } + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } = String.Empty; + + public string SubjectShortName { get; set; } + + public string VisitImageZipPath { get; set; } + + public PackState PackState { get; set; } + + public Arm ArmEnum { get; set; } + public Guid? DoctorUserId { get; set; } + + + public Guid TrialReadingCriterionId { get; set; } + + public string TrialReadingCriterionName { get; set; } + + public CriterionType CriterionType { get; set; } + + public DateTime CreateTime { get; set; } + + public UserSimpleInfo DoctorUser { get; set; } + + //任务阅片状态 + public ReadingTaskState ReadingTaskState { get; set; } + + //public bool IsUrgent { get; set; } + + ///// + ///// 加急类型 + ///// + //public TaskUrgentType? TaskUrgentType { get; set; } + + + //public string TaskUrgentRemake { get; set; } = string.Empty; + + + //public TaskAllocationState TaskAllocationState { get; set; } + //public bool IsNeedClinicalDataSign { get; set; } + + //public bool IsClinicalDataSign { get; set; } + + } + + public class AutoBindingPatientStudyVisitCommand + { + [NotDefault] + public Guid TrialId { get; set; } + + + } + public class TrialPatientStudyQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? SubjectCode { get; set; } + + + public Guid? PatientId { get; set; } + + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + public string? SubjectShortName { get; set; } + + public int? SubjectAge { get; set; } + public string? SubjectSex { get; set; } + + public DateTime? BeginStudyTime { get; set; } + + public DateTime? EndStudyTime { get; set; } + + public string? VisitName { get; set; } + + public bool? IsBindedVisit { get; set; } + } + + public class AddOrUpdateSubjectCommand + { + + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + public string Code { get; set; } = String.Empty; + + public int? Age { get; set; } + public string Sex { get; set; } = string.Empty; + + public string ShortName { get; set; } = string.Empty; + + + public DateTime? BirthDate { get; set; } + + + } + public class PatientSubjectQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? Code { get; set; } + + public string? ShortName { get; set; } + + public string? Sex { get; set; } + + public SubjectStatus? Status { get; set; } + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + } + + public class PatienSubejctView + { + public List PatientList { get; set; } + + public Guid TrialId { get; set; } + public Guid Id { get; set; } + public string Code { get; set; } + + + public string ShortName { get; set; } + + public string Sex { get; set; } + + public int? Age { get; set; } + + public SubjectStatus Status { get; set; } + + public DateTime? BirthDate { get; set; } + + public int? VisitCount { get; set; } + + public DateTime CreateTime { get; set; } + + public string LatestVisitName { get; set; } + + public DateTime? VisitOverTime { get; set; } + + public Guid? FinalSubjectVisitId { get; set; } + + public string Reason { get; set; } + } + + public class PatientSubejctVisitQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public string? SubjectCode { get; set; } + + + public string? SubjectShortName { get; set; } + + public string? SubjectSex { get; set; } + + public string? PatientName { get; set; } + public string? PatientIdStr { get; set; } + + public string? PatientSex { get; set; } + + public string? VisitName { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + public DateTime? BeginStudyTime { get; set; } + + public DateTime? EndStudyTime { get; set; } + } + + public class PatientStudyBeforeConfirmView : PatientStudyView + { + public Guid PatientId { get; set; } + public string CalledAE { get; set; } = string.Empty; + + public string CallingAE { get; set; } = string.Empty; + } + + public class PatientStudyView + { + + public string PatientName { get; set; } + public string PatientIdStr { get; set; } + + public string PatientSex { get; set; } + + public string PatientAge { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + + + public Guid StudyId { get; set; } + public string Modalities { get; set; } + + public string ModalityForEdit { get; set; } + + public DateTime? StudyTime { get; set; } + + public string Description { get; set; } = string.Empty; + public int SeriesCount { get; set; } = 0; + public int InstanceCount { get; set; } = 0; + + + + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public Guid? SubjectVisitId { get; set; } + public string? VisitName { get; set; } + + public decimal? VisitNum { get; set; } + + public DateTime? VisitEarliestStudyTime { get; set; } + + public DateTime? VisitLatestStudyTime { get; set; } + + public DateTime? SubmitTime { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + } + + public class PatientBasicInfo + { + public Guid PatientId { get; set; } + + public string PatientIdStr { get; set; } + + public string PatientSex { get; set; } + + public string PatientName { get; set; } + + public string PatientAge { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + } + + + public class PatientSubjectVisitView + { + public Guid TrialId { get; set; } + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + public List PatientList { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public Guid SubjectVisitId { get; set; } + public string VisitName { get; set; } + + public decimal VisitNum { get; set; } + + public DateTime? VisitEarliestStudyTime { get; set; } + + public DateTime? VisitLatestStudyTime { get; set; } + + public DateTime? SubmitTime { get; set; } + + public SubmitStateEnum SubmitState { get; set; } + + public string VisitImageZipPath { get; set; } + public PackState PackState { get; set; } + } + + public class SubjectPatientStudyView : PatientStudySimpleView + { + public Guid SubjectId { get; set; } + + public string SubjectCode { get; set; } + + public List PatientList { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + } + + public class SubjectVisitStudyQuery : PageInput + { + [NotDefault] + public Guid SujectVisitId { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + } + + public class PatientStudyOtherQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + public List PatientIdList { get; set; } + + [NotDefault] + public Guid SujectVisitId { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + public string? PatientIdStr { get; set; } + + } + + public class VisitImageDownloadQuery : PageInput + { + [NotDefault] + public Guid TrialId { get; set; } + + public string? IP { get; set; } + + public string? SubjectCode { get; set; } + + public string? VisitName { get; set; } + + public string? Name { get; set; } + + public DateTime? BeginDownloadTime { get; set; } + + public DateTime? EndDownloadTime { get; set; } + + public UserTypeEnum? UserTypeEnum { get; set; } + } + + public class VisitImageDownloadView + { + public string SubjectCode { get; set; } + + public string VisitName { get; set; } + + public DateTime DownloadTime { get; set; } + + public string DownloadUserName { get; set; } + + public string DownLoadUserFullName { get; set; } + + public string IP { get; set; } + + public long VisitImageZipSize { get; set; } + + //文件大小 + public int VisitImageFileCount { get; set; } + + public string VisitImageZipPath { get; set; } = string.Empty; + + public int? StudyCount { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + + } + + + public class SCPImageUploadQuery : PageInput + { + public string? CallingAE { get; set; } + + public string? CalledAE { get; set; } + + public string? CallingAEIP { get; set; } + + public DateTime? StartTime { get; set; } + + public DateTime? EndTime { get; set; } + } + + public class SCPImageUploadView + { + public string CallingAE { get; set; } = string.Empty; + + public string CalledAE { get; set; } = string.Empty; + + public string CallingAEIP { get; set; } = string.Empty; + + public DateTime StartTime { get; set; } + + public DateTime EndTime { get; set; } + + public int FileCount { get; set; } + + public long FileSize { get; set; } + + public int StudyCount { get; set; } + + } + public class VisitPatientStudyView : PatientStudySelectDto + { + + public string VisitName { get; set; } + } + + public class UnbindStudyView : VisitPatientStudyView + { + public Guid TrialId { get; set; } + + public Guid SubjectId { get; set; } + public string SubjectCode { get; set; } + + public string SubjectShortName { get; set; } = String.Empty; + + public int? SubjectAge { get; set; } + public string SubjectSex { get; set; } = string.Empty; + + public DateTime? SubjectBirthDate { get; set; } + + } + + public class PatientStudySimpleView + { + public Guid SCPStudyId { get; set; } + + public Guid PatientId { get; set; } + + 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 string CalledAE { get; set; } = string.Empty; + + public string CallingAE { get; set; } = string.Empty; + + } + + + + public class PatientSeriesDTO + { + + public bool IsDicom { get; set; } = true; + + + 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 DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + public bool IsDeleted { get; set; } + public bool IsReading { get; set; } = true; + + public List InstanceList { get; set; } = new List(); + + public List InstancePathList { get; set; } = new List(); + + public string ImageResizePath { get; set; } + } + + public class PatientStudySelectDto + { + public Guid SCPStudyId { get; set; } + + public Guid PatientId { get; set; } + public string PatientIdStr { 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 string PatientBirthDate { 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 string CalledAE { get; set; } = string.Empty; + + public string CallingAE { get; set; } + public Guid? SubjectVisitId { get; set; } + + public SubmitStateEnum? SubmitState { get; set; } + + //public Guid? SubjectVisitId { get; set; } + + //public string CallingAE { get; set; } = string.Empty; + + //public string BodyPartExamined { get; set; } = string.Empty; + + //public string BodyPartForEdit { get; set; } = string.Empty; + + //public string ModalityForEdit { get; set; } = string.Empty; + + + + } + +} diff --git a/IRaCIS.Core.Application/Service/Visit/PatientService.cs b/IRaCIS.Core.Application/Service/Visit/PatientService.cs new file mode 100644 index 000000000..c4bef2800 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -0,0 +1,1323 @@ +//using IRaCIS.Application.Interfaces; +//using IRaCIS.Application.Contracts; +//using IRaCIS.Core.Application.Filter; +//using IRaCIS.Core.Domain.Share; +//using Microsoft.AspNetCore.Mvc; +//using Microsoft.AspNetCore.Authorization; +//using IRaCIS.Core.Application.Auth; +//using MassTransit; +//using Panda.DynamicWebApi.Attributes; +//using DocumentFormat.OpenXml.Spreadsheet; +//using AutoMapper.EntityFrameworkCore; +//using IRaCIS.Core.Domain.Models; +//using IRaCIS.Core.Application.Service.Reading.Dto; +//using Microsoft.Extensions.Options; +//using Microsoft.Extensions.Configuration; +//using Microsoft.Extensions.Configuration.Json; +//using Newtonsoft.Json; +//using Newtonsoft.Json.Linq; +//using SharpCompress.Common; +//using System.Reactive.Subjects; +//using Subject = IRaCIS.Core.Domain.Models.Subject; +//using IRaCIS.Core.Application.ViewModel; +//using Medallion.Threading; +//using IRaCIS.Core.Infrastructure; +//using EasyCaching.Core; +//using Pipelines.Sockets.Unofficial.Arenas; +//using IRaCIS.Core.Application.Contracts; +//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; +//using DocumentFormat.OpenXml.EMMA; +//using Azure; +//using System.IO.Compression; +//using static IRaCIS.Core.Domain.Share.StaticData; +//using FellowOakDicom; +//using DocumentFormat.OpenXml.Office2010.Drawing; +//using EasyCaching.Core.DistributedLock; +//using IDistributedLockProvider = Medallion.Threading.IDistributedLockProvider; +//using DocumentFormat.OpenXml.InkML; + +//namespace IRaCIS.Application.Services +//{ +// [ApiExplorerSettings(GroupName = "Trial")] +// public class PatientService : BaseService +// { + +// private readonly IRepository _trialRepository; +// private readonly IRepository _patientRepository; +// private readonly IRepository _studyRepository; +// private readonly IRepository _subjectRepository; +// private readonly IRepository _subjectVisitRepository; +// private readonly IDistributedLockProvider _distributedLockProvider; + +// public PatientService(IRepository studyRepository, IRepository trialRepository, IRepository patientRepository, IRepository subjectRepository, IRepository subjectVisitRepository, IDistributedLockProvider distributedLockProvider) +// { +// _studyRepository = studyRepository; +// _trialRepository = trialRepository; +// _patientRepository = patientRepository; +// _subjectRepository = subjectRepository; +// _subjectVisitRepository = subjectVisitRepository; +// _distributedLockProvider = distributedLockProvider; +// } + + + + + +// #region 患者检查管理 + +// /// +// ///检查管理-> 检查Tab 患者列表 (带加入的项目信息 以及检查统计) 原型标注错误,不是检查列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientList(PatientTrialQuery inQuery) +// { + +// var isAdminOrOA = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.Admin || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.OA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin; + +// #region new ok +// var query = _patientRepository +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) +// //.WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), t => t.SubjectPatientList.Any(t => t.Subject.Trial.ExperimentName.Contains(inQuery.ExperimentName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) +// .WhereIf(inQuery.BeginPushTime != null, t => t.LatestPushTime >= inQuery.BeginPushTime) +// .WhereIf(inQuery.EndPushTime != null, t => t.LatestPushTime <= inQuery.EndPushTime); + +// foreach (var calledAE in inQuery.CalledAEList) +// { +// query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); +// } + + +// var resultQuery = from patient in query + +// select new PatientTrialView() +// { +// PatientId = patient.Id, +// PatientBirthDate = patient.PatientBirthDate, +// CreateTime = patient.CreateTime, +// CalledAEList = patient.SCPStudyList.Select(t => t.CalledAE).Distinct().ToList(), +// CallingAEList = patient.SCPStudyList.Select(t => t.CallingAE).Distinct().ToList(), +// CreateUserId = patient.CreateUserId, +// UpdateTime = patient.UpdateTime, +// UpdateUserId = patient.UpdateUserId, + +// EarliestStudyTime = patient.EarliestStudyTime, +// LatestStudyTime = patient.LatestStudyTime, +// LatestPushTime = patient.LatestPushTime, +// PatientAge = patient.PatientAge, +// PatientName = patient.PatientName, +// PatientIdStr = patient.PatientIdStr, +// PatientSex = patient.PatientSex, + +// StudyCount = patient.SCPStudyList.Count(), + +// TrialList = patient.SubjectPatientList.Where(t => isAdminOrOA ? true : t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)).Select(c => new PatientTrialStatInfo() +// { +// ExperimentName = c.Subject.Trial.ExperimentName, +// VisitCount = c.Subject.SubjectVisitList.Count() +// }).ToList(), + +// }; + + + + +// var pageList = await resultQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); +// #endregion + + +// return ResponseOutput.Ok(pageList); +// } + + + + + +// /// +// /// 检查管理-> 获取患者检查列表(同步影像数据之前的) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetPatientStudyList(PatientStudyInfoQuery inQuery) +// { +// var query = from scpStudy in _studyRepository.Where(t => t.PatientId == inQuery.PatientId) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modalities), t => t.Modalities.Contains(inQuery.Modalities)) +// select new PatientStudySimpleView() +// { +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// CallingAE = scpStudy.CallingAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, +// }; + + +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + +// return list; +// } + + + +// public async Task>> GetPatientSeriesList(Guid scpStudyId, +// [FromServices] IRepository _seriesRepository, +// [FromServices] IRepository _instanceRepository +// ) +// { + +// var seriesList = await _seriesRepository.Where(s => s.StudyId == scpStudyId).OrderBy(s => s.SeriesNumber). +// ThenBy(s => s.SeriesTime).ThenBy(s => s.CreateTime) +// .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + +// var idList = await _instanceRepository.Where(s => s.StudyId == scpStudyId).OrderBy(t => t.SeriesId).ThenBy(t => t.InstanceNumber) +// .ThenBy(s => s.InstanceTime).ThenBy(s => s.CreateTime) +// .Select(t => new { t.SeriesId, t.Id, t.Path, t.NumberOfFrames, t.InstanceNumber }).ToListAsync();//.GroupBy(u => u.SeriesId); + +// foreach (var item in seriesList) +// { +// item.InstanceList = idList.Where(s => s.SeriesId == item.Id).Select(u => u.Id).ToList(); + +// //处理多帧 +// item.InstancePathList = idList.Where(s => s.SeriesId == item.Id).OrderBy(t => t.InstanceNumber) +// .SelectMany(u => +// { + +// if (u.NumberOfFrames > 1) +// { +// var pathList = new List(); + +// for (int i = 1; i <= u.NumberOfFrames; i++) +// { +// pathList.Add(u.Path + "?frame=" + (i - 1)); +// } +// return pathList; +// } +// else +// { +// return new List { u.Path }; + +// } +// }) +// .ToList(); +// } + + +// var study = await _studyRepository.FindAsync(scpStudyId); + +// return ResponseOutput.Ok(seriesList, study); +// } + + + + + +// /// +// /// 清除该患者绑定的受试者的所有的数据、(subject subjectVisit visitTask dicom) +// /// +// /// +// /// +// [UnitOfWork] +// public async Task DeletePatientStudyAllData(Guid patientId, +// [FromServices] IRepository _visitTaskRepository, +// [FromServices] IRepository _SeriesRepository, +// [FromServices] IRepository _instanceRepository, +// [FromServices] IRepository _dicomStudyRepository, +// [FromServices] IRepository _dicomSeriesRepository, +// [FromServices] IRepository _dicomInstanceRepository, +// [FromServices] IOSSService oSSService) +// { +// //清理自己管理的项目的数据 +// var subjectPatientList = await _subjectPatientRepository.Where(t => t.PatientId == patientId && t.Subject.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) +// .Select(t => new { t.SubjectId, StudyInstanceUidList = t.Patient.SCPStudyList.Select(t => t.StudyInstanceUid).ToList() }).ToListAsync(); + +// if (_studyRepository.Any(t => t.IsUploadFinished == false && t.PatientId == patientId)) +// { +// return ResponseOutput.NotOk("当前患者有检查正在上传,不允许清理数据"); +// } + +// foreach (var item in subjectPatientList) +// { +// var subjectId = item.SubjectId; + +// await _subjectRepository.BatchDeleteNoTrackingAsync(t => t.Id == subjectId); +// await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// } + +// var instanceUidList = subjectPatientList.SelectMany(t => t.StudyInstanceUidList).Distinct().ToList(); +// foreach (var studyInstanceUid in instanceUidList) +// { +// { +// var ossFolderPath = $"Dicom/{studyInstanceUid}"; + +// await oSSService.DeleteFromPrefix(ossFolderPath); + +// } +// } + + +// var sCPStudyIdList = _studyRepository.Where(t => t.PatientId == patientId).Select(t => t.Id).ToList(); + +// await _patientRepository.BatchDeleteNoTrackingAsync(t => t.Id == patientId); + +// foreach (var item in sCPStudyIdList) +// { +// await _studyRepository.BatchDeleteNoTrackingAsync(t => t.Id == item); +// await _SeriesRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == item); +// await _instanceRepository.BatchDeleteNoTrackingAsync(t => t.StudyId == item); +// } + + + +// return ResponseOutput.Ok(); +// } +// #endregion + + +// #region 受试者管理 + + +// [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] +// [HttpPost] +// public async Task> AddOrUpdateSubject([FromBody] AddOrUpdateSubjectCommand subjectCommand) +// { +// var svlist = new List(); + +// var verifyExp1 = new EntityVerifyExp() +// { +// VerifyExp = u => u.Code == subjectCommand.Code && u.TrialId == subjectCommand.TrialId, +// //---已存在具有相关受试者编号的受试者。 +// VerifyMsg = _localizer["Subject_DuplicateSubjectNum"] +// }; + + +// Subject? mapedSubject = null; + +// if (subjectCommand.Id == null) //insert +// { + + +// mapedSubject = await _subjectRepository.InsertFromDTOAsync(subjectCommand, false, verifyExp1); + + +// } +// else //update +// { + +// mapedSubject = await _subjectRepository.UpdateFromDTOAsync(subjectCommand, false, false, verifyExp1/*, verifyExp2*/); + +// } + + + +// await _subjectRepository.SaveChangesAsync(); + +// return ResponseOutput.Ok(mapedSubject.Id.ToString()); + +// } + +// /// +// /// 受试者管理-> 受试者列表 (带患者信息,患者信息是数组) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientSubejctList(PatientSubjectQuery inQuery) +// { +// var subjectQuery = _subjectRepository.Where(u => u.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Code), t => t.Code.Contains(inQuery.Code)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ShortName), t => t.ShortName.Contains(inQuery.ShortName)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Sex), t => t.Sex.Contains(inQuery.Sex)) + +// //.WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) + +// .WhereIf(inQuery.Status != null, t => t.Status == inQuery.Status) +// .ProjectTo(_mapper.ConfigurationProvider); + +// var pageList = await subjectQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatienSubejctView.Code) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + +// /// +// /// 受试者管理-> 患者列表 (subject 列表进入,进行关系绑定初始化列表,排除已绑定的患者和已绑定给其他subject的患者) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientInitList(PatientQuery inQuery) +// { +// var query = _patientRepository +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.PatientName.Contains(inQuery.PatientName)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.EarliestStudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.LatestStudyTime <= inQuery.LatestStudyTime) + +// //排除该受试者已绑定的患者 +// //.WhereIf(inQuery.SubjectId != null, t => !t.SubjectPatientList.Any(u => u.SubjectId == inQuery.SubjectId)) + +// //排除该项目已绑定的其他患者 +// .Where(t => !t.SubjectPatientList.Any(c => c.Subject.TrialId == inQuery.TrialId)); + +// foreach (var calledAE in inQuery.CalledAEList) +// { +// query = query.Where(t => t.SCPStudyList.Select(c => c.CalledAE).Contains(calledAE)); +// } + + +// var patientQuery = query.ProjectTo(_mapper.ConfigurationProvider); + + +// var pageList = await patientQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientQueryView.PatientIdStr) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + +// /// +// /// 受试者管理->患者列表 Dicom AE 下拉框数据获取 +// /// +// /// +// public async Task> GetDicomCalledAEList() +// { +// var list = await _studyRepository.Select(t => t.CalledAE).Distinct().ToListAsync(); + +// return list; +// } + +// public async Task> GetDicomCallingAEList() +// { +// var list = await _studyRepository.Select(t => t.CallingAE).Distinct().ToListAsync(); + +// return list; +// } + +// /// +// ///受试者管理-> 患者列表 模糊搜索下拉 选择subject 排除已绑定并提交的 +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetTrialSubejctSelectList(SubjectSelectQuery inQuery) +// { +// var list = await _subjectRepository.Where(t => t.TrialId == inQuery.TrialId && t.Status == SubjectStatus.OnVisit) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(inQuery.SubjectId != null, t => t.Id == inQuery.SubjectId) +// //.Where(t => !t.SubjectVisitList.SelectMany(t => t.SCPStudySubjectVisitList).Any(c => c.StudyId != null)) +// .Select(t => new SubjectSelectDto() +// { +// SubejctId = t.Id, +// SubjectCode = t.Code, +// Status = t.Status, +// Sex = t.Sex, +// ShortName = t.ShortName, +// Age = t.Age, +// BirthDate = t.BirthDate, +// //PatientList = t.SubjectPatientList.Select(c => new PatienBasicInfo() { PatientId = c.PatientId, PatientIdStr = c.Patient.PatientIdStr }).ToList() +// }) +// .ToListAsync(); + +// return list; +// } + +// #endregion + + + +// #region 患者和受试者绑定,生成访视,预先绑定检查和访视 + + + + + + + +// /// +// /// 提交 患者检查和访视的绑定 +// /// +// /// +// /// +// [HttpPost] +// [UnitOfWork] +// [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] +// public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand, [FromServices] IOptionsMonitor _basicSystemConfigConfig) +// { + +// //防止访视重复 +// inCommand.SubjectVisitIdList = inCommand.SubjectVisitIdList.Distinct().ToList(); + +// //确认当前提交的最大的访视之前所有的访视都已提交,并且没有漏的 + +// var curentMaxNum = await _subjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.Id)).Select(t => t.VisitNum).MaxAsync(); + +// var allVisitList = _subjectVisitRepository.Where(t => t.TrialId == inCommand.TrialId && t.SubjectId == inCommand.SubjectId && t.VisitNum <= curentMaxNum).Select(t => new { SubjectVisitId = t.Id, t.Subject.Status, t.VisitNum, t.SubmitState }).OrderBy(t => t.VisitNum).ToList(); + +// //批量提交 +// if (inCommand.SubjectVisitIdList.Count > 1) +// { +// if (allVisitList.Where(t => t.SubmitState != SubmitStateEnum.Submitted).Count() != inCommand.SubjectVisitIdList.Count()) +// { +// return ResponseOutput.NotOk(_localizer["当前批量提交的访视中间有遗漏的访视或者前序有访视未提交"]); +// } +// } + +// else +// { +// if (allVisitList.Any(t => t.VisitNum < curentMaxNum && t.SubmitState != SubmitStateEnum.Submitted)) +// { +// return ResponseOutput.NotOk(_localizer["前序有访视未提交,请先提交前序访视"]); +// } +// } + +// if (allVisitList.Any(t => t.Status == SubjectStatus.EndOfVisit)) +// { +// return ResponseOutput.NotOk(_localizer["受试者状态为访视结束,不允许提交访视生成任务"]); +// } + +// //var list = await _studySubjectVisitRepository.Where(t => inCommand.SubjectVisitIdList.Contains(t.SubjectVisitId)).Select(t => new { t.SCPStudyId, t.SCPStudy.PatientId, t.SubjectVisitId, t.SubjectVisit.VisitNum, t.SubjectVisit.SubjectId, SubjectCode = t.SubjectVisit.Subject.Code, t.SubjectVisit.TrialId, t.SCPStudy.StudyTime, t.StudyId, t.SCPStudy.IsUploadFinished }).OrderBy(t => t.StudyTime).ToListAsync(); + +// //if (list.Any(t => t.StudyId != null)) +// //{ +// // return ResponseOutput.NotOk(_localizer["有访视和检查处于已绑定关系,不允许再次提交绑定"]); +// //} + +// //if (list.Any(t => t.IsUploadFinished == false)) +// //{ +// // return ResponseOutput.NotOk(_localizer["有访视检查正在传输中,不允许提交"]); +// //} + + +// ////判断每个subject 批量提交的是否符合时间要求 +// //foreach (var g in list.GroupBy(t => new { t.SubjectId, t.SubjectCode })) +// //{ +// // var visitOrderStudyList = g.OrderBy(t => t.VisitNum).ThenBy(t => t.StudyTime).ToList(); + +// // var studyTimeOrderList = visitOrderStudyList.OrderBy(t => t.StudyTime).ToList(); + +// // bool arraysEqual = visitOrderStudyList.SequenceEqual(studyTimeOrderList); + +// // if (!arraysEqual) +// // { +// // return ResponseOutput.NotOk(_localizer[$"{g.Key.SubjectCode}所提交的访视中的检查时间,不符合后续访视的检查时间比前序检查的时间大的要求"]); +// // } + +// // if (DateTime.Now < studyTimeOrderList.Max(t => t.StudyTime)) +// // { +// // return ResponseOutput.NotOk(_localizer[$"您当前修改了服务器时间,试图绕过软件授权,请恢复服务器时间,并联系授权方授权才可进行操作"]); +// // } +// //} + +// var trialConfig = await _trialRepository.Where(t => t.Id == inCommand.TrialId).Select(t => new { t.IsEnrollementQualificationConfirm, t.IsPDProgressView, t.AuthorizationEncrypt }).FirstOrDefaultAsync(); + + + +// //var decodedText = Cryptography.DecryptString(trialConfig.AuthorizationEncrypt, _basicSystemConfigConfig.CurrentValue.AESKey, "Trial_AuthorizationEncrypt"); + +// //var authInfo = JsonConvert.DeserializeObject(decodedText); + + + + +// var @lock = _distributedLockProvider.CreateLock($"StudyCode"); + +// using (await @lock.AcquireAsync()) +// { +// var dbStudyCodeIntMax = _repository.Where(s => s.TrialId == inCommand.TrialId).Select(t => t.Code).DefaultIfEmpty().Max(); + +// int currentNextCodeInt = dbStudyCodeIntMax + 1; + +// foreach (var item in list) +// { + + +// var dbSubjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == item.SubjectVisitId); + +// //处理脏数据,可能之前的绑定的数据状态是待上传,但是已经绑定了检查 +// if (dbSubjectVisit.SubmitState == SubmitStateEnum.ToSubmit || dbSubjectVisit.SubmitState == SubmitStateEnum.None) +// { +// dbSubjectVisit.SubmitState = SubmitStateEnum.Submitted; +// dbSubjectVisit.SubmitTime = DateTime.Now; +// dbSubjectVisit.SubmitUserId = _userInfo.Id; + +// //维护统一状态 +// //dbSubjectVisit.ReadingStatus = ReadingStatusEnum.TaskAllocate; + +// dbSubjectVisit.AuditState = AuditStateEnum.QCPassed; +// dbSubjectVisit.CheckState = CheckStateEnum.CVPassed; + +// dbSubjectVisit.IsEnrollmentConfirm = dbSubjectVisit.IsBaseLine ? trialConfig.IsEnrollementQualificationConfirm : false; + +// dbSubjectVisit.PDState = trialConfig.IsPDProgressView ? PDStateEnum.PDProgress : PDStateEnum.None; +// } + + + + +// var find = _studyRepository.Where(t => t.Id == item.SCPStudyId).Include(t => t.SeriesList).Include(t => t.InstanceList).FirstOrDefault(); + +// if (find != null) +// { +// //重新算Id +// Guid studyId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid); +// find.Id = studyId; +// var newStuty = _mapper.Map(find); + +// await _repository.AddAsync(newStuty); +// //newStuty.Id = NewId.NextSequentialGuid(); + +// newStuty.SeqId = Guid.Empty; +// newStuty.Code = currentNextCodeInt; +// newStuty.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); +// newStuty.TrialId = item.TrialId; +// newStuty.SubjectId = item.SubjectId; +// newStuty.SubjectVisitId = item.SubjectVisitId; + +// var newSeriesList = _mapper.Map>(find.SeriesList); + +// foreach (var series in newSeriesList) +// { +// Guid seriesId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, series.SeriesInstanceUid); + +// //重新算Id +// series.Id = seriesId; +// series.StudyId = newStuty.Id; + +// series.SeqId = Guid.Empty; +// series.TrialId = item.TrialId; +// series.SubjectId = item.SubjectId; +// series.SubjectVisitId = item.SubjectVisitId; +// } + +// await _repository.AddRangeAsync(newSeriesList); + +// var newInstanceList = _mapper.Map>(find.InstanceList); + +// foreach (var instance in newInstanceList) +// { +// Guid seriesId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, instance.SeriesInstanceUid); +// Guid instanceId = IdentifierHelper.CreateGuid(item.TrialId.ToString(), find.StudyInstanceUid, instance.SeriesInstanceUid, instance.SopInstanceUid); +// //重新算Id +// instance.Id = instanceId; +// instance.SeriesId = seriesId; +// instance.StudyId = newStuty.Id; + +// instance.SeqId = Guid.Empty; +// instance.TrialId = item.TrialId; +// instance.SubjectId = item.SubjectId; +// instance.SubjectVisitId = item.SubjectVisitId; +// } +// await _repository.AddRangeAsync(newInstanceList); +// } + +// currentNextCodeInt++; + +// //await _studySubjectVisitRepository.BatchUpdateNoTrackingAsync(t => t.SubjectVisitId == item.SubjectVisitId && t.SCPStudyId == item.SCPStudyId, u => new SCPStudySubjectVisit() { StudyId = find.Id }); + +// //await _subjectPatientRepository.BatchUpdateNoTrackingAsync(t => t.SubjectId == item.SubjectId && t.PatientId == item.PatientId, u => new SubjectPatient() { IsBinded = true }); + +// } + + +// } + + + +// //await _studySubjectVisitRepository.SaveChangesAsync(); + +// return ResponseOutput.Ok(); +// } + +// #endregion + +// #region 访视基本管理 + + +// /// +// /// 绑定访视 初始化患者检查列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetVisitPatientStudyList(PatientStudyQuery inQuery) +// { +// var patientQuery = from scpStudy in _studyRepository +// .Where(t => inQuery.PatientIdList.Contains(t.PatientId)) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) + +// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into cc +// from scpStudySubjectVisit in cc.DefaultIfEmpty() +// select new PatientStudySelectDto() +// { +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.Patient.Id, + +// PatientIdStr = scpStudy.PatientIdStr, +// PatientAge = scpStudy.PatientAge, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientSex = scpStudy.PatientSex, +// PatientName = scpStudy.PatientName, + +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, + +// CallingAE = scpStudy.CallingAE, + +// SubmitState = scpStudySubjectVisit.SubjectVisit.SubmitState, +// SubjectVisitId = scpStudySubjectVisit.SubjectVisitId +// } +// ; + +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + + +// return list; +// } + + + +// /// +// /// 访视管理- 获取subject 已存在的访视列表 ,同时获取项目访视的配置 在otherData里 +// /// +// /// +// /// +// /// +// [HttpPost] +// public async Task GetSubjectVisitSelectList(SubjectVisitSelectQuery inQuery, [FromServices] IRepository _subjectVisitReposiotry) +// { + +// var scpStudyList = await _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId && t.SubjectId == inQuery.SubjectId && t.SCPStudyId != inQuery.SCPStudyId) +// .Select(t => new { t.SubjectVisitId, StudyTime = t.SCPStudy.StudyTime }) +// .ToListAsync(); + +// var result = scpStudyList.GroupBy(t => t.SubjectVisitId) +// .Select(g => new +// { + +// SubejctVisitId = g.Key, +// VisitMaxStudyTime = g.Max(c => c.StudyTime), +// VisitMinStudyTime = g.Min(c => c.StudyTime) +// }).ToList(); + +// var list = _subjectVisitReposiotry.Where(t => t.SubjectId == inQuery.SubjectId).ProjectTo(_mapper.ConfigurationProvider).ToList(); + +// foreach (var item in list) +// { +// item.VisitMaxStudyTime = result.Where(t => t.SubejctVisitId == item.Id).FirstOrDefault()?.VisitMaxStudyTime; +// item.VisitMinStudyTime = result.Where(t => t.SubejctVisitId == item.Id).FirstOrDefault()?.VisitMinStudyTime; +// } + +// var trialconfig = _trialRepository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.BlindBaseLineName, t.BlindFollowUpPrefix }).FirstOrDefault(); + +// return ResponseOutput.Ok(list, trialconfig); +// } + + + + +// /// +// ///访视管理-> 访视列表 (带患者信息,患者信息是数组) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientSubejctVisitList(PatientSubejctVisitQuery inQuery) +// { +// var query = _subjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), u => u.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), u => u.VisitName.Contains(inQuery.VisitName)) +// .WhereIf(inQuery.SubmitState != null, u => u.SubmitState == inQuery.SubmitState) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime) >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime) <= inQuery.EndStudyTime) + +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) +// .Select(t => new PatientSubjectVisitView() +// { +// PatientList = t.Subject.SubjectPatientList.Select(c => new PatientBasicInfo() +// { +// PatientId = c.PatientId, +// PatientAge = c.Patient.PatientAge, +// PatientBirthDate = c.Patient.PatientBirthDate, +// PatientIdStr = c.Patient.PatientIdStr, +// PatientSex = c.Patient.PatientSex, +// PatientName = c.Patient.PatientName, +// }).ToList(), + +// TrialId = t.TrialId, +// SubjectId = t.SubjectId, +// SubjectVisitId = t.Id, +// SubjectAge = t.Subject.Age, +// SubjectSex = t.Subject.Sex, +// SubjectShortName = t.Subject.ShortName, +// SubjectCode = t.Subject.Code, +// SubmitState = t.SubmitState, +// SubmitTime = t.SubmitTime, +// VisitNum = t.VisitNum, +// VisitName = t.VisitName, +// VisitEarliestStudyTime = t.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), +// VisitLatestStudyTime = t.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), +// VisitImageZipPath = t.VisitImageZipPath, +// PackState = t.PackState, +// }); + +// var defalutSortArray = new string[] { nameof(PatientSubjectVisitView.SubjectId), nameof(PatientSubjectVisitView.VisitNum) }; +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + + +// return ResponseOutput.Ok(pageList); +// } + + +// /// +// ///访视管理-> 获取当前访视 已绑定的患者检查 (从访视列表 进入修改绑定) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetCurrentVisitPatientStudyList(SubjectVisitStudyQuery inQuery) +// { +// var patientQuery = _studySubjectVisitRepository.Where(t => t.SubjectVisitId == inQuery.SujectVisitId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted) +// //.WhereIf(inQuery.SubmitState != null, u => u.SubjectVisit.SubmitState == inQuery.SubmitState) +// .ProjectTo(_mapper.ConfigurationProvider); + +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? patientQuery.OrderBy(sortField) : patientQuery.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + +// return list; +// } + + +// /// +// /// 访视管理-> 获取可选访视列表 (从访视列表 进入修改绑定) +// /// +// /// +// /// +// [HttpPost] +// public async Task> GetPatientOtherStudyList(PatientStudyOtherQuery inQuery) +// { +// var query = from scpStudy in _studyRepository.Where(t => inQuery.PatientIdList.Contains(t.PatientId) && !t.SCPStudySubjectVisitList.Any(t => (t.SubjectVisitId == inQuery.SujectVisitId || t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted) && t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.EarliestStudyTime != null, t => t.StudyTime >= inQuery.EarliestStudyTime) +// .WhereIf(inQuery.LatestStudyTime != null, t => t.StudyTime <= inQuery.LatestStudyTime) +// .WhereIf(!string.IsNullOrEmpty(inQuery.PatientIdStr), t => t.PatientIdStr.Contains(inQuery.PatientIdStr)) +// //不属于该访视的检查 或者未绑定的检查 +// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(c => c.TrialId == inQuery.TrialId) on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId +// into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new VisitPatientStudyView() +// { +// PatientIdStr = scpStudy.PatientIdStr, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientAge = scpStudy.PatientAge, +// PatientName = scpStudy.PatientName, +// PatientSex = scpStudy.PatientSex, +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// CallingAE = scpStudy.CallingAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, + +// SubmitState = scpStudySV.SubjectVisit.SubmitState, +// SubjectVisitId = scpStudySV.SubjectVisitId, +// VisitName = scpStudySV.SubjectVisit.VisitName, +// }; + +// #region 废弃 +// //var notCurrentVisitQuey = _studySubjectVisitRepository.Where(t => t.SubjectVisitId != inQuery.SujectVisitId && t.SCPStudy.Patient.Id == inQuery.PatientId) +// // .Select(t => new VisitPatientStudyView() +// // { +// // Description = t.SCPStudy.Description, +// // CalledAE = t.SCPStudy.CalledAE, +// // InstanceCount = t.SCPStudy.InstanceCount, +// // Modalities = t.SCPStudy.Modalities, +// // PatientId = t.SCPStudy.PatientId, +// // SCPStudyId = t.SCPStudy.PatientId, +// // SeriesCount = t.SCPStudy.SeriesCount, +// // StudyTime = t.SCPStudy.StudyTime, +// // SubjectVisitId = t.SubjectVisitId, +// // VisitName = t.SubjectVisit.VisitName, +// // }); + +// //var notBindQuery= _studyRepository.Where(t => t.PatientId == inQuery.PatientId && t.pa) + +// //var patientQuery = query + +// // .ProjectTo(_mapper.ConfigurationProvider); +// #endregion +// var sortField = string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(VisitPatientStudyView.StudyTime) : inQuery.SortField; +// var orderQuery = inQuery.Asc ? query.OrderBy(sortField) : query.OrderBy(sortField + " desc"); + +// var list = await orderQuery.ToListAsync(); + +// return list; +// } + +// #endregion + +// #region 检查管理 + + + +// /// +// ///检查管理-> 检查列表 (同步影像数据之前的) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientStudyBeforeConfirmList(TrialPatientStudyQuery inQuery) +// { +// #region 只查询已绑定的 +// //var query = _studySubjectVisitRepository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) +// // .WhereIf(inQuery.PatientId != null, t => t.SCPStudy.PatientId == inQuery.PatientId) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.SCPStudy.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.SCPStudy.Patient.PatientSex.Contains(inQuery.PatientSex)) +// // .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// // .WhereIf(inQuery.BeginStudyTime != null, t => t.SCPStudy.StudyTime >= inQuery.BeginStudyTime) +// // .WhereIf(inQuery.EndStudyTime != null, t => t.SCPStudy.StudyTime <= inQuery.EndStudyTime) +// // .Select(t => new PatientStudyBeforeConfirmView() +// // { +// // SubjectId = t.SubjectVisit.SubjectId, +// // SubjectAge = t.SubjectVisit.Subject.Age, +// // SubjectSex = t.SubjectVisit.Subject.Sex, +// // SubjectShortName = t.SubjectVisit.Subject.ShortName, + + +// // PatientId = t.SCPStudy.PatientId, +// // PatientAge = t.SCPStudy.PatientAge, +// // PatientBirthDate = t.SCPStudy.PatientBirthDate, +// // PatientIdStr = t.SCPStudy.PatientIdStr, +// // PatientSex = t.SCPStudy.PatientSex, + +// // //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// // //{ +// // // PatientId = t.PatientId, +// // // PatientAge = t.Patient.PatientAge, +// // // PatientBirthDate = t.Patient.PatientBirthDate, +// // // PatientIdStr = t.Patient.PatientIdStr, +// // // PatientSex = t.Patient.PatientSex, +// // //}).ToList(), + +// // SubjectCode = t.SubjectVisit.Subject.Code, +// // SubmitState = t.SubjectVisit.SubmitState, +// // SubmitTime = t.SubjectVisit.SubmitTime, +// // VisitName = t.SubjectVisit.VisitName, +// // SubjectVisitId = t.SubjectVisitId, +// // VisitEarliestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), +// // VisitLatestStudyTime = t.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), + +// // StudyId = t.SCPStudyId, +// // StudyTime = t.SCPStudy.StudyTime, +// // CallingAE = t.SCPStudy.CallingAE, +// // CalledAE = t.SCPStudy.CalledAE + +// // }); +// #endregion + +// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.SubjectVisit.SubmitState == SubmitStateEnum.Submitted && t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.IsBindedVisit == false, t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(inQuery.IsBindedVisit == true, t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.VisitName), t => t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId && t.SubjectVisit.VisitName.Contains(inQuery.VisitName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// on scpStudy.PatientId equals subjectPatient.PatientId +// join scpStudySubjectVisit in _studySubjectVisitRepository.Where(t => t.TrialId == inQuery.TrialId) +// on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new PatientStudyBeforeConfirmView() +// { +// SubjectId = subjectPatient.Subject.Id, +// SubjectAge = subjectPatient.Subject.Age, +// SubjectSex = subjectPatient.Subject.Sex, +// SubjectShortName = subjectPatient.Subject.ShortName, +// SubjectCode = subjectPatient.Subject.Code, + +// PatientId = scpStudy.PatientId, +// PatientName = scpStudy.PatientName, +// PatientAge = scpStudy.PatientAge, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientIdStr = scpStudy.PatientIdStr, +// PatientSex = scpStudy.PatientSex, + +// //PatientList = t.SubjectVisit.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// //{ +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// //}).ToList(), + + +// SubmitState = scpStudySV.SubjectVisit.SubmitState, +// SubmitTime = scpStudySV.SubjectVisit.SubmitTime, +// VisitName = scpStudySV.SubjectVisit.VisitName, +// VisitNum = scpStudySV.SubjectVisit.VisitNum, +// SubjectVisitId = scpStudySV.SubjectVisit.Id, +// VisitEarliestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Min(t => t.SCPStudy.StudyTime), +// VisitLatestStudyTime = scpStudySV.SubjectVisit.SCPStudySubjectVisitList.Max(t => t.SCPStudy.StudyTime), + +// StudyId = scpStudy.Id, +// StudyTime = scpStudy.StudyTime, +// CallingAE = scpStudy.CallingAE, +// CalledAE = scpStudy.CalledAE, +// Description = scpStudy.Description, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// ModalityForEdit = scpStudy.ModalityForEdit, +// SeriesCount = scpStudy.SeriesCount + +// }; + +// var defalutSortArray = new string[] { nameof(PatientStudyBeforeConfirmView.SubjectCode), nameof(PatientStudyBeforeConfirmView.VisitNum), nameof(PatientStudyBeforeConfirmView.StudyTime) }; +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + + +// return ResponseOutput.Ok(pageList); +// } + + +// /// +// ///检查管理-> 检查列表 (同步影像数据之后的 带患者信息 患者信息是数组) +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetTrialPatientStudyList(TrialPatientStudyQuery inQuery) +// { +// var query = _repository.Where(t => t.SubjectVisit.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(inQuery.SubjectAge != null, t => t.SubjectVisit.Subject.Age == inQuery.SubjectAge) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.SubjectVisit.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.SubjectVisit.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// .Select(t => new PatientStudyView() +// { +// SubjectId = t.SubjectVisit.SubjectId, +// SubjectAge = t.Subject.Age, +// SubjectSex = t.Subject.Sex, +// SubjectShortName = t.Subject.ShortName, + + +// //PatientId = Guid.Empty, +// PatientAge = t.PatientAge, +// PatientName = t.PatientName, +// PatientBirthDate = t.PatientBirthDate, +// PatientIdStr = t.PatientIdStr, +// PatientSex = t.PatientSex, + +// //PatientList = t.Subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// //{ +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// //}).ToList(), + +// Modalities = t.Modalities, +// ModalityForEdit = t.ModalityForEdit, +// SubjectCode = t.SubjectVisit.Subject.Code, +// SubmitState = t.SubjectVisit.SubmitState, +// SubmitTime = t.SubjectVisit.SubmitTime, +// VisitName = t.SubjectVisit.VisitName, +// VisitNum = t.SubjectVisit.VisitNum, +// SubjectVisitId = t.SubjectVisitId, +// VisitEarliestStudyTime = t.SubjectVisit.StudyList.Min(t => t.StudyTime), +// VisitLatestStudyTime = t.SubjectVisit.StudyList.Max(t => t.StudyTime), + +// StudyId = t.Id, +// StudyTime = t.StudyTime, +// Description = t.Description, +// SeriesCount = t.SeriesCount, +// InstanceCount = t.InstanceCount, +// }); + +// var defalutSortArray = new string[] { nameof(PatientStudyView.SubjectCode), nameof(PatientStudyView.VisitNum), nameof(PatientStudyView.StudyTime) }; + +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + + +// return ResponseOutput.Ok(pageList); +// } + + + +// /// +// /// 获取该项目 患者已绑定subject ,新来了的检查 可能需要新建访视 但是新增的检查未绑定访视的检查列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetTrialUnbindSubjectVisitStudyList(TrialPatientStudyQuery inQuery) +// { +// //属于该项目的已绑定患者的检查,同时没有绑定任何访视 +// var query = from scpStudy in _studyRepository.Where(t => !t.SCPStudySubjectVisitList.Any(t => t.TrialId == inQuery.TrialId)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// join subjectPatient in _subjectPatientRepository.Where(t => t.Subject.TrialId == inQuery.TrialId) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Subject.Code.Contains(inQuery.SubjectCode)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Subject.Sex.Contains(inQuery.SubjectSex)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// on scpStudy.PatientId equals subjectPatient.PatientId +// join scpStudySubjectVisit in _studySubjectVisitRepository.AsQueryable() on scpStudy.Id equals scpStudySubjectVisit.SCPStudyId +// into dd +// from scpStudySV in dd.DefaultIfEmpty() +// select new UnbindStudyView() +// { +// PatientIdStr = scpStudy.PatientIdStr, +// PatientBirthDate = scpStudy.PatientBirthDate, +// PatientAge = scpStudy.PatientAge, +// PatientName = scpStudy.PatientName, +// PatientSex = scpStudy.PatientSex, +// Description = scpStudy.Description, +// CalledAE = scpStudy.CalledAE, +// InstanceCount = scpStudy.InstanceCount, +// Modalities = scpStudy.Modalities, +// PatientId = scpStudy.PatientId, +// SCPStudyId = scpStudy.Id, +// SeriesCount = scpStudy.SeriesCount, +// StudyTime = scpStudy.StudyTime, + +// SubjectVisitId = scpStudySV.SubjectVisitId, +// VisitName = scpStudySV.SubjectVisit.VisitName, + +// SubjectId = subjectPatient.SubjectId, +// SubjectCode = subjectPatient.Subject.Code, +// TrialId = subjectPatient.Subject.TrialId, +// SubjectAge = subjectPatient.Subject.Age, +// SubjectSex = subjectPatient.Subject.Sex, +// SubjectShortName = subjectPatient.Subject.ShortName, +// SubjectBirthDate = subjectPatient.Subject.BirthDate +// }; + +// #region 废弃 +// //var query = from subject in _subjectRepository.Where(t => t.TrialId == inQuery.TrialId) +// // .WhereIf(inQuery.SubjectAge != null, t => t.Age == inQuery.SubjectAge) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectCode), u => u.Code.Contains(inQuery.SubjectCode)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectSex), u => u.Sex.Contains(inQuery.SubjectSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectShortName), t => t.ShortName.Contains(inQuery.SubjectShortName)) +// // join subjectPatient in _subjectPatientRepository.AsQueryable() on subject.Id equals subjectPatient.PatientId +// // //没有绑定任何访视 +// // join scpStudy in _studyRepository.AsQueryable() +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Patient.PatientSex.Contains(inQuery.PatientSex)) +// // .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), u => u.Patient.PatientIdStr.Contains(inQuery.PatientIdStr)) +// // .WhereIf(inQuery.BeginStudyTime != null, t => t.StudyTime >= inQuery.BeginStudyTime) +// // .WhereIf(inQuery.EndStudyTime != null, t => t.StudyTime <= inQuery.EndStudyTime) +// // on subjectPatient.PatientId equals scpStudy.PatientId +// // select new SubjectPatientStudyView() +// // { +// // SubjectId = subject.Id, +// // SubjectAge = subject.Age, +// // SubjectSex = subject.Sex, +// // SubjectShortName = subject.ShortName, + +// // PatientList = subject.SubjectPatientList.Select(t => new PatientBasicInfo() +// // { +// // PatientId = t.PatientId, +// // PatientAge = t.Patient.PatientAge, +// // PatientBirthDate = t.Patient.PatientBirthDate, +// // PatientIdStr = t.Patient.PatientIdStr, +// // PatientSex = t.Patient.PatientSex, +// // }).ToList(), + +// // SubjectCode = subject.Code, + +// // SeriesCount = scpStudy.SeriesCount, +// // CalledAE = scpStudy.CalledAE, +// // InstanceCount = scpStudy.InstanceCount, +// // Description = scpStudy.Description, +// // Modalities = scpStudy.Modalities, +// // PatientId = scpStudy.PatientId, + +// // SCPStudyId = scpStudy.Id, +// // StudyTime = scpStudy.StudyTime + +// // }; +// #endregion + + + + + + +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(UnbindStudyView.StudyTime) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + +// /// +// /// 删除某个项目 未提交的访视检查绑定, 清理数据,方便测试自动绑定 +// /// +// /// +// /// +// [HttpDelete] +// public async Task DeleteUnSubmittedStudyBind(Guid trialId, Guid? subjectId, +// [FromServices] IRepository _visitTaskRepository, +// [FromServices] IRepository _dicomStudyRepository, +// [FromServices] IRepository _dicomSeriesRepository, +// [FromServices] IRepository _dicomInstanceRepository) +// { +// if (subjectId != null) +// { +// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectId == subjectId); + +// await _subjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// await _visitTaskRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// await _dicomStudyRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomSeriesRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); +// await _dicomInstanceRepository.BatchDeleteNoTrackingAsync(t => t.SubjectId == subjectId); + +// } +// else +// { +// await _studySubjectVisitRepository.BatchDeleteNoTrackingAsync(t => t.TrialId == trialId && t.SubjectVisit.SubmitState != SubmitStateEnum.Submitted); + +// } + + +// return ResponseOutput.Ok(); +// } + + +// /// +// /// 阅片管理-> 任务列表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetPatientVisitTaskList([FromServices] IRepository _visitTaskRepository, PatientVisitTaskQuery inQuery) +// { +// var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsAnalysisCreate == false) +// .WhereIf(inQuery.ReadingCategory != null, t => t.ReadingCategory == inQuery.ReadingCategory) +// .WhereIf(inQuery.ReadingCategory == null, t => t.ReadingCategory != ReadingCategory.Judge) + +// .WhereIf(inQuery.ReadingTaskState != null, t => t.ReadingTaskState == inQuery.ReadingTaskState) +// .WhereIf(inQuery.TaskState != null, t => t.TaskState == inQuery.TaskState) +// .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) +// .WhereIf(inQuery.DoctorUserId != null, t => t.DoctorUserId == inQuery.DoctorUserId) +// .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + +// .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.TaskName.Contains(inQuery.TaskName) || t.TaskBlindName.Contains(inQuery.TaskName)) +// .WhereIf(inQuery.BeginSignTime != null, t => t.SignTime >= inQuery.BeginSignTime) +// .WhereIf(inQuery.EndSignTime != null, t => t.SignTime <= inQuery.EndSignTime) + +// .WhereIf(inQuery.BeginTaskCreateTime != null, t => t.CreateTime >= inQuery.BeginTaskCreateTime) +// .WhereIf(inQuery.EndTaskCreateTime != null, t => t.CreateTime <= inQuery.EndTaskCreateTime) + +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientIdStr), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientIdStr.Contains(inQuery.PatientIdStr))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientName), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientName.Contains(inQuery.PatientName))) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientSex), t => t.Subject.SubjectPatientList.Any(t => t.Patient.PatientSex.Contains(inQuery.PatientSex))) +// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectShortName), t => t.Subject.ShortName.Contains(inQuery.SubjectShortName)) +// .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => (t.Subject.Code.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.IsAnalysisCreate)) +// .ProjectTo(_mapper.ConfigurationProvider); + +// var defalutSortArray = new string[] { nameof(PatientVisitTaskDTO.SubjectId), nameof(PatientVisitTaskDTO.VisitTaskNum) }; + +// var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); + +// return ResponseOutput.Ok(pageList); +// } + + +// #endregion + + + + + +// /// +// /// scp 影像推送记录表 +// /// +// /// +// /// +// [HttpPost] +// public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) +// { +// var query = _repository.Where() +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.CalledAE.Contains(inQuery.CalledAE)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAEIP), t => t.CallingAEIP.Contains(inQuery.CallingAEIP)) +// .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) +// .WhereIf(inQuery.StartTime != null, t => t.StartTime >= inQuery.StartTime) +// .WhereIf(inQuery.EndTime != null, t => t.EndTime <= inQuery.EndTime) + +// .ProjectTo(_mapper.ConfigurationProvider); + + +// var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(SCPImageUploadView.CallingAE) : inQuery.SortField, inQuery.Asc); + + +// return ResponseOutput.Ok(pageList); +// } + + + + +// } + + + +//} diff --git a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj index f2772d9ce..f2b268e49 100644 --- a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj +++ b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj @@ -9,13 +9,13 @@ - - + + - - + + - +