diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..551c83917 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM registry.cn-shanghai.aliyuncs.com/extimaging/aspnetcore:v8.2 +WORKDIR /app +COPY publish . +ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"] 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..629cf9636 --- /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 + typeof(BaseService).Assembly.GetName().Name+".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..00ee49222 --- /dev/null +++ b/IRC.Core.SCP/HostConfig/EFSetup.cs @@ -0,0 +1,61 @@ +using EntityFramework.Exceptions.SqlServer; +using IRaCIS.Core.Domain.Share; +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.AddHttpContextAccessor(); + services.AddScoped(); + + + //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext + //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 + services.AddDbContextPool(options => + { + // 在控制台 + //public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); + var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); + + options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, + contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + + options.UseLoggerFactory(logFactory); + + options.UseExceptionProcessor(); + + options.EnableSensitiveDataLogging(); + + options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); + + options.UseProjectables(); + + + + }); + + //// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above, + //services.AddScoped(); + + //// Finally, arrange for a context to get injected from our Scoped factory: + //services.AddScoped(sp => sp.GetRequiredService().CreateDbContext()); + + //注意区分 easy caching 也有 IDistributedLockProvider + services.AddSingleton(sp => + { + //var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!); + + return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value); + }); + + } + } +} 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..dfa4bb17e --- /dev/null +++ b/IRC.Core.SCP/Program.cs @@ -0,0 +1,215 @@ + +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(); + +Log.Logger = new LoggerConfiguration() + //.MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .WriteTo.Console() + .WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day) + .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 logger = app.Services.GetService>(); + +var server = DicomServerFactory.Create(_configuration.GetSection("DicomSCPServiceConfig").GetValue("ServerPort"), userState: app.Services,logger: logger); + + +app.Run(); diff --git a/IRC.Core.SCP/Properties/launchSettings.json b/IRC.Core.SCP/Properties/launchSettings.json new file mode 100644 index 000000000..11587df61 --- /dev/null +++ b/IRC.Core.SCP/Properties/launchSettings.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "Test_IRC_SCP": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:6200", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Test_IRC_SCP" + } + } +} 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..6cfb381ad --- /dev/null +++ b/IRC.Core.SCP/Service/CStoreSCPService.cs @@ -0,0 +1,370 @@ +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 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 Guid _trialId { get; set; } + + private Guid _trialSiteId { 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, //1.2.840.10008.1.2.4.80 + DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90 + DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70 + DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14) + DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5 + // Lossy + DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81" + DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91 + DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50 + DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51 + // Uncompressed + DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1 + DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2 + DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2 + }; + + + 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 _trialDicomAERepository = _serviceProvider.GetService>(); + + + var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList(); + var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList(); + + Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList)); + + var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault(); + + var isCanReceiveIamge = false; + + if (findCalledAE != null) + { + _trialId = findCalledAE.TrialId; + + var _trialSiteDicomAERepository = _serviceProvider.GetService>(); + + + var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault(); + + if (findTrialSiteAE != null) + { + _trialSiteId = findTrialSiteAE.TrialSiteId; + + isCanReceiveIamge = true; + } + + + } + + if (association.CallingAE == "test-callingAE") + { + isCanReceiveIamge = true; + } + + if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false) + { + + Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} 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; + _upload.TrialId = _trialId; + _upload.TrialSiteId = _trialSiteId; + + await _SCPImageUploadRepository.AddAsync(_upload, true); + + await SendAssociationReleaseResponseAsync(); + } + + + private async Task DataMaintenanceAsaync() + { + Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality"); + + + + //处理检查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 studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString()); + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString()); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString()); + + + var ossService = _serviceProvider.GetService(); + var dicomArchiveService = _serviceProvider.GetService(); + var _seriesRepository = _serviceProvider.GetService>(); + + var _distributedLockProvider = _serviceProvider.GetService(); + + var storeRelativePath = string.Empty; + var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}{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, _trialId, _trialSiteId, 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.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..ee456af4a --- /dev/null +++ b/IRC.Core.SCP/Service/DicomArchiveService.cs @@ -0,0 +1,352 @@ +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; +using System.Runtime.Intrinsics.X86; + +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, Guid trialId, Guid trialSiteId, 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.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty); + + //Guid patientId= IdentifierHelper.CreateGuid(patientIdStr); + Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString()); + Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString()); + Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString()); + + 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 && t.TrialSiteId==trialSiteId ); + 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(), + TrialId=trialId, + TrialSiteId=trialSiteId, + 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; + + DateTime birthDate; + + if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate)) + { + var patientAge = studyTime.Value.Year - birthDate.Year; + // 如果生日还未到,年龄减去一岁 + if (studyTime.Value < birthDate.AddYears(patientAge)) + { + patientAge--; + } + + findPatient.PatientAge = patientAge.ToString(); + } + + } + 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, + TrialId = trialId, + TrialSiteId = trialSiteId, + 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..1c107e94b --- /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,Guid trialId,Guid trialSiteId, 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..eb34ec64f --- /dev/null +++ b/IRC.Core.SCP/Service/OSSService.cs @@ -0,0 +1,442 @@ +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; +using System.Runtime.InteropServices; + +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 internalEndpoint { 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}/{Guid.NewGuid()}_{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(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, 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(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, 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(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, 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(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, 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 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(); + + var args = new PresignedGetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithExpiry(3600); + + var presignedUrl = await minioClient.PresignedGetObjectAsync(args); + + Uri uri = new Uri(presignedUrl); + + string relativePath = uri.PathAndQuery; + + + return relativePath; + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message); + } + + } + } + + + +} diff --git a/IRC.Core.SCP/appsettings.Prod_IRC_SCP.json b/IRC.Core.SCP/appsettings.Prod_IRC_SCP.json new file mode 100644 index 000000000..6def61882 --- /dev/null +++ b/IRC.Core.SCP/appsettings.Prod_IRC_SCP.json @@ -0,0 +1,64 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", + "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.Test_IRC_SCP.json b/IRC.Core.SCP/appsettings.Test_IRC_SCP.json new file mode 100644 index 000000000..0c47eb434 --- /dev/null +++ b/IRC.Core.SCP/appsettings.Test_IRC_SCP.json @@ -0,0 +1,134 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, +<<<<<<<< HEAD:IRaCIS.Core.API/appsettings.US_Test_IRC.json + "ConnectionStrings": { + "RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + + "ObjectStoreService": { + + "ObjectStoreUse": "MinIO", +======== + "ObjectStoreService": { + "ObjectStoreUse": "AliyunOSS", +>>>>>>>> Test_IRC_Net8:IRC.Core.SCP/appsettings.Test_IRC_SCP.json + "AliyunOSS": { + "regionId": "cn-shanghai", + "internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", + "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": { +<<<<<<<< HEAD:IRaCIS.Core.API/appsettings.US_Test_IRC.json + //"endPoint": "hir-oss.uat.extimaging.com", + //"port": "443", + //"useSSL": true, + //"viewEndpoint": "https://hir-oss.uat.extimaging.com/hir-uat", + + "endPoint": "47.117.164.182", + "port": "9001", + "useSSL": false, + "viewEndpoint": "http://47.117.164.182:9001/test-irc-us", + + "accessKey": "b9Ul0e98xPzt6PwRXA1Q", + "secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ", + "bucketName": "test-irc-us" + +======== + "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/" +>>>>>>>> Test_IRC_Net8:IRC.Core.SCP/appsettings.Test_IRC_SCP.json + }, + + "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_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", + "Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" + }, + "BasicSystemConfig": { + + "OpenUserComplexPassword": false, + + "OpenSignDocumentBeforeWork": false, + + "OpenTrialRelationDelete": true, + + "OpenLoginLimit": false, + "LoginMaxFailCount": 5, + +<<<<<<<< HEAD:IRaCIS.Core.API/appsettings.US_Test_IRC.json + "LoginFailLockMinutes": 30, + "AutoLoginOutMinutes": 60, + + "ContinuousReadingTimeMin": 120, + + "ReadingRestTimeMin": 10 + }, + + "SystemEmailSendConfig": { + "Port": 587, + "Host": "smtp-mail.outlook.com", + "FromEmail": "donotreply@elevateimaging.ai", + "FromName": "LiLi", + "AuthorizationCode": "Q#669869497420ul", + + "OrganizationName": "Elevate Imaging", + "OrganizationNameCN": "Elevate Imaging", + "CompanyName": "Elevate Imaging Inc.", + "CompanyNameCN": "上海展影医疗科技有限公司", + "CompanyShortName": "Elevate Imaging", + "CompanyShortNameCN": "展影医疗", + "SiteUrl": "https://lili.test.elevateimaging.ai/login" + } +======== + "LoginFailLockMinutes": 30 + + }, + + "SystemEmailSendConfig": { + "Port": 465, + "Host": "smtp.qiye.aliyun.com", + "FromEmail": "test-study@extimaging.com", + "FromName": "Test_Study", + "AuthorizationCode": "zhanying123", +>>>>>>>> Test_IRC_Net8:IRC.Core.SCP/appsettings.Test_IRC_SCP.json + + "SiteUrl": "http://study.test.extimaging.com/login" + }, + "DicomSCPServiceConfig": { + "CalledAEList": [ + "STORESCP", + "Value1", + "Value2", + "Value3" + ], + "ServerPort": 11112 + } +} diff --git a/IRC.Core.SCP/appsettings.Uat_IRC_SCP.json b/IRC.Core.SCP/appsettings.Uat_IRC_SCP.json new file mode 100644 index 000000000..9079082c4 --- /dev/null +++ b/IRC.Core.SCP/appsettings.Uat_IRC_SCP.json @@ -0,0 +1,77 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "ObjectStoreService": { + "ObjectStoreUse": "MinIO", + "AliyunOSS": { + "regionId": "cn-shanghai", + "internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", + "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/Controllers/InspectionController.cs b/IRaCIS.Core.API/Controllers/InspectionController.cs index 7de33aaae..bbeaf4e35 100644 --- a/IRaCIS.Core.API/Controllers/InspectionController.cs +++ b/IRaCIS.Core.API/Controllers/InspectionController.cs @@ -308,11 +308,28 @@ namespace IRaCIS.Core.API.Controllers } - /// - /// 提交结构化录入并签名 - /// - /// - /// + /// + /// PM签名一致性分析临床数据 + /// + /// + /// + [HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + + [UnitOfWork] + public async Task SignConsistencyAnalysisReadingClinicalData(DataInspectionDto opt) + { + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _readingClinicalDataService.SignConsistencyAnalysisReadingClinicalData(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + + /// + /// 提交结构化录入并签名 + /// + /// + /// [HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")] [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] @@ -402,6 +419,19 @@ namespace IRaCIS.Core.API.Controllers return result; } + + [HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })] + public async Task ConfigTrialPACSInfoConfirm(DataInspectionDto opt) + { + opt.Data.IsTrialPACSConfirmed = true; + var singid = await _inspectionService.RecordSing(opt.SignInfo); + var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data); + await _inspectionService.CompletedSign(singid, result); + return result; + } + /// /// 签名确认 /// diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.csproj b/IRaCIS.Core.API/IRaCIS.Core.API.csproj index f6e1614ef..5a7be3fe9 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.csproj +++ b/IRaCIS.Core.API/IRaCIS.Core.API.csproj @@ -64,22 +64,23 @@ - + true - + - + + - + - + diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml index ddbe96072..726d34a48 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.xml +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -167,6 +167,13 @@ + + + PM签名一致性分析临床数据 + + + + 提交结构化录入并签名 diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs index 885b11f66..915ff6018 100644 --- a/IRaCIS.Core.API/Progranm.cs +++ b/IRaCIS.Core.API/Progranm.cs @@ -28,6 +28,7 @@ using FellowOakDicom.Network; using IRaCIS.Core.Application.Service.ImageAndDoc; using IP2Region.Net.Abstractions; using IP2Region.Net.XDB; +using IRaCIS.Core.Application.BusinessFilter; #region 获取环境变量 @@ -76,8 +77,8 @@ if (urlsIndex > -1) NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); -builder.Configuration.AddJsonFile("appsettings.json", false, true) - .AddJsonFile($"appsettings.{enviromentName}.json", false, true); +builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true) + .AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true); builder.Host .UseServiceProviderFactory(new AutofacServiceProviderFactory()) @@ -104,11 +105,9 @@ builder.Services.AddControllers(options => options.Filters.Add(); options.Filters.Add(); options.Filters.Add(); + options.Filters.Add(10); + options.Filters.Add(); - if (_configuration.GetSection("BasicSystemConfig").GetValue("OpenLoginLimit")) - { - options.Filters.Add(); - } }) .AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理 @@ -117,6 +116,8 @@ builder.Services.AddOptions().Configure(_configuration.Ge builder.Services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); +builder.Services.AddOptions().Configure(_configuration.GetSection("EncrypteResponseConfig")); +builder.Services.AddOptions().Configure(_configuration.GetSection("SystemPacsConfig")); //动态WebApi + UnifiedApiResultFilter 省掉控制器代码 @@ -297,7 +298,7 @@ try - var server = DicomServerFactory.Create(11112,userState: app.Services); + var server = DicomServerFactory.Create(11112, userState: app.Services); app.Run(); diff --git a/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs index 45ab1ec1f..d94b96b65 100644 --- a/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs +++ b/IRaCIS.Core.API/_PipelineExtensions/LocalizationConfig.cs @@ -1,4 +1,5 @@ -using Microsoft.AspNetCore.Builder; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Localization; using System.Collections.Generic; using System.Globalization; @@ -12,8 +13,9 @@ namespace IRaCIS.Core.API { var supportedCultures = new List { - new CultureInfo("en-US"), - new CultureInfo("zh-CN") + + new CultureInfo(StaticData.CultureInfo.en_US), + new CultureInfo(StaticData.CultureInfo.zh_CN) }; var options = new RequestLocalizationOptions diff --git a/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs index 015405bf1..3806353b2 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/AutofacModuleSetup.cs @@ -55,8 +55,8 @@ namespace IRaCIS.Core.API - containerBuilder.RegisterType().As().SingleInstance(); - containerBuilder.RegisterType().As().InstancePerLifetimeScope(); + //containerBuilder.RegisterType().As().SingleInstance(); + //containerBuilder.RegisterType().As().InstancePerLifetimeScope(); //注册hangfire任务 依赖注入 diff --git a/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs index 1f4ca4079..820a2a67e 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/EFSetup.cs @@ -1,11 +1,15 @@ -using Hangfire.SqlServer; +using Castle.Core.Logging; +using EntityFramework.Exceptions.SqlServer; +using Hangfire.SqlServer; using IRaCIS.Core.Application.Triggers; +using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infra.EFCore; using Medallion.Threading; using Medallion.Threading.SqlServer; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using StackExchange.Redis; namespace IRaCIS.Core.API @@ -14,15 +18,28 @@ namespace IRaCIS.Core.API { public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration) { - //services.AddScoped(); + + services.AddHttpContextAccessor(); + services.AddScoped(); + + // First, register a pooling context factory as a Singleton service, as usual: //这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext //Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点 services.AddDbContext(options => { + + // 在控制台 + //public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); + var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); + options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure()); + options.UseLoggerFactory(logFactory); + + options.UseExceptionProcessor(); + options.EnableSensitiveDataLogging(); options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor()); @@ -62,6 +79,12 @@ namespace IRaCIS.Core.API }); + // Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above, + //services.AddScoped(); + + //// Finally, arrange for a context to get injected from our Scoped factory: + //services.AddScoped(sp => sp.GetRequiredService().CreateDbContext()); + //注意区分 easy caching 也有 IDistributedLockProvider services.AddSingleton(sp => { diff --git a/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs index 6037273da..5bc11b6cb 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/ResponseCompressionSetup.cs @@ -1,18 +1,30 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.Extensions.DependencyInjection; +using System.IO.Compression; namespace IRaCIS.Core.API { - public static class ResponseCompressionSetup + public static class ResponseCompressionSetup { public static void AddResponseCompressionSetup(this IServiceCollection services) { services.AddResponseCompression(options => { + options.EnableForHttps = true; options.Providers.Add(); options.Providers.Add(); }); + + services.Configure(options => + { + options.Level = CompressionLevel.Optimal; + }); + + services.Configure(options => + { + options.Level = CompressionLevel.Optimal; + }); } } } diff --git a/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs index 00b718e2c..e2dc37494 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/hangfireSetup.cs @@ -3,6 +3,7 @@ using Hangfire.SqlServer; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; +using System.Runtime.InteropServices; namespace IRaCIS.Core.API { @@ -14,19 +15,29 @@ namespace IRaCIS.Core.API services.AddHangfire(hangFireConfig => { - - //hangFireConfig.UseInMemoryStorage(); - - //指定存储介质 - hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions() + //本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地 + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - SchemaName = "dbo", - CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), - SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), - QueuePollInterval = TimeSpan.Zero, - UseRecommendedIsolationLevel = true, - DisableGlobalLocks = true - }); + hangFireConfig.UseInMemoryStorage(); + + } + else + { + //指定存储介质 + hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions() + { + SchemaName = "dbo", + CommandBatchMaxTimeout = TimeSpan.FromMinutes(5), + SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5), + QueuePollInterval = TimeSpan.Zero, + UseRecommendedIsolationLevel = true, + DisableGlobalLocks = true + }); + } + + + + //hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer //.UseHangfireHttpJob(); diff --git a/IRaCIS.Core.API/appsettings.Event_IRC.json b/IRaCIS.Core.API/appsettings.Event_IRC.json index 216571d78..fa5674cb6 100644 --- a/IRaCIS.Core.API/appsettings.Event_IRC.json +++ b/IRaCIS.Core.API/appsettings.Event_IRC.json @@ -47,7 +47,11 @@ "ContinuousReadingTimeMin": 120, - "ReadingRestTimeMin": 10 + "ReadingRestTimeMin": 10, + + "IsNeedChangePassWord": true, + + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { "Port": 465, diff --git a/IRaCIS.Core.API/appsettings.Prod_IRC.json b/IRaCIS.Core.API/appsettings.Prod_IRC.json index 7b2b1d376..c3dd1e8a2 100644 --- a/IRaCIS.Core.API/appsettings.Prod_IRC.json +++ b/IRaCIS.Core.API/appsettings.Prod_IRC.json @@ -49,8 +49,10 @@ "ContinuousReadingTimeMin": 120, - "ReadingRestTimeMin": 10 + "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { diff --git a/IRaCIS.Core.API/appsettings.Test_IRC.json b/IRaCIS.Core.API/appsettings.Test_IRC.json index b0093efe9..938cbb330 100644 --- a/IRaCIS.Core.API/appsettings.Test_IRC.json +++ b/IRaCIS.Core.API/appsettings.Test_IRC.json @@ -9,7 +9,6 @@ "ConnectionStrings": { "RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true", "Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true" - }, "ObjectStoreService": { @@ -36,7 +35,6 @@ "bucketName": "irc-test", "viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test" }, - "AWS": { "endPoint": "s3.us-east-1.amazonaws.com", "useSSL": true, @@ -67,7 +65,10 @@ "ContinuousReadingTimeMin": 120, - "ReadingRestTimeMin": 10 + "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, + + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { @@ -76,7 +77,6 @@ "FromEmail": "test@extimaging.com", "FromName": "Test_IRC", "AuthorizationCode": "SHzyyl2021", - "SiteUrl": "http://irc.test.extimaging.com/login", "OrganizationName": "Extlmaging", @@ -85,6 +85,10 @@ "CompanyNameCN": "上海展影医疗科技有限公司", "CompanyShortName": "Extensive Imaging", "CompanyShortNameCN": "展影医疗" - } + }, + "SystemPacsConfig": { + "Port": "11113", + "IP": "106.14.89.110" + } } diff --git a/IRaCIS.Core.API/appsettings.US_Prod_IRC.json b/IRaCIS.Core.API/appsettings.US_Prod_IRC.json index 238ba0e24..b2d650a67 100644 --- a/IRaCIS.Core.API/appsettings.US_Prod_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Prod_IRC.json @@ -15,6 +15,18 @@ "ObjectStoreService": { "ObjectStoreUse": "AWS", + "AliyunOSS": { + "regionId": "cn-shanghai", + "internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com", + "endPoint": "https://oss-cn-shanghai.aliyuncs.com", + "accessKeyId": "", + "accessKeySecret": "", + "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": "44.210.231.169", "port": "9001", @@ -47,14 +59,14 @@ "LoginMaxFailCount": 5, "LoginFailLockMinutes": 30, - "AutoLoginOutMinutes": 60, "ContinuousReadingTimeMin": 120, "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, - "OpenLoginMFA": false + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { diff --git a/IRaCIS.Core.API/appsettings.US_Test_IRC.json b/IRaCIS.Core.API/appsettings.US_Test_IRC.json index 67d4b1fe7..a0eed7266 100644 --- a/IRaCIS.Core.API/appsettings.US_Test_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Test_IRC.json @@ -69,7 +69,10 @@ "ContinuousReadingTimeMin": 120, - "ReadingRestTimeMin": 10 + "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, + + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { diff --git a/IRaCIS.Core.API/appsettings.US_Uat_IRC.json b/IRaCIS.Core.API/appsettings.US_Uat_IRC.json index 8697adda1..f16b2410d 100644 --- a/IRaCIS.Core.API/appsettings.US_Uat_IRC.json +++ b/IRaCIS.Core.API/appsettings.US_Uat_IRC.json @@ -62,6 +62,9 @@ "ContinuousReadingTimeMin": 120, "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, + + "ChangePassWordDays": 90, "OpenLoginMFA": true }, diff --git a/IRaCIS.Core.API/appsettings.Uat_IRC.json b/IRaCIS.Core.API/appsettings.Uat_IRC.json index 775d9d17e..a107b1342 100644 --- a/IRaCIS.Core.API/appsettings.Uat_IRC.json +++ b/IRaCIS.Core.API/appsettings.Uat_IRC.json @@ -12,7 +12,7 @@ }, "ObjectStoreService": { - "ObjectStoreUse": "AliyunOSS", + "ObjectStoreUse": "MinIO", "AliyunOSS": { "regionId": "cn-shanghai", @@ -28,12 +28,12 @@ "MinIO": { "endPoint": "hir-oss.uat.extimaging.com", - //"port": "80", - //"useSSL": false, - //"viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat", - "port": "443", - "useSSL": true, - "viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat", + "port": "80", + "useSSL": false, + "viewEndpoint": "http://hir-oss.uat.extimaging.com/irc-uat", + //"port": "443", + //"useSSL": true, + //"viewEndpoint": "https://hir-oss.uat.extimaging.com/irc-uat", "accessKey": "b9Ul0e98xPzt6PwRXA1Q", "secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ", "bucketName": "irc-uat" @@ -56,7 +56,10 @@ "ContinuousReadingTimeMin": 120, - "ReadingRestTimeMin": 10 + "ReadingRestTimeMin": 10, + "IsNeedChangePassWord": true, + + "ChangePassWordDays": 90 }, "SystemEmailSendConfig": { diff --git a/IRaCIS.Core.API/appsettings.json b/IRaCIS.Core.API/appsettings.json index b6d0f4125..8fa44ed41 100644 --- a/IRaCIS.Core.API/appsettings.json +++ b/IRaCIS.Core.API/appsettings.json @@ -90,7 +90,13 @@ "DicomStudyCodePrefix": "ST", "SystemSiteCodePrefix": "S", "DefaultPassword": "123456", - "DefaultInternalOrganizationName": "ExtImaging", "ImageShareExpireDays": 10 + }, + + "EncrypteResponseConfig": { + "IsEnable": true, + "ApiPathList": [ + "/test/get" + ] } } \ No newline at end of file diff --git a/IRaCIS.Core.Application/BackGroundJob/IRaCISCHangfireJob.cs b/IRaCIS.Core.Application/BackGroundJob/IRaCISCHangfireJob.cs index 81da08136..514df5174 100644 --- a/IRaCIS.Core.Application/BackGroundJob/IRaCISCHangfireJob.cs +++ b/IRaCIS.Core.Application/BackGroundJob/IRaCISCHangfireJob.cs @@ -25,8 +25,6 @@ namespace IRaCIS.Application.Services.BackGroundJob { public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources); - public static FileSystemWatcher FileSystemWatcher_US { get; set; } - public static FileSystemWatcher FileSystemWatcher_CN { get; set; } private readonly IRepository _trialRepository; private readonly IEasyCachingProvider _provider; private readonly ILogger _logger; @@ -62,7 +60,7 @@ namespace IRaCIS.Application.Services.BackGroundJob //初始化 - await InitInternationlizationDataAndWatchJsonFileAsync(); + await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_internationalizationRepository); //创建邮件定时任务 await InitSysAndTrialCronJobAsync(); @@ -96,93 +94,8 @@ namespace IRaCIS.Application.Services.BackGroundJob } - #region 国际化 初始化 - public async Task InitInternationlizationDataAndWatchJsonFileAsync() - { - //查询数据库的数据 - var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new - { - t.Code, - t.Value, - t.ValueCN - }).ToListAsync(); - - //组织成json 文件 - - var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json); - var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json); - - - //本地静态文件国际化需要 - foreach (var tojsonItem in toJsonList) - { - StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value; - StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN; - } - - File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic)); - File.WriteAllText(cnJsonPath, JsonConvert.SerializeObject(StaticData.Zh_CN_Dic)); - - - //监测Json文件变更 实时刷新数据 - - if (!File.Exists(usJsonPath)|| !File.Exists(cnJsonPath)) - { - throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound")); - } - - FileSystemWatcher_US = new FileSystemWatcher - { - Path = Path.GetDirectoryName(usJsonPath)!, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size, - Filter = Path.GetFileName(usJsonPath), - EnableRaisingEvents = true, - - }; - // 添加文件更改事件的处理程序 - FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(usJsonPath); - - - FileSystemWatcher_CN = new FileSystemWatcher - { - Path = Path.GetDirectoryName(cnJsonPath)!, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size, - Filter = Path.GetFileName(cnJsonPath), - EnableRaisingEvents = true, - - }; - FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(cnJsonPath); - - } - - - - - private void LoadJsonFile(string filePath) - { - Console.WriteLine("刷新json内存数据"); - IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath,false,true); - - IConfigurationRoot enConfiguration = builder.Build(); - - foreach (IConfigurationSection section in enConfiguration.GetChildren()) - { - if (filePath.Contains(StaticData.En_US_Json)) - { - StaticData.En_US_Dic[section.Key] = section.Value; - - } - else - { - StaticData.Zh_CN_Dic[section.Key] = section.Value; - } - } - } - - #endregion - public async Task InitSysAndTrialCronJobAsync() { //var deleteJobIdList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr != StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend) diff --git a/IRaCIS.Core.Application/BaseService.cs b/IRaCIS.Core.Application/BaseService.cs index 9101ffa80..00df0c376 100644 --- a/IRaCIS.Core.Application/BaseService.cs +++ b/IRaCIS.Core.Application/BaseService.cs @@ -8,6 +8,7 @@ using Panda.DynamicWebApi; using Panda.DynamicWebApi.Attributes; using System.Diagnostics.CodeAnalysis; using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; namespace IRaCIS.Core.Application { @@ -17,7 +18,8 @@ namespace IRaCIS.Core.Application #region 非泛型版本 - [Authorize, DynamicWebApi, UnifiedApiResultFilter] + [TypeFilter(typeof(UnifiedApiResultFilter))] + [Authorize, DynamicWebApi] public class BaseService : IBaseService, IDynamicWebApi { public IMapper _mapper { get; set; } @@ -83,8 +85,8 @@ namespace IRaCIS.Core.Application } - - [Authorize, DynamicWebApi, UnifiedApiResultFilter] + [TypeFilter(typeof(UnifiedApiResultFilter))] + [Authorize, DynamicWebApi] public class BaseServiceTest : IBaseServiceTest, IDynamicWebApi where T : Entity { public IMapper _mapper { get; set; } diff --git a/IRaCIS.Core.Application/BusinessFilter/EncreptApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/EncreptApiResultFilter.cs new file mode 100644 index 000000000..fd58ae885 --- /dev/null +++ b/IRaCIS.Core.Application/BusinessFilter/EncreptApiResultFilter.cs @@ -0,0 +1,71 @@ +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.Options; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Application.BusinessFilter +{ + public class EncreptApiResultFilter : IAsyncResultFilter + { + + private readonly IOptionsMonitor _encreptResponseMonitor; + + public EncreptApiResultFilter(IOptionsMonitor encreptResponseMonitor) + { + _encreptResponseMonitor = encreptResponseMonitor; + } + + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + + if(_encreptResponseMonitor.CurrentValue.IsEnable) + { + + if (context.Result is ObjectResult objectResult) + { + var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; + + var objectValue = objectResult.Value; + + + if (objectValue is IResponseOutput) + { + var responseOutput = objectValue as IResponseOutput; + + var path = context.HttpContext?.Request.Path.Value?.ToLower(); + + + if(!string.IsNullOrEmpty(path) && path.Length>5 && _encreptResponseMonitor.CurrentValue.ApiPathList.Contains(path.ToLower())) + { + + if(responseOutput.IsSuccess) + { + responseOutput.Code = ApiResponseCodeEnum.ResultEncrepted; + responseOutput.Data = JsonConvert.SerializeObject(Convert.ToBase64String(Encoding.UTF8.GetBytes(responseOutput.Data.ToString()))); + + objectResult.Value = responseOutput; + } + + } + + } + + + + } + } + + + + + await next.Invoke(); + + } + } +} diff --git a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs index 5638e52ea..823c168da 100644 --- a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs @@ -1,4 +1,5 @@ -using IRaCIS.Core.Infrastructure; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure.Extention; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; @@ -11,13 +12,13 @@ namespace IRaCIS.Core.Application.Filter { private readonly ILogger _logger; - public IStringLocalizer _localizer; + public IStringLocalizer _localizer; - public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) + public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) { _logger = logger; - _localizer = localizer; - } + _localizer = localizer; + } public void OnException(ExceptionContext context) { //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 @@ -26,7 +27,7 @@ namespace IRaCIS.Core.Application.Filter { if (context.Exception.GetType().Name == "DbUpdateConcurrencyException") { - //---并发更新,当前不允许该操作 + //---并发更新,当前不允许该操作 context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message)); } @@ -34,22 +35,31 @@ namespace IRaCIS.Core.Application.Filter { var error = context.Exception as BusinessValidationFailedException; - context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code)); + var info = string.Empty; + + if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey)) + { + info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"; + } + + + context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info)); + + //warning 级别记录 + //_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"); } - else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException)) + else if (context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException)) { - context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist)); + 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)); + } - - - _logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace)); - - } else { diff --git a/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs index a41c659b1..3e135e386 100644 --- a/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs @@ -1,6 +1,7 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc; using IRaCIS.Core.Domain.Share; +using Microsoft.Extensions.Logging; namespace IRaCIS.Application.Services.BusinessFilter { @@ -8,8 +9,15 @@ namespace IRaCIS.Application.Services.BusinessFilter /// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 /// by zhouhang 2021.09.12 周末 /// - public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter - { + public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter + { + private readonly ILogger _logger; + + public UnifiedApiResultFilter(ILogger logger) + { + _logger = logger; + } + /// /// 异步版本 /// @@ -26,25 +34,15 @@ namespace IRaCIS.Application.Services.BusinessFilter //是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<,>))) + 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); @@ -64,18 +62,20 @@ namespace IRaCIS.Application.Services.BusinessFilter objectResult.DeclaredType = apiResponse.GetType(); } - - //} - + } - //如果不是200 是IResponseOutput 不处理 - else if (statusCode != 200 && (objectResult.Value is IResponseOutput)) + //如果是200 是IResponseOutput 记录下日志 + else if (statusCode == 200 && (objectResult.Value is IResponseOutput)) { + var result = objectResult.Value as IResponseOutput; + + //统一在这里记录国际化的日志信息 + _logger.LogWarning($"{result.LocalizedInfo}"); } - 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; diff --git a/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs index fb48b1d97..069eb3b3f 100644 --- a/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs +++ b/IRaCIS.Core.Application/Helper/ExcelExportHelper.cs @@ -117,7 +117,7 @@ public static class ExcelExportHelper - var (physicalPath, fileNmae) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); + var (physicalPath, fileName) = await FileStoreHelper.GetCommonDocPhysicalFilePathAsync(_hostEnvironment, _commonDocumentRepository, code); //模板路径 @@ -157,7 +157,7 @@ public static class ExcelExportHelper } // 文件名称 从sheet里面取 - fileNmae = workbook.GetSheetName(0); + //fileNmae = workbook.GetSheetName(0); #endregion @@ -181,7 +181,7 @@ public static class ExcelExportHelper return new FileStreamResult(memoryStream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") { - FileDownloadName = $"{exportFileNamePrefix}_{fileNmae/*.Substring(0, fileNmae.LastIndexOf('.'))*/}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" + FileDownloadName = $"{(string.IsNullOrEmpty(exportFileNamePrefix) ? "" : exportFileNamePrefix+ "_") }{Path.GetFileNameWithoutExtension(fileName) }_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" }; #endregion @@ -328,7 +328,7 @@ public static class ExcelExportHelper } // 文件名称 从sheet里面取 - fileName = workbook.GetSheetName(0); + //fileName = workbook.GetSheetName(0); #endregion #region MiniExcel diff --git a/IRaCIS.Core.Application/Helper/FileStoreHelper.cs b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs index 0be0c270e..55c01d7f0 100644 --- a/IRaCIS.Core.Application/Helper/FileStoreHelper.cs +++ b/IRaCIS.Core.Application/Helper/FileStoreHelper.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; using Newtonsoft.Json.Linq; using System.Configuration; +using System.Globalization; using System.Text.RegularExpressions; namespace IRaCIS.Core.Application.Helper; @@ -128,65 +129,6 @@ public static class FileStoreHelper #endregion - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task<(string PhysicalPath, string FileName)> GetSystemClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository _clinicalDataTrialSetRepository, Guid id) - { - var systemClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id); - - if (systemClinicalData == null || systemClinicalData.Path == string.Empty) - { - //---数据库没有找到对应的数据模板文件,请联系系统运维人员。 - throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileNotFound")); - } - - var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, systemClinicalData.Path); - - if (!System.IO.File.Exists(filePath)) - { - //---数据模板文件存储路径上未找对应文件,请联系系统运维人员。 - throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid")); - } - - return (filePath, systemClinicalData.FileName); - } - - - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task<(string PhysicalPath, string FileName)> GetTrialClinicalPathAsync(IWebHostEnvironment _hostEnvironment, IRepository _clinicalDataTrialSetRepository, Guid id) - { - var trialClinicalData = await _clinicalDataTrialSetRepository.FirstOrDefaultAsync(t => t.Id == id); - - if (trialClinicalData == null|| trialClinicalData.Path==string.Empty) - { - //---数据库没有找到对应的数据模板文件,请联系系统运维人员。 - throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileNotFound")); - } - - var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, trialClinicalData.Path); - - if (!System.IO.File.Exists(filePath)) - { - //---数据模板文件存储路径上未找对应文件,请联系系统运维人员。 - throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid")); - } - - return (filePath, trialClinicalData.FileName); - } - //通过编码获取通用文档具体物理路径 @@ -194,6 +136,7 @@ public static class FileStoreHelper { var doc = await _commonDocumentRepository.FirstOrDefaultAsync(t => t.Code == code); + var isEn_US = CultureInfo.CurrentCulture.Name!= "zh-CN"; if (doc == null) { //---数据库没有找到对应的数据模板文件,请联系系统运维人员。 @@ -208,7 +151,7 @@ public static class FileStoreHelper throw new BusinessValidationFailedException(StaticData.International("FileStore_TemplateFileStoragePathInvalid")); } - return (filePath, doc.Name.Trim('/')); + return (filePath, isEn_US? doc.Name.Trim('/'): doc.NameCN.Trim('/')); } @@ -235,44 +178,6 @@ public static class FileStoreHelper return writeCount; } - // 获取项目签名文档存放路径 - - public static (string PhysicalPath, string RelativePath) GetTrialSignDocPath(IWebHostEnvironment _hostEnvironment, Guid trialId, string fileName) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.SignDocumentFolder); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } - - // 获取系统签名文档存放路径 - public static (string PhysicalPath, string RelativePath) GetSystemSignDocPath(IWebHostEnvironment _hostEnvironment, string fileName) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.SignDocumentFolder); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{ StaticData.Folder.SignDocumentFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } // 获取通用文档存放路径(excel模板 ) @@ -295,224 +200,6 @@ public static class FileStoreHelper return (serverFilePath, relativePath); } - //获取系统通知文档存放路径 - - public static (string PhysicalPath, string RelativePath) GetSystemNoticePath(IWebHostEnvironment _hostEnvironment, string fileName) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.SystemDataFolder, StaticData.Folder.NoticeAttachment); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.SystemDataFolder}/{StaticData.Folder.NoticeAttachment}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } - - // 获取一致性核查路径 - public static (string PhysicalPath, string RelativePath) GetTrialCheckFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //上传根路径 - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), StaticData.Folder.UploadEDCData); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - //存放核对表 - var (trustedFileNameForFileStorage, realFileName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{StaticData.Folder.UploadEDCData}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } - - - public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalTemplatePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId) - { - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, "ClinicalTemplate", trialId.ToString(), StaticData.Folder.TreatmenthistoryFolder); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/ClinicalTemplate/{trialId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - - } - - - public static (string PhysicalPath, string RelativePath, string FileRealName) GetReadClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid readingId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), StaticData.Folder.Reading, readingId.ToString()); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{StaticData.Folder.Reading}/{readingId}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - - } - - //获取临床数据存放路径 - public static (string PhysicalPath, string RelativePath,string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName,Guid trialId,Guid siteId,Guid subjectId,Guid subjectVisitId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(),siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.TreatmenthistoryFolder); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - - } - - - public static (string PhysicalPath, string RelativePath, string FileRealName) GetClinicalDataPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId , Guid subjectId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), subjectId.ToString(), StaticData.Folder.TreatmenthistoryFolder); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{subjectId}/{StaticData.Folder.TreatmenthistoryFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - - } - - - /// - /// 上传截图 - /// - /// - /// - /// - /// - /// - /// - public static (string PhysicalPath, string RelativePath, string FileRealName) GetUploadPrintscreenFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteid, Guid subjectId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteid.ToString(), subjectId.ToString()); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteid}/{subjectId}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - } - - - /// - /// 通用获取文件路径 - /// - /// - /// - /// - /// - /// - /// - public static (string PhysicalPath, string RelativePath, string FileRealName) GetFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid id,string type) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), id.ToString(), type); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{id}/{type}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - } - - public static (string PhysicalPath, string RelativePath, string FileRealName) GetMedicalReviewImage(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid taskMedicalReviewId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), taskMedicalReviewId.ToString(), StaticData.Folder.MedicalReview); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{taskMedicalReviewId}/{StaticData.Folder.MedicalReview}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - } - - //获取非dicom文件存放路径 - public static (string PhysicalPath, string RelativePath, string FileRealName) GetNoneDicomFilePath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid siteId, Guid subjectId, Guid subjectVisitId) - { - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - string uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.TrialDataFolder, trialId.ToString(), siteId.ToString(), subjectId.ToString(), subjectVisitId.ToString(), StaticData.Folder.NoneDicomFolder); - - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.TrialDataFolder}/{trialId}/{siteId}/{subjectId}/{subjectVisitId}/{StaticData.Folder.NoneDicomFolder}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath, fileRealName); - } - // 获取 入组确认 PD 进展发送邮件Word|PDF 存放路径 public static (string PhysicalPath, string RelativePath, string FileRealName) GetSubjectEnrollConfirmOrPDEmailPath(IWebHostEnvironment _hostEnvironment, string fileName, Guid trialId, Guid trialSiteId, Guid subjectId,bool isChangeToPdfFormat=false) @@ -629,49 +316,7 @@ public static class FileStoreHelper - // 获取医生通用文件存放路径 - public static (string PhysicalPath, string RelativePath) GetDoctorOrdinaryFilePath(IWebHostEnvironment _hostEnvironment, string fileName,Guid doctorId,string attachmentType) - { - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(rootPath, "UploadFile", doctorId.ToString(), attachmentType); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{doctorId}/{attachmentType}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } - - public static (string PhysicalPath, string RelativePath) GetNonDoctorFilePath(IWebHostEnvironment _hostEnvironment, string fileName, string attachmentType) - { - - var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); - - //文件类型路径处理 - var uploadFolderPath = Path.Combine(rootPath, StaticData.Folder.UploadFileFolder, attachmentType); - if (!Directory.Exists(uploadFolderPath)) Directory.CreateDirectory(uploadFolderPath); - - - var (trustedFileNameForFileStorage, fileRealName) = FileStoreHelper.GetStoreFileName(fileName); - - - - var relativePath = $"/{StaticData.Folder.IRaCISDataFolder}/{StaticData.Folder.UploadFileFolder}/{attachmentType}/{trustedFileNameForFileStorage}"; - - var serverFilePath = Path.Combine(uploadFolderPath, trustedFileNameForFileStorage); - - return (serverFilePath, relativePath); - } diff --git a/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs b/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs index 5d3bfb633..910f35e14 100644 --- a/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs +++ b/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs @@ -9,7 +9,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using static BeetleX.Redis.Commands.HSCAN; using static IRaCIS.Core.Application.Service.Common.SystemMonitor; namespace IRaCIS.Core.Application.Helper @@ -18,6 +17,9 @@ namespace IRaCIS.Core.Application.Helper { public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources); + public static FileSystemWatcher FileSystemWatcher_US { get; set; } + public static FileSystemWatcher FileSystemWatcher_CN { get; set; } + private static void VerifyFolder() { if (!Directory.Exists(JsonFileFolder) || @@ -51,6 +53,9 @@ namespace IRaCIS.Core.Application.Helper jsonObject[item.Code] = item.Value; StaticData.En_US_Dic[item.Code] = item.Value; + + //日志记录该信息方便自己人看, 返回给客户的是配置的 + StaticData.Log_Locoalize_Dic[item.Code] = item.Description; } } else @@ -69,7 +74,7 @@ namespace IRaCIS.Core.Application.Helper } } - public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN) + public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN,string description) { VerifyFolder(); @@ -92,6 +97,9 @@ namespace IRaCIS.Core.Application.Helper jsonObject[key] = value; StaticData.En_US_Dic[key] = value; + + //日志记录该信息方便自己人看, 返回给客户的是配置的 + StaticData.Log_Locoalize_Dic[key] = description; } else { @@ -107,14 +115,16 @@ namespace IRaCIS.Core.Application.Helper } - public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository _repository) + + public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository _internationalizationRepository) { //查询数据库的数据 - var toJsonList = await _repository.Where(t => t.InternationalizationType == 1).Select(t => new + var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new { t.Code, t.Value, - t.ValueCN + t.ValueCN, + t.Description }).ToListAsync(); //组织成json 文件 @@ -128,6 +138,9 @@ namespace IRaCIS.Core.Application.Helper { StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value; StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN; + + //日志记录该信息方便自己人看, 返回给客户的是配置的 + StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description; } File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic)); @@ -136,29 +149,45 @@ namespace IRaCIS.Core.Application.Helper //监测Json文件变更 实时刷新数据 - WatchJsonFile(usJsonPath); - WatchJsonFile(cnJsonPath); - - } - - public static void WatchJsonFile(string filePath) - { - if (!File.Exists(filePath)) + if (!File.Exists(usJsonPath) || !File.Exists(cnJsonPath)) { - throw new BusinessValidationFailedException("国际化Json文件不存在"); + throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound")); } - FileSystemWatcher watcher = new FileSystemWatcher(Path.GetDirectoryName(filePath), Path.GetFileName(filePath)); - watcher.Changed += (sender, e) => LoadJsonFile(filePath); - watcher.EnableRaisingEvents = true; + + // //监测Json文件变更 实时刷新数据 + + + FileSystemWatcher_US = new FileSystemWatcher + { + Path = Path.GetDirectoryName(usJsonPath)!, + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size, + Filter = Path.GetFileName(usJsonPath), + EnableRaisingEvents = true, + + }; + // 添加文件更改事件的处理程序 + FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json); + + + FileSystemWatcher_CN = new FileSystemWatcher + { + Path = Path.GetDirectoryName(cnJsonPath)!, + NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size, + Filter = Path.GetFileName(cnJsonPath), + EnableRaisingEvents = true, + + }; + FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json); } + private static void LoadJsonFile(string filePath) { - - IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath); + Console.WriteLine("刷新json内存数据"); + IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false); IConfigurationRoot enConfiguration = builder.Build(); @@ -175,5 +204,11 @@ namespace IRaCIS.Core.Application.Helper } } } + + + + + + } } diff --git a/IRaCIS.Core.Application/Helper/OSSService.cs b/IRaCIS.Core.Application/Helper/OSSService.cs index 9d5a754ad..a50c92da3 100644 --- a/IRaCIS.Core.Application/Helper/OSSService.cs +++ b/IRaCIS.Core.Application/Helper/OSSService.cs @@ -1,11 +1,9 @@ using Aliyun.OSS; -using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Infrastructure; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Options; using Minio.DataModel.Args; using Minio; -using NPOI.HPSF; using SharpCompress.Common; using System; using System.Collections.Generic; @@ -15,6 +13,7 @@ using System.Linq; using System.Security.AccessControl; using System.Text; using System.Threading.Tasks; +using System.Runtime.InteropServices; namespace IRaCIS.Core.Application.Helper { @@ -134,66 +133,76 @@ namespace IRaCIS.Core.Application.Helper //var ossRelativePath = $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}"; //var ossRelativePath = oosFolderPath + "/" + fileRealName; - using (var memoryStream = new MemoryStream()) + try { - fileStream.Seek(0, SeekOrigin.Begin); - - fileStream.CopyTo(memoryStream); - - memoryStream.Seek(0, SeekOrigin.Begin); - - - if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + using (var memoryStream = new MemoryStream()) { - var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + fileStream.Seek(0, SeekOrigin.Begin); - var _ossClient = new OssClient(aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + fileStream.CopyTo(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + { + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + + var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); - // 上传文件 - var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream); + // 上传文件 + var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream); - } - else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") - { - var minIOConfig = ObjectStoreServiceOptions.MinIO; + } + 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 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); + 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; + 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 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); + var putObjectArgs = new PutObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); - await minioClient.PutObjectAsync(putObjectArgs); - } - else - { - throw new BusinessValidationFailedException("未定义的存储介质类型"); + await minioClient.PutObjectAsync(putObjectArgs); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } } } + catch (Exception ex) + { + + throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); + } + + return "/" + ossRelativePath; @@ -223,7 +232,7 @@ namespace IRaCIS.Core.Application.Helper { var aliConfig = ObjectStoreServiceOptions.AliyunOSS; - var _ossClient = new OssClient(aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)? aliConfig.endPoint: aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); // 上传文件 var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath); @@ -282,7 +291,7 @@ namespace IRaCIS.Core.Application.Helper { var aliConfig = ObjectStoreServiceOptions.AliyunOSS; - var _ossClient = new OssClient(aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); // 上传文件 var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath); @@ -354,7 +363,7 @@ namespace IRaCIS.Core.Application.Helper { var aliConfig = ObjectStoreServiceOptions.AliyunOSS; - var _ossClient = new OssClient(aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.endPoint : aliConfig.internalEndpoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); // 生成签名URL。 var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get) @@ -375,10 +384,6 @@ namespace IRaCIS.Core.Application.Helper .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) @@ -404,7 +409,19 @@ namespace IRaCIS.Core.Application.Helper .WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL) .Build(); - return string.Empty; + var args = new PresignedGetObjectArgs() + .WithBucket(minIOConfig.bucketName) + .WithObject(ossRelativePath) + .WithExpiry(3600); + + var presignedUrl = await minioClient.PresignedGetObjectAsync(args); + + Uri uri = new Uri(presignedUrl); + + string relativePath = uri.PathAndQuery; + + + return relativePath; } else { diff --git a/IRaCIS.Core.Application/Helper/RSAHelper.cs b/IRaCIS.Core.Application/Helper/RSAHelper.cs new file mode 100644 index 000000000..fb5309691 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/RSAHelper.cs @@ -0,0 +1,97 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Encodings; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; + +namespace IRaCIS.Core.Application.Helper +{ + /// + /// https://www.cnblogs.com/NBDWDYS2214143926/p/13329231.html + /// + public class RSAHelper + { + + public static AsymmetricCipherKeyPair GenerateRSAKeyPair(int keySize) + { + var keyGenerationParameters = new KeyGenerationParameters(new SecureRandom(), keySize); + var keyPairGenerator = new RsaKeyPairGenerator(); + keyPairGenerator.Init(keyGenerationParameters); + return keyPairGenerator.GenerateKeyPair(); + } + + public static string ExportPublicKey(AsymmetricKeyParameter publicKey) + { + using (StringWriter sw = new StringWriter()) + { + PemWriter pw = new PemWriter(sw); + pw.WriteObject(publicKey); + pw.Writer.Flush(); + return sw.ToString(); + } + } + + public static string ExportPrivateKey(AsymmetricKeyParameter privateKey) + { + using (StringWriter sw = new StringWriter()) + { + PemWriter pw = new PemWriter(sw); + pw.WriteObject(privateKey); + pw.Writer.Flush(); + return sw.ToString(); + } + } + + /// + /// RSA解密 + /// + /// 私钥 + /// 待解密的字符串(Base64) + /// 解密后的字符串 + public static string Decrypt(string privateKey, string decryptstring) + { + using (TextReader reader = new StringReader(privateKey)) + { + dynamic key = new PemReader(reader).ReadObject(); + var rsaDecrypt = new Pkcs1Encoding(new RsaEngine()); + if (key is AsymmetricKeyParameter) + { + key = (AsymmetricKeyParameter)key; + } + else if (key is AsymmetricCipherKeyPair) + { + key = ((AsymmetricCipherKeyPair)key).Private; + } + rsaDecrypt.Init(false, key); //这里加密是true;解密是false + + byte[] entData = Convert.FromBase64String(decryptstring); + entData = rsaDecrypt.ProcessBlock(entData, 0, entData.Length); + return Encoding.UTF8.GetString(entData); + } + }/// + + /// 加密 + /// + /// 公钥 + /// 待加密的字符串 + /// 加密后的Base64 + public static string Encrypt(string publicKey, string encryptstring) + { + using (TextReader reader = new StringReader(publicKey)) + { + AsymmetricKeyParameter key = new PemReader(reader).ReadObject() as AsymmetricKeyParameter; + Pkcs1Encoding pkcs1 = new Pkcs1Encoding(new RsaEngine()); + pkcs1.Init(true, key);//加密是true;解密是false; + byte[] entData = Encoding.UTF8.GetBytes(encryptstring); + entData = pkcs1.ProcessBlock(entData, 0, entData.Length); + return Convert.ToBase64String(entData); + } + } + } +} diff --git a/IRaCIS.Core.Application/Helper/SendEmailHelper.cs b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs index 974a60f02..7a223b275 100644 --- a/IRaCIS.Core.Application/Helper/SendEmailHelper.cs +++ b/IRaCIS.Core.Application/Helper/SendEmailHelper.cs @@ -52,15 +52,19 @@ public static class SendEmailHelper public static async Task TestEmailConfigAsync(SystemEmailSendConfig _systemEmailConfig) { - using (var client = new MailKit.Net.Smtp.SmtpClient()) + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5))) { + using (var client = new MailKit.Net.Smtp.SmtpClient()) + { - await client.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto); + await client.ConnectAsync(_systemEmailConfig.Host, _systemEmailConfig.Port, SecureSocketOptions.Auto, cts.Token); - await client.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode); + await client.AuthenticateAsync(_systemEmailConfig.FromEmail, _systemEmailConfig.AuthorizationCode, cts.Token); - await client.DisconnectAsync(true); + await client.DisconnectAsync(true); + } } + return true; } diff --git a/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs b/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs new file mode 100644 index 000000000..9f605be81 --- /dev/null +++ b/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs @@ -0,0 +1,101 @@ +using IRaCIS.Core.Domain.Share; +using NPOI.XWPF.UserModel; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xceed.Document.NET; +using Xceed.Words.NET; + +namespace IRaCIS.Core.Application.Helper +{ + /// + /// 利用DocX 库 处理word国际化模板 + /// + public static class WordTempleteHelper + { + public static void DocX_GetInternationalTempleteStream(string filePath, Stream memoryStream) + { + + var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; + + using (DocX document = DocX.Load(filePath)) + { + // 查找书签 + var bookmarkEn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == StaticData.CultureInfo.en_US_bookMark); + + if (bookmarkEn_Start != null) + { + // 获取书签的起始位置 + //int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex; + + var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex; + + // 创建一个要删除段落的列表 + List paragraphsToRemove = new List(); + + foreach (var item in document.Paragraphs) + { + //中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的 + + if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos) + { + paragraphsToRemove.Add(item); + } + } + + foreach (var paragraph in paragraphsToRemove) + { + document.RemoveParagraph(paragraph); + } + + } + + // 保存修改 + document.SaveAs(memoryStream); + } + } + + + public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream) + { + + var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; + + using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) + { + XWPFDocument doc = new XWPFDocument(fs); + + // 查找包含指定书签的段落及其索引 + var bookmarkParagraph = doc.Paragraphs + .FirstOrDefault(p => p.GetCTP().GetBookmarkStartList().Any(b => b.name == StaticData.CultureInfo.en_US_bookMark)); + + if (bookmarkParagraph != null) + { + int bookmarkIndex = doc.Paragraphs.IndexOf(bookmarkParagraph); + + if (isEn_US) + { + // 从书签所在段落开始,删除之前的所有段落 + for (int i = bookmarkIndex - 1; i >= 0; i--) + { + doc.RemoveBodyElement(i); + } + } + else + { + // 删除书签之后的所有段落 + for (int i = doc.Paragraphs.Count - 1; i >= bookmarkIndex; i--) + { + doc.RemoveBodyElement(i); + } + } + } + doc.Write(memoryStream); + } + } + + } +} diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj index 72dc549f8..ab00314fc 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.csproj @@ -42,7 +42,7 @@ - + @@ -50,11 +50,6 @@ - - PreserveNewest - true - PreserveNewest - Always true @@ -63,10 +58,10 @@ - + + - - + @@ -74,22 +69,22 @@ - - - + + + - + true - + - + diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 2250120e5..36374a2b0 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -103,6 +103,25 @@ + + + https://www.cnblogs.com/NBDWDYS2214143926/p/13329231.html + + + + + RSA解密 + + 私钥 + 待解密的字符串(Base64) + 解密后的字符串 + + + + + 利用DocX 库 处理word国际化模板 + + 分配规则 @@ -214,14 +233,6 @@ - - - 随机算法,选择指定数量的 subject - - - - - 医学审核生成规则 废弃 @@ -408,7 +419,7 @@ PM 重阅追踪 - + @@ -537,11 +548,43 @@ + + + 项目列表导出---new + + + + + + + + + + getDocumentConfirmList 培训记录导出--new + + + + + + + + 影像上传列表 只导出已上传状态的访视记录 - + + + + + + + + + + 影像质控导出---new + + @@ -552,18 +595,19 @@ 质疑列表 - + - + 受试者信息导出表 - + + @@ -582,7 +626,7 @@ 影像上传监控表 - + @@ -612,7 +656,7 @@ 一致性核查记录表 - + @@ -629,22 +673,24 @@ - + PM 重阅追踪 - + + - + PM 医学审核(挑选任务生成后的列表) + @@ -707,6 +753,11 @@ + + + ExploreRecommendService + + InternationalizationService @@ -738,6 +789,13 @@ + + + 批量更新状态和发布版本 + + + + PublishLogService @@ -871,21 +929,33 @@ - + 打包和匿名化影像 默认是匿名化打包,也可以不匿名化打包 + - + + + 受试者级别所有的影像 + 访视级别的影响 传递subjectVisitId + 标准Id是可选的 不同标准有些检查可能有过滤 + + + + + + 后台任务调用,前端忽略该接口 + @@ -1287,6 +1357,18 @@ 获取登陆用户的系统通知列表 只是过滤了用户类型 和已经发布的 + + + UserFeedBackService + + + + + 批量更新状态 + + + + 验证CRC 是否已提交 已提交 就不允许进行任何操作,如果是IQC 那么还验证是否是当前任务领取人 @@ -2309,6 +2391,534 @@ + + + 获取阅片的计算数据 + + + + + + + 获取Sod的值 + + + + + 基线任务Id + + + + + 影像是否无法融合 + + + + + 删除病灶获取起始病灶序号(RECIST1Point1 固定是1) + + + + + + 获取阅片报告 + + + + + + + 获取是否是最大suvmax 病灶 + + + + + + + 获取是否可选择不能融合影像 + + + + + + + 测试计算 + + + + + + + + 获取最低PDD信息 + + + + + + + 计算任务 + + + + + + + 获取报告验证的信息(这里每个标准可能不一样 返回用object) + + + + + + + 自动计算 + + + + + + + + 获取影像是否无法融合 + + + + + + + 获取报告整体整体评估 + + + + + + + 获取报告是否存在疾病 + + + + + + + 验证访视提交 + + + + + + + 计算融合靶病灶的状态 + + + + + + + 计算分裂靶病灶状态 + + + + + + + 获取分裂病灶的PPd之和 不包括当前的主病灶 + + + + + + 将上一次的访视病灶添加到这一次 + + + + + + + 获取SOD + + + 靶病灶径线之和(SOD) + 非淋巴结的长径 和淋巴结的短径 + + + + + + 非淋巴结靶病灶长径之和 + + + + + + + 与基线SOD相比变化量(mm) + + + + + + + 与基线访视相比SOD变化百分比 + + + + + + + 与整个访视期间SOD最低点相比增加的值(mm) + + + + 要更新之前的 + + + + + + 与整个访视期间SOD最低点相比增加的百分比 + + + + 要更新之前的 + + + + + + 整个访视期间SOD最低点访视名称 + + + + 要更新之前的 + + + + + + 是否存在非淋巴结靶病灶 + + + + + + + 是否存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上 + + + + + + + 获取存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上的病灶 + + + + + + + 被评估为NE的单个靶病灶 + + + + + + + 整体肿瘤评估 + + + + + + + 是否存在疾病 + + + + + + + 修改最低方式点名称 + + + + + + + 修改所有访视任务的答案 + + + + + + + + + 获取基线任务的Id + + + + + + + 获取基线SOD + + + + + + + 获取最低方式 + + + + + + + 获取访视任务信息 + + + + + + + 获取上一个访视任务Id + + + + + + 获取SPD + + + + + + + 获取脾脏垂直径 + + + + + + + 与基线相比SPD变化的百分比 + + + + + + + 与基线相比脾肿大增加的百分比 + [(当前垂直径-130)-(基线垂直径-130)]/(基线垂直径-130) + + + + + + + 与最低点相比脾脏垂直径长度的增加值 + + + + + + + 脾肿垂直径最低点访视 + + + + + + + 获取脾脏状态 + + + + + + + + 获取最低垂直径 + + + + + + 获取脾脏验证 + + + + + + + 获取脾脏状态 + + + + + + + 获取脾脏评估 + + + + + + + 获取PET5PS评分 + + + + + + + 获取PET5PS评分备注 + + + + + + + 与基线相比摄取值变化 + + + + + + + 获取上一次FDGPET 评估 + + + + + + + FDG-PET总体评估结果 + + + + + + + 计算FDG-PET总体评估结果【测试】 + + PET5PS评分 + 与基线相比摄取值变化 + 骨髓中是否存在局灶性 FDG亲和病灶的证据 + + + + + 骨髓中是否存在局灶性 FDG亲和病灶的证据 + + + + + + + 获取肝脏评估 + + + + + + + SuvMax所在病灶 + + + + + + + 最大sum + + + + + + + 是否存在PET + + + + + + + 影像学整体肿瘤评估 + + + + + + + 计算整体肿瘤评估 【测试】 + + CT/MRI总体评估 + FDG-PET总体评估 + 上一次 FDG-PET总体评估 + + + + + 获取基线脾脏长度 + + + + + + + 与最低点相比脾脏垂直径长度的增加值 + + + + + + + 获取靶病灶评估 + + + + + + + 获取非靶病灶评估 + + + + + + + 获取新病灶评估 + + + + + + + CTMRI 总体评估 + + + + + + + 计算CTMRI 总体评估 【测试】 + + 靶病灶评估 + 非靶病灶评估 + 存在新病灶 + 肝脏评估 + 脾脏评估 + + + + + 保留小数 + + + + + 获取阅片的计算数据 @@ -4632,6 +5242,16 @@ 是否是正在转化 + + + 是否盲化 + + + + + 是否完整 + + 项目ID @@ -4727,6 +5347,16 @@ 路径 + + + 大小 + + + + + 文件类型 + + 临床数据类型Id @@ -4747,6 +5377,11 @@ 只查询已经签名的临床数据 + + + 是否查询所有的一致性分析临床数据 (为否只查询PDF) + + 获取访视列表 @@ -4927,6 +5562,16 @@ FileName + + + 大小 + + + + + 文件类型 + + 上传时间 @@ -5087,6 +5732,11 @@ 单位 + + + 高亮问题的答案 + + 问题英文分组 @@ -6377,6 +7027,11 @@ 是否申请重阅 + + + 项目Id + + 项目Id @@ -6412,6 +7067,11 @@ 无效的 为True无效 + + + 获取不加急的 + + 医学审核对话关闭原因 @@ -8068,6 +8728,11 @@ 关联Value + + + 高亮问题的答案 + + 类型 @@ -8168,6 +8833,11 @@ Id + + + 高亮问题的答案 + + 分类算法 @@ -8692,6 +9362,14 @@ + + + 获取下一个IR医学反馈 + + + + + 获取IR医学审核信息 @@ -9235,6 +9913,24 @@ + + + DicomAEService + + + + + 获取项目dicom AE 配置信息,otherinfo里面有IsPACSConnect IsTrialPACSConfirmed + + + + + + + 测试scp server 是否可以连接 + + + 项目外部人员 录入流程相关 @@ -9247,6 +9943,11 @@ + + + TrialSiteDicomAEService + + TaskAllocationRuleView 列表视图模型 @@ -9387,6 +10088,15 @@ CommonDocumentAddOrEdit 列表查询参数模型 + + ExploreRecommendView 列表视图模型 + + + ExploreRecommendQuery 列表查询参数模型 + + + ExploreRecommendAddOrEdit 列表查询参数模型 + FrontAuditConfigView 列表视图模型 @@ -9625,6 +10335,15 @@ SystemNoticeAddOrEdit 列表查询参数模型 + + UserFeedBackView 列表视图模型 + + + UserFeedBackQuery 列表查询参数模型 + + + UserFeedBackAddOrEdit 列表查询参数模型 + UserLogView 列表视图模型 @@ -10386,6 +11105,15 @@ UserWLTemplateAddOrEdit 列表查询参数模型 + + DicomAEView 列表视图模型 + + + DicomAEQuery 列表查询参数模型 + + + DicomAEAddOrEdit 列表查询参数模型 + TrialExternalUserView 列表视图模型 @@ -10395,6 +11123,15 @@ TrialExternalUserAddOrEdit 列表查询参数模型 + + TrialSiteDicomAEView 列表视图模型 + + + TrialSiteDicomAEQuery 列表查询参数模型 + + + TrialSiteDicomAEAddOrEdit 列表查询参数模型 + TrialUserPreparation View 列表视图模型 @@ -10429,6 +11166,11 @@ ICommonDocumentService + + + IExploreRecommendService + + IFrontAuditConfigService @@ -10459,6 +11201,11 @@ ISystemNoticeService + + + IUserFeedBackService + + IUserLogService @@ -10479,11 +11226,21 @@ IOrganInfoService + + + IDicomAEService + + ITrialExternalUserService + + + ITrialSiteDicomAEService + + EmailNoticeConfigView 列表视图模型 @@ -10787,6 +11544,11 @@ 组件一致性和原Arm2是否有差异 + + + QCChallengeId + + @@ -11609,7 +12371,7 @@ 获取确认列表情况 项目文档+系统文档+具体的人 - + @@ -11780,14 +12542,22 @@ CRC 访视上传列表 - + + + + 获取下一个crc的未关闭的质疑 + + + + + CRC 质疑列表 - + @@ -11808,14 +12578,14 @@ QC 访视列表 - + 获取一致性核查列表 CRC/PM 公用 - + @@ -12454,6 +13224,13 @@ + + + 配置pacs信息 + + + + 更新项目状态 @@ -12731,7 +13508,7 @@ - 发送验证码 + Reviewer简历录入 发送验证码 @@ -13403,6 +14180,13 @@ 影像阅片临床数据签名 + + + 一致性分析的临床数据 + + + + 获取下拉菜单 @@ -13424,10 +14208,32 @@ - + - 获取临床数据集合 + 获取阅片临床数据列表 (在任务列表) + + + + + + 新增或修改一致性分析临床数据 + + + + + + + 一致性分析临床数据签名 + + + + + + + 一致性分析临床数据签名完设置任务为有效 + + @@ -13964,6 +14770,19 @@ + + + 清除跳过阅片的缓存 + + + + + + 设置跳过阅片的缓存 + + + + 获取下一个阅片任务 @@ -14392,6 +15211,41 @@ 9-拒绝入组,10-确认入组 + + + scp 影像推送记录表 + + + + + + + 影像检查列表-患者为维度组织 + + + + + + + 影像检查列表-> 获取患者的检查列表 + + + + + + + 影像访视上传 检查列表 + + + + + + + 提交 患者检查和访视的绑定 + + + + 添加或更新受试者信息[New] @@ -14401,7 +15255,7 @@ 分页获取受试者列表[New] - /// state:1-访视中,2-出组。0-全部 + /// state:1-访视中,2-出组。0-全部 @@ -14529,6 +15383,13 @@ + + + 清理一致性分析任务 + + + + 维护临床数据 --一定要在同步表前同步数据才行 diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs index a77887387..0a566607e 100644 --- a/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/TaskConsistentRuleViewModel.cs @@ -107,7 +107,7 @@ namespace IRaCIS.Core.Application.ViewModel public class DoctorSelfConsistentSubjectView: ConsistentCommonView { - + public string FirstGlobalVisitName { get; set; } = string.Empty; public string? BlindSubjectCode { get; set; } public List VisitTaskList { get; set; } @@ -183,7 +183,8 @@ namespace IRaCIS.Core.Application.ViewModel public string TaskCode { get; set; } - + + [JsonIgnore] //可以不查询 public List GlobalVisitTaskList { get; set; } diff --git a/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs b/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs index 27bcb5d88..302351e3d 100644 --- a/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs +++ b/IRaCIS.Core.Application/Service/Allocation/DTO/VisitTaskViewModel.cs @@ -201,6 +201,8 @@ namespace IRaCIS.Core.Application.ViewModel public Guid? SourceSubjectVisitId { get; set; } public Guid? SouceReadModuleId { get; set; } + + public bool IsHaveFeedBack { get; set; } //public bool IsAfterConvertedTask { get; set; } } diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs index dbb8aa148..440aefe37 100644 --- a/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/TaskConsistentRuleService.cs @@ -19,6 +19,7 @@ using Medallion.Threading; using IRaCIS.Core.Infrastructure.Extention; using System; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NPOI.SS.Formula.Functions; namespace IRaCIS.Core.Application.Service { @@ -87,7 +88,7 @@ namespace IRaCIS.Core.Application.Service /// /// [HttpPost] - public async Task>> GetAnalysisTaskList(VisitTaskQuery queryVisitTask ) + public async Task>> GetAnalysisTaskList(VisitTaskQuery queryVisitTask) { var visitTaskQueryable = _visitTaskRepository.Where(t => t.TrialId == queryVisitTask.TrialId) .Where(t => t.IsAnalysisCreate) @@ -106,7 +107,7 @@ namespace IRaCIS.Core.Application.Service .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => /*(t.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.IsAnalysisCreate) ||*/ (t.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) /*&& t.IsAnalysisCreate == false*/)) .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.TaskName.Contains(queryVisitTask.TaskName) || t.TaskBlindName.Contains(queryVisitTask.TaskName)) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => ((t.Subject.Code.Contains(queryVisitTask.SubjectCode)||t.Subject.MedicalNo.Contains(queryVisitTask.SubjectCode)) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) + .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => ((t.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.Subject.MedicalNo.Contains(queryVisitTask.SubjectCode)) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime >= queryVisitTask.BeginAllocateDate) .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime <= queryVisitTask.EndAllocateDate) .WhereIf(queryVisitTask.BeginSignTime != null, t => t.SignTime >= queryVisitTask.BeginSignTime) @@ -117,7 +118,7 @@ namespace IRaCIS.Core.Application.Service var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); - var trialTaskConfig = _repository.Where(t => t.Id == queryVisitTask.TrialId).Select(t => new { IsHaveDoubleReadCriterion=t.TrialReadingCriterionList.Any(t=>t.IsSigned && t.IsConfirm && t.ReadingType==ReadingMethod.Double), t.VitrualSiteCode }).FirstOrDefault(); + var trialTaskConfig = _repository.Where(t => t.Id == queryVisitTask.TrialId).Select(t => new { IsHaveDoubleReadCriterion = t.TrialReadingCriterionList.Any(t => t.IsSigned && t.IsConfirm && t.ReadingType == ReadingMethod.Double), t.VitrualSiteCode }).FirstOrDefault(); return ResponseOutput.Ok(pageList, trialTaskConfig); } @@ -153,7 +154,7 @@ namespace IRaCIS.Core.Application.Service { - var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.Id == inCommand.TaskConsistentRuleId); + var filterObj = await _taskConsistentRuleRepository.Where(t => t.Id == inCommand.TaskConsistentRuleId).Include(t => t.TrialReadingCriterion).FirstOrDefaultAsync(); var doctorUserId = inCommand.DoctorUserId; var trialReadingCriterionId = filterObj.TrialReadingCriterionId; @@ -168,19 +169,17 @@ namespace IRaCIS.Core.Application.Service throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]); } - if (subjectList.Count<2 * filterObj.PlanSubjectCount) + if (subjectList.Count < 2 * filterObj.PlanSubjectCount) { throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate1"]); } - inCommand.SubejctIdList = GetRandomSubjectIdList(subjectList.Select(t => t.SubjectId).ToList(), filterObj.PlanSubjectCount); + inCommand.SubejctIdList = subjectList.Select(t => t.SubjectId).ToList().GetRandomCountList(filterObj.PlanSubjectCount); } var list = await GetIQueryableDoctorSelfConsistentSubjectView(filterObj, doctorUserId, inCommand.SubejctIdList).ToListAsync(); - //var (group, query) = GetIQueryableDoctorSelfConsistentRuleSubjectView(filterObj, inCommand.SubejctIdList); - //var list = query.OrderByDescending(t => t.IsHaveGeneratedTask).ToList(); var @lock = _distributedLockProvider.CreateLock($"VisitTaskCode"); @@ -213,7 +212,18 @@ namespace IRaCIS.Core.Application.Service blindSubjectCode = filterObj.BlindTrialSiteCode + (maxCodeInt + 1).ToString($"D{filterObj.BlindSubjectNumberOfPlaces}"); } - subject.VisitTaskList = subject.VisitTaskList.Take(filterObj.PlanVisitCount).ToList(); + //有序阅片 + if (filterObj.TrialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) + { + subject.VisitTaskList = subject.VisitTaskList.Take(filterObj.PlanVisitCount).ToList(); + + } + else + { + //完全随机 和受试者随机,都是随机挑选访视 + subject.VisitTaskList = subject.VisitTaskList.GetRandomCountList(filterObj.PlanVisitCount); + } + subject.VisitTaskList.ForEach(t => { @@ -244,6 +254,17 @@ namespace IRaCIS.Core.Application.Service TaskBlindName = lastTask.TaskBlindName + "_Global", TrialReadingCriterionId = trialReadingCriterionId, }; + + var afterGlobal = _visitTaskRepository.Where(t => t.SubjectId == lastTask.SubjectId && t.TrialReadingCriterionId == trialReadingCriterionId && t.TaskState == TaskState.Effect && t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > lastTask.VisitTaskNum).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + + if (afterGlobal == null) + { + throw new BusinessValidationFailedException("联系后台排查数据,没找到后续全局"); + } + else + { + existGlobal.SouceReadModuleId = afterGlobal.SouceReadModuleId; + } } @@ -284,7 +305,7 @@ namespace IRaCIS.Core.Application.Service /// /// [HttpPost] - public async Task> > GetGroupConsistentRuleSubjectList(GroupConsistentQuery inQuery) + public async Task>> GetGroupConsistentRuleSubjectList(GroupConsistentQuery inQuery) { var trialId = inQuery.TrialId; @@ -294,7 +315,7 @@ namespace IRaCIS.Core.Application.Service if (filterObj == null) { object tt = null; - return ResponseOutput.Ok(new PageOutput(), new { Rule = tt, IsAllowAutoAllocate = false }) ; + return ResponseOutput.Ok(new PageOutput(), new { Rule = tt, IsAllowAutoAllocate = false }); } var query = await GetGroupConsistentQueryAsync(filterObj); @@ -306,9 +327,9 @@ namespace IRaCIS.Core.Application.Service var list = await GetGroupConsistentRuleMatchSubjectIdListAsync(new GroupConsistentSimpleQuery() { TrialId = inQuery.TrialId, TrialReadingCriterionId = inQuery.TrialReadingCriterionId }); - var isAllowAutoAllocate = !list.Any(t => t.IsHaveGeneratedTask) && list.Count() > 2*(rule?.PlanSubjectCount??0); + var isAllowAutoAllocate = !list.Any(t => t.IsHaveGeneratedTask) && list.Count() > 2 * (rule?.PlanSubjectCount ?? 0); - return ResponseOutput.Ok(pagedList, new {Rule=rule, IsAllowAutoAllocate = isAllowAutoAllocate }); + return ResponseOutput.Ok(pagedList, new { Rule = rule, IsAllowAutoAllocate = isAllowAutoAllocate }); } @@ -326,10 +347,12 @@ namespace IRaCIS.Core.Application.Service { var trialId = inCommand.TrialId; - var filterObj = await _taskConsistentRuleRepository.FirstOrDefaultAsync(t => t.TrialId == trialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.IsSelfAnalysis == false); + var filterObj = await _taskConsistentRuleRepository.Where(t => t.TrialId == trialId && t.TrialReadingCriterionId == inCommand.TrialReadingCriterionId && t.IsSelfAnalysis == false).Include(t => t.TrialReadingCriterion).FirstNotNullAsync(); var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + + //随机分配 if (inCommand.IsAutoAllocateGenerateTask) { @@ -341,12 +364,12 @@ namespace IRaCIS.Core.Application.Service throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate"]); } - if (subjectSelectList.Count< 2 * filterObj.PlanSubjectCount) + if (subjectSelectList.Count < 2 * filterObj.PlanSubjectCount) { throw new BusinessValidationFailedException(_localizer["TaskConsistent_NotAllowedGenerate1"]); } - inCommand.SubejctIdList = GetRandomSubjectIdList(subjectSelectList.Select(t => t.SubjectId).ToList(), filterObj.PlanSubjectCount); + inCommand.SubejctIdList = subjectSelectList.Select(t => t.SubjectId).ToList().GetRandomCountList(filterObj.PlanSubjectCount); } @@ -416,6 +439,11 @@ namespace IRaCIS.Core.Application.Service foreach (var needAddDoctorUserId in needAddDoctorUserIdList) { + //if(filterObj.TrialReadingCriterion.IsReadingTaskViewInOrder== ReadingOrder.InOrder) + //{ + + //} + //每个医生 都生成处理的任务 foreach (var task in subject.SubjectTaskVisitList.Take(filterObj.PlanVisitCount)) { @@ -524,10 +552,13 @@ namespace IRaCIS.Core.Application.Service var trialReadingCriterionId = filterObj.TrialReadingCriterionId; + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + #region Subejct 维度 Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId && - u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject) && u.DoctorUserId == doctorUserId; + u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject) && u.DoctorUserId == doctorUserId; @@ -545,14 +576,15 @@ namespace IRaCIS.Core.Application.Service //{ // //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍 // visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned && - // t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject))); + // t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject))); //} var subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId && t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).Count() >= filterObj.PlanVisitCount) - .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global)) + .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter) + .Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Any(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > filterObj.PlanVisitCount - 1)) ; @@ -567,6 +599,8 @@ namespace IRaCIS.Core.Application.Service IsReReadingOrBackInfluenceAnalysis = t.IsReReadingOrBackInfluenceAnalysis, + FirstGlobalVisitName= t.ReadModuleList.Where(c=>c.TrialReadingCriterionId== trialReadingCriterionId && c.ModuleType== ModuleTypeEnum.Global).OrderBy(k=>k.SubjectVisit.VisitNum).Select(u=>u.SubjectVisit.VisitName).FirstOrDefault(), + BlindSubjectCode = t.SubjectVisitTaskList.Where(t => t.IsAnalysisCreate && t.TrialReadingCriterionId == trialReadingCriterionId).OrderByDescending(t => t.BlindSubjectCode).Select(t => t.BlindSubjectCode).FirstOrDefault(), IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.DoctorUserId == doctorUserId && c.IsSelfAnalysis == true && c.TrialReadingCriterionId == trialReadingCriterionId), @@ -596,29 +630,27 @@ namespace IRaCIS.Core.Application.Service IsClinicalDataSign = c.IsClinicalDataSign, IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, - //自身一致性才有意义 - //IsHaveGeneratedTask = c.Subject.SubjectVisitTaskList.Any(t => t.ConsistentAnalysisOriginalTaskId == c.Id), - GlobalVisitTaskList = c.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).Select(c => new VisitTaskSimpleDTO() - { - Id = c.Id, - ReadingCategory = c.ReadingCategory, - ReadingTaskState = c.ReadingTaskState, - TaskBlindName = c.TaskBlindName, - TaskCode = c.TaskCode, - TaskName = c.TaskName, - TaskState = c.TaskState, - ArmEnum = c.ArmEnum, - SubjectId = c.SubjectId, - VisitTaskNum = c.VisitTaskNum, - TrialId = c.TrialId, - SourceSubjectVisitId = c.SourceSubjectVisitId, - SouceReadModuleId = c.SouceReadModuleId, + //GlobalVisitTaskList = c.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global]).Select(c => new VisitTaskSimpleDTO() + //{ + // Id = c.Id, + // ReadingCategory = c.ReadingCategory, + // ReadingTaskState = c.ReadingTaskState, + // TaskBlindName = c.TaskBlindName, + // TaskCode = c.TaskCode, + // TaskName = c.TaskName, + // TaskState = c.TaskState, + // ArmEnum = c.ArmEnum, + // SubjectId = c.SubjectId, + // VisitTaskNum = c.VisitTaskNum, + // TrialId = c.TrialId, + // SourceSubjectVisitId = c.SourceSubjectVisitId, + // SouceReadModuleId = c.SouceReadModuleId, - TrialReadingCriterionId = c.TrialReadingCriterionId, - IsClinicalDataSign = c.IsClinicalDataSign, - IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, - }).ToList(), + // TrialReadingCriterionId = c.TrialReadingCriterionId, + // IsClinicalDataSign = c.IsClinicalDataSign, + // IsNeedClinicalDataSign = c.IsNeedClinicalDataSign, + //}).ToList(), }).ToList() }); @@ -638,13 +670,11 @@ namespace IRaCIS.Core.Application.Service /// private async Task> GetGroupConsistentQueryAsync(TaskConsistentRule filterObj, List? subejctIdList = null) { + //单重阅片没有组件一致性 var trialId = filterObj.TrialId; var trialReadingCriterionId = filterObj.TrialReadingCriterionId; - //var trialConfig = (await _repository.Where(t => t.Id == trialId).Select(t => new { TrialId = t.Id, t.ReadingType, t.IsReadingTaskViewInOrder }).FirstOrDefaultAsync()).IfNullThrowException(); - - Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && u.TrialReadingCriterionId == trialReadingCriterionId && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject); @@ -658,28 +688,16 @@ namespace IRaCIS.Core.Application.Service Expression> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit); - ////所选访视数量 的访视 其中必有一个访视后有全局任务 - //if (filterObj.IsHaveReadingPeriod == true) - //{ - // //visitTaskFilter = visitTaskFilter.And(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Any(u => u.VisitTaskNum == t.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && u.ReadingCategory == ReadingCategory.Global)); - - // //这里的过滤条件 不能用 where(comonTaskFilter) 会报错,奇怪的问题 只能重新写一遍 - // visitTaskFilter = visitTaskFilter.And(c => c.Subject.SubjectVisitTaskList.Any(t => t.VisitTaskNum == c.VisitTaskNum + ReadingCommon.TaskNumDic[ReadingCategory.Global] && t.ReadingCategory == ReadingCategory.Global && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned && - // t.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && (t.ReReadingApplyState == ReReadingApplyState.Default || t.ReReadingApplyState == ReReadingApplyState.Reject))); - - //} - - IQueryable subjectQuery = default; - //单重阅片没有组件一致性 + //双重阅片,并且都阅片完成 subjectQuery = _subjectRepository.Where(t => t.TrialId == trialId && t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Select(t => t.DoctorUserId).Distinct().Count() == 2 && t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter).GroupBy(t => new { t.SubjectId, t.VisitTaskNum }).Where(g => g.Count() == 2).Count() >= filterObj.PlanVisitCount ) - .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount * 2 + 2).Any(t => t.ReadingCategory == ReadingCategory.Global)) - + //.WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(filterObj.PlanVisitCount * 2 + 2).Any(t => t.ReadingCategory == ReadingCategory.Global)) + .WhereIf(filterObj.IsHaveReadingPeriod == true, u => u.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter).Where(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > filterObj.PlanVisitCount - 1).Select(t => t.DoctorUserId).Distinct().Count() == 2) ; @@ -695,7 +713,7 @@ namespace IRaCIS.Core.Application.Service IsHaveGeneratedTask = t.SubjectVisitTaskList.Any(c => c.IsSelfAnalysis == false && c.TrialReadingCriterionId == trialReadingCriterionId), - DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed).Select(t => new UserSimpleInfo() + DoctorUserList = t.SubjectDoctorList.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsConfirmed && t.ArmEnum <= Arm.DoubleReadingArm2).Select(t => new UserSimpleInfo() { UserId = t.Id, FullName = t.DoctorUser.FullName, @@ -756,9 +774,12 @@ namespace IRaCIS.Core.Application.Service var trialId = inQuery.TrialId; + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + Expression> comonTaskFilter = u => u.TrialId == trialId && u.IsAnalysisCreate == false && u.TaskState == TaskState.Effect && u.ReadingTaskState == ReadingTaskState.HaveSigned && (u.ReReadingApplyState == ReReadingApplyState.Default || u.ReReadingApplyState == ReReadingApplyState.Reject); - //&& u.TrialReadingCriterionId == trialReadingCriterionId &&u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < DateTime.Now && u.DoctorUserId == doctorUserId; + //&& u.TrialReadingCriterionId == trialReadingCriterionId &&u.SignTime!.Value.AddDays(filterObj.IntervalWeeks * 7) < appDateTimeNow && u.DoctorUserId == doctorUserId; Expression> visitTaskFilter = comonTaskFilter.And(t => t.ReadingCategory == ReadingCategory.Visit); @@ -790,12 +811,20 @@ namespace IRaCIS.Core.Application.Service MatchSubejctCount = taskConsistentRule.Trial.SubjectList.AsQueryable() .Where(t => taskConsistentRule.IsHaveReadingPeriod == false ? t.SubjectVisitTaskList.AsQueryable() - .Where(visitTaskFilter).Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.Id) + .Where(visitTaskFilter).Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id) .Count() >= taskConsistentRule.PlanVisitCount : + //全局要>计划访视数量后面 t.SubjectVisitTaskList.AsQueryable().Where(comonTaskFilter) - .Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < DateTime.Now && t.DoctorUserId == user.Id) - .Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).OrderBy(t => t.VisitTaskNum).Take(taskConsistentRule.PlanVisitCount + 1).Any(t => t.ReadingCategory == ReadingCategory.Global)) + .Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id) + .Where(t => t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global).Any(t => t.ReadingCategory == ReadingCategory.Global && t.VisitTaskNum > taskConsistentRule.PlanVisitCount - 1) + && + t.SubjectVisitTaskList.AsQueryable().Where(visitTaskFilter) + .Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId && t.SignTime!.Value.AddDays(taskConsistentRule.IntervalWeeks * 7) < appDateTimeNow && t.DoctorUserId == user.Id) + .Count() >= taskConsistentRule.PlanVisitCount + + ) + .Count(), @@ -825,9 +854,9 @@ namespace IRaCIS.Core.Application.Service //var taskConsistentRuleQueryable = _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId) // .ProjectTo(_mapper.ConfigurationProvider); - var list= await taskConsistentRuleQueryable.ToListAsync(); + var list = await taskConsistentRuleQueryable.ToListAsync(); - var rule= await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == true && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + var rule = await _taskConsistentRuleRepository.Where(t => t.TrialId == inQuery.TrialId && t.IsSelfAnalysis == true && t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); return ResponseOutput.Ok(list, rule); } @@ -864,7 +893,7 @@ namespace IRaCIS.Core.Application.Service public async Task> GetUpdateVirtualSiteCodeList(Guid trialId) { - var list = await _trialVirtualSiteCodeUpdateRepository.Where(t => t.TrialId == trialId).Select(t => new UpdateTrialSiteCodeCommandView() { TrialId = trialId, VirtualSiteCode = t.VirturalSiteCode, Creatime = t.CreateTime }).OrderByDescending(t=>t.Creatime).ToListAsync(); + var list = await _trialVirtualSiteCodeUpdateRepository.Where(t => t.TrialId == trialId).Select(t => new UpdateTrialSiteCodeCommandView() { TrialId = trialId, VirtualSiteCode = t.VirturalSiteCode, Creatime = t.CreateTime }).OrderByDescending(t => t.Creatime).ToListAsync(); return list; } @@ -960,35 +989,10 @@ namespace IRaCIS.Core.Application.Service - /// - /// 随机算法,选择指定数量的 subject - /// - /// - /// - /// - public List GetRandomSubjectIdList(List matchSubjectIdList, int countToSelect) - { - // 使用 Fisher-Yates 随机置换算法来选择指定数量的 GUID - Random random = new Random(); - for (int i = 0; i < countToSelect; i++) - { - // 生成一个随机索引 - int randomIndex = random.Next(i, matchSubjectIdList.Count); - // 将选中的元素与当前元素交换位置 - Guid temp = matchSubjectIdList[randomIndex]; - matchSubjectIdList[randomIndex] = matchSubjectIdList[i]; - matchSubjectIdList[i] = temp; - } - return matchSubjectIdList.Take(countToSelect).ToList(); - - // 使用洗牌算法来随机选择指定数量的GUID - //Random random = new Random(); - //return matchSubjectIdList.OrderBy(g => random.Next()).Take(countToSelect).ToList(); - } } } diff --git a/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs index d272aef84..b8e31ed21 100644 --- a/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/TaskMedicalReviewService.cs @@ -56,9 +56,6 @@ namespace IRaCIS.Core.Application.Service var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId) - .WhereIf(inQuery.IsEffect == true, t => t.VisitTask.TaskState == TaskState.Effect || t.VisitTask.TaskState == TaskState.Freeze) - .WhereIf(inQuery.IsEffect == false, t => t.VisitTask.TaskState == TaskState.Adbandon || t.VisitTask.TaskState == TaskState.HaveReturned) - .WhereIf(inQuery.TrialSiteId != null, t => t.VisitTask.Subject.TrialSiteId == inQuery.TrialSiteId) .WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId) .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode) || t.VisitTask.Subject.MedicalNo.Contains(inQuery.SubjectCode)) diff --git a/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs b/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs index c29b021a3..72553ebd3 100644 --- a/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/VisitTaskHelpeService.cs @@ -51,6 +51,7 @@ namespace IRaCIS.Core.Application.Service private readonly IRepository _trialClinicalDataSetRepository; private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _readingConsistentClinicalDataRepository; private readonly IRepository _subjectCriteriaEvaluationRepository; @@ -67,8 +68,9 @@ namespace IRaCIS.Core.Application.Service IRepository readingJudgeInfoRepository, IRepository taskAllocationRuleRepository, IMapper mapper, IUserInfo userInfo, IRepository visitTaskReReadingRepository, IRepository trialReadingCriterionRepository, IRepository trialClinicalDataSetRepository, IRepository readingClinicalDataRepository, - IRepository subjectCriteriaEvaluationRepository) + IRepository subjectCriteriaEvaluationRepository, IRepository readingConsistentClinicalDataRepository) { + _readingConsistentClinicalDataRepository = readingConsistentClinicalDataRepository; _readingClinicalDataRepository = readingClinicalDataRepository; _trialClinicalDataSetRepository = trialClinicalDataSetRepository; @@ -207,7 +209,7 @@ namespace IRaCIS.Core.Application.Service if (trialReadingCriterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder) { - if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0 && subjectVisit.VisitNum==0) + if (visitNumList.IndexOf(subjectVisit.VisitNum) == 0 && subjectVisit.VisitNum == 0) { blindTaskName = visitBlindConfig.BlindBaseLineName; } @@ -217,9 +219,9 @@ namespace IRaCIS.Core.Application.Service } } - else + else { - blindTaskName ="Timepoint"; + blindTaskName = "Timepoint"; } @@ -1412,8 +1414,57 @@ namespace IRaCIS.Core.Application.Service break; case GenerateTaskCategory.SelfConsistent: + var readingCriterionId = generateTaskCommand.GenerataConsistentTaskList.First().TrialReadingCriterionId; + var subjectId = generateTaskCommand.GenerataConsistentTaskList.First().SubjectId; + //var trialReadingCriterion=_trialReadingCriterionRepository.Where(t=>t.Id== trialReadingCriterionId).FirstOrDefault(); + + //判断是否存在传输方式为Pdf得临床数据 + + var firsttask = generateTaskCommand.GenerataConsistentTaskList[0]; + var clinicalDataList = _readingClinicalDataRepository.Where(t => t.SubjectId == subjectId + && t.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == firsttask.TrialReadingCriterionId) + && t.ClinicalDataTrialSet.ClinicalDataLevel!= ClinicalLevel.Study + && t.ClinicalDataTrialSet.ClinicalUploadType == ClinicalUploadType.PDF) + // crc受试者和访视的临床数据没上传 一致性分析的时候也不用显示 + .Where(x=>x.ClinicalDataTrialSet.UploadRole == UploadRole.PM||x.FileCount>0) + .Include(t => t.ReadingClinicalDataPDFList).Include(t => t.ClinicalDataTrialSet).ToList(); + + + foreach (var clinicalData in clinicalDataList) + { + var consistnentClinicalData = _mapper.Map(clinicalData); + var id = NewId.NextSequentialGuid(); + consistnentClinicalData.Id = id; + + if (consistnentClinicalData.ClinicalDataTrialSet.ClinicalUploadType == ClinicalUploadType.PDF) + { + consistnentClinicalData.IsSign = false; + consistnentClinicalData.IsBlind = false; + consistnentClinicalData.IsComplete = true; + consistnentClinicalData.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; + consistnentClinicalData.ClinicalDataTrialSet = null; + } + + var consistanClinicalDataPdfList = _mapper.Map>(clinicalData.ReadingClinicalDataPDFList); + + consistanClinicalDataPdfList.ForEach(t => { t.ReadingConsistentClinicalDataId = id; t.Id = Guid.Empty; }); + + consistnentClinicalData.ReadingClinicalDataPDFList = consistanClinicalDataPdfList; + + await _readingConsistentClinicalDataRepository.AddAsync(consistnentClinicalData); + } + foreach (var task in generateTaskCommand.GenerataConsistentTaskList) { + + var exsitPDF = await _readingClinicalDataRepository.AnyAsync(t => t.TrialId == trialId && + t.SubjectId== task.SubjectId&& + + t.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(c => c.TrialReadingCriterionId == readingCriterionId) + && t.ClinicalDataTrialSet.ClinicalDataLevel != ClinicalLevel.Study + && t.ClinicalDataTrialSet.ClinicalUploadType == ClinicalUploadType.PDF); + + var taskState = exsitPDF ? TaskState.NotEffect : TaskState.Effect; var consistentTask = new VisitTask() { TrialId = task.TrialId, @@ -1425,7 +1476,7 @@ namespace IRaCIS.Core.Application.Service VisitTaskNum = task.VisitTaskNum, ReadingCategory = task.ReadingCategory, - TaskState = TaskState.Effect, + TaskState = taskState, Code = currentMaxCodeInt + 1, TaskCode = AppSettings.GetCodeStr(currentMaxCodeInt + 1, nameof(VisitTask)), diff --git a/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs index cb98ce646..12f90e6ca 100644 --- a/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs @@ -19,7 +19,6 @@ using DocumentFormat.OpenXml.Office2010.Word; using System.Linq.Dynamic.Core; using System.Linq; using DocumentFormat.OpenXml.Bibliography; -using Org.BouncyCastle.Crypto; using IRaCIS.Core.Domain.Share.Reading; using MassTransit; using System.Reactive.Subjects; @@ -291,7 +290,7 @@ namespace IRaCIS.Core.Application.Service.Allocation { foreach (var command in cancelCommand.CancelList.Where(t => t.IsCancelAssign)) { - if (await _visitTaskRepository.AnyAsync(t => t.TrialReadingCriterionId == cancelCommand.TrialReadingCriterionId && t.SubjectId == command.SubjectId && t.DoctorUserId == command.DoctorUserId && t.ArmEnum == command.ArmEnum && t.ReadingTaskState != ReadingTaskState.WaitReading)) + if (await _visitTaskRepository.AnyAsync(t => t.TaskState==TaskState.Effect && t.TrialReadingCriterionId == cancelCommand.TrialReadingCriterionId && t.SubjectId == command.SubjectId && t.DoctorUserId == command.DoctorUserId && t.ArmEnum == command.ArmEnum && t.ReadingTaskState != ReadingTaskState.WaitReading)) { //---当前医生已开始做该Subject 该标准的任务,不允许取消分配 throw new BusinessValidationFailedException(_localizer["VisitTask_DoctorConfigNotFound"]); @@ -787,7 +786,7 @@ namespace IRaCIS.Core.Application.Service.Allocation .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => (t.Subject.Code.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate == false) || (t.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.IsAnalysisCreate)) .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.AllocateTime > queryVisitTask.BeginAllocateDate) .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.AllocateTime < queryVisitTask.EndAllocateDate) - .WhereIf(queryVisitTask.BeginSignTime != null, t => t.SignTime > queryVisitTask.BeginSignTime) + .WhereIf(queryVisitTask.BeginSignTime != null, t => t.SignTime > queryVisitTask.BeginSignTime) .WhereIf(queryVisitTask.EndSignTime != null, t => t.SignTime < queryVisitTask.EndSignTime) .ProjectTo(_mapper.ConfigurationProvider); @@ -804,43 +803,43 @@ namespace IRaCIS.Core.Application.Service.Allocation /// /// PM 重阅追踪 /// - /// + /// /// [HttpPost] - public async Task> GetReReadingTaskList(VisitTaskQuery queryVisitTask) + public async Task> GetReReadingTaskList(VisitTaskQuery inQuery) { var visitTaskQueryable = _visitTaskReReadingRepository - .Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) - .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) - .WhereIf(queryVisitTask.TrialSiteId != null, t => t.OriginalReReadingTask.Subject.TrialSiteId == queryVisitTask.TrialSiteId) - .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) - .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == queryVisitTask.ReReadingApplyState) - .WhereIf(queryVisitTask.RequestReReadingType != null, t => t.RequestReReadingType == queryVisitTask.RequestReReadingType) - .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) - .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) - .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) - .WhereIf(queryVisitTask.ArmEnum != null, t => t.OriginalReReadingTask.ArmEnum == queryVisitTask.ArmEnum) - .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) - .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) - .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) - .WhereIf(queryVisitTask.ReadingCategory != null, t => t.OriginalReReadingTask.ReadingCategory == queryVisitTask.ReadingCategory) + .Where(t => t.OriginalReReadingTask.TrialId == inQuery.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) + .WhereIf(inQuery.RootReReadingTaskId != null, t => t.RootReReadingTaskId == inQuery.RootReReadingTaskId || t.OriginalReReadingTaskId == inQuery.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(inQuery.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(inQuery.TaskCode!)) + .WhereIf(inQuery.TrialSiteId != null, t => t.OriginalReReadingTask.Subject.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.TaskState != null, t => t.OriginalReReadingTask.TaskState == inQuery.TaskState) + .WhereIf(inQuery.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == inQuery.ReReadingApplyState) + .WhereIf(inQuery.RequestReReadingType != null, t => t.RequestReReadingType == inQuery.RequestReReadingType) + .WhereIf(inQuery.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(inQuery.ArmEnum != null, t => t.OriginalReReadingTask.ArmEnum == inQuery.ArmEnum) + .WhereIf(inQuery.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == inQuery.TaskAllocationState) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.ReadingCategory != null, t => t.OriginalReReadingTask.ReadingCategory == inQuery.ReadingCategory) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.RequestReReadingReason), t => t.RequestReReadingReason.Contains(queryVisitTask.RequestReReadingReason)) + .WhereIf(!string.IsNullOrEmpty(inQuery.RequestReReadingReason), t => t.RequestReReadingReason.Contains(inQuery.RequestReReadingReason)) - .WhereIf(queryVisitTask.RequestReReadingResultEnum != null, t => t.RequestReReadingResultEnum == queryVisitTask.RequestReReadingResultEnum) + .WhereIf(inQuery.RequestReReadingResultEnum != null, t => t.RequestReReadingResultEnum == inQuery.RequestReReadingResultEnum) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => ((t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.OriginalReReadingTask.Subject.MedicalNo.Contains(queryVisitTask.SubjectCode)) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) - .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) - .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(inQuery.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => ((t.OriginalReReadingTask.Subject.Code.Contains(inQuery.SubjectCode) || t.OriginalReReadingTask.Subject.MedicalNo.Contains(inQuery.SubjectCode)) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) + .WhereIf(inQuery.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > inQuery.BeginAllocateDate) + .WhereIf(inQuery.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1)) - .WhereIf(queryVisitTask.BeginRequestReReadingTime != null, t => t.RequestReReadingTime >= queryVisitTask.BeginRequestReReadingTime) - .WhereIf(queryVisitTask.EndRequestReReadingTime != null, t => t.RequestReReadingTime <= queryVisitTask.EndRequestReReadingTime!.Value.AddDays(1)) + .WhereIf(inQuery.BeginRequestReReadingTime != null, t => t.RequestReReadingTime >= inQuery.BeginRequestReReadingTime) + .WhereIf(inQuery.EndRequestReReadingTime != null, t => t.RequestReReadingTime <= inQuery.EndRequestReReadingTime!.Value.AddDays(1)) .ProjectTo(_mapper.ConfigurationProvider); @@ -854,7 +853,7 @@ namespace IRaCIS.Core.Application.Service.Allocation - var pageList = await visitTaskQueryable.ToPagedListAsync(queryVisitTask.PageIndex, queryVisitTask.PageSize, queryVisitTask.SortField, queryVisitTask.Asc, string.IsNullOrWhiteSpace(queryVisitTask.SortField), defalutSortArray); + var pageList = await visitTaskQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); return pageList; } @@ -1374,7 +1373,7 @@ namespace IRaCIS.Core.Application.Service.Allocation - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + if (IsPMOrAPm()) { if (criterionConfig.IsAutoCreate == false) { @@ -1657,7 +1656,7 @@ namespace IRaCIS.Core.Application.Service.Allocation if (criterionConfig.CriterionType == CriterionType.RECIST1Point1 && criterionConfig.IsAdditionalAssessment) { // PM申请 SPM / CPM审批 - if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.TrialGroupApply && (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM)) + if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.TrialGroupApply && (IsSpmOrCPM())) { filterExpression = filterExpression.And(t => t.TrialReadingCriterionId == origenalTask.TrialReadingCriterionId || t.TrialReadingCriterion.CriterionType == CriterionType.RECIST1Pointt1_MB); } @@ -1666,7 +1665,7 @@ namespace IRaCIS.Core.Application.Service.Allocation { // 1.1 基线任务影响BM任务 - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && _subjectVisitRepository.Any(t => t.Id == origenalTask.SourceSubjectVisitId && t.IsBaseLine == true)) + if ((IsPMOrAPm()) && _subjectVisitRepository.Any(t => t.Id == origenalTask.SourceSubjectVisitId && t.IsBaseLine == true)) { isIR1Point1AdditionalAssessmentBaseline = true; @@ -1689,7 +1688,7 @@ namespace IRaCIS.Core.Application.Service.Allocation if (agreeReReadingCommand.RequestReReadingResultEnum == RequestReReadingResult.Agree) { //PM申请 SPM / CPM审批 回退访视,因此这里不生成访视任务 影响多个标准的任务 - if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.TrialGroupApply && (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM)) + if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.TrialGroupApply && (IsSpmOrCPM())) { // 不管有序 无序 都会 回退访视 @@ -1809,7 +1808,7 @@ namespace IRaCIS.Core.Application.Service.Allocation } //IR申请 PM 审批 注意这里有一致性分析的申请同意 不会回退访视,在此要生成影响的访视任务 - else if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.DocotorApply && (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.AIR)) + else if (visitTaskReReadingAppply.RequestReReadingType == RequestReReadingType.DocotorApply && (IsPMOrAPm() || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.AIR)) { @@ -2314,7 +2313,7 @@ namespace IRaCIS.Core.Application.Service.Allocation //PM 才允许操作 - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + if (IsPMOrAPm()) { #region 有序 无序公用流程 @@ -2579,6 +2578,16 @@ namespace IRaCIS.Core.Application.Service.Allocation } + private bool IsPMOrAPm() + { + return _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM; + } + + private bool IsSpmOrCPM() + { + return _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM; + } + /// /// 影响提示列表 重阅仅仅针对已完成的任务申请 退回针对的是未完成的(退回仅仅针对是访视类型的) /// @@ -2610,12 +2619,12 @@ namespace IRaCIS.Core.Application.Service.Allocation { // IR 申请 PM 同意 - if (((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.IndependentReviewer)) + if ((( IsPMOrAPm() && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.IndependentReviewer)) || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer && applyId == null))) { // 1.1 基线任务影响BM任务 - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && _subjectVisitRepository.Any(t => t.Id == filterObj.SourceSubjectVisitId && t.IsBaseLine == true)) + if (IsPMOrAPm() && _subjectVisitRepository.Any(t => t.Id == filterObj.SourceSubjectVisitId && t.IsBaseLine == true)) { isIR1Point1AdditionalAssessmentBaseline = true; @@ -2653,7 +2662,7 @@ namespace IRaCIS.Core.Application.Service.Allocation //IR 申请 PM 同意 仅仅影响自己 - if ((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.IndependentReviewer)) + if ((IsPMOrAPm() && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.IndependentReviewer)) || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer && applyId == null)) { @@ -2771,8 +2780,9 @@ namespace IRaCIS.Core.Application.Service.Allocation } //PM 影响所有阅片人 仅仅针对访视 SPM CPM 调用 - else if (((_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM) && applyId != null && await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && t.CreateUser.UserTypeEnum == UserTypeEnum.ProjectManager) && filterObj.IsAnalysisCreate == false && filterObj.ReadingCategory == ReadingCategory.Visit) - || (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager && applyId == null)) + else if (((IsSpmOrCPM()) && applyId != null && + await _visitTaskReReadingRepository.AnyAsync(t => t.Id == applyId && (t.CreateUser.UserTypeEnum == UserTypeEnum.ProjectManager || t.CreateUser.UserTypeEnum == UserTypeEnum.APM)) && filterObj.IsAnalysisCreate == false && filterObj.ReadingCategory == ReadingCategory.Visit) + || (IsPMOrAPm() && applyId == null)) { @@ -2835,7 +2845,7 @@ namespace IRaCIS.Core.Application.Service.Allocation throw new BusinessValidationFailedException(_localizer["VisitTask_NoConsistencyReturn"]); } - if (filterObj.ReadingCategory == ReadingCategory.Visit && _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager) + if (filterObj.ReadingCategory == ReadingCategory.Visit && IsPMOrAPm()) { //有序 if (criterionConfig.IsReadingTaskViewInOrder == ReadingOrder.InOrder) diff --git a/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs b/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs index d6fc8810b..00dc9b3ef 100644 --- a/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Allocation/_MapConfig.cs @@ -104,7 +104,7 @@ namespace IRaCIS.Core.Application.Service CreateMap().IncludeBase() - .ForMember(o => o.IsHaveReading, t => t.MapFrom(u => u.Subject.SubjectVisitTaskList.Any(t => t.ReadingTaskState != ReadingTaskState.WaitReading && t.TrialReadingCriterionId==u.TrialReadingCriterionId && t.DoctorUserId==u.DoctorUserId))); + .ForMember(o => o.IsHaveReading, t => t.MapFrom(u => u.Subject.SubjectVisitTaskList.Any(t => t.ReadingTaskState != ReadingTaskState.WaitReading && t.TrialReadingCriterionId==u.TrialReadingCriterionId && t.DoctorUserId==u.DoctorUserId && t.TaskState == TaskState.Effect))); CreateMap(); @@ -145,7 +145,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(o => o.HistoryReadingDoctorUserList, t => t.MapFrom(u => u.JudgeVisitList)); CreateMap().IncludeBase() - + .ForMember(o => o.IsHaveFeedBack, t => t.MapFrom(u => u.UserFeedBackList.Any(t=>t.State==0))) ; CreateMap().IncludeBase() @@ -238,7 +238,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => /*u.IsAnalysisCreate == true ? u.BlindTrialSiteCode :*/ u.Subject.TrialSite.TrialSiteCode)) .ForMember(o => o.SubjectCode, t => t.MapFrom(u => /*u.IsAnalysisCreate == true ? u.BlindSubjectCode :*/ u.Subject.Code)) - .ForMember(o => o.GeneratedMedicalReviewCount, t => t.MapFrom(u => u.TaskMedicalReviewList.Count())) + .ForMember(o => o.GeneratedMedicalReviewCount, t => t.MapFrom(u => u.TaskMedicalReviewList.Count(t=>t.MedicalManagerUserId!=null))) .ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.Subject.MedicalNo)) .ForMember(o => o.IsGeneratedJudge, t => t.MapFrom(u => u.JudgeVisitTaskId != null)) .ForMember(o => o.ReadingDurationTimeSpan, t => t.MapFrom(u => u.SignTime - u.FirstReadingTime)) @@ -318,6 +318,9 @@ namespace IRaCIS.Core.Application.Service + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + } } diff --git a/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs b/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs index 0171c67c2..59038b64f 100644 --- a/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs +++ b/IRaCIS.Core.Application/Service/Common/CommonDocumentService.cs @@ -119,7 +119,7 @@ namespace IRaCIS.Core.Application.Service var filePath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, dbbeforeEntity.Path); - if (File.Exists(filePath)) + if (File.Exists(filePath) && dbbeforeEntity.Path!=addOrEditCommonDocument.Path) { File.Delete(filePath); } diff --git a/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs index 77e41901d..41891c999 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/CommonDocumentViewModel.cs @@ -48,6 +48,8 @@ namespace IRaCIS.Core.Application.ViewModel { public Guid? Id { get; set; } public string Name { get; set; } = String.Empty; + + public string NameCN { get; set; } = String.Empty; public string Path { get; set; } = String.Empty; public string Description { get; set; } = String.Empty; public bool IsDeleted { get; set; } diff --git a/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs index 691b1e8bf..c473097a0 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/EmailNoticeConfigViewModel.cs @@ -63,6 +63,8 @@ namespace IRaCIS.Core.Application.Contracts public bool? IsDistinguishCriteria { get; set; } + public SysEmailLevel? SystemLevel { get; set; } + } /// EmailNoticeConfigAddOrEdit 列表查询参数模型 @@ -126,6 +128,9 @@ namespace IRaCIS.Core.Application.Contracts public List ToUserTypeList { get; set; } public List CopyUserTypeList { get; set; } + + public SysEmailLevel SystemLevel { get; set; } + } diff --git a/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs new file mode 100644 index 000000000..c61cb31a8 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/DTO/ExploreRecommendViewModel.cs @@ -0,0 +1,61 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:29:36 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +namespace IRaCIS.Core.Application.ViewModel +{ + /// ExploreRecommendView 列表视图模型 + public class ExploreRecommendView: ExploreRecommendAddOrEdit + { + + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public DateTime? DeleteTime { get; set; } + public Guid? DeleteUserId { get; set; } + } + + ///ExploreRecommendQuery 列表查询参数模型 + public class ExploreRecommendQuery:PageInput + { + + public string? Version { get; set; } + + + public string? Title { get; set; } + + + public string? DownloadUrl { get; set; } + + + public string? FileName { get; set; } + + public bool? IsDeleted { get; set; } + + } + + /// ExploreRecommendAddOrEdit 列表查询参数模型 + public class ExploreRecommendAddOrEdit + { + public Guid? Id { get; set; } + public string ExploreType { get; set; } + + public string Version { get; set; } + public string Title { get; set; } + + public bool IsDeleted { get; set; } + public string DownloadUrl { get; set; } + public string Path { get; set; } + public string FileName { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs index c6b7567b3..53b162923 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs @@ -17,6 +17,9 @@ namespace IRaCIS.Core.Application.ViewModel public DateTime UpdateTime { get; set; } public Guid UpdateUserId { get; set; } + + public string Version { get; set; } + } ///InternationalizationQuery 列表查询参数模型 @@ -30,6 +33,10 @@ namespace IRaCIS.Core.Application.ViewModel public string? ValueCN { get; set; } + public string? Module { get; set; } + //关联版本历史记录表Id + public Guid? PublishLogId { get; set; } + } /// InternationalizationAddOrEdit 列表查询参数模型 @@ -45,6 +52,9 @@ namespace IRaCIS.Core.Application.ViewModel public string FrontType { get; set; } = string.Empty; public int InternationalizationType { get; set; } + public string Module { get; set; } = string.Empty; + //关联版本历史记录表Id + public Guid? PublishLogId { get; set; } } public class BatchAddInternationalization @@ -64,16 +74,28 @@ namespace IRaCIS.Core.Application.ViewModel public string Value { get; set; } = string.Empty; public string FrontType { get; set; } = string.Empty; public string ValueCN { get; set; } = string.Empty; + + public string Module { get; set; } = string.Empty; + } public class BatchAddInternationalizationDto : BatchInternationalizationDto { - + } - public class InternationalizationSimpleDto: BatchInternationalizationDto + public class InternationalizationSimpleDto : BatchInternationalizationDto { - + + } + + public class BatchUpdateInfoCommand + { + public List IdList { get; set; } + + public Guid? PublishLogId { get; set; } + + public int State { get; set; } } } diff --git a/IRaCIS.Core.Application/Service/Common/DTO/PublishLogViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/PublishLogViewModel.cs index b76607051..5cd098251 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/PublishLogViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/PublishLogViewModel.cs @@ -26,6 +26,8 @@ namespace IRaCIS.Core.Application.ViewModel public string? UpdateContent { get; set; } + public int? State { get; set; } + } /// PublishLogAddOrEdit 列表查询参数模型 @@ -33,9 +35,24 @@ namespace IRaCIS.Core.Application.ViewModel { public Guid? Id { get; set; } public string Version { get; set; } - public DateTime PublishTime { get; set; } + public DateTime? PublishTime { get; set; } public string UpdateContent { get; set; } + //0 开发中 ,已发布 + public int State { get; set; } + + public bool IsCurrentVersion { get; set; } + + } + + + public class PublishVersionSelect + { + public Guid Id { get; set; } + public string Version { get; set; } + public int State { get; set; } + public DateTime? PublishTime { get; set; } + public bool IsCurrentVersion { get; set; } } diff --git a/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs index aa07c35c1..a9d45632e 100644 --- a/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs +++ b/IRaCIS.Core.Application/Service/Common/EmailNoticeConfigService.cs @@ -29,6 +29,8 @@ namespace IRaCIS.Core.Application.Contracts public async Task> GetEmailNoticeConfigList(EmailNoticeConfigQuery queryEmailNoticeConfig) { var emailNoticeConfigQueryable = _emailNoticeConfigrepository + .WhereIf(queryEmailNoticeConfig.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys) + .WhereIf(queryEmailNoticeConfig.SystemLevel!=null, t =>t.SystemLevel == queryEmailNoticeConfig.SystemLevel) .WhereIf(queryEmailNoticeConfig.IsDistinguishCriteria != null, t => t.IsDistinguishCriteria == queryEmailNoticeConfig.IsDistinguishCriteria) .WhereIf(queryEmailNoticeConfig.CriterionTypeEnum != null, t => t.CriterionTypeEnum == queryEmailNoticeConfig.CriterionTypeEnum) .WhereIf(queryEmailNoticeConfig.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryEmailNoticeConfig.BusinessScenarioEnum) diff --git a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs index cfcfc40e7..830742482 100644 --- a/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs +++ b/IRaCIS.Core.Application/Service/Common/ExcelExportService.cs @@ -16,6 +16,7 @@ using MiniExcelLibs; using MiniExcelLibs.OpenXml; using NPOI.HPSF; using NPOI.HSSF.UserModel; +using NPOI.SS.Formula.Functions; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; @@ -51,8 +52,8 @@ namespace IRaCIS.Core.Application.Service.Common public async Task TrialUserListExport(TrialMaintenanceExportQuery param, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, - [FromServices] IRepository _trialRepository, - [FromServices] IRepository _trialUseRepository + [FromServices] IRepository _trialRepository, + [FromServices] IRepository _trialUseRepository ) { @@ -95,8 +96,8 @@ namespace IRaCIS.Core.Application.Service.Common public async Task TrialSiteUserListExport(SiteCRCExportQueryDTO param, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, - [FromServices] IRepository _trialRepository, - [FromServices] IRepository _trialSiteUserRepository + [FromServices] IRepository _trialRepository, + [FromServices] IRepository _trialSiteUserRepository ) { @@ -144,8 +145,8 @@ namespace IRaCIS.Core.Application.Service.Common [FromServices] IRepository _commonDocumentRepository, [FromServices] IRepository _trialSiteSurveyRepository, [FromServices] IRepository _trialSiteUserSurveyRepository, - [FromServices] IRepository _trialRepository, - [FromServices] IDictionaryService _dictionaryService + [FromServices] IRepository _trialRepository, + [FromServices] IDictionaryService _dictionaryService ) { @@ -164,7 +165,6 @@ namespace IRaCIS.Core.Application.Service.Common .WhereIf(queryParam.UserTypeId != null, t => t.UserTypeId == queryParam.UserTypeId) .WhereIf(queryParam.IsGenerateAccount != null, t => t.IsGenerateAccount == queryParam.IsGenerateAccount) .WhereIf(queryParam.State != null && queryParam.State != TrialSiteUserStateEnum.OverTime, t => t.InviteState == queryParam.State) - //.WhereIf(queryParam.State != null && queryParam.State == TrialSiteUserStateEnum.OverTime, t => t.InviteState == TrialSiteUserStateEnum.HasSend && t.ExpireTime < DateTime.Now) .WhereIf(!string.IsNullOrEmpty(queryParam.UserName), t => (t.LastName + " / " + t.FirstName).Contains(queryParam.UserName)) .WhereIf(!string.IsNullOrEmpty(queryParam.OrganizationName), t => t.OrganizationName.Contains(queryParam.OrganizationName)) .ProjectTo(_mapper.ConfigurationProvider); @@ -182,6 +182,132 @@ namespace IRaCIS.Core.Application.Service.Common } + /// + /// 项目列表导出---new + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task GetTrialList_Export(TrialToBeDoneQuery inQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _trialRepository + ) + { + var isPM = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ProjectManager || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.APM; + var isCRC = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator; + var isIQC = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.IQC; + var isMIM = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.MIM; + var isSPMOrCPM = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.SPM || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CPM; + var isIR = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer; + + + var query = _trialRepository.AsQueryable().IgnoreQueryFilters() + + .WhereIf(inQuery.SponsorId != null, o => o.SponsorId == inQuery.SponsorId) + .WhereIf(!string.IsNullOrEmpty(inQuery.Code), o => o.TrialCode.Contains(inQuery.Code)) + .WhereIf(!string.IsNullOrEmpty(inQuery.ResearchProgramNo), o => o.ResearchProgramNo.Contains(inQuery.ResearchProgramNo)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.ExperimentName), o => o.ExperimentName.Contains(inQuery.ExperimentName)) + .WhereIf(_userInfo.UserTypeEnumInt != (int)UserTypeEnum.SuperAdmin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.Admin && _userInfo.UserTypeEnumInt != (int)UserTypeEnum.OP, t => t.TrialUserList.Any(t => t.UserId == _userInfo.Id && t.IsDeleted == false) && t.IsDeleted == false) + .WhereIf(inQuery.CriterionType != null, o => o.TrialReadingCriterionList.Any(t => t.CriterionType == inQuery.CriterionType && t.IsSigned && t.IsConfirm)) + .WhereIf(!string.IsNullOrEmpty(inQuery.PM_EMail), o => o.TrialUserList.Any(t => t.User.EMail.Contains(inQuery.PM_EMail) && (t.User.UserTypeEnum == UserTypeEnum.ProjectManager || t.User.UserTypeEnum == UserTypeEnum.APM))) + .Select(t => new TrialToBeDoneDto() + { + TrialId = t.Id, + ResearchProgramNo = t.ResearchProgramNo, + ExperimentName = t.ExperimentName, + TrialCode = t.TrialCode, + CreateTime = t.CreateTime, + Sponsor = _userInfo.IsEn_Us ? t.Sponsor.SponsorName : t.Sponsor.SponsorNameCN, + TrialStatusStr = t.TrialStatusStr, + + ExpetiedTaskCount = isPM ? t.VisitTaskList.Where(t => t.IsUrgent).Count() : 0, + + ReReadingApprovalCount = isPM ? t.VisitTaskReReadingList.Where(t => t.OriginalReReadingTask.ReReadingApplyState == ReReadingApplyState.DocotorHaveApplyed).Count() : 0, + + PendingReconciliationCount = isPM ? t.SubjectVisitList.Where(t => t.CheckState == CheckStateEnum.ToCheck).Count() : 0, + + PendingResponseCount = isPM ? t.SubjectVisitList.Where(u => u.CheckState == CheckStateEnum.CVIng && + u.CheckChallengeDialogList.OrderByDescending(t => t.CreateTime).First().UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Count() : 0, + + + SPM_ReReadingApprovalCount = isSPMOrCPM ? t.VisitTaskReReadingList.Where(t => t.OriginalReReadingTask.ReReadingApplyState == ReReadingApplyState.TrialGroupHaveApplyed).Count() : 0, + + SPM_ReviewerSelectApprovalCount = isSPMOrCPM ? t.EnrollList.Where(u => u.EnrollStatus == EnrollStatus.HasCommittedToCRO).Count() : 0, + + MIM_UrgentCount = isMIM ? t.TaskMedicalReviewList.Where(t => t.IsClosedDialog == false && t.VisitTask.TaskState == TaskState.Effect && t.IsInvalid == false && t.MedicalManagerUserId == _userInfo.Id) + .Where(u => u.VisitTask.IsUrgent && + u.AuditState != MedicalReviewAuditState.HaveSigned).Count() : 0, + + MIM_PendingResponseCount = isMIM ? t.TaskMedicalReviewList.Where(t => t.IsClosedDialog == false && t.VisitTask.TaskState == TaskState.Effect && t.IsInvalid == false && t.MedicalManagerUserId == _userInfo.Id) + .Where(u => u.LatestReplyUser.UserTypeEnum == UserTypeEnum.IndependentReviewer && u.AuditState == MedicalReviewAuditState.Auditing).Count() : 0, + + MIM_PendingReviewCount = isMIM ? t.TaskMedicalReviewList.Where(t => t.IsClosedDialog == false && t.VisitTask.TaskState == TaskState.Effect && t.IsInvalid == false && t.MedicalManagerUserId == _userInfo.Id) + .Where(u => u.AuditState != MedicalReviewAuditState.HaveSigned && u.LatestReplyUser.UserTypeEnum != UserTypeEnum.IndependentReviewer).Count() : 0, + + CRC_UrgentCount = isCRC ? t.SubjectVisitList.Where(c => c.TrialSite.CRCUserList.Any(u => u.UserId == _userInfo.Id) && t.IsUrgent).Count() : 0, + + CRC_CheckQuestionCount = isCRC ? t.SubjectVisitList.Where(c => c.TrialSite.CRCUserList.Any(u => u.UserId == _userInfo.Id)) + .Where(u => u.CheckState == CheckStateEnum.CVIng && u.CheckChallengeState == CheckChanllengeTypeEnum.PMWaitCRCReply).Count() : 0, + + CRC_QCQuestionCount = isCRC ? t.SubjectVisitList.Where(c => c.TrialSite.CRCUserList.Any(u => u.UserId == _userInfo.Id)).SelectMany(c => c.QCChallengeList) + .Where(u => u.IsClosed == false && (u.LatestReplyUser.UserTypeEnum == UserTypeEnum.IQC || u.LatestReplyUserId == null)).Count() : 0, + + + //待审核 审核中 加急的数量 + IQC_UrgentCount = isIQC ? t.SubjectVisitList.Where(u => u.CurrentActionUserId == _userInfo.Id && t.QCProcessEnum != TrialQCProcess.NotAudit && t.IsUrgent).Count() : 0, + + //审核未完成 + IQC_AuditToBeDealedCount = isIQC ? t.SubjectVisitList.Where(u => u.CurrentActionUserId == _userInfo.Id && t.QCProcessEnum != TrialQCProcess.NotAudit).Count() : 0, + + //质疑待处理 + IQC_QuestionToBeDealedCount = isIQC ? t.SubjectVisitList.SelectMany(c => c.QCChallengeList) + .Where(u => u.CreateUserId == _userInfo.Id && u.IsClosed == false && u.LatestReplyUser.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator).Count() : 0, + + //待领取 + IQC_ToBeClaimedCount = isIQC ? t.SubjectVisitList.Where(t => t.SubmitState == SubmitStateEnum.Submitted && t.AuditState != AuditStateEnum.QCPassed) + .Where(u => u.CurrentActionUserId == null && (u.PreliminaryAuditUserId == null || (u.PreliminaryAuditUserId != _userInfo.Id && u.ReviewAuditUserId == null))).Count() : 0, + + + IR_ReadingCriterionList = isIR ? t.TrialReadingCriterionList.Where(t => t.IsConfirm && t.IsSigned).OrderBy(t => t.CriterionName).Select(t => t.CriterionName).ToList() : null, + + IR_PMEmailList = isIR ? t.TrialUserList.Where(t => t.User.UserTypeEnum == UserTypeEnum.ProjectManager || t.User.UserTypeEnum == UserTypeEnum.APM).OrderBy(t => t.User.EMail).Select(t => t.User.EMail).ToList() : null, + + IR_TotalReadCount = isIR ? t.VisitTaskList.Where(t => t.DoctorUserId == _userInfo.Id && t.TaskState == TaskState.Effect && t.ReadingTaskState == ReadingTaskState.HaveSigned).Count() : 0, + + + IR_UnReadCount = isIR ? t.VisitTaskList + .Where(c => c.DoctorUserId == _userInfo.Id && c.ReadingTaskState != ReadingTaskState.HaveSigned && c.TaskState == TaskState.Effect && c.TrialReadingCriterion.IsSigned) + // 前序 不存在 未一致性核查未通过的 + .Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum)) + //前序 不存在 未生成任务的访视 + .Where(t => t.TrialReadingCriterion.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true) + + .Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)).Count() : 0, + + IR_UrgentCount = isIR ? t.VisitTaskList.Where(t => t.SourceSubjectVisit.IsUrgent).Count() : 0, + + }); + + + var list = query.ToList(); + + var exportInfo = new ExcelExportInfo(); + + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); + exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialList_Export, exportInfo, exportInfo.TrialCode, _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrialToBeDoneDto)); + + } + + #endregion @@ -190,9 +316,118 @@ namespace IRaCIS.Core.Application.Service.Common #region 导表查询 + /// + /// getDocumentConfirmList 培训记录导出--new + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task PMTrainingRecordList_Export(DocumentTrialUnionQuery inQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _subjectVisitRepository, + [FromServices] IRepository _trialRepository + ) + { + var trialInfo = (await _repository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.TrialFinishedTime, t.TrialStatusStr }).FirstNotNullAsync()); + + var trialDocQuery = from trialDocumentNeedConfirmedUserType in _repository.Where(t => t.TrialDocument.TrialId == inQuery.TrialId) + join trialUser in _repository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.UserId != null, t => t.UserId == inQuery.UserId) + .WhereIf(inQuery.UserTypeId != null, t => t.User.UserTypeId == inQuery.UserTypeId) + on trialDocumentNeedConfirmedUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + + join confirm in _repository.Where(t => t.TrialDocument.TrialId == inQuery.TrialId) on + new { trialUser.UserId, TrialDocumentId = trialDocumentNeedConfirmedUserType.TrialDocumentId } equals new { UserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new TrainingRecordExportDTO() + { + IsSystemDoc = false, + + Id = trialDocumentNeedConfirmedUserType.TrialDocument.Id, + CreateTime = trialDocumentNeedConfirmedUserType.TrialDocument.CreateTime, + IsDeleted = trialDocumentNeedConfirmedUserType.TrialDocument.IsDeleted, + //SignViewMinimumMinutes = trialDocumentNeedConfirmedUserType.TrialDocument.SignViewMinimumMinutes, + Name = trialDocumentNeedConfirmedUserType.TrialDocument.Name, + //Path = trialDocumentNeedConfirmedUserType.TrialDocument.Path, + FileTypeId = trialDocumentNeedConfirmedUserType.TrialDocument.FileTypeId, + FileType = _userInfo.IsEn_Us ? trialDocumentNeedConfirmedUserType.TrialDocument.FileType.Value : trialDocumentNeedConfirmedUserType.TrialDocument.FileType.ValueCN, + //UpdateTime = trialDocumentNeedConfirmedUserType.TrialDocument.UpdateTime, + //IsConfirmed= confirm.ConfirmTime!=null, + //ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName, + + //FullFilePath = trialDocumentNeedConfirmedUserType.TrialDocument.Path + }; + + + + var systemDocQuery = from needConfirmEdUserType in _repository.WhereIf(trialInfo.TrialFinishedTime != null, u => u.SystemDocument.CreateTime < trialInfo.TrialFinishedTime) + + join trialUser in _repository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.UserId != null, t => t.UserId == inQuery.UserId) + on needConfirmEdUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId + join confirm in _repository.GetQueryable() on new { ConfirmUserId = trialUser.UserId, SystemDocumentId = needConfirmEdUserType.SystemDocumentId } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc + from confirm in cc.DefaultIfEmpty() + select new TrainingRecordExportDTO() + { + IsSystemDoc = true, + + Id = needConfirmEdUserType.SystemDocument.Id, + CreateTime = needConfirmEdUserType.SystemDocument.CreateTime, + IsDeleted = needConfirmEdUserType.SystemDocument.IsDeleted, + //SignViewMinimumMinutes = needConfirmEdUserType.SystemDocument.SignViewMinimumMinutes, + Name = needConfirmEdUserType.SystemDocument.Name, + //Path = needConfirmEdUserType.SystemDocument.Path, + FileType = _userInfo.IsEn_Us ? needConfirmEdUserType.SystemDocument.FileType.Value : needConfirmEdUserType.SystemDocument.FileType.ValueCN, + FileTypeId = needConfirmEdUserType.SystemDocument.FileTypeId, + //UpdateTime = needConfirmEdUserType.SystemDocument.UpdateTime, + //IsConfirmed = confirm.ConfirmTime != null, + + //ConfirmUserId = confirm.ConfirmUserId, + ConfirmTime = confirm.ConfirmTime, + RealName = trialUser.User.FullName, + UserName = trialUser.User.UserName, + UserTypeId = trialUser.User.UserTypeId, + UserTypeShortName = trialUser.User.UserTypeRole.UserTypeShortName, + + //FullFilePath = needConfirmEdUserType.SystemDocument.Path + }; + + var unionQuery = trialDocQuery.Union(systemDocQuery) + .WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name)) + .WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId) + .WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null) + .WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null) + .WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted) + .WhereIf(inQuery.UserTypeId != null, t => t.UserTypeId == inQuery.UserTypeId); + + var list = await unionQuery.ToListAsync(); + + + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); + exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialTrainingRecordList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(TrainingRecordExportDTO)); + + } + @@ -202,7 +437,7 @@ namespace IRaCIS.Core.Application.Service.Common /// /// 影像上传列表 只导出已上传状态的访视记录 /// - /// + /// /// /// /// @@ -210,7 +445,7 @@ namespace IRaCIS.Core.Application.Service.Common /// [HttpPost] [AllowAnonymous] - public async Task CRCVisitList_Export(CRCVisitSearchDTO visitSearchDTO, + public async Task CRCVisitList_Export(CRCVisitSearchDTO inQuery, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _subjectVisitRepository, @@ -218,26 +453,26 @@ namespace IRaCIS.Core.Application.Service.Common ) { - var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); - var list = await _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) .Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true) - .WhereIf(visitSearchDTO.TrialSiteId != null, t => t.TrialSiteId == visitSearchDTO.TrialSiteId) - .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) - .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) - .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) - .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) - .WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) - .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) - .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .WhereIf(inQuery.AuditStateArray != null && inQuery.AuditStateArray?.Length > 0, t => inQuery.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(inQuery.SubmitState != null, t => t.SubmitState == inQuery.SubmitState) + .WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState) + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .ProjectTo(_mapper.ConfigurationProvider); - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.BlindName).ToList(); + var list = query.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.BlindName).ToList(); - var exportInfo = (await _trialRepository.Where(t => t.Id == visitSearchDTO.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -248,47 +483,105 @@ namespace IRaCIS.Core.Application.Service.Common } + /// + /// 影像质控导出---new + /// + /// + /// + /// + /// + /// + /// + [HttpPost] + [AllowAnonymous] + public async Task QCVisitList_Export(QCVisitSearchDTO inQuery, + [FromServices] IRepository _commonDocumentRepository, + [FromServices] IDictionaryService _dictionaryService, + [FromServices] IRepository _subjectVisitRepository, + [FromServices] IRepository _trialRepository + ) + { + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) + .WhereIf(inQuery.VisitId != null, t => t.Id == inQuery.VisitId) + .WhereIf(inQuery.CurrentActionUserId != null, t => t.CurrentActionUserId == inQuery.CurrentActionUserId) + .WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => /*t.Subject.FirstName.Contains(subjectInfo) || t.Subject.LastName.Contains(subjectInfo) ||*/ t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.VisitNum.ToString().Contains(".") : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) + //.WhereIf(visitSearchDTO.AuditState != null, t => t.AuditState == visitSearchDTO.AuditState) + .WhereIf(inQuery.AuditStateArray != null && inQuery.AuditStateArray?.Length > 0, t => inQuery.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(inQuery.HandleUserId != null, t => t.PreliminaryAuditUserId == inQuery.HandleUserId || t.CurrentActionUserId == inQuery.HandleUserId + || t.ReviewAuditUserId == inQuery.HandleUserId + || t.QCChallengeList.Any(t => t.CreateUserId == inQuery.HandleUserId) + || t.QCChallengeDialogList.Any(t => t.CreateUserId == inQuery.HandleUserId)) + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) + .Where(t => t.SubmitState != SubmitStateEnum.None) + .WhereIf(inQuery.BeginAuditTime != null, t => t.Trial.QCProcessEnum == TrialQCProcess.SingleAudit ? t.PreliminaryAuditTime >= inQuery.BeginAuditTime : + (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit ? t.ReviewAuditTime >= inQuery.BeginAuditTime : true)) + + .WhereIf(inQuery.EndAuditTime != null, t => t.Trial.QCProcessEnum == TrialQCProcess.SingleAudit ? t.PreliminaryAuditTime <= inQuery.EndAuditTime : + (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit ? t.ReviewAuditTime <= inQuery.EndAuditTime : true)) + + .ProjectTo(_mapper.ConfigurationProvider); + + + var list = query.ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); + exportInfo.IsEn_US = _userInfo.IsEn_Us; + exportInfo.ClientZoneId = _userInfo.TimeZoneId; + + + return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialQCVisitImageList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(CRCVisitExportDTO)); + + } /// /// 质疑列表 /// - /// + /// /// /// /// /// [HttpPost] [AllowAnonymous] - public async Task GetQCChallengeList_Export(ChallengeQuery challengeQuery, + public async Task GetQCChallengeList_Export(ChallengeQuery inQuery, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository ) { + var svExpression = QCCommon.GetQCChallengeFilter(inQuery.VisitPlanArray); - var svExpression = QCCommon.GetQCChallengeFilter(challengeQuery.VisitPlanArray); - - var list = await _repository.Where(x => x.TrialId == challengeQuery.TrialId) + var query = _repository.Where(x => x.TrialId == inQuery.TrialId) //.WhereIf(challengeQuery.ChallengeState != null, t => t.SubjectVisit.ChallengeState == challengeQuery.ChallengeState) - .WhereIf(challengeQuery.ReuploadEnum != null, t => t.ReuploadEnum == challengeQuery.ReuploadEnum) - .WhereIf(challengeQuery.IsClosed != null, t => t.IsClosed == challengeQuery.IsClosed) - .WhereIf(challengeQuery.TrialSiteId != null, t => t.SubjectVisit.TrialSiteId == challengeQuery.TrialSiteId) - .WhereIf(challengeQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == challengeQuery.SubjectId) - .WhereIf(challengeQuery.CreateUserId != null, t => t.CreateUserId == challengeQuery.CreateUserId) - .WhereIf(!string.IsNullOrEmpty(challengeQuery.SubjectCode), t => t.SubjectVisit.Subject.Code.Contains(challengeQuery.SubjectCode)) - .WhereIf(challengeQuery.VisitPlanArray != null && challengeQuery.VisitPlanArray?.Length > 0, svExpression) - //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.InPlan == false : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) - .WhereIf(challengeQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == challengeQuery.IsUrgent) - .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) - .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .WhereIf(inQuery.ReuploadEnum != null, t => t.ReuploadEnum == inQuery.ReuploadEnum) + .WhereIf(inQuery.IsClosed != null, t => t.IsClosed == inQuery.IsClosed) + .WhereIf(inQuery.TrialSiteId != null, t => t.SubjectVisit.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.CreateUserId != null, t => t.CreateUserId == inQuery.CreateUserId) + .WhereIf(inQuery.SubjectCode != null, t => t.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode!)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) + .WhereIf(inQuery.IsOverTime != null && inQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) + .WhereIf(inQuery.IsOverTime != null && inQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) + .WhereIf(inQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == inQuery.IsUrgent) + .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.SubjectVisit.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.ChallengeCode).ToList(); + .ProjectTo(_mapper.ConfigurationProvider); - var exportInfo = (await _trialRepository.Where(t => t.Id == challengeQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var list = query.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.ChallengeCode).ToList(); + + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -301,32 +594,38 @@ namespace IRaCIS.Core.Application.Service.Common /// /// 受试者信息导出表 /// - /// + /// /// + /// /// /// /// [HttpPost] - public async Task GetSubjectList_Export(SubjectQueryParam param, + public async Task GetSubjectList_Export(SubjectQueryParam inQuery, [FromServices] IRepository _commonDocumentRepository, + [FromServices] IRepository _subjectRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository) { - var list = await _repository.Where(u => u.TrialId == param.TrialId) - .WhereIf(!string.IsNullOrWhiteSpace(param.Code), t => t.Code.Contains(param.Code)) - .WhereIf(!string.IsNullOrWhiteSpace(param.Name), t => t.ShortName.Contains(param.Name)) - .WhereIf(!string.IsNullOrWhiteSpace(param.Sex), t => t.Sex.Contains(param.Sex)) - .WhereIf(param.Status != null, t => t.Status == param.Status) - .WhereIf(param.TrialSiteId != null, t => t.TrialSiteId == param.TrialSiteId) + var subjectQuery = _subjectRepository.Where(u => u.TrialId == inQuery.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Code), t => t.Code.Contains(inQuery.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Name), t => t.ShortName.Contains(inQuery.Name)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Sex), t => t.Sex.Contains(inQuery.Sex)) + .WhereIf(inQuery.Status != null, t => t.Status == inQuery.Status) + + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) // CRC 只负责他管理site的受试者 .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .ProjectTo(_mapper.ConfigurationProvider) + .WhereIf(inQuery.IsMissingImages == true, t => t.MissingSubmmitCount > 0) + .WhereIf(inQuery.IsMissingImages == false, t => t.MissingSubmmitCount == 0) + ; - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.Code).ToList(); + var list = subjectQuery.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.Code).ToList(); - var exportInfo = (await _trialRepository.Where(t => t.Id == param.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -338,39 +637,6 @@ namespace IRaCIS.Core.Application.Service.Common - ///// - ///// 受试者 阅片期 进度表 导出 - ///// - ///// - ///// - ///// - ///// - //[HttpPost] - //public async Task GetSubjectReadingPeriod_Export(GetReadModuleDto dto, - // [FromServices] IRepository _commonDocumentRepository, - // [FromServices] IDictionaryService _dictionaryService, - // [FromServices] IRepository _trialRepository) - //{ - - - // var list = await _repository.Where(u => u.TrialId == dto.TrialId) - // .WhereIf(dto.SubjectId != null, x => x.Id == dto.SubjectId) - // .WhereIf(dto.TrialSiteCode != null && dto.TrialSiteCode != string.Empty, x => x.TrialSite.TrialSiteCode == dto.TrialSiteCode) - // .WhereIf(dto.SubjectCode != null && dto.SubjectCode != string.Empty, x => x.Code == dto.SubjectCode) - // //.WhereIf(dto.ReadingStatus != null, x => x.ReadingStatus == dto.ReadingStatus) - - // .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - - - // list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.Code).ToList(); - - // var exportInfo = (await _trialRepository.Where(t => t.Id == dto.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); - - // exportInfo.List = list; - - // return await ExcelExportHelper.DataExportAsync(StaticData.Export.TrialSubjectList_Export, exportInfo, $"{exportInfo.ResearchProgramNo}", _commonDocumentRepository, _hostEnvironment, _dictionaryService, typeof(SubjectExportDTO)); - //} - /// @@ -518,26 +784,31 @@ namespace IRaCIS.Core.Application.Service.Common /// /// 影像上传监控表 /// - /// + /// /// /// /// /// [HttpPost] - public async Task GetStudyUploadMonitor_Export(StudyQuery studyQuery, + public async Task GetStudyUploadMonitor_Export(StudyQuery inQuery, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository) { - var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(studyQuery.VisitPlanArray); - var StudyMonitorQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId) + var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(inQuery.VisitPlanArray); + var StudyMonitorQuery = _repository.Where(t => t.TrialId == inQuery.TrialId, ignoreQueryFilters: true) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.Subject.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) - .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) - .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) - .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) - .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) - .WhereIf(studyQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == studyQuery.TrialSiteId) + //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectId == inQuery.SubjectVisitId) + .WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.IsDicom != null, t => t.IsDicom == inQuery.IsDicom) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Uploader), t => t.Uploader.UserName.Contains(inQuery.Uploader)) + .WhereIf(inQuery.IsSuccess != null, t => t.IsSuccess == inQuery.IsSuccess) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode)) .Select(t => new UnionStudyMonitorExportDto() { TrialSiteCode = t.Subject.TrialSite.TrialSiteCode, @@ -576,7 +847,7 @@ namespace IRaCIS.Core.Application.Service.Common var list = await StudyMonitorQuery.ToListAsync(); - var exportInfo = (await _trialRepository.Where(t => t.Id == studyQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -716,34 +987,35 @@ namespace IRaCIS.Core.Application.Service.Common /// /// 一致性核查记录表 /// - /// + /// /// /// /// /// /// [HttpPost] - public async Task GetConsistencyVerificationList_Export(CheckQuery checkQuery, + public async Task GetConsistencyVerificationList_Export(CheckQuery inQuery, [FromServices] IRepository _commonDocumentRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository, [FromServices] IRepository _subjectVisitRepository) { - var svExpression = QCCommon.GetSubjectVisitFilter(checkQuery.VisitPlanArray); + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); - var list = await _subjectVisitRepository.Where(x => x.TrialId == checkQuery.TrialId) + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) .Where(x => x.AuditState == AuditStateEnum.QCPassed) //一致性核查中的,或者还没一致性核查的 - .WhereIf(checkQuery.CheckState != null, t => t.CheckState == checkQuery.CheckState) - .WhereIf(checkQuery.TrialSiteId != null, t => t.TrialSiteId == checkQuery.TrialSiteId) - .WhereIf(!string.IsNullOrEmpty(checkQuery.SubjectInfo), t => t.Subject.Code.Contains(checkQuery.SubjectInfo)) - .WhereIf(checkQuery.VisitPlanArray != null && checkQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(inQuery.CheckState != null, t => t.CheckState == inQuery.CheckState) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) + //.WhereIf(!string.IsNullOrEmpty(checkQuery.VisitPlanInfo), checkQuery.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(checkQuery.VisitPlanInfo)) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id))//CRC 过滤负责的site - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .ProjectTo(_mapper.ConfigurationProvider); - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ToList(); + var list = query.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitNum).ToList(); - var exportInfo = (await _trialRepository.Where(t => t.Id == checkQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -770,10 +1042,10 @@ namespace IRaCIS.Core.Application.Service.Common [FromServices] IRepository _trialRepository) { var list = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false) - //.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null) + //.Where(t => t.IsAnalysisCreate == false && t.DoctorUserId != null) - .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) - .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) + .WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) + .WhereIf(queryVisitTask.ArmEnum != null, t => t.ArmEnum == queryVisitTask.ArmEnum) .WhereIf(queryVisitTask.TrialSiteId != null, t => t.Subject.TrialSiteId == queryVisitTask.TrialSiteId) .WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) .WhereIf(queryVisitTask.IsUrgent != null, t => t.IsUrgent == queryVisitTask.IsUrgent) @@ -811,44 +1083,58 @@ namespace IRaCIS.Core.Application.Service.Common /// /// PM 重阅追踪 /// - /// + /// /// + /// /// /// /// [HttpPost] - public async Task GetReReadingTaskList_Export(VisitTaskQuery queryVisitTask, + public async Task GetReReadingTaskList_Export(VisitTaskQuery inQuery, [FromServices] IRepository _commonDocumentRepository, + [FromServices] IRepository _visitTaskReReadingRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository) { - var list = await _repository.Where(t => t.OriginalReReadingTask.TrialId == queryVisitTask.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) - .WhereIf(queryVisitTask.RootReReadingTaskId != null, t => t.RootReReadingTaskId == queryVisitTask.RootReReadingTaskId || t.OriginalReReadingTaskId == queryVisitTask.RootReReadingTaskId) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(queryVisitTask.TaskCode!)) - .WhereIf(queryVisitTask.TrialSiteId != null, t => t.OriginalReReadingTask.Subject.TrialSiteId == queryVisitTask.TrialSiteId) - .WhereIf(queryVisitTask.TaskState != null, t => t.OriginalReReadingTask.TaskState == queryVisitTask.TaskState) - .WhereIf(queryVisitTask.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == queryVisitTask.ReReadingApplyState) - .WhereIf(queryVisitTask.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == queryVisitTask.SubjectId) - .WhereIf(queryVisitTask.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == queryVisitTask.IsUrgent) - .WhereIf(queryVisitTask.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == queryVisitTask.DoctorUserId) - .WhereIf(queryVisitTask.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == queryVisitTask.ReadingTaskState) - .WhereIf(queryVisitTask.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == queryVisitTask.TaskAllocationState) - .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) + var visitTaskQueryable = _visitTaskReReadingRepository + .Where(t => t.OriginalReReadingTask.TrialId == inQuery.TrialId /*&& t.OriginalReReadingTask.IsAnalysisCreate == false*/) + .WhereIf(inQuery.RootReReadingTaskId != null, t => t.RootReReadingTaskId == inQuery.RootReReadingTaskId || t.OriginalReReadingTaskId == inQuery.RootReReadingTaskId) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskCode), t => t.OriginalReReadingTask.TaskCode.Contains(inQuery.TaskCode!) || t.RootReReadingTask.TaskCode.Contains(inQuery.TaskCode!)) + .WhereIf(inQuery.TrialSiteId != null, t => t.OriginalReReadingTask.Subject.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.TaskState != null, t => t.OriginalReReadingTask.TaskState == inQuery.TaskState) + .WhereIf(inQuery.ReReadingApplyState != null, t => t.OriginalReReadingTask.ReReadingApplyState == inQuery.ReReadingApplyState) + .WhereIf(inQuery.RequestReReadingType != null, t => t.RequestReReadingType == inQuery.RequestReReadingType) + .WhereIf(inQuery.SubjectId != null, t => t.OriginalReReadingTask.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.IsUrgent != null, t => t.OriginalReReadingTask.IsUrgent == inQuery.IsUrgent) + .WhereIf(inQuery.DoctorUserId != null, t => t.OriginalReReadingTask.DoctorUserId == inQuery.DoctorUserId) + .WhereIf(inQuery.ArmEnum != null, t => t.OriginalReReadingTask.ArmEnum == inQuery.ArmEnum) + .WhereIf(inQuery.ReadingTaskState != null, t => t.OriginalReReadingTask.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.TaskAllocationState != null, t => t.OriginalReReadingTask.TaskAllocationState == inQuery.TaskAllocationState) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.OriginalReReadingTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.ReadingCategory != null, t => t.OriginalReReadingTask.ReadingCategory == inQuery.ReadingCategory) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(queryVisitTask.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(queryVisitTask.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(queryVisitTask.TaskName)) - .WhereIf(!string.IsNullOrEmpty(queryVisitTask.SubjectCode), t => t.OriginalReReadingTask.Subject.Code.Contains(queryVisitTask.SubjectCode) || t.OriginalReReadingTask.BlindSubjectCode.Contains(queryVisitTask.SubjectCode)) - .WhereIf(queryVisitTask.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > queryVisitTask.BeginAllocateDate) - .WhereIf(queryVisitTask.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < queryVisitTask.EndAllocateDate!.Value.AddDays(1)) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .WhereIf(!string.IsNullOrEmpty(inQuery.RequestReReadingReason), t => t.RequestReReadingReason.Contains(inQuery.RequestReReadingReason)) + + .WhereIf(inQuery.RequestReReadingResultEnum != null, t => t.RequestReReadingResultEnum == inQuery.RequestReReadingResultEnum) + + + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.OriginalReReadingTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate) || (t.OriginalReReadingTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode!) && t.OriginalReReadingTask.IsAnalysisCreate == false)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.OriginalReReadingTask.TaskName.Contains(inQuery.TaskName) || t.OriginalReReadingTask.TaskBlindName.Contains(inQuery.TaskName)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => ((t.OriginalReReadingTask.Subject.Code.Contains(inQuery.SubjectCode) || t.OriginalReReadingTask.Subject.MedicalNo.Contains(inQuery.SubjectCode)) && t.OriginalReReadingTask.IsAnalysisCreate == false) || (t.OriginalReReadingTask.BlindSubjectCode.Contains(inQuery.SubjectCode) && t.OriginalReReadingTask.IsAnalysisCreate)) + .WhereIf(inQuery.BeginAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime > inQuery.BeginAllocateDate) + .WhereIf(inQuery.EndAllocateDate != null, t => t.OriginalReReadingTask.AllocateTime < inQuery.EndAllocateDate!.Value.AddDays(1)) + + .WhereIf(inQuery.BeginRequestReReadingTime != null, t => t.RequestReReadingTime >= inQuery.BeginRequestReReadingTime) + .WhereIf(inQuery.EndRequestReReadingTime != null, t => t.RequestReReadingTime <= inQuery.EndRequestReReadingTime!.Value.AddDays(1)) + .ProjectTo(_mapper.ConfigurationProvider); //var defalutSortArray = new string[] { nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.IsUrgent) + " desc", nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.SubjectId), nameof(ReReadingTaskView.OriginalReReadingTask) + "." + nameof(ReReadingTaskView.OriginalReReadingTask.VisitTaskNum) }; - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + var list = visitTaskQueryable.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); - var exportInfo = (await _trialRepository.Where(t => t.Id == queryVisitTask.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); + var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; @@ -864,31 +1150,48 @@ namespace IRaCIS.Core.Application.Service.Common /// /// /// + /// /// /// /// [HttpPost] public async Task GetMedicalReviewTaskList_Export(TaskMedicalReviewQuery inQuery, [FromServices] IRepository _commonDocumentRepository, + [FromServices] IRepository _taskMedicalReviewRepository, [FromServices] IDictionaryService _dictionaryService, [FromServices] IRepository _trialRepository) { - var list = await _repository.Where(t => t.VisitTask.TrialId == inQuery.TrialId) + var taskMedicalReviewQueryable = _taskMedicalReviewRepository.Where(t => t.VisitTask.TrialId == inQuery.TrialId) + .WhereIf(inQuery.TrialSiteId != null, t => t.VisitTask.Subject.TrialSiteId == inQuery.TrialSiteId) .WhereIf(inQuery.SubjectId != null, t => t.VisitTask.SubjectId == inQuery.SubjectId) - .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectCode), t => t.VisitTask.Subject.Code.Contains(inQuery.SubjectCode) || t.VisitTask.Subject.MedicalNo.Contains(inQuery.SubjectCode)) .WhereIf(!string.IsNullOrEmpty(inQuery.TaskName), t => t.VisitTask.TaskName.Contains(inQuery.TaskName) || t.VisitTask.TaskBlindName.Contains(inQuery.TaskName)) .WhereIf(inQuery.IsUrgent != null, t => t.VisitTask.IsUrgent == inQuery.IsUrgent) .WhereIf(inQuery.DoctorUserId != null, t => t.VisitTask.DoctorUserId == inQuery.DoctorUserId) .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => (t.VisitTask.BlindTrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate) || (t.VisitTask.Subject.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode) && t.VisitTask.IsAnalysisCreate == false)) .WhereIf(inQuery.ReadingCategory != null, t => t.VisitTask.ReadingCategory == inQuery.ReadingCategory) - .WhereIf(inQuery.ReadingTaskState != null, t => t.VisitTask.ReadingTaskState == inQuery.ReadingTaskState) + .WhereIf(inQuery.TaskState != null, t => t.VisitTask.TaskState == inQuery.TaskState) + .WhereIf(inQuery.ArmEnum != null, t => t.VisitTask.ArmEnum == inQuery.ArmEnum) + .WhereIf(inQuery.AuditState != null, t => t.AuditState == inQuery.AuditState) + .WhereIf(inQuery.MedicalManagerUserId != null, t => t.MedicalManagerUserId == inQuery.MedicalManagerUserId) + .WhereIf(inQuery.BeginSignTime != null, t => t.VisitTask.SignTime > inQuery.BeginSignTime) + .WhereIf(inQuery.EndSignTime != null, t => t.VisitTask.SignTime < inQuery.EndSignTime) + .WhereIf(inQuery.AuditAdviceEnum != null, t => t.AuditAdviceEnum == inQuery.AuditAdviceEnum) - .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) - .ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + .WhereIf(inQuery.DoctorUserIdeaEnum != null, t => t.DoctorUserIdeaEnum == inQuery.DoctorUserIdeaEnum) + .WhereIf(inQuery.TrialReadingCriterionId != null, t => t.VisitTask.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) + .WhereIf(inQuery.IsInvalid != null, t => t.IsInvalid == inQuery.IsInvalid) + .WhereIf(inQuery.IsHaveQuestion != null, t => t.IsHaveQuestion == inQuery.IsHaveQuestion) - list = list.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); + .WhereIf(inQuery.BeginAllocateDate != null, t => t.AllocateTime >= inQuery.BeginAllocateDate) + .WhereIf(inQuery.EndAllocateDate != null, t => t.AllocateTime <= inQuery.EndAllocateDate) + .WhereIf(inQuery.BeginAuditSignTime != null, t => t.AuditSignTime >= inQuery.BeginAuditSignTime) + .WhereIf(inQuery.EndAuditSignTime != null, t => t.AuditSignTime <= inQuery.EndAuditSignTime) + .ProjectTo(_mapper.ConfigurationProvider); + + var list = taskMedicalReviewQueryable.OrderBy(t => t.TrialSiteCode).ThenBy(t => t.SubjectCode).ThenBy(t => t.VisitTaskNum).ToList(); var exportInfo = (await _trialRepository.Where(t => t.Id == inQuery.TrialId).IgnoreQueryFilters().ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync()).IfNullThrowException(); @@ -1138,7 +1441,7 @@ namespace IRaCIS.Core.Application.Service.Common list.Add(new ExportDocumentDes() { Code = StaticData.Export.PCWG3Point1DetailedOfEvaluatedLesion_Export, ExportCatogory = ExportCatogory.DetailedOfEvaluatedLesion }); } - var result = _repository.Where(t => list.Select(c => c.Code).Contains(t.Code)).Select(c => new ExportDocumentDes() { Code = c.Code, FileName = c.Name }).ToList(); + var result = _repository.Where(t => list.Select(c => c.Code).Contains(t.Code)).Select(c => new ExportDocumentDes() { Code = c.Code, FileName = _userInfo.IsEn_Us ? c.Name : c.NameCN }).ToList(); foreach (var item in list) { @@ -1149,43 +1452,154 @@ namespace IRaCIS.Core.Application.Service.Common public List DealJudgeMark(ArbitrationRule arbitrationRule, IEnumerable list) where T : OverallTumorEvaluationExport { - //处理裁判标记 + //处理访视任务的裁判标记 + var resultExceptJudgeList = list.Where(t => t.ReadingCategory != ReadingCategory.Judge).ToList(); - var resultList = list.Where(t => t.ReadingCategory != ReadingCategory.Judge).ToList(); + var judegeList = list.Where(t => t.ReadingCategory == ReadingCategory.Judge).ToList(); if (arbitrationRule == ArbitrationRule.Visit) { - foreach (var item in resultList) + foreach (var item in resultExceptJudgeList) { - item.IsGenerateJudge = list.FirstOrDefault(t => t.ReadingCategory == ReadingCategory.Judge && t.SubjectCode == item.SubjectCode + item.IsGenerateJudge = judegeList.FirstOrDefault(t => t.SubjectCode == item.SubjectCode && (t.VisitTaskNum - ReadingCommon.TaskNumDic[ReadingCategory.Judge]) == item.VisitTaskNum)?.JudgeArmEnum == item.ArmEnum ? true : false; } + //如果没有产生裁判,默认选择R1 + + //找到没有裁判的访视任务 + var notJudgeList = resultExceptJudgeList.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum }).Where(g => g.All(t => t.IsGenerateJudge == false)).Select(g => new { g.Key.SubjectCode, g.Key.VisitTaskNum }).ToList(); + + foreach (var item in resultExceptJudgeList) + { + if (notJudgeList.Any(t => t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum) && item.ArmEnum == Arm.DoubleReadingArm1) + { + item.IsGenerateJudge = true; + } + } + } if (arbitrationRule == ArbitrationRule.Reading) { - foreach (var item in resultList) + //处理访视裁判标记 + foreach (var visitItem in resultExceptJudgeList.Where(t => t.ReadingCategory == ReadingCategory.Visit)) { - item.IsGenerateJudge = list.Where(t => t.ReadingCategory == ReadingCategory.Judge && t.SubjectCode == item.SubjectCode && t.VisitTaskNum > item.VisitTaskNum - ).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault()?.JudgeArmEnum == item.ArmEnum ? true : false; + //默认设置为false 只处理为true 和 空的情况 + visitItem.IsGenerateJudge = false; + + if (judegeList.Count > 0) + { + + var maxFinishedJudge = judegeList.Where(t => t.ReadingTaskState == ReadingTaskState.HaveSigned).FirstOrDefault(); + + var maxNotFinishedJudge = judegeList.Where(t => t.ReadingTaskState != ReadingTaskState.HaveSigned).FirstOrDefault(); + + if (maxFinishedJudge == null && maxNotFinishedJudge != null) + { + //仅有一个未完成的全局裁判,那么都是null + if (visitItem.VisitTaskNum < maxNotFinishedJudge.VisitTaskNum) + { + visitItem.IsGenerateJudge = null; + } + } + else if (maxFinishedJudge != null && maxNotFinishedJudge == null) + { + //全局裁判都完成了,那么以最后一次裁判选择的为准 + + if (visitItem.ArmEnum == maxFinishedJudge.JudgeArmEnum && visitItem.VisitTaskNum < maxFinishedJudge.VisitTaskNum) + { + visitItem.IsGenerateJudge = true; + } + + else if (visitItem.ArmEnum == Arm.DoubleReadingArm1) + { + visitItem.IsGenerateJudge = true; + } + } + else + { + //两个都不为null 肯定是不同的裁判 + + //在完成裁判之后的,和未完成裁判之前的 + if (/*visitItem.VisitTaskNum < maxNotFinishedJudge.VisitTaskNum &&*/ visitItem.VisitTaskNum > maxFinishedJudge.VisitTaskNum) + { + visitItem.IsGenerateJudge = null; + } + else if (visitItem.ArmEnum == maxFinishedJudge.JudgeArmEnum && visitItem.VisitTaskNum < maxFinishedJudge.VisitTaskNum) + { + visitItem.IsGenerateJudge = true; + } + //else if (visitItem.ArmEnum == Arm.DoubleReadingArm1) + //{ + // visitItem.IsGenerateJudge = true; + //} + + } + } + else + { + //不存在裁判 将R1设置 + if (visitItem.ArmEnum == Arm.DoubleReadingArm1) + { + visitItem.IsGenerateJudge = true; + } + } + #region 全局的维度考虑 + + + //// 该阅片人 subject 已完成的最大的全局(全局都是已完成的任务,裁判可能完成了,可能没完成) + //var subjectMaxGlobal = resultExceptJudgeList.Where(t => t.ReadingCategory == ReadingCategory.Global && t.SubjectCode == item.SubjectCode && t.ArmEnum == item.ArmEnum).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault(); + + ////没有全局 + //if (subjectMaxGlobal == null) + //{ + // //这个时候肯定没有裁判 默认选择R1 + + // if (item.ArmEnum == Arm.DoubleReadingArm1) + // { + // item.IsGenerateJudge = true; + // } + + + //} + ////有全局 + //else + //{ + // //判断当前受试者已完成的最大的全局是否有裁判 + // var existSubjectMaxGlobalJudge = judegeList.Where(t => t.SubjectCode == item.SubjectCode && t.VisitTaskNum - ReadingCommon.TaskNumDic[ReadingCategory.Judge] == subjectMaxGlobal.VisitTaskNum).FirstOrDefault(); + + // //最大的全局没有裁判(有一个做完了,有一个没做,或者都做完了,没产生裁判) + // if (existSubjectMaxGlobalJudge == null) + // { + // //找到最大的全局裁判 + // var maxJudgedGlobal= judegeList.Where(t => t.SubjectCode == item.SubjectCode ).OrderByDescending(t => t.VisitTaskNum).FirstOrDefault(); + + + // } + // else + // { + // //将该裁判选择的全局的阅片人之前的裁判标记都设置为是 + + // //是否是裁判选择的 + // if (item.ArmEnum == existSubjectMaxGlobalJudge.JudgeArmEnum) + // { + // //是否是裁判选择之前的 + // if (item.VisitTaskNum < existSubjectMaxGlobalJudge.VisitTaskNum) + // { + // item.IsGenerateJudge = true; + + // } + // } + + // } + + //} + + #endregion } } - - //如果没有产生裁判,默认选择R1 - - //找到没有裁判的访视任务 - var notJudgeList = resultList.GroupBy(t => new { t.SubjectCode, t.VisitTaskNum }).Where(g => g.All(t => t.IsGenerateJudge == false)).Select(g => new { g.Key.SubjectCode, g.Key.VisitTaskNum }).ToList(); - - foreach (var item in resultList) - { - if (notJudgeList.Any(t => t.SubjectCode == item.SubjectCode && t.VisitTaskNum == item.VisitTaskNum) && item.ArmEnum == Arm.DoubleReadingArm1) - { - item.IsGenerateJudge = true; - } - } - - return resultList; + return resultExceptJudgeList; } @@ -1217,8 +1631,10 @@ namespace IRaCIS.Core.Application.Service.Common throw new Exception(_localizer["ExcelExport_UnsupportedExport"]); } - var list = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) + var list = await _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.IsAnalysisCreate == false && t.TaskState == TaskState.Effect) + //访视和全局查询已签名完成的,裁判可以是未签名,未完成的 + .Where(t => (t.ReadingTaskState == ReadingTaskState.HaveSigned && (t.ReadingCategory == ReadingCategory.Visit || t.ReadingCategory == ReadingCategory.Global)) || (t.ReadingCategory == ReadingCategory.Judge)) //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) @@ -1247,7 +1663,7 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; exportInfo.ClientZoneId = _userInfo.TimeZoneId; @@ -1307,7 +1723,7 @@ namespace IRaCIS.Core.Application.Service.Common //处理裁判标记 list = DealJudgeMark(criterion.ArbitrationRule, list); - exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list, _userInfo.TimeZoneId); + exportInfo.List = ExportExcelConverterDate.ConvertToClientTimeInObject(list.Where(t => t.ReadingCategory != ReadingCategory.Global).ToList(), _userInfo.TimeZoneId); exportInfo.IsEn_US = _userInfo.IsEn_Us; exportInfo.ClientZoneId = _userInfo.TimeZoneId; @@ -1345,10 +1761,6 @@ namespace IRaCIS.Core.Application.Service.Common var query = _repository.Where(t => t.TrialId == queryVisitTask.TrialId && t.TaskState == TaskState.Effect && t.IsAnalysisCreate == false && t.ReadingTaskState == ReadingTaskState.HaveSigned) - //.WhereIf(queryVisitTask.SubjectId != null, t => t.SubjectId == queryVisitTask.SubjectId) - //.WhereIf(queryVisitTask.TaskState != null, t => t.TaskState == queryVisitTask.TaskState) - //.WhereIf(queryVisitTask.IsSelfAnalysis != null, t => t.IsSelfAnalysis == queryVisitTask.IsSelfAnalysis) - .WhereIf(queryVisitTask.TrialReadingCriterionId != null, t => t.TrialReadingCriterionId == queryVisitTask.TrialReadingCriterionId) .WhereIf(queryVisitTask.TrialSiteId != null, t => t.Subject.TrialSiteId == queryVisitTask.TrialSiteId) diff --git a/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs new file mode 100644 index 000000000..944a72442 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/ExploreRecommendService.cs @@ -0,0 +1,93 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:26:59 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Microsoft.AspNetCore.Authorization; +namespace IRaCIS.Core.Application.Service +{ + /// + /// ExploreRecommendService + /// + [ApiExplorerSettings(GroupName = "Common")] + public class ExploreRecommendService : BaseService, IExploreRecommendService + { + + private readonly IRepository _exploreRecommendRepository; + + public ExploreRecommendService(IRepository exploreRecommendRepository) + { + _exploreRecommendRepository = exploreRecommendRepository; + } + + [HttpPost] + public async Task> GetExploreRecommendList(ExploreRecommendQuery inQuery) + { + + var exploreRecommendQueryable = + + _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)) + .WhereIf(string.IsNullOrEmpty(inQuery.Version), t => t.Title.Contains(inQuery.Version)) + .WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == t.IsDeleted) + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await exploreRecommendQueryable + .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(ExploreRecommendView.Id) : inQuery.SortField, + inQuery.Asc); + + return pageList; + } + + + public async Task AddOrUpdateExploreRecommend(ExploreRecommendAddOrEdit addOrEditExploreRecommend) + { + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = u => u.IsDeleted == addOrEditExploreRecommend.IsDeleted && u.ExploreType == addOrEditExploreRecommend.ExploreType, + + // "当前类型浏览器启用版本只允许有一个" + VerifyMsg = _localizer["ExploreRecommend_OnlyOneTypePerType"] , + + IsVerify = addOrEditExploreRecommend.IsDeleted == false + }; + + var entity = await _exploreRecommendRepository.InsertOrUpdateAsync(addOrEditExploreRecommend, true, verifyExp2); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{exploreRecommendId:guid}")] + public async Task DeleteExploreRecommend(Guid exploreRecommendId) + { + var success = await _exploreRecommendRepository.DeleteFromQueryAsync(t => t.Id == exploreRecommendId, true,true); + return ResponseOutput.Ok(); + } + + [AllowAnonymous] + public async Task > GetExploreRecommentInfo() + { + + + var result = await _exploreRecommendRepository.Where(t => t.IsDeleted == false).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + if (result .Count==0) + { //"系统浏览器版本推荐未维护,请联系维护人员" + throw new QueryBusinessObjectNotExistException(_localizer["ExploreRecommend_NoExporeRecord"]); + } + + return result; + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs b/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs new file mode 100644 index 000000000..2dc5556b2 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Common/Interface/IExploreRecommendService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:27:36 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IExploreRecommendService + /// + public interface IExploreRecommendService + { + + Task> GetExploreRecommendList(ExploreRecommendQuery inQuery); + + Task AddOrUpdateExploreRecommend(ExploreRecommendAddOrEdit addOrEditExploreRecommend); + + Task DeleteExploreRecommend(Guid exploreRecommendId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs b/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs index ff6b43a4c..47723708e 100644 --- a/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs +++ b/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs @@ -81,7 +81,9 @@ namespace IRaCIS.Core.Application.Service { var mapItem = _mapper.Map(item); mapItem.InternationalizationType = 0; - mapItem.State = 1; + + // 0 是预翻译 1是已确认 2是后端废弃 + mapItem.State = 0; await _internationalizationRepository.AddAsync(mapItem); } @@ -100,6 +102,8 @@ namespace IRaCIS.Core.Application.Service var internationalizationQueryable = _internationalizationRepository .WhereIf(inQuery.Description != null, t => t.Description.Contains(inQuery.Description)) + .WhereIf(inQuery.Module != null, t => t.Module.Contains(inQuery.Module)) + .WhereIf(inQuery.PublishLogId != null, t => t.PublishLogId==inQuery.PublishLogId) .WhereIf(inQuery.Code != null, t => t.Code.Contains(inQuery.Code)) .WhereIf(inQuery.State != null, t => t.State == inQuery.State) .WhereIf(inQuery.InternationalizationType != null, t => t.InternationalizationType == inQuery.InternationalizationType) @@ -180,7 +184,7 @@ namespace IRaCIS.Core.Application.Service if (addOrEditInternationalization.InternationalizationType == 1) { - await InternationalizationHelper.AddOrUpdateJsonKeyValueAsync(entity.Code, addOrEditInternationalization.Value, addOrEditInternationalization.ValueCN); + await InternationalizationHelper.AddOrUpdateJsonKeyValueAsync(entity.Code, addOrEditInternationalization.Value, addOrEditInternationalization.ValueCN,addOrEditInternationalization.Description); } else { @@ -202,6 +206,19 @@ namespace IRaCIS.Core.Application.Service return ResponseOutput.Ok(); } + /// + /// 批量更新状态和发布版本 + /// + /// + /// + [HttpPut] + public async Task BatchUpdateInternationalInfo(BatchUpdateInfoCommand inCommand) + { + await _internationalizationRepository.BatchUpdateNoTrackingAsync(t => inCommand.IdList.Contains(t.Id), t => new Internationalization() { State = inCommand.State, PublishLogId = inCommand.PublishLogId }); + + return ResponseOutput.Ok(); + } + } } diff --git a/IRaCIS.Core.Application/Service/Common/MailService.cs b/IRaCIS.Core.Application/Service/Common/MailService.cs index 139c39155..f71f70f4a 100644 --- a/IRaCIS.Core.Application/Service/Common/MailService.cs +++ b/IRaCIS.Core.Application/Service/Common/MailService.cs @@ -9,6 +9,19 @@ using IRaCIS.Application.Contracts; using Microsoft.Extensions.Options; using Medallion.Threading; using System.Text.RegularExpressions; +using IRaCIS.Core.Infrastructure; +using DocumentFormat.OpenXml.Spreadsheet; +using IRaCIS.Core.Application.Service.Reading.Dto; +using NPOI.SS.Formula.Functions; +using Microsoft.AspNetCore.Mvc.ViewFeatures; +using IRaCIS.Core.Domain.Models; +using DocumentFormat.OpenXml.Office2013.Excel; +using IRaCIS.Core.Application.Contracts; +using DocumentFormat.OpenXml.Vml; +using System.Net.Mail; +using IP2Region.Net.XDB; +using NPOI.SS.Formula.Eval; +using System.Linq; namespace IRaCIS.Application.Services { @@ -19,7 +32,7 @@ namespace IRaCIS.Application.Services Task SendEmailVerification(string emailAddress, int verificationCode); - Task SiteSurveyRejectEmail(MimeMessage messageToSend); + Task SiteSurveyRejectEmail(MimeMessage messageToSend, TrialSiteSurvey trialSiteSurvey, string routeUrl, User? user = null); Task SenMFAVerifyEmail(Guid userId, string userName, string emailAddress, int verificationCode, UserMFAType mfaType = UserMFAType.Login); @@ -37,10 +50,10 @@ namespace IRaCIS.Application.Services Task DoctorJoinTrialEmail(Guid trialId, Guid doctorId, string baseUrl, string rootUrl); - + Task UserFeedBackMail(Guid feedBackId); } - public class MailVerificationService : BaseService, IMailVerificationService + public class MailVerificationService : BaseService, IMailVerificationService { private readonly IRepository _verificationCodeRepository; @@ -59,15 +72,16 @@ namespace IRaCIS.Application.Services private readonly SystemEmailSendConfig _systemEmailConfig; + private readonly IRepository _emailNoticeConfigrepository; - - public MailVerificationService(IRepository verificationCodeRepository, + public MailVerificationService(IRepository verificationCodeRepository, IRepository systemBasicDatarepository, IRepository userRepository, ITokenService tokenService, IRepository trialRepository, IRepository userTypeRepository, IRepository doctorTypeRepository, + IRepository emailNoticeConfigRepository, IOptionsMonitor systemEmailConfig, IDistributedLockProvider distributedLockProvider) { _systemEmailConfig = systemEmailConfig.CurrentValue; @@ -80,16 +94,85 @@ namespace IRaCIS.Application.Services _userTypeRepository = userTypeRepository; _doctorTypeRepository = doctorTypeRepository; _distributedLockProvider = distributedLockProvider; - + _emailNoticeConfigrepository = emailNoticeConfigRepository; } + private async Task GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario scenario, MimeMessage messageToSend, + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailFunc) + { + var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).FirstOrDefaultAsync(); + + if (configInfo == null) + { + throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查"); + } + + + var (topicStr, htmlBodyStr) = _userInfo.IsEn_Us ? (configInfo.EmailTopic, configInfo.EmailHtmlContent) : (configInfo.EmailTopicCN, configInfo.EmailHtmlContentCN); + + try + { + //每个场景修改主题 和body的逻辑不一样 + (topicStr, htmlBodyStr) = emailFunc((topicStr, htmlBodyStr)); + } + catch (Exception ex) + { + + throw new BusinessValidationFailedException("邮件模板内容有误,填充内容出现问题,请联系运维人员核查"); + } + + + messageToSend.Subject = topicStr; + + var builder = new BodyBuilder(); + + builder.HtmlBody = htmlBodyStr; + + messageToSend.Body = builder.ToMessageBody(); + + return configInfo; + } + + public async Task GetEmailConfigInfoAsync(EmailBusinessScenario scenario) + { + var configInfo = await _emailNoticeConfigrepository.Where(t => t.BusinessScenarioEnum == scenario).Include(t=>t.EmailNoticeUserTypeList).FirstOrDefaultAsync(); + + if (configInfo == null) + { + throw new BusinessValidationFailedException("系统未找到当前场景邮件配置信息,请联系运维人员核查"); + } + + return configInfo; + } + + + private EventHandler GetEmailSuccessHandle(Guid userId, int verificationCode, string email = "") + { + EventHandler sucessHandle = (sender, args) => + { + // args.Response + var code = verificationCode.ToString(); + _ = _verificationCodeRepository.AddAsync(new VerificationCode() + { + CodeType = VerifyType.Email, + HasSend = true, + Code = code, + UserId = userId, + EmailOrPhone = email, + ExpirationTime = DateTime.Now.AddMinutes(3) + }).Result; + _ = _verificationCodeRepository.SaveChangesAsync().Result; + + }; + + return sucessHandle; + } private string ReplaceCompanyName(string needDealtxt) { - var str= needDealtxt.Replace("{company}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN) + var str = needDealtxt.Replace("{company}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN) .Replace("{company abbreviation}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN); - return str; } @@ -101,120 +184,65 @@ namespace IRaCIS.Application.Services messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); - //主题 - //---[来自{0}] 关于MFA邮箱验证的提醒 - messageToSend.Subject = _localizer[mfaType == UserMFAType.Login? "Mail_EmailMFALoginTopic":"Mail_EmailMFAUnlockTopic", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + //主题---[来自{0}] 关于MFA邮箱验证的提醒 - var builder = new BodyBuilder(); + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "UserOptCommon.html"; - + (_userInfo.IsEn_Us ? "UserOptCommon_US.html" : "UserOptCommon.html"); - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); - - - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), + var topicStr = string.Format(input.topicStr, companyName); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), userName, - _localizer[mfaType == UserMFAType.Login ? "Mail_EmailMFALoginEmail": "Mail_EmailMFAUnlockEmail"], verificationCode ); - } - - - messageToSend.Body = builder.ToMessageBody(); - - - - EventHandler sucessHandle = (sender, args) => - { - // args.Response - var code = verificationCode.ToString(); - _ = _verificationCodeRepository.AddAsync(new VerificationCode() - { - CodeType = 0, - HasSend = true, - Code = code, - UserId = userId, - ExpirationTime = DateTime.Now.AddMinutes(3) - }).Result; - _ = _verificationCodeRepository.SaveChangesAsync().Result; + return (topicStr, htmlBodyStr); }; + await GetEmailSubejctAndHtmlInfoAndBuildAsync(mfaType == UserMFAType.Login ? EmailBusinessScenario.MFALogin : EmailBusinessScenario.MFAUnlock, messageToSend, emailConfigFunc); + + + var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } - //重置邮箱 + //用户重置邮箱 public async Task SendMailEditEmail(Guid userId, string userName, string emailAddress, int verificationCode) { - - var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(userName, emailAddress)); //主题 - //---[来自展影IRC] 关于重置邮箱的提醒 - messageToSend.Subject = _localizer["Mail_EmailResetReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + //---[来自展影IRC] 关于重置邮箱的提醒 - var builder = new BodyBuilder(); + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "UserOptCommon.html"; - +(_userInfo.IsEn_Us ? "UserOptCommon_US.html" : "UserOptCommon.html"); - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName); - - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), userName, - //---尊敬的 - //_localizer["Mail_Dear", userName], - //---您正在进行邮箱重置操作 - _localizer["Mail_ResettingEmail"], + //---您正在进行邮箱重置操作 verificationCode ); - } + + return (topicStr, htmlBodyStr); + }; - messageToSend.Body = builder.ToMessageBody(); + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UserResetEmail, messageToSend, emailConfigFunc); - - EventHandler sucessHandle = (sender, args) => - { - // args.Response - var code = verificationCode.ToString(); - _ = _verificationCodeRepository.AddAsync(new VerificationCode() - { - CodeType = 0, - HasSend = true, - Code = code, - UserId = userId, - ExpirationTime = DateTime.Now.AddMinutes(3) - }).Result; - _ = _verificationCodeRepository.SaveChangesAsync().Result; - - }; - + var sucessHandle = GetEmailSuccessHandle(userId, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); @@ -231,61 +259,36 @@ namespace IRaCIS.Application.Services messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); - //主题 - //---[来自展影IRC] 关于重置密码的提醒 - messageToSend.Subject = _localizer["Mail_IRCResettingPassword", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - var builder = new BodyBuilder(); - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "UserOptCommon.html"; - + (_userInfo.IsEn_Us ? "UserOptCommon_US.html" : "UserOptCommon.html"); - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), - "Sir/Madam", + "Sir/Madam", //---您正在进行邮箱重置密码操作 - _localizer["Mail_ResettingPassword"], verificationCode ); - } + + return (topicStr, htmlBodyStr); + }; + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.UnloginUseEmailResetPassword, messageToSend, emailConfigFunc); - messageToSend.Body = builder.ToMessageBody(); - - - EventHandler sucessHandle = (sender, args) => - { - var code = verificationCode.ToString(); - _ = _verificationCodeRepository.AddAsync(new VerificationCode() - { - CodeType = Core.Domain.Share.VerifyType.Email, - HasSend = true, - Code = code, - UserId = Guid.Empty,//此时不知道用户 - EmailOrPhone = emailAddress, - ExpirationTime = DateTime.Now.AddMinutes(3) - }).Result; - _ = _verificationCodeRepository.SaveChangesAsync().Result; - }; - + ////此时不知道用户 + var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); } /// - /// 发送验证码 + /// Reviewer简历录入 发送验证码 /// /// /// @@ -293,61 +296,37 @@ namespace IRaCIS.Application.Services public async Task SendEmailVerification(string emailAddress, int verificationCode) { - var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); //主题 - //---[来自展影IRC]的提醒 - messageToSend.Subject = _localizer["Mail_ImagingIRCReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + //---[来自展影IRC]的提醒 + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - - - var builder = new BodyBuilder(); - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - + (_userInfo.IsEn_Us ? "UserOptCommon_US.html": "UserOptCommon.html" ); - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), - "Sir/Madam", + "Sir/Madam", //---您正在参与展影医疗IRC项目 - _localizer["Mail_IRCProject", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN], + companyName, verificationCode ); - } - messageToSend.Body = builder.ToMessageBody(); - - - - EventHandler sucessHandle = (sender, args) => - { - // args.Response - var code = verificationCode.ToString(); - _ = _verificationCodeRepository.AddAsync(new VerificationCode() - { - CodeType = VerifyType.Email, - HasSend = true, - Code = code, - UserId = Guid.Empty,//此时不知道用户 - EmailOrPhone = emailAddress, - ExpirationTime = DateTime.Now.AddMinutes(3) - }).Result; - _ = _verificationCodeRepository.SaveChangesAsync().Result; + return (topicStr, htmlBodyStr); }; + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.ReviewerLogin, messageToSend, emailConfigFunc); + + //此时不知道用户 + var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); @@ -359,7 +338,6 @@ namespace IRaCIS.Application.Services public async Task AnolymousSendEmail(string researchProgramNo, string emailAddress, int verificationCode) { - var messageToSend = new MimeMessage(); //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); @@ -367,67 +345,74 @@ namespace IRaCIS.Application.Services messageToSend.To.Add(new MailboxAddress(String.Empty, emailAddress)); //主题 //$"[来自展影IRC] [{researchProgramNo}] 关于中心调研的提醒"; - messageToSend.Subject = _localizer["Mail_ProjectParticipationReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN, researchProgramNo]; + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - - - var builder = new BodyBuilder(); - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "UserOptCommon.html"; - + (_userInfo.IsEn_Us ? "UserOptCommon_US.html" : "UserOptCommon.html"); - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName, researchProgramNo); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), "Sir/Madam", //---您正在参与展影医疗IRC项目中心调研工作 - _localizer["Mail_CenterResearchReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN], + companyName, verificationCode ); - } - messageToSend.Body = builder.ToMessageBody(); - - - - EventHandler sucessHandle = (sender, args) => - { - // args.Response - var code = verificationCode.ToString(); - _ = _verificationCodeRepository.AddAsync(new VerificationCode() - { - CodeType = VerifyType.Email, - HasSend = true, - Code = code, - UserId = Guid.Empty,//此时不知道用户 - EmailOrPhone = emailAddress, - ExpirationTime = DateTime.Now.AddMinutes(3) - }).Result; - _ = _verificationCodeRepository.SaveChangesAsync().Result; + return (topicStr, htmlBodyStr); }; + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurveyLogin, messageToSend, emailConfigFunc); - await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig,sucessHandle); + //此时不知道用户 + var sucessHandle = GetEmailSuccessHandle(Guid.Empty, verificationCode, emailAddress); + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, sucessHandle); + } - public async Task SiteSurveyRejectEmail(MimeMessage messageToSend) + public async Task SiteSurveyRejectEmail(MimeMessage messageToSend, TrialSiteSurvey trialSiteSurvey, string routeUrl, User? user) { - + //发件地址 messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == trialSiteSurvey.TrialId); + + var siteInfo = await _repository.FirstOrDefaultAsync(t => t.TrialId == trialSiteSurvey.TrialId && t.Id == trialSiteSurvey.TrialSiteId, true); + + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => + { + var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); + + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), + + (user == null ? trialSiteSurvey.UserName : user.LastName + "/ " + user.FirstName), + trialInfo.TrialCode, + trialInfo.ResearchProgramNo, + trialInfo.ExperimentName, + siteInfo.TrialSiteCode, + siteInfo.TrialSiteAliasName, + trialSiteSurvey.LatestBackReason, + routeUrl, + (trialSiteSurvey.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none") + ); + + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SiteSurveyReject, messageToSend, emailConfigFunc); + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); @@ -446,49 +431,42 @@ namespace IRaCIS.Application.Services //收件地址 messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); //主题 - //---[来自展影IRC] 关于创建账户的提醒 - messageToSend.Subject = _localizer["Mail_AccountCreationReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + //---[来自展影IRC] 关于创建账户的提醒 - - var builder = new BodyBuilder(); - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "AdminAddUser.html"; - + (_userInfo.IsEn_Us ? "AdminAddUser_US.html" : "AdminAddUser.html"); - var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map(sysUserInfo))); await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); - routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us?"en":"zh") + "&access_token=" + token; + routeUrl = routeUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), - sysUserInfo.FullName, + sysUserInfo.FullName, sysUserInfo.UserName, sysUserInfo.UserTypeRole.UserTypeShortName, redirectUrl ); - } + + return (topicStr, htmlBodyStr); + }; - - messageToSend.Body = builder.ToMessageBody(); + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysCreateUser, messageToSend, emailConfigFunc); - await SendEmailHelper.SendEmailAsync(messageToSend,_systemEmailConfig); + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } //管理员重置密码发送邮件 @@ -502,38 +480,27 @@ namespace IRaCIS.Application.Services //收件地址 messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); //主题 - //---[来自展影IRC] 关于重置账户密码的提醒 - messageToSend.Subject = _localizer["Mail_AccountPasswordResetReminder", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN]; + //---[来自展影IRC] 关于重置账户密码的提醒 + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; - - var builder = new BodyBuilder(); - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - //+ "AdminResetUser.html"; - + (_userInfo.IsEn_Us ? "AdminResetUser_US.html" : "AdminResetUser.html"); - - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), - sysUserInfo.FullName, + sysUserInfo.FullName, sysUserInfo.UserName, sysUserInfo.UserTypeRole.UserTypeShortName, pwdNotMd5 ); - } + + return (topicStr, htmlBodyStr); + }; - - messageToSend.Body = builder.ToMessageBody(); - + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysResetPassword, messageToSend, emailConfigFunc); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } @@ -553,11 +520,6 @@ namespace IRaCIS.Application.Services messageToSend.To.Add(new MailboxAddress(sysUserInfo.FullName, sysUserInfo.EMail)); //主题 // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信"; - messageToSend.Subject = _localizer["Mail_InvitationEmail", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN, trialInfo.ResearchProgramNo]; - - - - var builder = new BodyBuilder(); var token = _tokenService.GetToken(IRaCISClaims.Create(_mapper.Map(sysUserInfo))); @@ -567,24 +529,21 @@ namespace IRaCIS.Application.Services await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); } + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - + (sysUserInfo.IsFirstAdd ? (_userInfo.IsEn_Us ? "TrialUserFirstJoin_US.html" : "TrialUserFirstJoin.html") : (_userInfo.IsEn_Us ? "TrialUserExistJoin_US.html" : "TrialUserExistJoin.html")); + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); - var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); - - var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; - - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), sysUserInfo.FullName, trialInfo.ExperimentName, trialInfo.ResearchProgramNo, @@ -593,11 +552,15 @@ namespace IRaCIS.Application.Services sysUserInfo.UserTypeRole.UserTypeShortName, sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl ); - } - messageToSend.Body = builder.ToMessageBody(); + return (topicStr, htmlBodyStr); + }; - await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig,null); + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc); + + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); } @@ -617,10 +580,6 @@ namespace IRaCIS.Application.Services messageToSend.To.Add(new MailboxAddress(String.Empty, sysUserInfo.EMail)); //主题 // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信"; - messageToSend.Subject = _localizer["Mail_InvitationEmail", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN,trialInfo.ResearchProgramNo]; - - - var builder = new BodyBuilder(); @@ -630,23 +589,21 @@ namespace IRaCIS.Application.Services await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); } - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - + (sysUserInfo.IsFirstAdd ? (_userInfo.IsEn_Us ? "TrialUserFirstJoin_US.html" : "TrialUserFirstJoin.html") : (_userInfo.IsEn_Us ? "TrialUserExistJoin_US.html" : "TrialUserExistJoin.html") ); + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; + + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); - var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + sysUserInfo.UserTypeRole.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; - - var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; - - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), sysUserInfo.FullName, trialInfo.ExperimentName, trialInfo.ResearchProgramNo, @@ -655,11 +612,12 @@ namespace IRaCIS.Application.Services sysUserInfo.UserTypeRole.UserTypeShortName, sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl ); - } + + return (topicStr, htmlBodyStr); + }; - - messageToSend.Body = builder.ToMessageBody(); + await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.SiteUseOrExternalUserFirstrJoinTrial : EmailBusinessScenario.SiteUserOrExternalUserExistJoinTrial, messageToSend, emailConfigFunc); await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null); @@ -730,13 +688,8 @@ namespace IRaCIS.Application.Services messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); //收件地址 messageToSend.To.Add(new MailboxAddress(doctor.FullName, doctor.EMail)); - //主题 - // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信"; - messageToSend.Subject = _localizer["Mail_InvitationEmail", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN, trialInfo.ResearchProgramNo]; - var builder = new BodyBuilder(); - var basicInfo = IRaCISClaims.Create(_mapper.Map(sysUserInfo)); @@ -751,25 +704,25 @@ namespace IRaCIS.Application.Services await _userRepository.BatchUpdateNoTrackingAsync(t => t.Id == sysUserInfo.Id, u => new User() { EmailToken = token }); } + var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + userType.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - + (sysUserInfo.IsFirstAdd ? (_userInfo.IsEn_Us ? "TrialDoctorFirstJoin_US.html" : "TrialDoctorFirstJoin.html") : (_userInfo.IsEn_Us ? "TrialDoctorExistJoin_US.html" : "TrialDoctorExistJoin.html") ); + var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) + var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; + + //主题 + // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}]邀请信"; + + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => { - var templateInfo = SourceReader.ReadToEnd(); + var topicStr = string.Format(input.topicStr, companyName, trialInfo.ResearchProgramNo); - var routeUrl = rootUrl + "?UserId=" + sysUserInfo.Id + "&Email=" + sysUserInfo.EMail + "&UserName=" + sysUserInfo.UserName + "&UserType=" + userType.UserTypeShortName + "&lang=" + (_userInfo.IsEn_Us ? "en" : "zh") + "&access_token=" + token; + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), - var domain = baseUrl.Substring(0, baseUrl.IndexOf("/login")); - - var redirectUrl = $"{domain}/api/User/UserRedirect?url={System.Web.HttpUtility.UrlEncode(routeUrl)}"; - - builder.HtmlBody = string.Format(ReplaceCompanyName(templateInfo), - sysUserInfo.FullName, + sysUserInfo.FullName, trialInfo.ExperimentName, trialInfo.ResearchProgramNo, trialInfo.TrialCode, @@ -777,9 +730,13 @@ namespace IRaCIS.Application.Services userType.UserTypeShortName, sysUserInfo.IsFirstAdd ? redirectUrl : baseUrl ); - } - messageToSend.Body = builder.ToMessageBody(); + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(sysUserInfo.IsFirstAdd ? EmailBusinessScenario.DoctorUserFirstJoinTrial : EmailBusinessScenario.DoctorUserExistJoinTrial, messageToSend, emailConfigFunc); + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig, null); @@ -787,6 +744,124 @@ namespace IRaCIS.Application.Services } + //用户反馈邮件 + public async Task UserFeedBackMail(Guid feedBackId) + { + var feedBack = await _repository.Where(t => t.Id == feedBackId).Include(t => t.CreateUser).ThenInclude(t => t.UserTypeRole).FirstNotNullAsync(); + + var messageToSend = new MimeMessage(); + //发件地址 + messageToSend.From.Add(new MailboxAddress(_systemEmailConfig.FromName, _systemEmailConfig.FromEmail)); + + var isHaveTrialId = feedBack.TrialId != null; + + var companyName = _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN; + + var emailConfigInfo = await GetEmailConfigInfoAsync(feedBack.VisitTaskId != null? EmailBusinessScenario.IRImageError:(feedBack.TrialId != null? EmailBusinessScenario.TrialFeedBack: EmailBusinessScenario.SysFeedBack)); + + var userTypeEnumList = emailConfigInfo.EmailNoticeUserTypeList.Where(t => t.EmailUserType == EmailUserType.To).Select(t => t.UserType).ToList(); + + var emailList = await _repository.Where(t => userTypeEnumList.Contains(t.UserTypeEnum) && + (isHaveTrialId ? t.UserTrials.Any(t => t.TrialId == feedBack.TrialId) : true)).Select(t => new { t.EMail, t.UserTypeEnum, t.FullName }).ToListAsync(); + + + foreach (var email in emailList) + { + messageToSend.To.Add(new MailboxAddress(email.FullName, email.EMail)); + } + + //影像阅片反馈 pm + if (feedBack.VisitTaskId != null) + { + var userNames = string.Join(',', emailList.Where(email => email.UserTypeEnum == UserTypeEnum.ProjectManager).Select(t => t.FullName)); + + var emailType = await _repository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.IRImageError).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync(); + + + var info = await _repository.Where(t => t.Id == feedBack.VisitTaskId).Select(t => new { t.Trial.ResearchProgramNo, t.Trial.TrialCode, SubejctCode = t.Subject.Code, t.SourceSubjectVisit.VisitName }).FirstNotNullAsync(); + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => + { + var topicStr = string.Format(input.topicStr, info.SubejctCode, info.VisitName, info.ResearchProgramNo); + + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), + userNames, + info.TrialCode, + info.SubejctCode, + info.VisitName, + feedBack.CreateUser.UserTypeRole.UserTypeShortName, + feedBack.CreateUser.FullName, + emailType, + feedBack.QuestionDescription, + _systemEmailConfig.SiteUrl + ); + + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.IRImageError, messageToSend, emailConfigFunc); + + } + //项目相关的反馈 pm admin + else if (feedBack.TrialId != null) + { + var userNames = string.Join(',', emailList.Where(email => email.UserTypeEnum == UserTypeEnum.ProjectManager || email.UserTypeEnum == UserTypeEnum.Admin).Select(t => t.FullName)); + + var emailType = await _repository.Where(t => t.Parent.Code == "Email_BusinessScenario" && t.ParentId != null && t.Code == ((int)EmailBusinessScenario.TrialFeedBack).ToString()).Select(t => _userInfo.IsEn_Us ? t.Value : t.ValueCN).FirstOrDefaultAsync(); + + + var info = await _repository.Where(t => t.Id == feedBack.TrialId).Select(t => new { t.ResearchProgramNo, t.TrialCode }).FirstNotNullAsync(); + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => + { + var topicStr = string.Format(input.topicStr, info.ResearchProgramNo); + + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), + userNames, + info.TrialCode, + feedBack.CreateUser.UserTypeRole.UserTypeShortName, + feedBack.CreateUser.FullName, + emailType, + feedBack.QuestionDescription, + _systemEmailConfig.SiteUrl + ); + + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.TrialFeedBack, messageToSend, emailConfigFunc); + + } + //项目无关的反馈 admin zyss + else + { + + var userNames = string.Join(',', emailList.Where(email => email.UserTypeEnum == UserTypeEnum.ZYSS || email.UserTypeEnum == UserTypeEnum.Admin).Select(t => t.FullName)); + + Func<(string topicStr, string htmlBodyStr), (string topicStr, string htmlBodyStr)> emailConfigFunc = input => + { + var topicStr = string.Format(input.topicStr); + + var htmlBodyStr = string.Format(ReplaceCompanyName(input.htmlBodyStr), + userNames, + feedBack.CreateUser.UserTypeRole.UserTypeShortName, + feedBack.CreateUser.FullName, + feedBack.QuestionDescription, + _systemEmailConfig.SiteUrl + ); + + return (topicStr, htmlBodyStr); + }; + + + await GetEmailSubejctAndHtmlInfoAndBuildAsync(EmailBusinessScenario.SysFeedBack, messageToSend, emailConfigFunc); + } + + await SendEmailHelper.SendEmailAsync(messageToSend, _systemEmailConfig); + + } } } diff --git a/IRaCIS.Core.Application/Service/Common/PublishLogService.cs b/IRaCIS.Core.Application/Service/Common/PublishLogService.cs index 5afb3aaf8..f5e0bc35d 100644 --- a/IRaCIS.Core.Application/Service/Common/PublishLogService.cs +++ b/IRaCIS.Core.Application/Service/Common/PublishLogService.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc; using IRaCIS.Core.Application.Interfaces; using IRaCIS.Core.Application.ViewModel; using IRaCIS.Application.Contracts; +using Microsoft.AspNetCore.Authorization; namespace IRaCIS.Core.Application.Service { @@ -31,8 +32,9 @@ namespace IRaCIS.Core.Application.Service { var publishLogQueryable = _publishLogRepository - .WhereIf(!string.IsNullOrEmpty(inQuery.Version) , t => t.Version.Contains(inQuery.Version)) + .WhereIf(!string.IsNullOrEmpty(inQuery.Version), t => t.Version.Contains(inQuery.Version)) .WhereIf(!string.IsNullOrEmpty(inQuery.UpdateContent), t => t.UpdateContent.Contains(inQuery.UpdateContent)) + .WhereIf(inQuery.State != null, t => t.State == inQuery.State) .ProjectTo(_mapper.ConfigurationProvider); var pageList = await publishLogQueryable @@ -48,19 +50,29 @@ namespace IRaCIS.Core.Application.Service if (!Version.TryParse(addOrEditPublishLog.Version, out version)) { - return ResponseOutput.NotOk("版本号不符合要求"); + //"版本号不符合要求" + return ResponseOutput.NotOk(_localizer["PublishLog_NotValidVersion"] ); } - + var verifyExp1 = new EntityVerifyExp() { - VerifyExp = u => u.Version == addOrEditPublishLog.Version , - - VerifyMsg = "发布编号不能重复" + VerifyExp = u => u.Version == addOrEditPublishLog.Version, + + //"发布编号不能重复" + VerifyMsg = _localizer["PublishLog_RepeatVersion"] }; + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = u => u.IsCurrentVersion == addOrEditPublishLog.IsCurrentVersion, + //"当前发布版本只允许有一个" + VerifyMsg = _localizer["PublishLog_OnlyOneCurrentVersion"] , - var entity = await _publishLogRepository.InsertOrUpdateAsync(addOrEditPublishLog, true, verifyExp1); + IsVerify=addOrEditPublishLog.IsCurrentVersion==true + }; + + var entity = await _publishLogRepository.InsertOrUpdateAsync(addOrEditPublishLog, true, verifyExp1, verifyExp2); return ResponseOutput.Ok(entity.Id.ToString()); @@ -74,6 +86,25 @@ namespace IRaCIS.Core.Application.Service return ResponseOutput.Ok(); } + [AllowAnonymous] + public async Task GetCurrentPublishInfo() + { + var result = await _publishLogRepository.Where(t => t.IsCurrentVersion == true).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + if (result == null) + { + //"系统当前版本未标记,请联系维护人员" + throw new QueryBusinessObjectNotExistException(_localizer["PublishLog_NoCurrentVersion"] ); + } + + return result; + } + + + public async Task> GetPublishVersionSelect() + { + return await _publishLogRepository.ProjectTo(_mapper.ConfigurationProvider).OrderByDescending(t => t.Version).ToListAsync(); + } } } diff --git a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs index 18058ee40..916cabf1f 100644 --- a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs @@ -66,7 +66,8 @@ namespace IRaCIS.Core.Application.Service CreateMap().ReverseMap(); - CreateMap(); + CreateMap() + .ForMember(o => o.Version, t => t.MapFrom(u => u.PublishLog.Version)); CreateMap().ReverseMap(); CreateMap().ReverseMap(); @@ -78,6 +79,10 @@ namespace IRaCIS.Core.Application.Service CreateMap(); CreateMap().ReverseMap(); + CreateMap(); + + CreateMap(); + CreateMap().ReverseMap(); } } diff --git a/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs b/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs index c9178a1d5..bf0a5b710 100644 --- a/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs +++ b/IRaCIS.Core.Application/Service/Doctor/DoctorService.cs @@ -536,10 +536,13 @@ namespace IRaCIS.Application.Services [HttpGet("{doctorId:guid}")] public async Task GetAuditState(Guid doctorId) { + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + var doctor = (await _doctorRepository .ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(t => t.Id == doctorId)).IfNullThrowException(); - doctor.InHoliday = (await _repository.CountAsync(x => x.DoctorId == doctorId && x.EndDate <= DateTime.Now && x.StartDate <= DateTime.Now)) > 0; + doctor.InHoliday = (await _repository.CountAsync(x => x.DoctorId == doctorId && x.EndDate <= appDateTimeNow && x.StartDate <= appDateTimeNow)) > 0; return doctor; } diff --git a/IRaCIS.Core.Application/Service/Doctor/VacationService.cs b/IRaCIS.Core.Application/Service/Doctor/VacationService.cs index 54f7b4ba0..118688920 100644 --- a/IRaCIS.Core.Application/Service/Doctor/VacationService.cs +++ b/IRaCIS.Core.Application/Service/Doctor/VacationService.cs @@ -79,7 +79,10 @@ namespace IRaCIS.Application.Services [NonDynamicMethod] public async Task OnVacation(Guid doctorId) { - var count = await _vacationRepository.CountAsync(u => u.DoctorId == doctorId && u.EndDate >= DateTime.Now && u.StartDate <= DateTime.Now); + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + + var count = await _vacationRepository.CountAsync(u => u.DoctorId == doctorId && u.EndDate >= appDateTimeNow && u.StartDate <= appDateTimeNow); return ResponseOutput.Result(count > 0); } diff --git a/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs index ca19ab0b9..1c32b6040 100644 --- a/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs +++ b/IRaCIS.Core.Application/Service/Document/TrialDocumentService.cs @@ -369,10 +369,10 @@ namespace IRaCIS.Core.Application.Services /// /// 获取确认列表情况 项目文档+系统文档+具体的人 /// - /// + /// /// [HttpPost] - public async Task<(PageOutput, object)> GetDocumentConfirmList(DocumentTrialUnionQuery querySystemDocument) + public async Task<(PageOutput, object)> GetDocumentConfirmList(DocumentTrialUnionQuery inQuery) { @@ -409,15 +409,15 @@ namespace IRaCIS.Core.Application.Services #endregion - var trialInfo = (await _repository.Where(t => t.Id == querySystemDocument.TrialId).Select(t => new { t.TrialFinishedTime, t.TrialStatusStr }).FirstNotNullAsync()); + var trialInfo = (await _repository.Where(t => t.Id == inQuery.TrialId).Select(t => new { t.TrialFinishedTime, t.TrialStatusStr }).FirstNotNullAsync()); - var trialDocQuery = from trialDocumentNeedConfirmedUserType in _repository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) - join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId) - .WhereIf(querySystemDocument.UserId != null, t => t.UserId == querySystemDocument.UserId) - .WhereIf(querySystemDocument.UserTypeId != null, t => t.User.UserTypeId == querySystemDocument.UserTypeId) + var trialDocQuery = from trialDocumentNeedConfirmedUserType in _repository.Where(t => t.TrialDocument.TrialId == inQuery.TrialId) + join trialUser in _repository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.UserId != null, t => t.UserId == inQuery.UserId) + .WhereIf(inQuery.UserTypeId != null, t => t.User.UserTypeId == inQuery.UserTypeId) on trialDocumentNeedConfirmedUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId - join confirm in _repository.Where(t => t.TrialDocument.TrialId == querySystemDocument.TrialId) on + join confirm in _repository.Where(t => t.TrialDocument.TrialId == inQuery.TrialId) on new { trialUser.UserId, TrialDocumentId = trialDocumentNeedConfirmedUserType.TrialDocumentId } equals new { UserId = confirm.ConfirmUserId, confirm.TrialDocumentId } into cc from confirm in cc.DefaultIfEmpty() select new UnionDocumentWithConfirmInfoView() @@ -451,8 +451,8 @@ namespace IRaCIS.Core.Application.Services var systemDocQuery = from needConfirmEdUserType in _repository.WhereIf(trialInfo.TrialFinishedTime != null, u => u.SystemDocument.CreateTime < trialInfo.TrialFinishedTime) - join trialUser in _repository.Where(t => t.TrialId == querySystemDocument.TrialId) - .WhereIf(querySystemDocument.UserId != null, t => t.UserId == querySystemDocument.UserId) + join trialUser in _repository.Where(t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.UserId != null, t => t.UserId == inQuery.UserId) on needConfirmEdUserType.NeedConfirmUserTypeId equals trialUser.User.UserTypeId join confirm in _repository.GetQueryable() on new { ConfirmUserId = trialUser.UserId, SystemDocumentId = needConfirmEdUserType.SystemDocumentId } equals new { confirm.ConfirmUserId, confirm.SystemDocumentId } into cc from confirm in cc.DefaultIfEmpty() @@ -482,14 +482,14 @@ namespace IRaCIS.Core.Application.Services }; var unionQuery = trialDocQuery.Union(systemDocQuery) - .WhereIf(!string.IsNullOrEmpty(querySystemDocument.Name), t => t.Name.Contains(querySystemDocument.Name)) - .WhereIf(querySystemDocument.FileTypeId != null, t => t.FileTypeId == querySystemDocument.FileTypeId) - .WhereIf(querySystemDocument.IsConfirmed == true, t => t.ConfirmTime != null) - .WhereIf(querySystemDocument.IsConfirmed == false, t => t.ConfirmTime == null) - .WhereIf(querySystemDocument.IsDeleted != null, t => t.IsDeleted == querySystemDocument.IsDeleted) - .WhereIf(querySystemDocument.UserTypeId != null, t => t.UserTypeId == querySystemDocument.UserTypeId); + .WhereIf(!string.IsNullOrEmpty(inQuery.Name), t => t.Name.Contains(inQuery.Name)) + .WhereIf(inQuery.FileTypeId != null, t => t.FileTypeId == inQuery.FileTypeId) + .WhereIf(inQuery.IsConfirmed == true, t => t.ConfirmTime != null) + .WhereIf(inQuery.IsConfirmed == false, t => t.ConfirmTime == null) + .WhereIf(inQuery.IsDeleted != null, t => t.IsDeleted == inQuery.IsDeleted) + .WhereIf(inQuery.UserTypeId != null, t => t.UserTypeId == inQuery.UserTypeId); - var result = await unionQuery.ToPagedListAsync(querySystemDocument.PageIndex, querySystemDocument.PageSize, querySystemDocument.SortField, querySystemDocument.Asc); + var result = await unionQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc); var needSignTrialDocCount = await _trialDocumentRepository.AsQueryable(true) .Where(t => t.Trial.TrialUserList.Any(t => t.UserId == _userInfo.Id)) diff --git a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs index dbd0d57d0..5698b1e26 100644 --- a/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs +++ b/IRaCIS.Core.Application/Service/Document/TrialEmailNoticeConfigService.cs @@ -1272,6 +1272,8 @@ namespace IRaCIS.Core.Application.Service public async Task> GetSysEmailNoticeConfigList(EmailNoticeConfigQuery queryEmailNoticeConfig) { var emailNoticeConfigQueryable = _emailNoticeConfigRepository + .WhereIf(queryEmailNoticeConfig.SystemLevel == null, t => t.SystemLevel == SysEmailLevel.not_sys) + .WhereIf(queryEmailNoticeConfig.SystemLevel != null, t => t.SystemLevel == queryEmailNoticeConfig.SystemLevel) .WhereIf(queryEmailNoticeConfig.BusinessScenarioEnum != null, t => t.BusinessScenarioEnum == queryEmailNoticeConfig.BusinessScenarioEnum) .WhereIf(queryEmailNoticeConfig.IsReturnRequired != null, t => t.IsReturnRequired == queryEmailNoticeConfig.IsReturnRequired) .WhereIf(queryEmailNoticeConfig.IsEnable != null, t => t.IsEnable == queryEmailNoticeConfig.IsEnable) @@ -1477,7 +1479,7 @@ namespace IRaCIS.Core.Application.Service { //---发件人配置错误,请核对服务器地址或者授权码是否填写有误 - throw new BusinessValidationFailedException(_localizer["TrialEmailN_InvalidSenderEmailConfig"] + ex.Message); + throw new BusinessValidationFailedException(_localizer["TrialEmailN_InvalidSenderEmailConfig"]); } diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs index ceca4a370..155bc42ad 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/DicomSeriesModel.cs @@ -64,6 +64,8 @@ namespace IRaCIS.Core.Application.Contracts.Dicom.DTO public Guid? StudyId { get; set; } public Guid? InstanceId { get; set; } + + public int? NumberOfFrames { get; set; } } public class InstanceBasicInfo { @@ -77,6 +79,10 @@ namespace IRaCIS.Core.Application.Contracts.Dicom.DTO public int InstanceNumber { get; set; } + public Guid? StudyId { get; set; } + + public Guid? SeriesId { get; set; } + [JsonIgnore] public int ShowOrder { get; set; } [JsonIgnore] diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs index 8226693c0..24982f635 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs @@ -44,6 +44,9 @@ namespace IRaCIS.Core.Application.Contracts public string RecordPath { get; set; } = string.Empty; public bool IsDicom { get; set; } + + + } public class UnionStudyMonitorModel : UnionStudyBaseModel @@ -124,7 +127,7 @@ namespace IRaCIS.Core.Application.Contracts public Guid Id { get; set; } - + public bool IsFromPACS { get; set; } public int? Count { get; set; } @@ -164,6 +167,8 @@ namespace IRaCIS.Core.Application.Contracts public bool? IsSuccess { get; set; } public string? StudyCode { get; set; } + + public bool? IsFromPACS { get; set; } } @@ -209,6 +214,20 @@ namespace IRaCIS.Core.Application.Contracts public int FileCount { get; set; } } + + public class SubejctZipInfoQuery + { + public Guid? SubejctId { get; set; } + + public string? SubjectCode { get; set; } + + + public Guid? SubejectVisitId { get; set; } + + public Guid? TrialReadingCriterionId { get; set; } + + } + public class TaskArchiveStudyCommand { [NotDefault] diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs index 23bc0c273..acf4c3dd5 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DownloadAndUploadService.cs @@ -4,6 +4,7 @@ using IRaCIS.Core.Application.Contracts; using IRaCIS.Core.Application.Filter; using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service.ImageAndDoc.DTO; +using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure.Extention; @@ -11,6 +12,7 @@ using MassTransit; using MathNet.Numerics; using Medallion.Threading; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal; using Newtonsoft.Json; using System; using System.Collections.Generic; @@ -535,6 +537,137 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc } + + /// + /// 受试者级别所有的影像 + /// 访视级别的影响 传递subjectVisitId + /// 标准Id是可选的 不同标准有些检查可能有过滤 + /// + /// + /// + /// + public async Task GetSubejectOrVisitZipInfo([FromServices] IRepository _subjectRepository, SubejctZipInfoQuery inQuery) + { + var isImageFilter = false; + + var criterionModalitys = string.Empty; + + if (inQuery.TrialReadingCriterionId != null) + { + var criterionInfo = await _repository.Where(t => t.Id == inQuery.TrialReadingCriterionId).Select(t => new { t.IsImageFilter, t.CriterionModalitys }).FirstOrDefaultAsync(); + + if (criterionInfo != null) + { + isImageFilter = criterionInfo.IsImageFilter; + criterionModalitys = criterionInfo.CriterionModalitys; + } + + } + + if (inQuery.SubejectVisitId != null) + { + var query = from sv in _subjectVisitRepository.Where(t => t.Id == inQuery.SubejectVisitId) + + + select new + { + SubjectCode = sv.Subject.Code, + VisitName = sv.VisitName, + StudyList = sv.StudyList.AsQueryable().WhereIf(isImageFilter, t => ("|" + criterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|")) + .Select(u => new + { + u.PatientId, + u.StudyTime, + u.StudyCode, + + SeriesList = u.SeriesList.Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList.Select(k => new + { + k.Path + }) + }) + + }), + + NoneDicomStudyList = sv.NoneDicomStudyList.AsQueryable().WhereIf(isImageFilter, t => ("|" + criterionModalitys + "|").Contains("|" + t.Modality + "|")) + .Select(nd => new + { + nd.Modality, + nd.StudyCode, + nd.ImageDate, + + FileList = nd.NoneDicomFileList.Select(file => new + { + file.FileName, + file.Path, + file.FileType + }) + }) + }; + + var result = query.ToList(); + + return ResponseOutput.Ok(result); + } + else if (inQuery.SubejctId != null) + { + var query = from sv in _subjectRepository.Where(t => t.Id == inQuery.SubejctId).SelectMany(t=>t.SubjectVisitList) + + + select new + { + SubjectCode = sv.Subject.Code, + VisitName = sv.VisitName, + StudyList = sv.StudyList.AsQueryable().WhereIf(isImageFilter, t => ("|" + criterionModalitys + "|").Contains("|" + t.ModalityForEdit + "|")) + .Select(u => new + { + u.PatientId, + u.StudyTime, + u.StudyCode, + + SeriesList = u.SeriesList.Select(z => new + { + z.Modality, + + InstancePathList = z.DicomInstanceList.Select(k => new + { + k.Path + }) + }) + + }), + + NoneDicomStudyList = sv.NoneDicomStudyList.AsQueryable().WhereIf(isImageFilter, t => ("|" + criterionModalitys + "|").Contains("|" + t.Modality + "|")) + .Select(nd => new + { + nd.Modality, + nd.StudyCode, + nd.ImageDate, + + FileList = nd.NoneDicomFileList.Select(file => new + { + file.FileName, + file.Path, + file.FileType + }) + }) + }; + + var result = query.ToList(); + + return ResponseOutput.Ok(result); + } + else + { + return ResponseOutput.NotOk("不允许 subjectId subjectId 都不传递"); + } + + + } + /// /// 后台任务调用,前端忽略该接口 /// @@ -732,9 +865,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc foreach (var file in noneDicomStudy.FileList) { string destinationPath = Path.Combine(studyNoneDicomFolderPath, Path.GetFileName(file.FileName)); - + //下载到当前目录 - await _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path) , destinationPath); + await _oSSService.DownLoadFromOSSAsync(HttpUtility.UrlDecode(file.Path), destinationPath); } } diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs index 50c2882a5..cdd161e9b 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -145,6 +145,23 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] public async Task AddOrUpdateArchiveStudy(NewArchiveStudyCommand incommand) { + + var @uploadLock = _distributedLockProvider.CreateLock($"UploadDicom"); + + using (await @uploadLock.AcquireAsync()) + { + if (_provider.Exists($"{incommand.TrialId}_{incommand.Study.StudyInstanceUid}")) + { + //---当前已有人正在上传和归档该检查! + return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_ArchiveInProgress")); + } + else + { + //在事务未完成前 防止前端重复提交 + _provider.Set($"{incommand.TrialId}_{incommand.Study.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(3)); + } + } + var modalitys = string.Empty; try @@ -191,7 +208,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc study.SubjectVisitId = incommand.SubjectVisitId; //如果因为意外情况,连续点击两次,导致第一次插入了,第二次进来也会插入,在此判断一下 - var findStudy = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == study.Id); + var findStudy = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == study.Id); if (findStudy != null) { @@ -339,6 +356,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc finally { _provider.Remove($"StudyUid_{incommand.TrialId}_{incommand.Study.StudyInstanceUid}"); + _provider.Remove($"{incommand.TrialId}_{incommand.Study.StudyInstanceUid}"); } @@ -372,6 +390,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc VisitNum = t.SubjectVisit.VisitNum, IsDicom = true, + IsFromPACS = t.IsFromPACS, SubjectCode = t.Subject.Code, @@ -421,6 +440,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc VisitNum = t.SubjectVisit.VisitNum, IsDicom = false, + IsFromPACS = false, SubjectCode = t.Subject.Code, @@ -455,28 +475,29 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc var unionQuery = dicomStudyQuery.Union(nodeDicomStudyQuery) .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) - .WhereIf(studyQuery.TrialSiteId != null, t => t.TrialSiteId == studyQuery.TrialSiteId); + .WhereIf(studyQuery.TrialSiteId != null, t => t.TrialSiteId == studyQuery.TrialSiteId) + .WhereIf(studyQuery.IsFromPACS != null, t => t.IsFromPACS == studyQuery.IsFromPACS); return await unionQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, studyQuery.SortField, studyQuery.Asc); } [HttpPost] - public async Task> GetDicomAndNoneDicomStudyMonitorList(StudyQuery studyQuery) + public async Task> GetDicomAndNoneDicomStudyMonitorList(StudyQuery inQuery) { - var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(studyQuery.VisitPlanArray); - var StudyMonitorQuery = _repository.Where(t => t.TrialId == studyQuery.TrialId, ignoreQueryFilters: true) + var svExpression = QCCommon.GetStudyMonitorSubjectVisitFilter(inQuery.VisitPlanArray); + var StudyMonitorQuery = _repository.Where(t => t.TrialId == inQuery.TrialId, ignoreQueryFilters: true) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.Subject.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) //.WhereIf(!string.IsNullOrEmpty(studyQuery.VisitPlanInfo), studyQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(studyQuery.VisitPlanInfo)) - .WhereIf(studyQuery.VisitPlanArray != null && studyQuery.VisitPlanArray?.Length > 0, svExpression) - .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.SubjectInfo), t => t.Subject.Code.Contains(studyQuery.SubjectInfo)) - .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) - .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) - .WhereIf(studyQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == studyQuery.TrialSiteId) - .WhereIf(studyQuery.IsDicom != null, t => t.IsDicom == studyQuery.IsDicom) - .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.Uploader), t => t.Uploader.UserName.Contains(studyQuery.Uploader)) - .WhereIf(studyQuery.IsSuccess != null, t => t.IsSuccess == studyQuery.IsSuccess) - .WhereIf(!string.IsNullOrWhiteSpace(studyQuery.StudyCode), t => t.StudyCode.Contains(studyQuery.StudyCode)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.SubjectId != null, t => t.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.SubjectVisitId != null, t => t.SubjectId == inQuery.SubjectVisitId) + .WhereIf(inQuery.TrialSiteId != null, t => t.Subject.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.IsDicom != null, t => t.IsDicom == inQuery.IsDicom) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Uploader), t => t.Uploader.UserName.Contains(inQuery.Uploader)) + .WhereIf(inQuery.IsSuccess != null, t => t.IsSuccess == inQuery.IsSuccess) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.StudyCode), t => t.StudyCode.Contains(inQuery.StudyCode)) .Select(t => new UnionStudyMonitorModel() { TrialId = t.TrialId, @@ -520,7 +541,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc }); - return await StudyMonitorQuery.ToPagedListAsync(studyQuery.PageIndex, studyQuery.PageSize, string.IsNullOrEmpty(studyQuery.SortField) ? "UploadTime" : studyQuery.SortField, studyQuery.Asc); + return await StudyMonitorQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrEmpty(inQuery.SortField) ? "UploadTime" : inQuery.SortField, inQuery.Asc); @@ -540,7 +561,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc path = (await _dicomInstanceRepository.Where(s => s.StudyId == studyId).Select(t => t.Path).FirstOrDefaultAsync()).IfNullThrowException(); - + using (var sw = ImageHelper.RenderPreviewJpeg(path)) { diff --git a/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs b/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs index e900e5c59..55213b696 100644 --- a/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs +++ b/IRaCIS.Core.Application/Service/Institution/DTO/SiteModel.cs @@ -36,6 +36,7 @@ namespace IRaCIS.Application.Contracts public class SiteSelectionDTO { public Guid Id { get; set; } + public string SiteName { get; set; } = String.Empty; public string SiteNameCN { get; set; } = String.Empty; diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserFeedBackViewModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserFeedBackViewModel.cs new file mode 100644 index 000000000..63f864ece --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/DTO/UserFeedBackViewModel.cs @@ -0,0 +1,108 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-30 10:39:12 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using Newtonsoft.Json; +using System.ComponentModel.DataAnnotations; +namespace IRaCIS.Core.Application.ViewModel +{ + /// UserFeedBackView 列表视图模型 + public class UserFeedBackView : UserFeedBackAddOrEdit + { + public string TrialCode { get; set; } + + public string ExperimentName { get; set; } + + public string SubjectCode { get; set; } + + public string TrialSiteCode { get; set; } + + public string SubjectVisitName { get; set; } + + + public string FeedBackUserName { get; set; } + + public string FeedBackFullName { get; set; } + + + public DateTime CreateTime { get; set; } + public DateTime UpdateTime { get; set; } + + public UserTypeEnum UserTypeEnum { get; set; } + } + + + public class GetUserFeedBackQuery + { + public Guid? Id { get; set; } + + public Guid? VisitTaskId { get; set; } + } + + ///UserFeedBackQuery 列表查询参数模型 + public class UserFeedBackQuery : PageInput + { + + public string? TrialKeyInfo { get; set; } + + public string? SubejctAndVisitKeyInfo { get; set; } + + public UserTypeEnum? UserTypeEnum { get; set; } + + public string? FeedBackUserKeyInfo { get; set; } + + public string? QuestionDescription { get; set; } + + public int? QuestionType { get; set; } + + public int? State { get; set; } + + public string? TrialSiteCode { get; set; } + + public DateTime? BeginCreatime { get; set; } + + public DateTime? EndCreatime { get; set; } + + public Guid? TrialId { get; set; } + + + + } + + /// UserFeedBackAddOrEdit 列表查询参数模型 + public class UserFeedBackAddOrEdit + { + public Guid? Id { get; set; } + public Guid? SubjectId { get; set; } + public Guid? SubjectVisitId { get; set; } + public int QuestionType { get; set; } + public string QuestionDescription { get; set; } + + public int State { get; set; } + public Guid? TrialSiteId { get; set; } + + public Guid? TrialId { get; set; } + + public Guid? VisitTaskId { get; set; } + + public List ScreenshotList { get; set; } + + [JsonIgnore] + public string ScreenshotListStr { get; set; } = string.Empty; + } + + + public class BatchUpdateCommand + { + public List IdList { get; set; } + + public int State { get; set; } + } + +} + + diff --git a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs index a54ec9ad4..3f22371f2 100644 --- a/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs +++ b/IRaCIS.Core.Application/Service/Management/DTO/UserModel.cs @@ -39,7 +39,7 @@ namespace IRaCIS.Application.Contracts public Guid Id { get; set; } public string UserName { get; set; } = string.Empty; public string RealName { get; set; } = string.Empty; - public int Sex { get; set; } // 1-男 2-女 + public int? Sex { get; set; } // 1-男 2-女 /// /// LastLoginIP @@ -131,7 +131,7 @@ namespace IRaCIS.Application.Contracts public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; - public int Sex { get; set; } // 1-男 2-女 + public int? Sex { get; set; } // 1-男 2-女 public int Status { get; set; } = 1; // 0-已删除 1-正常 diff --git a/IRaCIS.Core.Application/Service/Management/Interface/IUserFeedBackService.cs b/IRaCIS.Core.Application/Service/Management/Interface/IUserFeedBackService.cs new file mode 100644 index 000000000..e4420f4a3 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/Interface/IUserFeedBackService.cs @@ -0,0 +1,23 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-30 10:39:05 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IUserFeedBackService + /// + public interface IUserFeedBackService + { + + Task> GetUserFeedBackList(UserFeedBackQuery inQuery); + + + Task DeleteUserFeedBack(Guid userFeedBackId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs b/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs index 9bbe12288..e4735770c 100644 --- a/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs +++ b/IRaCIS.Core.Application/Service/Management/SystemNoticeService.cs @@ -27,14 +27,15 @@ namespace IRaCIS.Core.Application.Service [HttpPost] public async Task> GetSystemNoticeList(SystemNoticeQuery querySystemNotice) { - + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; var systemNoticeQueryable = _systemNoticeRepository .WhereIf(querySystemNotice.ApplicableProjectEnum != null, t => t.ApplicableProjectEnum == querySystemNotice.ApplicableProjectEnum) .WhereIf(querySystemNotice.NoticeLevelEnum != null, t => t.NoticeLevelEnum == querySystemNotice.NoticeLevelEnum) .WhereIf(querySystemNotice.NoticeModeEnum != null, t => t.NoticeModeEnum == querySystemNotice.NoticeModeEnum) .WhereIf(querySystemNotice.NoticeStateEnum != null && querySystemNotice.NoticeStateEnum != Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == querySystemNotice.NoticeStateEnum) - .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate !=null && t.EndDate < DateTime.Now) + .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate !=null && t.EndDate < appDateTimeNow) .WhereIf(querySystemNotice.NoticeTypeEnum != null, t => t.NoticeTypeEnum == querySystemNotice.NoticeTypeEnum) .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.FileName), t => t.FileName.Contains(querySystemNotice.FileName)) .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.NoticeContent), t => t.NoticeContent.Contains(querySystemNotice.NoticeContent)) @@ -117,13 +118,16 @@ namespace IRaCIS.Core.Application.Service [HttpPost] public async Task> GetUserSystemNoticeList(SystemNoticeQuery querySystemNotice) { + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + var systemNoticeQueryable = _systemNoticeRepository .Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum==Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished) .WhereIf(querySystemNotice.ApplicableProjectEnum != null, t => t.ApplicableProjectEnum == querySystemNotice.ApplicableProjectEnum) .WhereIf(querySystemNotice.NoticeLevelEnum != null, t => t.NoticeLevelEnum == querySystemNotice.NoticeLevelEnum) .WhereIf(querySystemNotice.NoticeModeEnum != null, t => t.NoticeModeEnum == querySystemNotice.NoticeModeEnum) .WhereIf(querySystemNotice.NoticeStateEnum != null && querySystemNotice.NoticeStateEnum != Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == querySystemNotice.NoticeStateEnum) - .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate != null && t.EndDate < DateTime.Now) + .WhereIf(querySystemNotice.NoticeModeEnum != null && querySystemNotice.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HaveExpired, t => t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && t.EndDate != null && t.EndDate < appDateTimeNow) .WhereIf(querySystemNotice.NoticeTypeEnum != null, t => t.NoticeTypeEnum == querySystemNotice.NoticeTypeEnum) .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.FileName), t => t.FileName.Contains(querySystemNotice.FileName)) .WhereIf(!string.IsNullOrWhiteSpace(querySystemNotice.NoticeContent), t => t.NoticeContent.Contains(querySystemNotice.NoticeContent)) @@ -135,9 +139,12 @@ namespace IRaCIS.Core.Application.Service public async Task> GetUserNoticeList() { + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; + var query = _systemNoticeRepository .Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && !t.NoticeUserReadList.Any(t => t.CreateUserId == _userInfo.Id)) - .Where(t=>t.EndDate==null || t.EndDate != null && t.EndDate > DateTime.Now) + .Where(t=>t.EndDate==null || t.EndDate != null && t.EndDate > appDateTimeNow) .ProjectTo(_mapper.ConfigurationProvider, new { token = _userInfo.UserToken, userId = _userInfo.Id }); return await query.ToListAsync(); diff --git a/IRaCIS.Core.Application/Service/Management/UserFeedBackService.cs b/IRaCIS.Core.Application/Service/Management/UserFeedBackService.cs new file mode 100644 index 000000000..7862f41e7 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Management/UserFeedBackService.cs @@ -0,0 +1,148 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-30 10:39:09 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Newtonsoft.Json; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure; +using MailKit; +using IRaCIS.Application.Services; +namespace IRaCIS.Core.Application.Service +{ + /// + /// UserFeedBackService + /// + [ApiExplorerSettings(GroupName = "Management")] + public class UserFeedBackService : BaseService, IUserFeedBackService + { + + private readonly IRepository _userFeedBackRepository; + + public UserFeedBackService(IRepository userFeedBackRepository) + { + _userFeedBackRepository = userFeedBackRepository; + } + + [HttpPost] + public async Task> GetUserFeedBackList(UserFeedBackQuery inQuery) + { + + var isCRCOrIR = _userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.IndependentReviewer; + + var userFeedBackQueryable = _userFeedBackRepository + .WhereIf(isCRCOrIR, t => t.CreateUserId == _userInfo.Id) + .WhereIf(inQuery.State != null, t => t.State == inQuery.State) + .WhereIf(inQuery.TrialId != null, t => t.TrialId == inQuery.TrialId) + .WhereIf(inQuery.QuestionType != null, t => t.QuestionType == inQuery.QuestionType) + .WhereIf(inQuery.BeginCreatime != null, t => t.CreateTime >= inQuery.BeginCreatime) + + .WhereIf(inQuery.EndCreatime != null, t => t.CreateTime == inQuery.EndCreatime) + .WhereIf(inQuery.UserTypeEnum != null, t => t.CreateUser.UserTypeEnum == inQuery.UserTypeEnum) + .WhereIf(!string.IsNullOrEmpty(inQuery.FeedBackUserKeyInfo), t => t.CreateUser.FullName.Contains(inQuery.FeedBackUserKeyInfo)|| t.CreateUser.UserName.Contains(inQuery.FeedBackUserKeyInfo)) + .WhereIf(!string.IsNullOrEmpty(inQuery.QuestionDescription), t => t.QuestionDescription.Contains(inQuery.QuestionDescription)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialKeyInfo), t => t.Trial.ExperimentName.Contains(inQuery.TrialKeyInfo) || t.Trial.TrialCode.Contains(inQuery.TrialKeyInfo)) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubejctAndVisitKeyInfo), t => t.Subject.Code.Contains(inQuery.SubejctAndVisitKeyInfo) || t.SubjectVisit.VisitName.Contains(inQuery.SubejctAndVisitKeyInfo)) + .WhereIf(!string.IsNullOrEmpty(inQuery.TrialSiteCode), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteCode)) + + .ProjectTo(_mapper.ConfigurationProvider); + + var pageList = await userFeedBackQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(UserFeedBackView.Id) : inQuery.SortField, inQuery.Asc); + + return pageList; + } + + [HttpPost] + public async Task GetUserFeedBackInfo(GetUserFeedBackQuery inQuery) + { + if (inQuery.Id == null && inQuery.VisitTaskId == null) + { + throw new BusinessValidationFailedException("Id 或者VisitTaskId 必传一个"); + } + + var result = await _userFeedBackRepository.WhereIf(inQuery.Id == null, t => t.VisitTaskId == inQuery.VisitTaskId) + .WhereIf(inQuery.VisitTaskId == null, t => t.Id == inQuery.Id).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync(); + + return ResponseOutput.Ok(result); + } + + + public async Task AddOrUpdateUserFeedBack(UserFeedBackAddOrEdit addOrEditUserFeedBack, + [FromServices] IMailVerificationService mailService) + { + addOrEditUserFeedBack.ScreenshotListStr = JsonConvert.SerializeObject(addOrEditUserFeedBack.ScreenshotList); + + if (addOrEditUserFeedBack.VisitTaskId != null) + { + var info = await _repository.Where(t => t.Id == addOrEditUserFeedBack.VisitTaskId).Select(t => new { t.SubjectId, t.SourceSubjectVisitId, t.Subject.TrialSiteId }).FirstOrDefaultAsync(); + + if (info != null) + { + addOrEditUserFeedBack.SubjectId = info.SubjectId; + addOrEditUserFeedBack.TrialSiteId = info.TrialSiteId; + addOrEditUserFeedBack.SubjectVisitId = info.SourceSubjectVisitId; + } + } + + else if (addOrEditUserFeedBack.SubjectVisitId != null) + { + var info = await _repository.Where(t => t.Id == addOrEditUserFeedBack.SubjectVisitId).Select(t => new { t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync(); + + if (info != null) + { + addOrEditUserFeedBack.TrialSiteId = info.TrialSiteId; + addOrEditUserFeedBack.SubjectId = info.SubjectId; + } + } + else if (addOrEditUserFeedBack.SubjectId != null) + { + var info = await _repository.Where(t => t.Id == addOrEditUserFeedBack.SubjectId).Select(t => new { t.TrialSiteId }).FirstOrDefaultAsync(); + + if (info != null) + { + addOrEditUserFeedBack.TrialSiteId = info.TrialSiteId; + } + } + + + + var entity = await _userFeedBackRepository.InsertOrUpdateAsync(addOrEditUserFeedBack, true); + + if (addOrEditUserFeedBack.VisitTaskId != null || addOrEditUserFeedBack.Id == null) + { + await mailService.UserFeedBackMail(entity.Id); + } + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + /// + /// 批量更新状态 + /// + /// + /// + [HttpPut] + public async Task BatchUpdateFeedBackState(BatchUpdateCommand batchUpdateCommand) + { + await _userFeedBackRepository.BatchUpdateNoTrackingAsync(t => batchUpdateCommand.IdList.Contains(t.Id), u => new UserFeedBack() { State = batchUpdateCommand.State }); + + return ResponseOutput.Ok(); + } + + + [HttpDelete("{userFeedBackId:guid}")] + public async Task DeleteUserFeedBack(Guid userFeedBackId) + { + var success = await _userFeedBackRepository.DeleteFromQueryAsync(t => t.Id == userFeedBackId, true); + return ResponseOutput.Ok(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index 9c455dbf4..3fe37375c 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -16,9 +16,9 @@ using EasyCaching.Core; using IRaCIS.Core.Application.Contracts; using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO; using IRaCIS.Core.Application.Auth; -using BeetleX.Redis.Commands; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Application.Helper; +using IP2Region.Net.Abstractions; namespace IRaCIS.Application.Services { @@ -38,6 +38,7 @@ namespace IRaCIS.Application.Services private readonly IReadingImageTaskService _readingImageTaskService; private readonly IOptionsMonitor _verifyConfig; private readonly SystemEmailSendConfig _systemEmailConfig; + public ISearcher _searcher; public UserService(IRepository userRepository, @@ -45,6 +46,7 @@ namespace IRaCIS.Application.Services IRepository verificationCodeRepository, IRepository doctorRepository, IEasyCachingProvider cache, + ISearcher searcher, IReadingImageTaskService readingImageTaskService, IRepository userTrialRepository, IOptionsMonitor verifyConfig, @@ -58,6 +60,7 @@ namespace IRaCIS.Application.Services this._userPassWordLogRepository = userPassWordLogRepository; _verifyConfig = verifyConfig; _cache = cache; + this._searcher = searcher; this._readingImageTaskService = readingImageTaskService; _userRepository = userRepository; _mailVerificationService = mailVerificationService; @@ -104,32 +107,38 @@ namespace IRaCIS.Application.Services { //var dbUser = (await _userRepository.FirstOrDefaultAsync(t => t.Id == userId)).IfNullThrowException(); - if (oldPwd != null && oldPwd == newPwd) + if (_verifyConfig.CurrentValue.OpenUserComplexPassword) { - //---新密码与旧密码相同。 - throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); + if (oldPwd != null && oldPwd == newPwd) + { + //---新密码与旧密码相同。 + throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); + } + + + var dbUser = (await _userRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); + + if (oldPwd != null && dbUser.Password != oldPwd) + { + //---旧密码验证失败。 + throw new BusinessValidationFailedException(_localizer["User_OldPwdInvalid"]); + } + + if (dbUser.Password == newPwd) + { + //---新密码与旧密码相同。 + throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); + } + + var passWordList = await _userPassWordLogRepository.Where(x => x.UserId == userId).OrderByDescending(x => x.CreateTime).Take(2).ToListAsync(); + + if (passWordList.Any(x => x.PassWord == newPwd)) + { + throw new BusinessValidationFailedException(_localizer["User_PassWordRepeat"]); + } + } - - var dbUser = (await _userRepository.Where(t => t.Id == userId).FirstOrDefaultAsync()).IfNullThrowException(); - - if (oldPwd != null && dbUser.Password != oldPwd) - { - //---旧密码验证失败。 - throw new BusinessValidationFailedException(_localizer["User_OldPwdInvalid"]); - } - - if (dbUser.Password == newPwd) - { - //---新密码与旧密码相同。 - throw new BusinessValidationFailedException(_localizer["User_NewOldPwdSame"]); - } - - var passWordList = await _userPassWordLogRepository.Where(x => x.UserId == userId).OrderByDescending(x => x.CreateTime).Take(2).ToListAsync(); - if (passWordList.Any(x => x.PassWord == newPwd)) - { - throw new BusinessValidationFailedException(_localizer["User_PassWordRepeat"]); - } if (oldPwd != null) { await _userPassWordLogRepository.AddAsync(new UserPassWordLog() @@ -141,7 +150,6 @@ namespace IRaCIS.Application.Services }); } - await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == userId, x => new User() { LastChangePassWordTime = DateTime.Now, @@ -149,7 +157,6 @@ namespace IRaCIS.Application.Services await _userPassWordLogRepository.SaveChangesAsync(); - await Task.CompletedTask; } @@ -205,6 +212,11 @@ namespace IRaCIS.Application.Services return ResponseOutput.NotOk(_localizer["User_VerificationCodeExpired"]); } + else if (verificationRecord.EmailOrPhone.Trim() != newEmail.Trim()) + { + //发送验证嘛的和提交的邮箱不一致 + return ResponseOutput.NotOk(_localizer["User_VerificationEmailNotSameWithBefore"]); + } else //验证码正确 并且 没有超时 { @@ -309,15 +321,15 @@ namespace IRaCIS.Application.Services } - try - { - await _mailVerificationService.AdminResetPwdSendEmailAsync(userId, pwd); - } - catch (Exception) - { - //---请检查邮箱地址或者联系维护人员, 邮件发送失败, 未能创建账户成功 - throw new BusinessValidationFailedException(_localizer["User_CreateFailed"]); - } + //try + //{ + await _mailVerificationService.AdminResetPwdSendEmailAsync(userId, pwd); + //} + //catch (Exception) + //{ + // //---请检查邮箱地址或者联系维护人员, 邮件发送失败, 未能创建账户成功 + // throw new BusinessValidationFailedException(_localizer["User_CreateFailed"]); + //} await _userRepository.UpdatePartialNowNoQueryAsync(userId, u => new User() @@ -551,7 +563,9 @@ namespace IRaCIS.Application.Services if (saveItem.IsZhiZhun) { - saveItem.OrganizationName = AppSettings.DefaultInternalOrganizationName; + var organizationName = _userInfo.IsEn_Us ? _systemEmailConfig.OrganizationName : _systemEmailConfig.OrganizationNameCN; + + saveItem.OrganizationName = organizationName; } @@ -648,7 +662,7 @@ namespace IRaCIS.Application.Services public async Task GetUserBasicInfo(Guid userId, string pwd) { - var info = await _userRepository.Where(u => u.Id == userId && u.Password==pwd).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); + var info = await _userRepository.Where(u => u.Id == userId && u.Password == pwd).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); return info; } @@ -660,13 +674,13 @@ namespace IRaCIS.Application.Services /// /// [AllowAnonymous] - public async Task SendMFAEmail(Guid userId, int mfaType ) + public async Task SendMFAEmail(Guid userId, int mfaType) { var userInfo = await _userRepository.Where(u => u.Id == userId).Select(t => new { t.FullName, t.EMail }).FirstOrDefaultAsync(); int verificationCode = new Random().Next(100000, 1000000); - await _mailVerificationService.SenMFAVerifyEmail(userId, userInfo.FullName, userInfo.EMail, verificationCode, (UserMFAType)mfaType ); + await _mailVerificationService.SenMFAVerifyEmail(userId, userInfo.FullName, userInfo.EMail, verificationCode, (UserMFAType)mfaType); var hiddenEmail = EmailMaskHelper.MaskEmail(userInfo.EMail); return ResponseOutput.Ok(hiddenEmail); @@ -682,7 +696,7 @@ namespace IRaCIS.Application.Services [AllowAnonymous] public async Task VerifyMFACodeAsync(Guid userId, string Code) { - var verificationRecord = await _repository.GetQueryable().OrderByDescending(x => x.ExpirationTime).Where(t => t.UserId == userId && t.Code == Code && t.CodeType == VerifyType.Email).FirstOrDefaultAsync(); + var verificationRecord = await _verificationCodeRepository.Where(t => t.UserId == userId && t.Code == Code && t.CodeType == VerifyType.Email).OrderByDescending(x => x.ExpirationTime).FirstOrDefaultAsync(); VerifyEmialGetDoctorInfoOutDto result = new VerifyEmialGetDoctorInfoOutDto(); //检查数据库是否存在该验证码 @@ -701,10 +715,14 @@ namespace IRaCIS.Application.Services //---验证码已经过期。 throw new BusinessValidationFailedException(_localizer["TrialSiteSurvey_ExpiredVerificationCode"]); - + } else //验证码正确 并且 没有超时 { + + + //删除验证码历史记录 + await _verificationCodeRepository.BatchDeleteNoTrackingAsync(t => t.Id == verificationRecord.Id); await _userLogRepository.AddAsync(new UserLog() { IP = _userInfo.IP, LoginUserId = userId, OptUserId = userId, OptType = UserOptType.MFALogin }, true); } @@ -779,7 +797,7 @@ namespace IRaCIS.Application.Services } //超过90天没修改密码 - if (loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-90) > loginUser.LastChangePassWordTime.Value) + if (_verifyConfig.CurrentValue.IsNeedChangePassWord&& loginUser.LastChangePassWordTime != null && DateTime.Now.AddDays(-_verifyConfig.CurrentValue.ChangePassWordDays) > loginUser.LastChangePassWordTime.Value) { loginUser.LoginState = 1; } @@ -788,12 +806,14 @@ namespace IRaCIS.Application.Services //登录成功 清除缓存 _cache.Set(cacheKey, 0, TimeSpan.FromMinutes(lockoutMinutes)); + var ipinfo = _searcher.Search(_userInfo.IP); + var iPRegion = string.Join('|', ipinfo.Split('|').TakeLast(3)); if (loginUser.LastLoginIP != string.Empty) { // 与上一次IP不一致 - if (loginUser.LastLoginIP != _userInfo.IP) + if (loginUser.LastLoginIP != iPRegion) { loginUser.LoginState = 2; } @@ -814,11 +834,11 @@ namespace IRaCIS.Application.Services }); } - + await _userRepository.BatchUpdateNoTrackingAsync(x => x.Id == loginUser.Id, x => new User() { - LastLoginIP = _userInfo.IP, - LastLoginTime= DateTime.Now + LastLoginIP = iPRegion, + LastLoginTime = DateTime.Now }); diff --git a/IRaCIS.Core.Application/Service/Management/UserTypeService.cs b/IRaCIS.Core.Application/Service/Management/UserTypeService.cs index f12c9e3e8..f608346b2 100644 --- a/IRaCIS.Core.Application/Service/Management/UserTypeService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserTypeService.cs @@ -119,12 +119,12 @@ namespace IRaCIS.Core.Application.Contracts if (userTypeSelectEnum == UserTypeSelectEnum.InnerUser) { - userTypeEnums = new List() { UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA ,UserTypeEnum.MW,UserTypeEnum.MC}; + userTypeEnums = new List() { UserTypeEnum.ClinicalResearchCoordinator, UserTypeEnum.ProjectManager, UserTypeEnum.CRA, UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA ,UserTypeEnum.MW,UserTypeEnum.MC}; - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) - { - userTypeEnums.Add(UserTypeEnum.ProjectManager); - } + //if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) + //{ + // userTypeEnums.Add(UserTypeEnum.ProjectManager); + //} } if (userTypeSelectEnum == UserTypeSelectEnum.SiteSurvey) diff --git a/IRaCIS.Core.Application/Service/Management/_MapConfig.cs b/IRaCIS.Core.Application/Service/Management/_MapConfig.cs index 744c906a8..d9ec78199 100644 --- a/IRaCIS.Core.Application/Service/Management/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Management/_MapConfig.cs @@ -125,6 +125,18 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.OptUserName, c => c.MapFrom(t => t.OptUser.UserName)) .ForMember(d => d.OptUserTypeEnum, c => c.MapFrom(t => t.OptUser.UserTypeEnum)) ; + + + CreateMap() + .ForMember(d => d.ExperimentName, c => c.MapFrom(t => t.Trial.ExperimentName)) + .ForMember(d => d.TrialCode, c => c.MapFrom(t => t.Trial.TrialCode)) + .ForMember(d => d.SubjectCode, c => c.MapFrom(t => t.Subject.Code)) + .ForMember(d => d.TrialSiteCode, c => c.MapFrom(t => t.VisitTask.IsAnalysisCreate? t.VisitTask.BlindTrialSiteCode: t.TrialSite.TrialSiteCode)) + .ForMember(d => d.SubjectVisitName, c => c.MapFrom(t => t.SubjectVisit.VisitName)) + .ForMember(d => d.FeedBackUserName, c => c.MapFrom(t => t.CreateUser.UserName)) + .ForMember(d => d.FeedBackFullName, c => c.MapFrom(t => t.CreateUser.FullName)) + .ForMember(d => d.UserTypeEnum, c => c.MapFrom(t => t.CreateUser.UserTypeEnum)); + CreateMap().ReverseMap(); } } diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index c6c72382b..d5981a4a3 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using System.Web; namespace IRaCIS.Core.Application.Contracts { @@ -161,10 +162,62 @@ namespace IRaCIS.Core.Application.Contracts #region 导出模型 + + public class TrainingRecordExportDTO + { + public Guid Id { get; set; } + + public Guid FileTypeId { get; set; } + + public Guid UserTypeId { get; set; } + + public string FileType { get; set; } + + public string Name { get; set; } + + [DictionaryTranslateAttribute("IsSystemDoc")] + public bool IsSystemDoc { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsDeleted { get; set; } + + //上传时间 + public DateTime CreateTime { get; set; } + + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsConfirmed { get; set; } + + public string RealName { get; set; } + + public string UserName { get; set; } + + public string UserTypeShortName { get; set; } + + + public DateTime? ConfirmTime { get; set; } + } + public class CRCVisitExportDTO { + //关闭 未关闭 + [DictionaryTranslateAttribute("CheckIsClosedEnum")] public ChallengeStateEnum ChallengeState { get; set; } + //强行要分为两个字段 有无质疑 + [DictionaryTranslateAttribute("YesOrNo")] + public bool IsHaveChallenge => (int)ChallengeState > 0; + + //临床数据收集 + + public string ClinicalDataCollect => $"{DicomStudyCount},{NoneDicomStudyCount}{(IsBaseLine? (ClinicalInformationTransmissionEnum>0 && IsHaveClinicalData ? ",w/" : ",w/o") :"" )}"; + + public int? DicomStudyCount { get; set; } + public int? NoneDicomStudyCount { get; set; } + + public int ClinicalInformationTransmissionEnum { get; set; } + + public bool IsBaseLine { get; set; } + [DictionaryTranslateAttribute("YesOrNo")] public bool IsUrgent { get; set; } public string BlindName { get; set; } = String.Empty; @@ -197,12 +250,31 @@ namespace IRaCIS.Core.Application.Contracts public DateTime? ReviewAuditTime { get; set; } public DateTime? PreliminaryAuditTime { get; set; } + //审核通过时间 public DateTime? AuditTime => QCProcessEnum == TrialQCProcess.SingleAudit ? PreliminaryAuditTime : (QCProcessEnum == TrialQCProcess.DoubleAudit ? ReviewAuditTime : null); } + public class QCVisitExportDTO: CRCVisitExportDTO + { + //上传人 + public string SubmitUserRealName { get; set; } + + public string PreliminaryAuditUserName { get; set; } + + public string ReviewAuditUserName { get; set; } + + public string CurrentActionUserName { get; set; } + + public string HistoryAuditUserName => string.Join(' ', PreliminaryAuditUserName, ReviewAuditUserName); + } + public class QCChanllengeExportDto { + //审核批次 + [DictionaryTranslateAttribute("CurrentQCType")] + public CurrentQC? CurrentQCEnum { get; set; } + [DictionaryTranslateAttribute("Subject_Visit_Status")] public SubjectStatus SubjectState { get; set; } @@ -253,7 +325,7 @@ namespace IRaCIS.Core.Application.Contracts //public bool IsUrgent { get; set; } - //public DateTime? ReUploadedTime { get; set; } + public DateTime? ReUploadedTime { get; set; } //public RequestBackStateEnum RequestBackState { get; set; } @@ -270,6 +342,7 @@ namespace IRaCIS.Core.Application.Contracts public class SubjectExportDTO { + public string Code { get; set; } = String.Empty; //public int? Age { get; set; } @@ -321,6 +394,8 @@ namespace IRaCIS.Core.Application.Contracts public string LatestVisitName { get; set; } = string.Empty; public string LatestBlindName { get; set; } = string.Empty; + + [DictionaryTranslateAttribute("YesOrNo")] public bool IsMissingImages => MissingSubmmitCount > 0; @@ -601,6 +676,8 @@ namespace IRaCIS.Core.Application.Contracts { public string? TalkContent { get; set; } = String.Empty; public string BlindName { get; set; } = String.Empty; + + [DictionaryTranslateAttribute("YesOrNo")] public bool IsUrgent { get; set; } public DateTime? CheckPassedTime { get; set; } @@ -654,7 +731,6 @@ namespace IRaCIS.Core.Application.Contracts public class ReadingTaskExportDto { - //public TaskAllocationState TaskAllocationState { get; set; } [DictionaryTranslateAttribute("Subject_Visit_Status")] public SubjectStatus SubjectStatus { get; set; } @@ -662,13 +738,14 @@ namespace IRaCIS.Core.Application.Contracts public DateTime? AllocateTime { get; set; } - //public bool IsPMSetBack { get; set; } public string TaskCode { get; set; } public string TaskName { get; set; } public string TaskBlindName { get; set; } + public string FinalTaskName => TaskName != TaskBlindName ? $"{TaskName}/{TaskBlindName}" : TaskName; + public decimal VisitTaskNum { get; set; } [DictionaryTranslateAttribute("ReadingCategory")] @@ -690,6 +767,8 @@ namespace IRaCIS.Core.Application.Contracts public String TrialSiteCode { get; set; } = String.Empty; public string SubjectCode { get; set; } = String.Empty; + public string MedicalNo { get; set; } + public string FinalSubjectCode { get; set; } public string TrialReadingCriterionName { get; set; } @@ -711,8 +790,7 @@ namespace IRaCIS.Core.Application.Contracts { - //public ReadingTaskExportDto ApplyTask { get; set; } - + public string? ReReadingNewTaskCode { get; set; } @@ -742,8 +820,7 @@ namespace IRaCIS.Core.Application.Contracts public class TaskMedicalReviewExportDto : ReadingTaskExportDto { - public string MedicalNo { get; set; } = string.Empty; - + public string QuestionContent { get; set; } [DictionaryTranslateAttribute("MedicalReviewAuditState")] public MedicalReviewAuditState AuditState { get; set; } @@ -755,16 +832,20 @@ namespace IRaCIS.Core.Application.Contracts [DictionaryTranslateAttribute("AuditAdvice")] public AuditAdvice AuditAdviceEnum { get; set; } - [DictionaryTranslateAttribute("YesOrNo")] + //审核结论 + public bool IsHaveQuestion { get; set; } + [DictionaryTranslateAttribute("IsPass")] - + public bool? IsHaveQuestionView => AuditState == MedicalReviewAuditState.WaitAudit ? null : IsHaveQuestion; //public UserSimpleInfo DoctorUser { get; set; } //public UserSimpleInfo MedicalManagerUser { get; set; } - //public string DoctorUserName { get; set; } + public string DoctorUserName { get; set; } + + public string MedicalManagerUserName { get; set; } /// @@ -854,6 +935,9 @@ namespace IRaCIS.Core.Application.Contracts public string SubjectCode { get; set; } = String.Empty; public Guid Id { get; set; } + + public Guid DoctorUserId { get; set; } + public string TaskName { get; set; } public string TaskBlindName { get; set; } @@ -866,6 +950,8 @@ namespace IRaCIS.Core.Application.Contracts public Arm ArmEnum { get; set; } public string UserName { get; set; } + public ReadingTaskState ReadingTaskState { get; set; } + public ReadingCategory ReadingCategory { get; set; } [DictionaryTranslateAttribute("ExistDisease", CriterionType.RECIST1Point1, nameof(OverallTumorEvaluationExport.IsBaseline), "true")] @@ -876,25 +962,32 @@ namespace IRaCIS.Core.Application.Contracts public string OverallTumorEvaluationResult { get; set; } - #region old 废弃 - - //public Guid? JudgeResultTaskId { get; set; } - - //[DictionaryTranslateAttribute("YesOrNo")] - //public bool IsGenerateJudge => JudgeResultTaskId != null; - #endregion public Arm? JudgeArmEnum { get; set; } + //public Guid? JudgeResultTaskId { get; set; } + //根据裁判的任务结果 设置访视任务的这个字段 该字段表示 裁判认同该任务的结果 [DictionaryTranslateAttribute("YesOrNo")] - public bool IsGenerateJudge { get; set; } + public bool? IsGenerateJudge { get; set; } + //[JsonIgnore] + + //public List GlobalTaskAnswerList { get; set; } + } + public class GlobalAnswerInfo + { + public decimal GlobalTaskVisitNum { get; set; } + public Guid VisitTaskId { get; set; } + + public string Answer { get; set; } + + } public class RECIST1Point1EvaluationOfTumorEfficacyExport : OverallTumorEvaluationExport @@ -1121,8 +1214,19 @@ namespace IRaCIS.Core.Application.Contracts } + public class GetNextCRCChallengeInDto + { + [NotDefault] + public Guid TrialId { get; set; } - public class ChallengeQuery : PageInput + /// + /// QCChallengeId + /// + public Guid QCChallengeId { get; set; } + } + + + public class ChallengeQuery : PageInput { [NotDefault] public Guid TrialId { get; set; } diff --git a/IRaCIS.Core.Application/Service/QC/QCListService.cs b/IRaCIS.Core.Application/Service/QC/QCListService.cs index 19699d957..657675744 100644 --- a/IRaCIS.Core.Application/Service/QC/QCListService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCListService.cs @@ -8,7 +8,7 @@ using IRaCIS.Application.Interfaces; using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Application.Service.Reading.Dto; -using static Org.BouncyCastle.Math.EC.ECCurve; +using IRaCIS.Core.Infrastructure; namespace IRaCIS.Core.Application.Image.QA { @@ -54,73 +54,128 @@ namespace IRaCIS.Core.Application.Image.QA /// /// CRC 访视上传列表 /// - /// + /// /// [HttpPost] - public async Task>> GetCRCVisitList(CRCVisitSearchDTO visitSearchDTO) + public async Task>> GetCRCVisitList(CRCVisitSearchDTO inQuery) { - var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); - var query = _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) .Where(t => t.Subject.FinalSubjectVisitId != null ? t.VisitNum <= t.Subject.FinalSubjectVisit.VisitNum : true) - .WhereIf(visitSearchDTO.TrialSiteId != null, t => t.TrialSiteId == visitSearchDTO.TrialSiteId) - .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) - .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) - .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) - .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) - .WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) - .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) - .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .WhereIf(inQuery.AuditStateArray != null && inQuery.AuditStateArray?.Length > 0, t => inQuery.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(inQuery.SubmitState != null, t => t.SubmitState == inQuery.SubmitState) + .WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState) + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) .ProjectTo(_mapper.ConfigurationProvider); var defalutSortArray = new string[] { nameof(SubjectVisit.IsUrgent) + " desc", nameof(SubjectVisit.SubjectId), nameof(SubjectVisit.VisitNum) }; - var pageList = await query.ToPagedListAsync(visitSearchDTO.PageIndex, visitSearchDTO.PageSize, visitSearchDTO.SortField, visitSearchDTO.Asc, string.IsNullOrWhiteSpace(visitSearchDTO.SortField), defalutSortArray); + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - var config = await _repository.Where(t => t.Id == visitSearchDTO.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); - config.IsHaveSubjectClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.Subject ) && x.UploadRole == UploadRole.CRC); - config.IsHaveVisitClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == visitSearchDTO.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && x.UploadRole == UploadRole.CRC); + var config = await _repository.Where(t => t.Id == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + config.IsHaveSubjectClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == inQuery.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.Subject ) && x.UploadRole == UploadRole.CRC); + config.IsHaveVisitClinicalData = await _clinicalDataTrialSet.AnyAsync(x => x.TrialId == inQuery.TrialId && x.IsConfirm && (x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) && x.UploadRole == UploadRole.CRC); return ResponseOutput.Ok (pageList, config); } + /// + /// 获取下一个crc的未关闭的质疑 + /// + /// + /// + /// + [HttpPost] + public async Task GetNextCRCChallenge(GetNextCRCChallengeInDto inDto) + { + var list =await GetCRCChallengeList(new ChallengeQuery() + { + TrialId = inDto.TrialId, + PageIndex = 1, + PageSize = 9999 + }); + + var pageList = list.Data.CurrentPageData.ToList(); + + + var data = pageList.Where(x=>x.Id==inDto.QCChallengeId||x.IsClosed==false).ToList(); + + if (data.Count > 0) + { + + var index = data.ToList().FindIndex(x => x.Id == inDto.QCChallengeId); + + var result = new QCCRCChallengeViewModel() { }; + + if (index + 1 == data.Count()) // 最后一个 + { + throw new BusinessValidationFailedException(_localizer["QCList_CRCFinishChallenge"]); + } + else if (index == -1 || data.Count == 1) // 第一个或者只有一个 + { + if (data[0].Id == inDto.QCChallengeId) + { + throw new BusinessValidationFailedException(_localizer["QCList_CRCFinishChallenge"]); + } + result = data[0]; + } + else + { + result = data.Skip(index + 1).Take(1).First(); + + } + return result; + + } + else + { + throw new BusinessValidationFailedException(_localizer["QCList_CRCFinishChallenge"]); + } + + } + /// /// CRC 质疑列表 /// - /// + /// /// [HttpPost] - public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetCRCChallengeList(ChallengeQuery challengeQuery) + public async Task>> GetCRCChallengeList(ChallengeQuery inQuery) { - var svExpression = QCCommon.GetQCChallengeFilter(challengeQuery.VisitPlanArray); + var svExpression = QCCommon.GetQCChallengeFilter(inQuery.VisitPlanArray); - var query2 = _repository.Where(x => x.TrialId == challengeQuery.TrialId) + var query = _repository.Where(x => x.TrialId == inQuery.TrialId) //.WhereIf(challengeQuery.ChallengeState != null, t => t.SubjectVisit.ChallengeState == challengeQuery.ChallengeState) - .WhereIf(challengeQuery.ReuploadEnum != null, t => t.ReuploadEnum == challengeQuery.ReuploadEnum) - .WhereIf(challengeQuery.IsClosed != null, t => t.IsClosed == challengeQuery.IsClosed) - .WhereIf(challengeQuery.TrialSiteId != null, t => t.SubjectVisit.TrialSiteId == challengeQuery.TrialSiteId) - .WhereIf(challengeQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == challengeQuery.SubjectId) - .WhereIf(challengeQuery.CreateUserId != null, t => t.CreateUserId == challengeQuery.CreateUserId) - .WhereIf(challengeQuery.SubjectCode != null, t => t.SubjectVisit.Subject.Code.Contains(challengeQuery.SubjectCode!)) - .WhereIf(challengeQuery.VisitPlanArray != null && challengeQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(inQuery.ReuploadEnum != null, t => t.ReuploadEnum == inQuery.ReuploadEnum) + .WhereIf(inQuery.IsClosed != null, t => t.IsClosed == inQuery.IsClosed) + .WhereIf(inQuery.TrialSiteId != null, t => t.SubjectVisit.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.SubjectVisit.SubjectId == inQuery.SubjectId) + .WhereIf(inQuery.CreateUserId != null, t => t.CreateUserId == inQuery.CreateUserId) + .WhereIf(inQuery.SubjectCode != null, t => t.SubjectVisit.Subject.Code.Contains(inQuery.SubjectCode!)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) //.WhereIf(!string.IsNullOrEmpty(challengeQuery.VisitPlanInfo), challengeQuery.VisitPlanInfo.Contains('.') ? t => t.SubjectVisit.VisitNum.ToString().Contains(".") : t => t.SubjectVisit.VisitNum == decimal.Parse(challengeQuery.VisitPlanInfo)) - .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) - .WhereIf(challengeQuery.IsOverTime != null && challengeQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) - .WhereIf(challengeQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == challengeQuery.IsUrgent) + .WhereIf(inQuery.IsOverTime != null && inQuery.IsOverTime == true, t => t.IsClosed ? t.ClosedTime > t.DeadlineTime : DateTime.Now > t.DeadlineTime) + .WhereIf(inQuery.IsOverTime != null && inQuery.IsOverTime == false, t => t.IsClosed ? t.ClosedTime < t.DeadlineTime : DateTime.Now < t.DeadlineTime) + .WhereIf(inQuery.IsUrgent != null, t => t.SubjectVisit.IsUrgent == inQuery.IsUrgent) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.SubjectVisit.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) .ProjectTo(_mapper.ConfigurationProvider); - var pageList = await query2.ToPagedListAsync(challengeQuery.PageIndex, challengeQuery.PageSize, challengeQuery.SortField, challengeQuery.Asc, string.IsNullOrWhiteSpace(challengeQuery.SortField), new string[] { "IsUrgent desc", "IsClosed asc" }); + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), new string[] { "IsUrgent desc", "CreateTime asc" }); - var config = await _repository.Where(t => t.Id == challengeQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); - return (pageList, config); + var config = await _repository.Where(t => t.Id == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + return ResponseOutput.Ok (pageList, config); } @@ -182,39 +237,39 @@ namespace IRaCIS.Core.Application.Image.QA return result.Data.CurrentPageData.Count > 0 ? result.Data.CurrentPageData[0] : null; } - /// - /// QC 访视列表 - /// - /// - /// - [HttpPost] - public async Task>> GetQCVisitList(QCVisitSearchDTO visitSearchDTO) + /// + /// QC 访视列表 + /// + /// + /// + [HttpPost] + public async Task>> GetQCVisitList(QCVisitSearchDTO inQuery) { - var svExpression = QCCommon.GetSubjectVisitFilter(visitSearchDTO.VisitPlanArray); - var query = _subjectVisitRepository.Where(x => x.TrialId == visitSearchDTO.TrialId) - .WhereIf(visitSearchDTO.VisitId != null, t => t.Id == visitSearchDTO.VisitId) - .WhereIf(visitSearchDTO.CurrentActionUserId != null, t => t.CurrentActionUserId == visitSearchDTO.CurrentActionUserId) - .WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) - .WhereIf(visitSearchDTO.TrialSiteId != null, t => t.TrialSiteId == visitSearchDTO.TrialSiteId) - .WhereIf(visitSearchDTO.SubjectId != null, t => t.Subject.Id == visitSearchDTO.SubjectId) - .WhereIf(!string.IsNullOrEmpty(visitSearchDTO.SubjectInfo), t => /*t.Subject.FirstName.Contains(subjectInfo) || t.Subject.LastName.Contains(subjectInfo) ||*/ t.Subject.Code.Contains(visitSearchDTO.SubjectInfo)) - .WhereIf(visitSearchDTO.VisitPlanArray != null && visitSearchDTO.VisitPlanArray?.Length > 0, svExpression) + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) + .WhereIf(inQuery.VisitId != null, t => t.Id == inQuery.VisitId) + .WhereIf(inQuery.CurrentActionUserId != null, t => t.CurrentActionUserId == inQuery.CurrentActionUserId) + .WhereIf(inQuery.ChallengeState != null, t => t.ChallengeState == inQuery.ChallengeState) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(inQuery.SubjectId != null, t => t.Subject.Id == inQuery.SubjectId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => /*t.Subject.FirstName.Contains(subjectInfo) || t.Subject.LastName.Contains(subjectInfo) ||*/ t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) //.WhereIf(!string.IsNullOrEmpty(visitSearchDTO.VisitPlanInfo), visitSearchDTO.VisitPlanInfo.Contains('.') ? t => t.VisitNum.ToString().Contains(".") : t => t.VisitNum == decimal.Parse(visitSearchDTO.VisitPlanInfo)) //.WhereIf(visitSearchDTO.AuditState != null, t => t.AuditState == visitSearchDTO.AuditState) - .WhereIf(visitSearchDTO.AuditStateArray != null && visitSearchDTO.AuditStateArray?.Length > 0, t => visitSearchDTO.AuditStateArray!.Contains(t.AuditState)) + .WhereIf(inQuery.AuditStateArray != null && inQuery.AuditStateArray?.Length > 0, t => inQuery.AuditStateArray!.Contains(t.AuditState)) - .WhereIf(visitSearchDTO.HandleUserId != null, t => t.PreliminaryAuditUserId == visitSearchDTO.HandleUserId || t.CurrentActionUserId == visitSearchDTO.HandleUserId - || t.ReviewAuditUserId == visitSearchDTO.HandleUserId - || t.QCChallengeList.Any(t => t.CreateUserId == visitSearchDTO.HandleUserId) - || t.QCChallengeDialogList.Any(t => t.CreateUserId == visitSearchDTO.HandleUserId)) - .WhereIf(visitSearchDTO.IsUrgent != null, t => t.IsUrgent == visitSearchDTO.IsUrgent) + .WhereIf(inQuery.HandleUserId != null, t => t.PreliminaryAuditUserId == inQuery.HandleUserId || t.CurrentActionUserId == inQuery.HandleUserId + || t.ReviewAuditUserId == inQuery.HandleUserId + || t.QCChallengeList.Any(t => t.CreateUserId == inQuery.HandleUserId) + || t.QCChallengeDialogList.Any(t => t.CreateUserId == inQuery.HandleUserId)) + .WhereIf(inQuery.IsUrgent != null, t => t.IsUrgent == inQuery.IsUrgent) .Where(t => t.SubmitState != SubmitStateEnum.None) - .WhereIf(visitSearchDTO.BeginAuditTime != null, t => t.Trial.QCProcessEnum==TrialQCProcess.SingleAudit? t.PreliminaryAuditTime>= visitSearchDTO.BeginAuditTime: - (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit?t.ReviewAuditTime>= visitSearchDTO.BeginAuditTime:true)) + .WhereIf(inQuery.BeginAuditTime != null, t => t.Trial.QCProcessEnum==TrialQCProcess.SingleAudit? t.PreliminaryAuditTime>= inQuery.BeginAuditTime: + (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit?t.ReviewAuditTime>= inQuery.BeginAuditTime:true)) - .WhereIf(visitSearchDTO.EndAuditTime != null, t => t.Trial.QCProcessEnum == TrialQCProcess.SingleAudit ? t.PreliminaryAuditTime <= visitSearchDTO.EndAuditTime : - (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit ? t.ReviewAuditTime <= visitSearchDTO.EndAuditTime : true)) + .WhereIf(inQuery.EndAuditTime != null, t => t.Trial.QCProcessEnum == TrialQCProcess.SingleAudit ? t.PreliminaryAuditTime <= inQuery.EndAuditTime : + (t.Trial.QCProcessEnum == TrialQCProcess.DoubleAudit ? t.ReviewAuditTime <= inQuery.EndAuditTime : true)) //.WhereIf(visitSearchDTO.SubmitState != null, t => t.SubmitState == visitSearchDTO.SubmitState) //.WhereIf(visitSearchDTO.ChallengeState != null, t => t.ChallengeState == visitSearchDTO.ChallengeState) @@ -222,12 +277,12 @@ namespace IRaCIS.Core.Application.Image.QA var defalutSortArray = new string[] { nameof(QCVisitViewModel.IsUrgent) + " desc", nameof(QCVisitViewModel.SubjectId), nameof(QCVisitViewModel.VisitNum) }; //var defalutSortArray = new string[] { nameof(SubjectVisit.IsUrgent) + " desc", nameof(QCVisitViewModel.AuditState) +" asc" }; - var pageList = await query.ToPagedListAsync(visitSearchDTO.PageIndex, visitSearchDTO.PageSize, visitSearchDTO.SortField, visitSearchDTO.Asc, string.IsNullOrWhiteSpace(visitSearchDTO.SortField), defalutSortArray); + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc, string.IsNullOrWhiteSpace(inQuery.SortField), defalutSortArray); - var config = await _repository.Where(t => t.Id == visitSearchDTO.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + var config = await _repository.Where(t => t.Id == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); return ResponseOutput.Ok (pageList, config); @@ -240,26 +295,26 @@ namespace IRaCIS.Core.Application.Image.QA /// /// 获取一致性核查列表 CRC/PM 公用 /// - /// + /// /// [HttpPost] - public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetConsistencyVerificationList(CheckQuery checkQuery) + public async Task<(PageOutput, TrialSubjectAndSVConfig)> GetConsistencyVerificationList(CheckQuery inQuery) { - var svExpression = QCCommon.GetSubjectVisitFilter(checkQuery.VisitPlanArray); + var svExpression = QCCommon.GetSubjectVisitFilter(inQuery.VisitPlanArray); - var query = _subjectVisitRepository.Where(x => x.TrialId == checkQuery.TrialId) + var query = _subjectVisitRepository.Where(x => x.TrialId == inQuery.TrialId) .Where(x => x.AuditState == AuditStateEnum.QCPassed) //一致性核查中的,或者还没一致性核查的 - .WhereIf(checkQuery.CheckState != null, t => t.CheckState == checkQuery.CheckState) - .WhereIf(checkQuery.TrialSiteId != null, t => t.TrialSiteId == checkQuery.TrialSiteId) - .WhereIf(!string.IsNullOrEmpty(checkQuery.SubjectInfo), t => t.Subject.Code.Contains(checkQuery.SubjectInfo)) - .WhereIf(checkQuery.VisitPlanArray != null && checkQuery.VisitPlanArray?.Length > 0, svExpression) + .WhereIf(inQuery.CheckState != null, t => t.CheckState == inQuery.CheckState) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) + .WhereIf(!string.IsNullOrEmpty(inQuery.SubjectInfo), t => t.Subject.Code.Contains(inQuery.SubjectInfo)) + .WhereIf(inQuery.VisitPlanArray != null && inQuery.VisitPlanArray?.Length > 0, svExpression) //.WhereIf(!string.IsNullOrEmpty(checkQuery.VisitPlanInfo), checkQuery.VisitPlanInfo.Contains('.') ? t => t.InPlan == false : t => t.VisitNum == decimal.Parse(checkQuery.VisitPlanInfo)) .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id))//CRC 过滤负责的site .ProjectTo(_mapper.ConfigurationProvider); - var pageList = await query.ToPagedListAsync(checkQuery.PageIndex, checkQuery.PageSize, checkQuery.SortField, checkQuery.Asc); - var config = await _repository.Where(t => t.Id == checkQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField, inQuery.Asc); + var config = await _repository.Where(t => t.Id == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); return (pageList, config); } diff --git a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs index e89394edd..3bccf5c80 100644 --- a/IRaCIS.Core.Application/Service/QC/QCOperationService.cs +++ b/IRaCIS.Core.Application/Service/QC/QCOperationService.cs @@ -17,6 +17,7 @@ using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Domain.Models; using IRaCIS.Core.Application.Service; using Medallion.Threading; +using DocumentFormat.OpenXml.Office2010.Excel; namespace IRaCIS.Core.Application.Image.QA { @@ -802,28 +803,46 @@ namespace IRaCIS.Core.Application.Image.QA var succeess2 = await _repository.BatchDeleteAsync(t => t.StudyId == id); var success3 = await _dicomSeriesrepository.BatchDeleteNoTrackingAsync(t => t.StudyId == id); + await _repository.BatchUpdateAsync(t => t.Id == id,u=>new SCPStudy() { SubjectVisitId=null}); + + + //var success3 = await _dicomSeriesrepository.DeleteFromQueryAsync(t => t.StudyId == id, true); //var success4 = await _repository.BatchDeleteAsync(t => t.StudyId == id); //删除 物理文件 - var instancePathList = await _repository.Where(t => t.StudyId == id) - .Select(t => t.Path).ToListAsync(); + //var instancePathList = await _repository.Where(t => t.StudyId == id) + // .Select(t => t.Path).ToListAsync(); - instancePathList.ForEach(path => - { + //instancePathList.ForEach(path => + //{ - var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); + // var physicalPath = FileStoreHelper.GetPhysicalFilePath(_hostEnvironment, path); - if (System.IO.File.Exists(physicalPath)) - { - File.Delete(physicalPath); - } - }); + // if (System.IO.File.Exists(physicalPath)) + // { + // File.Delete(physicalPath); + // } + //}); } + var subjectId=waitDeleteStudyList.Select(t=>t.SubjectId).FirstOrDefault(); + + var patientList = _repository.Where(t => t.SubjectId == subjectId).Select(t => t.Id).ToList(); + + foreach (var patientId in patientList) + { + if (_repository.Where(t=>t.Id== patientId).Any(t => t.SCPStudyList.Count() == t.SCPStudyList.Where(t => t.SubjectVisitId == null).Count())) + { + await _repository.BatchUpdateAsync(t => t.Id == patientId, u => new SCPPatient() { SubjectId = null }); + } + } + + + await _subjectVisitRepository.SaveChangesAsync(); diff --git a/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs b/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs index 0521af0ca..ea2f77451 100644 --- a/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs +++ b/IRaCIS.Core.Application/Service/QC/TrialQCQuestionService.cs @@ -54,9 +54,9 @@ namespace IRaCIS.Core.Application.Contracts var trialQCQuestionQueryable = _trialQcQuestionRepository.Where(t => t.TrialId == queryTrialQCQuestionConfigure.TrialId) .WhereIf(!string.IsNullOrWhiteSpace(queryTrialQCQuestionConfigure.QuestionName), t => t.QuestionName.Contains(queryTrialQCQuestionConfigure.QuestionName)) .WhereIf(!string.IsNullOrWhiteSpace(queryTrialQCQuestionConfigure.Type), t => t.Type.Contains(queryTrialQCQuestionConfigure.Type)) - .WhereIf(queryTrialQCQuestionConfigure.IsEnable != null, t => t.IsEnable == queryTrialQCQuestionConfigure.IsEnable) - .WhereIf(queryTrialQCQuestionConfigure.IsRequired != null, t => t.IsRequired == queryTrialQCQuestionConfigure.IsRequired) - .WhereIf(queryTrialQCQuestionConfigure.LanguageType != null, t => t.LanguageType == queryTrialQCQuestionConfigure.LanguageType) + .WhereIf(queryTrialQCQuestionConfigure.IsEnable != null, t => t.IsEnable == queryTrialQCQuestionConfigure.IsEnable) + .WhereIf(queryTrialQCQuestionConfigure.IsRequired != null, t => t.IsRequired == queryTrialQCQuestionConfigure.IsRequired) + .WhereIf(queryTrialQCQuestionConfigure.LanguageType != null, t => t.LanguageType == queryTrialQCQuestionConfigure.LanguageType) .ProjectTo(_mapper.ConfigurationProvider); diff --git a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs index 008c94347..b1bfd53c1 100644 --- a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs @@ -25,10 +25,22 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.QCProcessEnum, u => u.MapFrom(s => s.Trial.QCProcessEnum)) .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) .ForMember(d => d.SubmitUserName, u => u.MapFrom(s => s.SubmitUser.FullName)) - + .ForMember(d => d.ClinicalInformationTransmissionEnum, u => u.MapFrom(s => s.Trial.ClinicalInformationTransmissionEnum)) + .ForMember(d => d.IsHaveClinicalData, u => u.MapFrom(t => t.IsBaseLine ? t.PreviousHistoryList.Any() || t.PreviousOtherList.Any() || t.ReadingClinicalDataList.Any(x => x.ClinicalDataTrialSet.UploadRole == Domain.Share.UploadRole.CRC && x.ReadingClinicalDataPDFList.Count > 0) - || t.PreviousSurgeryList.Any() : false)); + || t.PreviousSurgeryList.Any() : false)) + + .ForMember(d => d.DicomStudyCount, u => u.MapFrom(t => t.StudyList.Count())) + .ForMember(d => d.NoneDicomStudyCount, u => u.MapFrom(t => t.NoneDicomStudyList.Count(t => t.NoneDicomFileList.Any()))); + + CreateMap().IncludeBase() + .ForMember(d => d.SubmitUserRealName, u => u.MapFrom(s => s.SubmitUser.FullName)) + + .ForMember(d => d.CurrentActionUserName, u => u.MapFrom(s => s.CurrentActionUser.UserName)) + .ForMember(d => d.PreliminaryAuditUserName, u => u.MapFrom(s => s.PreliminaryAuditUser.UserName)) + .ForMember(d => d.ReviewAuditUserName, u => u.MapFrom(s => s.ReviewAuditUser.UserName)); + @@ -41,7 +53,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.CreateUserName, u => u.MapFrom(s => s.CreateUser.UserName)) .ForMember(d => d.LatestReplyUserName, u => u.MapFrom(t => t.LatestReplyUser.UserName)) - .ForMember(d => d.DialogStr, u => u.MapFrom(t => string.Join(" | ", t.DialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " " + c.CreateTime.ToString("yyyy-mm-dd hh:mm:ss") + " :" + c.TalkContent)))) + .ForMember(d => d.DialogStr, u => u.MapFrom(t => string.Join("\n\n", t.DialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " (" + c.CreateTime.ToString("yyyy-MM-dd HH:mm:ss") + ") :" + c.TalkContent)))) .ForMember(d => d.SubjectState, u => u.MapFrom(s => s.SubjectVisit.Subject.Status)); @@ -105,7 +117,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.SubjectCode, u => u.MapFrom(s => s.Subject.Code)) .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) - .ForMember(d => d.CheckDialogStr, u => u.MapFrom(t => string.Join(" | ", t.CheckChallengeDialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " " + c.CreateTime.ToString("yyyy-MM-dd HH:mm:ss") + " :" + c.TalkContent)))) + .ForMember(d => d.CheckDialogStr, u => u.MapFrom(t => string.Join("\n\n", t.CheckChallengeDialogList.OrderBy(t => t.CreateTime).Select(c => c.CreateUser.UserName + " (" + c.CreateTime.ToString("yyyy-MM-dd HH:mm:ss") + ") :" + c.TalkContent)))) .ForMember(d => d.ModalityList, c => c.MapFrom(s => (s.NoneDicomStudyList.Select(t => t.Modality) .Union(s.StudyList.Select(k => k.ModalityForEdit))).Distinct())) @@ -141,13 +153,16 @@ namespace IRaCIS.Core.Application.Service .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.VisitTask.TrialReadingCriterion.CriterionName)) .ForMember(o => o.MedicalNo, t => t.MapFrom(u => u.VisitTask.Subject.MedicalNo)) - //.ForMember(o => o.DoctorUserName, t => t.MapFrom(u => u.VisitTask.DoctorUser.UserName)) - .ForMember(o => o.MedicalManagerUserName, t => t.MapFrom(u => u.MedicalManagerUser.UserName)); + .ForMember(o => o.DoctorUserName, t => t.MapFrom(u => u.VisitTask.DoctorUser.UserName)) + .ForMember(o => o.MedicalManagerUserName, t => t.MapFrom(u => u.MedicalManagerUser.UserName)) + .ForMember(o => o.QuestionContent, t => t.MapFrom(u=> string.Join("\n\n", u.ReadingMedicalReviewDialogList.Where(t => t.IsHaveQuestion).Select(t=>t.Questioning))) ); CreateMap() .ForMember(o => o.TrialReadingCriterionName, t => t.MapFrom(u => u.TrialReadingCriterion.CriterionName)) .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindTrialSiteCode : u.Subject.TrialSite.TrialSiteCode)) - .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code)); + .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.IsSelfAnalysis == true ? u.BlindSubjectCode : u.Subject.Code)) + + ; CreateMap() @@ -177,13 +192,17 @@ namespace IRaCIS.Core.Application.Service .ForMember(o => o.OverallTumorEvaluationResult, t => t.MapFrom(u => criterionType == CriterionType.RECIST1Point1 ?( u.SourceSubjectVisit.IsBaseLine==true ? u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.ExistDisease).FirstOrDefault()!.Answer: - u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).FirstOrDefault()!.Answer) + u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.Tumor).Select(t=>t.IsGlobalChange?t.GlobalChangeAnswer:t.Answer).FirstOrDefault()) : criterionType == CriterionType.PCWG3 ? u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.SiteVisitForTumorEvaluation).FirstOrDefault()!.Answer : String.Empty )) .ForMember(o => o.TrialSiteCode, t => t.MapFrom(u => u.Subject.TrialSite.TrialSiteCode)) .ForMember(o => o.SubjectCode, t => t.MapFrom(u => u.Subject.Code)) - .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)); + .ForMember(o => o.UserName, t => t.MapFrom(u => u.DoctorUser.UserName)) + //.ForMember(o => o.GlobalTaskAnswerList, t => t.MapFrom(u => u.GlobalVisitResultList.Where(t=>t.GlobalAnswerType== GlobalAnswerType.Question).Select(c=>new GlobalAnswerInfo() { GlobalTaskVisitNum=c.VisitTask.VisitTaskNum,VisitTaskId=c.TaskId ,Answer=c.Answer}))) + + + ; CreateMap().IncludeBase() .ForMember(o => o.TargetlesionEvaluationResult, t => t.MapFrom(u => u.ReadingTaskQuestionAnswerList.Where(c => c.ReadingQuestionTrial.QuestionType == QuestionType.TargetLesion).FirstOrDefault()!.Answer)) diff --git a/IRaCIS.Core.Application/Service/Reading/AdditionalEvaluate/SubjectCriteriaEvaluationService.cs b/IRaCIS.Core.Application/Service/Reading/AdditionalEvaluate/SubjectCriteriaEvaluationService.cs index e6b07c261..82f19867c 100644 --- a/IRaCIS.Core.Application/Service/Reading/AdditionalEvaluate/SubjectCriteriaEvaluationService.cs +++ b/IRaCIS.Core.Application/Service/Reading/AdditionalEvaluate/SubjectCriteriaEvaluationService.cs @@ -442,9 +442,9 @@ namespace IRaCIS.Core.Application.Service join subjectCriteriaEvaluationVisitFilter in _subjectCriteriaEvaluationVisitFilterRepository .Where(t => t.TrialReadingCriterionId == inQuery.TrialReadingCriterionId) - .WhereIf(inQuery.ImageDeterminationResultState != null, t => t.ImageDeterminationResultState == inQuery.ImageDeterminationResultState) - .WhereIf(inQuery.ImageFilterState != null, t => t.ImageFilterState == inQuery.ImageFilterState) - .WhereIf(inQuery.IsGeneratedTask != null, t => t.IsGeneratedTask == inQuery.IsGeneratedTask) + .WhereIf(inQuery.ImageDeterminationResultState != null, t => t.ImageDeterminationResultState == inQuery.ImageDeterminationResultState) + .WhereIf(inQuery.ImageFilterState != null, t => t.ImageFilterState == inQuery.ImageFilterState) + .WhereIf(inQuery.IsGeneratedTask != null, t => t.IsGeneratedTask == inQuery.IsGeneratedTask) on subjectVisit.Id equals subjectCriteriaEvaluationVisitFilter.SubjectVisitId //into d from subjectCriteriaEvaluationVisitFilter in d.DefaultIfEmpty() @@ -687,7 +687,7 @@ namespace IRaCIS.Core.Application.Service var subjectVisit = await _subjectVisitRepository.FirstOrDefaultAsync(t => t.Id == command.SubjectVisitId); //仅仅影响该标准自己的任务 - Expression> filterExpression = t => t.TrialId == command.TrialId && t.SubjectId == command.SubjectId && t.TaskState == TaskState.Effect && t.TaskAllocationState == TaskAllocationState.Allocated + Expression> filterExpression = t => t.TrialId == command.TrialId && t.SubjectId == command.SubjectId && t.TaskState == TaskState.Effect /*&& t.TaskAllocationState == TaskAllocationState.Allocated*/ && t.TrialReadingCriterionId == command.TrialReadingCriterionId; //有序 diff --git a/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs index 6256d045a..934262818 100644 --- a/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ClinicalData/ReadingClinicalDataService.cs @@ -10,6 +10,9 @@ using System.Linq.Expressions; using IRaCIS.Core.Infrastructure; using System.Linq.Dynamic.Core; using Microsoft.Extensions.Logging; +using IRaCIS.Core.Infrastructure.Extention; +using System.Linq; +using NPOI.SS.Formula.Functions; namespace IRaCIS.Application.Services { @@ -20,6 +23,7 @@ namespace IRaCIS.Application.Services public class ReadingClinicalDataService : BaseService, IReadingClinicalDataService { private readonly IRepository _readingClinicalDataRepository; + private readonly IRepository _readingConsistentClinicalDataRepository; private readonly IRepository _clinicalDataTrialSetRepository; private readonly IRepository _visitTaskRepository; private readonly IRepository _previousPDFRepository; @@ -35,8 +39,10 @@ namespace IRaCIS.Application.Services private readonly IRepository _subjectRepository; private readonly IRepository _readModuleRepository; private readonly IRepository _readingClinicalDataPDFRepository; + private readonly IRepository _readingConsistentClinicalDataPDFRepository; public ReadingClinicalDataService(IRepository readingClinicalDataRepository, + IRepository readingConsistentClinicalDataRepository, IRepository clinicalDataTrialSetRepository, IRepository previousPDFRepository, IRepository subjectVisitRepository, @@ -45,15 +51,18 @@ namespace IRaCIS.Application.Services IRepository clinicalFormRepository, IRepository previousOtherRepository, IRepository previousSurgeryRepository, + IRepository readingQuestionCriterionTrialRepository, IServiceProvider IServiceProvider, IRepository subjectRepository, IRepository readModuleRepository, IRepository readingClinicalDataPDFRepository, + IRepository readingConsistentClinicalDataPDFRepository, IRepository visitTaskRepository) { this._readingClinicalDataRepository = readingClinicalDataRepository; + this._readingConsistentClinicalDataRepository = readingConsistentClinicalDataRepository; this._clinicalDataTrialSetRepository = clinicalDataTrialSetRepository; this._previousPDFRepository = previousPDFRepository; this._subjectVisitRepository = subjectVisitRepository; @@ -67,6 +76,7 @@ namespace IRaCIS.Application.Services this._subjectRepository = subjectRepository; this._readModuleRepository = readModuleRepository; this._readingClinicalDataPDFRepository = readingClinicalDataPDFRepository; + this._readingConsistentClinicalDataPDFRepository = readingConsistentClinicalDataPDFRepository; this._visitTaskRepository = visitTaskRepository; } @@ -83,10 +93,7 @@ namespace IRaCIS.Application.Services { var existsQuery = _readingClinicalDataRepository .WhereIf(indto.Id != null, x => x.Id != indto.Id) - .Where(x => x.ClinicalDataTrialSetId == indto.ClinicalDataTrialSetId && x.ReadingId == indto.ReadingId&&x.StudyId==indto.StudyId); - - - + .Where(x => x.ClinicalDataTrialSetId == indto.ClinicalDataTrialSetId && x.ReadingId == indto.ReadingId && x.StudyId == indto.StudyId); if (await existsQuery.AnyAsync()) { @@ -103,10 +110,10 @@ namespace IRaCIS.Application.Services var entity = _mapper.Map(indto); entity.ReadingClinicalDataPDFList = indto.AddFileList.Select(x => new ReadingClinicalDataPDF() { - FileName = x.FileName, Path = x.Path, - + Size = x.Size, + Type = x.Type, }).ToList(); entity.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; @@ -127,7 +134,8 @@ namespace IRaCIS.Application.Services FileName = x.FileName, Path = x.Path, - + Size = x.Size, + Type = x.Type, ReadingClinicalDataId = entity.Id, }).ToList(); @@ -187,58 +195,60 @@ namespace IRaCIS.Application.Services public async Task> GetStudyClinicalData(GetStudyClinicalDataInDto inDto) { - var cRCClinicalDataList = await _readingClinicalDataRepository.Where(x => x.ReadingId == inDto.SubjectVisitId) - .Where(x => x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Study).Select(x => new GetCRCClinicalDataOutDto() - { - Id = x.Id, - ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), - ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, - ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, - ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, - ClinicalDataTrialSetId = x.ClinicalDataTrialSet.Id, - FileName = x.ClinicalDataTrialSet.FileName, - UploadRole = x.ClinicalDataTrialSet.UploadRole, - Path = x.ClinicalDataTrialSet.Path, - IsBlind = x.IsBlind, - IsComplete = x.IsComplete, - ClinicalFromList = x.Subject.ClinicalFormList.Where(y => y.ReadingId == x.ReadingId && y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() - { - CheckDate = y.CheckDate, - ClinicalFormId = y.Id - }).ToList(), - PDFFileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() - { - Id = y.Id, - FileName = y.FileName, - Path = y.Path, - CreateTime = y.CreateTime, - }).ToList(), + var cRCClinicalDataList = await _readingClinicalDataRepository.Where(x => x.ReadingId == inDto.SubjectVisitId) + .Where(x => x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Study).Select(x => new GetCRCClinicalDataOutDto() + { + Id = x.Id, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, + ClinicalDataTrialSetId = x.ClinicalDataTrialSet.Id, + FileName = x.ClinicalDataTrialSet.FileName, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + Path = x.ClinicalDataTrialSet.Path, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + ClinicalFromList = x.Subject.ClinicalFormList.Where(y => y.ReadingId == x.ReadingId && y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() + { + CheckDate = y.CheckDate, + ClinicalFormId = y.Id + }).ToList(), + PDFFileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + Size = y.Size, + Type = y.Type, + FileName = y.FileName, + Path = y.Path, + CreateTime = y.CreateTime, + }).ToList(), - }).ToListAsync(); + }).ToListAsync(); - var previousHistoryList = await _previousHistoryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - var previousOtherList = await _previousOtherRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - var previousSurgeryList = await _previousSurgeryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousHistoryList = await _previousHistoryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousOtherList = await _previousOtherRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + var previousSurgeryList = await _previousSurgeryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); - foreach (var item in cRCClinicalDataList) - { - item.ClinicalTableData = new ClinicalDataTable() - { - PreviousHistoryList = previousHistoryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), - PreviousOtherList = previousOtherList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), - PreviousSurgeryList = previousSurgeryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), - }; + foreach (var item in cRCClinicalDataList) + { + item.ClinicalTableData = new ClinicalDataTable() + { + PreviousHistoryList = previousHistoryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousOtherList = previousOtherList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + PreviousSurgeryList = previousSurgeryList.Where(x => x.ClinicalDataTrialSetId == item.ClinicalDataTrialSetId).ToList(), + }; - } - return cRCClinicalDataList; - } + } + return cRCClinicalDataList; + } - /// - /// 获取CRC上传的文件 - /// - /// - /// - [HttpPost] + /// + /// 获取CRC上传的文件 + /// + /// + /// + [HttpPost] public async Task> GetCRCClinicalData(GetCRCClinicalDataInDto inDto) { @@ -254,35 +264,38 @@ namespace IRaCIS.Application.Services .WhereIf(!inDto.IsBaseline, x => x.ClinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit) .Where(x => x.ClinicalDataTrialSet.TrialId == inDto.TrialId && x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC); - } + } - List cRCClinicalDataList = await query - .Select(x => new GetCRCClinicalDataOutDto() + List cRCClinicalDataList = await query + .Select(x => new GetCRCClinicalDataOutDto() { Id = x.Id, - ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us) , + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, - ClinicalDataLevel=x.ClinicalDataTrialSet.ClinicalDataLevel, - ClinicalDataSetEnName =x.ClinicalDataTrialSet.ClinicalDataSetEnName, + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, ClinicalDataTrialSetId = x.ClinicalDataTrialSet.Id, FileName = x.ClinicalDataTrialSet.FileName, UploadRole = x.ClinicalDataTrialSet.UploadRole, Path = x.ClinicalDataTrialSet.Path, IsBlind = x.IsBlind, IsComplete = x.IsComplete, - ClinicalFromList=x.Subject.ClinicalFormList.Where(y=>y.ReadingId==x.ReadingId&&y.ClinicalDataTrialSetId==x.ClinicalDataTrialSetId).Select(y=> new ClinicalFromData() { - CheckDate=y.CheckDate, - ClinicalFormId=y.Id + ClinicalFromList = x.Subject.ClinicalFormList.Where(y => y.ReadingId == x.ReadingId && y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() + { + CheckDate = y.CheckDate, + ClinicalFormId = y.Id }).ToList(), PDFFileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() { Id = y.Id, FileName = y.FileName, Path = y.Path, + Size = y.Size, + Type = y.Type, CreateTime = y.CreateTime, }).ToList(), - + }).ToListAsync(); var previousHistoryList = await _previousHistoryRepository.Where(x => x.SubjectVisitId == inDto.SubjectVisitId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); @@ -304,7 +317,7 @@ namespace IRaCIS.Application.Services { cRCClinicalDataList = cRCClinicalDataList.Where(x => !(x.ClinicalFromList.Count() == 0 && x.ClinicalUploadType == ClinicalUploadType.Structuring)).ToList(); - } + } return cRCClinicalDataList; } @@ -361,7 +374,7 @@ namespace IRaCIS.Application.Services //} data.IsBlind = inDto.IsBlind; - data.IsComplete=inDto.IsComplete; + data.IsComplete = inDto.IsComplete; data.IsSign = true; data.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned; @@ -379,8 +392,8 @@ namespace IRaCIS.Application.Services //await this.iServiceProvider.GetService().AddOncologyTask(readingId); //如果先生成了任务,再签名subject级别 PM 临床数据,那么会导致其他标准的任务签名状态无法得到维护 - - if (await _repository.AnyAsync(t=>t.Id == data.ClinicalDataTrialSetId && t.UploadRole == UploadRole.PM && t.ClinicalDataLevel == ClinicalLevel.Subject)) + + if (await _repository.AnyAsync(t => t.Id == data.ClinicalDataTrialSetId && t.UploadRole == UploadRole.PM && (t.ClinicalDataLevel == ClinicalLevel.Subject|| t.ClinicalDataLevel == ClinicalLevel.SubjectVisit))) { var needDealTrialReadingCriterionIdList = _repository.Where(t => t.Id == data.ClinicalDataTrialSetId) .SelectMany(t => t.TrialClinicalDataSetCriteriaList) @@ -402,11 +415,57 @@ namespace IRaCIS.Application.Services return ResponseOutput.Result(result); } + /// + /// 一致性分析的临床数据 + /// + /// + /// + //public async Task ReadClinicalDataSign(ReadingClinicalDataSignIndto inDto) + //{ + + + // var data = await _readingClinicalDataRepository.FirstOrDefaultAsync(t => t.Id == inDto.ReadingClinicalDataId); + + // data.IsBlind = inDto.IsBlind; + // data.IsComplete = inDto.IsComplete; + // data.IsSign = true; + // data.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveSigned; + // var result = await _readingClinicalDataRepository.SaveChangesAsync(); + + + // var readingId = await _readingClinicalDataRepository.Where(x => x.Id == inDto.ReadingClinicalDataId).Select(x => x.ReadingId).FirstOrDefaultAsync(); + + + + // //如果先生成了任务,再签名subject级别 PM 临床数据,那么会导致其他标准的任务签名状态无法得到维护 + + // if (await _repository.AnyAsync(t => t.Id == data.ClinicalDataTrialSetId && t.UploadRole == UploadRole.PM && t.ClinicalDataLevel == ClinicalLevel.Subject)) + // { + // var needDealTrialReadingCriterionIdList = _repository.Where(t => t.Id == data.ClinicalDataTrialSetId) + // .SelectMany(t => t.TrialClinicalDataSetCriteriaList) + // .Select(u => u.TrialReadingCriterionId).Distinct().ToList(); + + // foreach (var trialReadingCriterionId in needDealTrialReadingCriterionIdList) + // { + // await DealVisiTaskClinicalDataSignedAsync(data.TrialId, data.SubjectId, data.ReadingId, data.IsVisit, trialReadingCriterionId); + // } + // } + // else + // { + // await DealVisiTaskClinicalDataSignedAsync(data.TrialId, data.SubjectId, data.ReadingId, data.IsVisit, inDto.TrialReadingCriterionId); + + // } + + + + // return ResponseOutput.Result(result); + //} + //处理 任务 临床数据是否签名 public async Task DealVisiTaskClinicalDataSignedAsync(Guid trialId, Guid subjectId, Guid readingId, bool isVisit, Guid trialReadingCritrialId) { //获取确认的临床数据配置 - var trialClinicalDataSetList = _clinicalDataTrialSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).Include(t => t.TrialClinicalDataSetCriteriaList).ToList(); + var trialClinicalDataSetList = _clinicalDataTrialSetRepository.Where(t => t.TrialId == trialId && t.IsConfirm).Include(t => t.TrialClinicalDataSetCriteriaList).ToList(); //var criterionType = await _readingQuestionCriterionTrialRepository.Where(t => t.Id == trialReadingCritrialId).Select(t => t.CriterionType).FirstOrDefaultAsync(); @@ -416,12 +475,13 @@ namespace IRaCIS.Application.Services var needSignCount = 0; var haveSignedCount = _readingClinicalDataRepository.Where(t => t.TrialId == trialId && t.IsSign && t.ReadingClinicalDataState == ReadingClinicalDataStatus.HaveSigned && t.ReadingId == readingId && t.ClinicalDataTrialSet.UploadRole == UploadRole.PM).Count(); + ReadModule readModule = null; if (isVisit) { var isBaseLine = await _subjectVisitRepository.Where(t => t.Id == readingId).Select(t => t.IsBaseLine).FirstOrDefaultAsync(); - + //判断是否基线 if (isBaseLine) @@ -451,7 +511,7 @@ namespace IRaCIS.Application.Services else { //判断是影像学 还是肿瘤学阅片 - var readModule = await _readModuleRepository.Where(t => t.Id == readingId).FirstNotNullAsync(); + readModule = await _readModuleRepository.Where(t => t.Id == readingId).FirstNotNullAsync(); //CRC 阅片期自定义结构化录入是否签名 bool crcReadModuleSign = true; @@ -507,13 +567,13 @@ namespace IRaCIS.Application.Services } //有序阅片才维护 IsFrontTaskNeedSignButNotSign 这个状态 - if (_readingQuestionCriterionTrialRepository.Any(t=>t.Id==trialReadingCritrialId && t.IsReadingTaskViewInOrder == ReadingOrder.InOrder)) + if (_readingQuestionCriterionTrialRepository.Any(t => t.Id == trialReadingCritrialId && t.IsReadingTaskViewInOrder == ReadingOrder.InOrder)) { Expression> visitTaskLambda = x => x.TrialId == trialId && x.SubjectId == subjectId && x.TrialReadingCriterionId == trialReadingCritrialId; - if (isVisit) + if (isVisit ||(isVisit==false && readModule.ReadingSetType == ReadingSetType.ImageReading)) { //访视类型的任务 不影响肿瘤学任务的临床数据状态 visitTaskLambda = visitTaskLambda.And(x => x.ArmEnum != Arm.TumorArm); @@ -532,8 +592,8 @@ namespace IRaCIS.Application.Services (t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum) // 前序存在 未一致性核查未通过的 - || t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum>sv.VisitNum) - + || t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum) + )) .Select(t => t.Id); @@ -545,7 +605,7 @@ namespace IRaCIS.Application.Services var visitTaskIdQueryable2 = _visitTaskRepository.Where(visitTaskLambda) //该Subject 该标准的任务 - + .Where(t => t.IsFrontTaskNeedSignButNotSign == true && //前序任务 不存在需要签名 但是没签名 @@ -564,14 +624,14 @@ namespace IRaCIS.Application.Services } - + } // 废弃 接口合并到签名哪里 - + [HttpPut] public async Task PMClinicalDataConfirm(PMClinicalDataConfirmCommand command) { @@ -625,7 +685,7 @@ namespace IRaCIS.Application.Services keyValuePairs.Add(ModuleTypeEnum.Oncology, ClinicalLevel.OncologyRead); - + var usedIdsQuery = _readingClinicalDataRepository.Where(x => x.ReadingId == inDto.ReadingId && x.Id != inDto.ReadingClinicalDataId).Select(x => x.ClinicalDataTrialSetId); @@ -640,20 +700,20 @@ namespace IRaCIS.Application.Services .WhereIf(inDto.IsVisit && !inDto.IsBaseLine, x => x.ClinicalDataLevel == ClinicalLevel.SubjectVisit) .WhereIf(!inDto.IsVisit, x => x.ClinicalDataLevel == ClinicalLevel.ImageRead || x.ClinicalDataLevel == ClinicalLevel.OncologyRead) .WhereIf(readModule != null, x => x.ClinicalDataLevel == keyValuePairs[readModule!.ModuleType]) - .WhereIf(inDto.TrialReadingCriterionId!=null,x=>x.TrialClinicalDataSetCriteriaList.Any(y=>y.TrialReadingCriterionId== inDto.TrialReadingCriterionId)) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == inDto.TrialReadingCriterionId)) //.WhereIf(criterion!=null,x=>x.CriterionEnumListStr.Contains($"|{(int)criterion.CriterionType}|")) .Select(x => new GetTrialClinicalDataSelectOutDto() { ClinicalDataLevel = x.ClinicalDataLevel, ClinicalDataSetName = x.ClinicalDataSetName.LanguageName(x.ClinicalDataSetEnName, _userInfo.IsEn_Us), - ClinicalDataSetEnName=x.ClinicalDataSetEnName, + ClinicalDataSetEnName = x.ClinicalDataSetEnName, ClinicalUploadType = x.ClinicalUploadType, FileName = x.FileName, Path = x.Path, Id = x.Id, CriterionEnumList = x.CriterionEnumList, }).ToListAsync(); - + return clinicalList; } @@ -667,13 +727,17 @@ namespace IRaCIS.Application.Services public async Task<(List, object)> GetReadingOrTaskClinicalDataList(GetReadingOrTaskClinicalDataListInDto inDto) { var readingNameOrTaskBlindName = string.Empty; + var subjectCode = string.Empty; if (inDto.ReadingId == null) { - var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); - inDto.ReadingId = visitTask.SouceReadModuleId ?? visitTask.SourceSubjectVisitId; - readingNameOrTaskBlindName = visitTask.TaskBlindName; + var visitTask = await _visitTaskRepository.AsQueryable().Include(x=>x.Subject) + + .FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); + subjectCode=visitTask.BlindSubjectCode.IsNullOrEmpty() ? visitTask.Subject.Code : visitTask.BlindSubjectCode; + readingNameOrTaskBlindName = visitTask.TaskBlindName; } - + inDto.SelectIsSign = false; + var result = await GetClinicalDataList(inDto); if (readingNameOrTaskBlindName.IsNullOrEmpty()) { readingNameOrTaskBlindName = await _subjectVisitRepository.Where(x => x.Id == inDto.ReadingId).Select(x => x.VisitName).FirstOrDefaultAsync(); @@ -683,8 +747,7 @@ namespace IRaCIS.Application.Services } } - inDto.SelectIsSign = false; - var result = await GetClinicalDataList(inDto); + var readingIds = result.Select(x => x.ReadingId).ToList(); @@ -707,14 +770,14 @@ namespace IRaCIS.Application.Services {ClinicalLevel.ImageRead,2 }, {ClinicalLevel.OncologyRead,3 }, {ClinicalLevel.Subject,4 }, - {ClinicalLevel.Study,5 }, - }; + {ClinicalLevel.Study,5 }, + }; result = result.OrderBy(x => keys[x.ClinicalDataLevel]).ToList(); return (result, new { - SubjectCode = await _subjectRepository.Where(x => x.Id == inDto.SubjectId).Select(x => x.Code).FirstOrDefaultAsync(), + SubjectCode = subjectCode, ReadingNameOrTaskBlindName = readingNameOrTaskBlindName, }); } @@ -743,7 +806,15 @@ namespace IRaCIS.Application.Services var isBaseLine = await _subjectVisitRepository.AnyAsync(x => x.Id == inDto.ReadingId && x.IsBaseLine); - var result = await this.GetReadingClinicalList(inDto); + var result = await this.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() { + ClinicalDataTrialSetId = inDto.ClinicalDataTrialSetId, + GetClinicalType=inDto.GetClinicalType, + SubjectId=inDto.SubjectId, + TrialId=inDto.TrialId, + SelectIsSign=false, + ReadingId =inDto.ReadingId, + TrialReadingCriterionId = inDto.TrialReadingCriterionId, + }); var readingIds = result.Select(x => x.ReadingId).ToList(); var previousHistoryList = await _previousHistoryRepository.Where(x => readingIds.Contains(x.SubjectVisitId)).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); @@ -770,53 +841,162 @@ namespace IRaCIS.Application.Services public async Task> GetClinicalDataList(GetReadingOrTaskClinicalDataListInDto inDto) { + var resultQuery = _readingClinicalDataRepository.Where(x=>1==1).Select(x=> new GetReadingClinicalDataListOutDto() { }); + bool isSelfAnalysis = false; + var otherIsSelfAnalysisList=new List(); if (inDto.ReadingId == null) { var visitTask = await _visitTaskRepository.FirstOrDefaultAsync(x => x.Id == inDto.VisitTaskId); inDto.ReadingId = visitTask.SouceReadModuleId ?? visitTask.SourceSubjectVisitId; - } + inDto.TrialReadingCriterionId = visitTask.TrialReadingCriterionId; - var resultQuery = _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) - .WhereIf(inDto.ReadingClinicalDataId != null, x => x.Id == inDto.ReadingClinicalDataId) - .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalDataTrialSetId == inDto.ClinicalDataTrialSetId) - .WhereIf(inDto.SelectIsSign, x => x.IsSign == true) - .Where(x => x.ReadingId == inDto.ReadingId) - .Select(x => new GetReadingClinicalDataListOutDto() + if (visitTask.IsAnalysisCreate && visitTask.IsSelfAnalysis == true) + { + isSelfAnalysis = true; + } + } + // 一致性分析 + if (isSelfAnalysis) + { + // 一致性分析的 + resultQuery = _readingConsistentClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) + .WhereIf(inDto.ReadingClinicalDataId != null, x => x.Id == inDto.ReadingClinicalDataId) + .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalDataTrialSetId == inDto.ClinicalDataTrialSetId) + .WhereIf(inDto.SelectIsSign, x => x.IsSign == true) + .Where(x => x.ReadingId == inDto.ReadingId) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t => t.TrialReadingCriterionId == inDto.TrialReadingCriterionId)) + .Select(x => new GetReadingClinicalDataListOutDto() + { + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + SubjectId = x.SubjectId, + ReadingId = x.ReadingId, + ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), + ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, + IsSign = x.IsSign, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + CriterionEnumList = x.ClinicalDataTrialSet.CriterionEnumList, + TrialClinicalDataSetCriteriaList = x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList, + Id = x.Id, + // 这里现在都是PM上传的 前端会判断是否可以上传 + UploadRole = UploadRole.PM, + IsCRCUpload = false, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + FileCount = x.FileCount, + ReadingClinicalDataState = x.ReadingClinicalDataState, + FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + Size=y.Size, + Type=y.Type, + CreateTime = y.CreateTime, + }).ToList(), + }); + + // 是否获取所有一致性分析的数据 + if(inDto.IsGetAllConsistencyAnalysis) + { + // 原来的非PDF + otherIsSelfAnalysisList =await _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) + .WhereIf(inDto.ReadingClinicalDataId != null, x => x.Id == inDto.ReadingClinicalDataId) + .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalDataTrialSetId == inDto.ClinicalDataTrialSetId) + .WhereIf(inDto.SelectIsSign, x => x.IsSign == true) + .Where(x => x.ReadingId == inDto.ReadingId) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t => t.TrialReadingCriterionId == inDto.TrialReadingCriterionId)) + .Where(x => x.ClinicalDataTrialSet.ClinicalUploadType != ClinicalUploadType.PDF||x.ClinicalDataTrialSet.ClinicalDataLevel== ClinicalLevel.Study) + .Select(x => new GetReadingClinicalDataListOutDto() + { + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + SubjectId = x.SubjectId, + ReadingId = x.ReadingId, + ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), + ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, + IsSign = x.IsSign, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + CriterionEnumList = x.ClinicalDataTrialSet.CriterionEnumList, + TrialClinicalDataSetCriteriaList = x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList, + Id = x.Id, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + FileCount = x.FileCount, + ReadingClinicalDataState = x.ReadingClinicalDataState, + FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() + { + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + Size = y.Size, + Type = y.Type, + CreateTime = y.CreateTime, + }).ToList(), + }).ToListAsync(); + + + } + + } + else + { + resultQuery = _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) + .WhereIf(inDto.ReadingClinicalDataId != null, x => x.Id == inDto.ReadingClinicalDataId) + .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalDataTrialSetId == inDto.ClinicalDataTrialSetId) + .WhereIf(inDto.SelectIsSign, x => x.IsSign == true) + .Where(x => x.ReadingId == inDto.ReadingId) + .WhereIf(inDto.TrialReadingCriterionId != null, x => x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t => t.TrialReadingCriterionId == inDto.TrialReadingCriterionId)) + .Select(x => new GetReadingClinicalDataListOutDto() + { + ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, + SubjectId = x.SubjectId, + ReadingId = x.ReadingId, + ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, + ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), + ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, + IsSign = x.IsSign, + ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, + CriterionEnumList = x.ClinicalDataTrialSet.CriterionEnumList, + TrialClinicalDataSetCriteriaList = x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList, + Id = x.Id, + UploadRole = x.ClinicalDataTrialSet.UploadRole, + IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, + IsBlind = x.IsBlind, + IsComplete = x.IsComplete, + FileCount = x.FileCount, + ReadingClinicalDataState = x.ReadingClinicalDataState, + FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() { - ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, - SubjectId = x.SubjectId, - ReadingId = x.ReadingId, - ClinicalDataSetEnName = x.ClinicalDataTrialSet.ClinicalDataSetEnName, - ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), - ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, - IsSign = x.IsSign, - ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, - CriterionEnumList = x.ClinicalDataTrialSet.CriterionEnumList, - TrialClinicalDataSetCriteriaList = x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList, - Id = x.Id, - UploadRole = x.ClinicalDataTrialSet.UploadRole, - IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, - FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() - { - Id = y.Id, - FileName = y.FileName, - Path = y.Path, - CreateTime = y.CreateTime, - }).ToList(), - - }); + Id = y.Id, + FileName = y.FileName, + Path = y.Path, + Size = y.Size, + Type = y.Type, + CreateTime = y.CreateTime, + }).ToList(), + + }); + } + + + var result = new List(); if (!inDto.IsOnlyGetCRCReadModule) { - result = await resultQuery.ToListAsync(); + result = await resultQuery.ToListAsync(); + result.AddRange(otherIsSelfAnalysisList); } var readingIds = result.Select(x => x.ReadingId).ToList(); - var clinical = await _clinicalFormRepository.Where(x => readingIds.Contains(x.ReadingId??default(Guid))).ToListAsync(); - result.Where(x => x.ClinicalUploadType == ClinicalUploadType.Structuring).ForEach(x => { + var clinical = await _clinicalFormRepository.Where(x => readingIds.Contains(x.ReadingId ?? default(Guid))).ToListAsync(); + result.Where(x => x.ClinicalUploadType == ClinicalUploadType.Structuring).ForEach(x => + { - x.ClinicalFromList = clinical.Where(y => y.ReadingId == x.ReadingId&&y.ClinicalDataTrialSetId==x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() + x.ClinicalFromList = clinical.Where(y => y.ReadingId == x.ReadingId && y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() { CheckDate = y.CheckDate, ClinicalFormId = y.Id @@ -827,35 +1007,37 @@ namespace IRaCIS.Application.Services // 这里处理CRC上传 阅片期的临床数据 var readModule = await _readModuleRepository.Where(x => x.Id == inDto.ReadingId) - .WhereIf(inDto.SelectIsSign,x=>x.IsPMConfirm) - .WhereIf(!inDto.SelectIsSign, x => x.IsCRCConfirm) - .FirstOrDefaultAsync(); + //.Where(x=>x.IsCRCConfirm) + .WhereIf(inDto.SelectIsSign, x => x.IsPMConfirm) + .WhereIf(!inDto.SelectIsSign, x => x.IsCRCConfirm) + .FirstOrDefaultAsync(); if (readModule != null) { var moduleCriterionFromList = await _readModuleCriterionFromRepository .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalForm.ClinicalDataTrialSetId == inDto.ClinicalDataTrialSetId) - .Where(x => x.ReadModuleId == readModule.Id).Select(x => new{ - ClinicalFormId= x.ClinicalFormId, - CheckDate= x.ClinicalForm.CheckDate, - ClinicalDataTrialSetId= x.ClinicalForm.ClinicalDataTrialSetId - - - }).ToListAsync(); + .Where(x => x.ReadModuleId == readModule.Id).Select(x => new + { + ClinicalFormId = x.ClinicalFormId, + CheckDate = x.ClinicalForm.CheckDate, + ClinicalDataTrialSetId = x.ClinicalForm.ClinicalDataTrialSetId + + + }).ToListAsync(); var clinicalresult = await _clinicalDataTrialSetRepository.Where(x => x.UploadRole == UploadRole.CRC && x.ClinicalUploadType == ClinicalUploadType.Structuring) .WhereIf(readModule.ReadingSetType == ReadingSetType.ImageReading, x => x.ClinicalDataLevel == ClinicalLevel.ImageRead) .WhereIf(readModule.ReadingSetType == ReadingSetType.TumorReading, x => x.ClinicalDataLevel == ClinicalLevel.OncologyRead) - .Where(x=>x.TrialClinicalDataSetCriteriaList.Any(y=>y.TrialReadingCriterionId==readModule.TrialReadingCriterionId)) + .Where(x => x.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == readModule.TrialReadingCriterionId)) .Select(x => new GetReadingClinicalDataListOutDto() { ClinicalDataLevel = x.ClinicalDataLevel, SubjectId = inDto.SubjectId, ReadingId = default(Guid), - IsCRCApplicationRevoke=readModule.IsCRCApplicationRevoke, - IsCRCConfirm= readModule.IsCRCConfirm, - IsPMConfirm= readModule.IsPMConfirm, + IsCRCApplicationRevoke = readModule.IsCRCApplicationRevoke, + IsCRCConfirm = readModule.IsCRCConfirm, + IsPMConfirm = readModule.IsPMConfirm, ClinicalDataSetName = x.ClinicalDataSetName.LanguageName(x.ClinicalDataSetEnName, _userInfo.IsEn_Us), ClinicalDataSetEnName = x.ClinicalDataSetEnName, ClinicalDataTrialSetId = x.Id, @@ -866,7 +1048,7 @@ namespace IRaCIS.Application.Services IsCRCUpload = x.UploadRole == UploadRole.CRC, IsNeedMerge = true, ReadModuleId = readModule.Id, - TrialClinicalDataSetCriteriaList=x.TrialClinicalDataSetCriteriaList, + TrialClinicalDataSetCriteriaList = x.TrialClinicalDataSetCriteriaList, //FileCount = x.FileCount, //ReadingClinicalDataState = x.ReadingClinicalDataState, @@ -895,8 +1077,8 @@ namespace IRaCIS.Application.Services }); - result.AddRange(clinicalresult); - } + result.AddRange(clinicalresult); + } @@ -917,7 +1099,7 @@ namespace IRaCIS.Application.Services result = result.Where(x => x.UploadRole == UploadRole.PM).ToList(); break; case GetClinicalType.CRCConfirm: - result = result.Where(x => x.UploadRole == UploadRole.CRC&&x.IsCRCConfirm&&!x.IsPMConfirm).ToList(); + result = result.Where(x => x.UploadRole == UploadRole.CRC && x.IsCRCConfirm && !x.IsPMConfirm).ToList(); break; case GetClinicalType.HasSign: result = result.Where(x => x.IsSign).ToList(); @@ -943,158 +1125,180 @@ namespace IRaCIS.Application.Services return result; } + #endregion + + + #region 一致性分析临床数据 + /// + /// 获取阅片临床数据列表 (在任务列表) + /// + /// + /// + [HttpPost] + public async Task<(List, object)> GetConsistencyAnalysisReadingClinicalDataList(GetConsistencyAnalysisReadingClinicalDataListInDto inDto) + { + var result = await this.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() + { + SubjectId = inDto.SubjectId, + TrialId = inDto.TrialId, + SelectIsSign = false, + IsGetAllConsistencyAnalysis = false, + VisitTaskId=inDto.VisitTaskId, + }); + + return (result,true); + } + + /// - /// 获取临床数据集合 + /// 新增或修改一致性分析临床数据 /// + /// /// - [NonDynamicMethod] - public async Task> GetReadingClinicalList(GetReadingClinicalDataListIndto inDto) + [HttpPost] + public async Task AddOrUpdateConsistencyAnalysisReadingClinicalData(AddOrUpdateReadingClinicalDataDto indto) { - var resultQuery = _readingClinicalDataRepository.Where(x => x.SubjectId == inDto.SubjectId) - .Where(x => x.ReadingId == inDto.ReadingId) - .WhereIf(inDto.ClinicalDataTrialSetId!=null, x=>x.ClinicalDataTrialSetId==inDto.TrialReadingCriterionId) - .Where(x => x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t=>t.TrialReadingCriterionId==inDto.TrialReadingCriterionId)) - .Select(x => new GetReadingClinicalDataListOutDto() - { - ClinicalDataLevel = x.ClinicalDataTrialSet.ClinicalDataLevel, - SubjectId = x.SubjectId, - ReadingId = x.ReadingId, - ClinicalDataSetName = x.ClinicalDataTrialSet.ClinicalDataSetName.LanguageName(x.ClinicalDataTrialSet.ClinicalDataSetEnName, _userInfo.IsEn_Us), - ClinicalDataSetEnName =x.ClinicalDataTrialSet.ClinicalDataSetEnName, - ClinicalDataTrialSetId = x.ClinicalDataTrialSetId, - IsSign = x.IsSign, - ClinicalUploadType = x.ClinicalDataTrialSet.ClinicalUploadType, - Id = x.Id, - UploadRole = x.ClinicalDataTrialSet.UploadRole, - IsCRCUpload = x.ClinicalDataTrialSet.UploadRole == UploadRole.CRC, - IsBlind = x.IsBlind, - IsComplete = x.IsComplete, - FileCount = x.FileCount, + var existsQuery = _readingConsistentClinicalDataRepository + .WhereIf(indto.Id != null, x => x.Id != indto.Id) + .Where(x => x.ClinicalDataTrialSetId == indto.ClinicalDataTrialSetId && x.ReadingId == indto.ReadingId && x.StudyId == indto.StudyId); - ReadingClinicalDataState = x.ReadingClinicalDataState, + if (await existsQuery.AnyAsync()) + { + //---存在同类型的临床数据,操作失败 + return ResponseOutput.NotOk(_localizer["ReadingClinicalData_DupTypeFail"]); + } + var clinicalDataTrialSet = (await _clinicalDataTrialSetRepository.Where(x => x.Id == indto.ClinicalDataTrialSetId).FirstOrDefaultAsync()).IfNullThrowException(); - FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() - { - Id = y.Id, - FileName = y.FileName, - Path = y.Path, - CreateTime = y.CreateTime, - }).ToList() - }); + //subject 或者访视级别的 都是在访视传 + indto.IsVisit = clinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.Subject || clinicalDataTrialSet.ClinicalDataLevel == ClinicalLevel.SubjectVisit; - var result = await resultQuery.ToListAsync(); - - - var readingIds = result.Select(x => x.ReadingId).ToList(); - var clinical = await _clinicalFormRepository.Where(x => readingIds.Contains(x.ReadingId ?? default(Guid))).ToListAsync(); - result.Where(x => x.ClinicalUploadType == ClinicalUploadType.Structuring).ForEach(x => { - - x.ClinicalFromList = clinical.Where(y => y.ReadingId == x.ReadingId && y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Select(y => new ClinicalFromData() + if (indto.Id == null) + { + var entity = _mapper.Map(indto); + entity.ReadingClinicalDataPDFList = indto.AddFileList.Select(x => new ReadingConsistentClinicalDataPDF() { - CheckDate = y.CheckDate, - ClinicalFormId = y.Id + FileName = x.FileName, + Path = x.Path, + Size = x.Size, + Type = x.Type, }).ToList(); - x.FileCount = x.ClinicalFromList.Count; - }); - // 这里处理CRC上传 阅片期的临床数据 - var readModule = await _readModuleRepository.Where(x => x.Id == inDto.ReadingId&&x.IsCRCConfirm) - .FirstOrDefaultAsync(); - if (readModule != null) + entity.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; + entity.IsBlind = false; + entity.IsComplete = true; + entity.FileCount = entity.ReadingClinicalDataPDFList.Count; + await _readingConsistentClinicalDataRepository.AddAsync(entity, true); + var success = await _readingConsistentClinicalDataRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id); + } + else { - var moduleCriterionFromList = await _readModuleCriterionFromRepository.Where(x => x.ReadModuleId == readModule.Id) + var entity = (await _readingConsistentClinicalDataRepository.Where(t => t.Id == indto.Id, true).FirstOrDefaultAsync()).IfNullThrowException(); + await _readingConsistentClinicalDataPDFRepository.BatchDeleteNoTrackingAsync(x => indto.DeleteFileIds.Contains(x.Id)); - .WhereIf(inDto.ClinicalDataTrialSetId != null, x => x.ClinicalForm.ClinicalDataTrialSetId == inDto.TrialReadingCriterionId) - .Select(x => new { - ClinicalFormId = x.ClinicalFormId, - CheckDate = x.ClinicalForm.CheckDate, - ClinicalDataTrialSetId = x.ClinicalForm.ClinicalDataTrialSetId - - - }).ToListAsync(); - - - var clinicalresult = await _clinicalDataTrialSetRepository.Where(x => x.UploadRole == UploadRole.CRC&&x.ClinicalUploadType== ClinicalUploadType.Structuring) - .WhereIf(readModule.ReadingSetType == ReadingSetType.ImageReading,x=>x.ClinicalDataLevel== ClinicalLevel.ImageRead) - .WhereIf(readModule.ReadingSetType == ReadingSetType.TumorReading, x => x.ClinicalDataLevel == ClinicalLevel.OncologyRead) - .Where(x => x.TrialClinicalDataSetCriteriaList.Any(y => y.TrialReadingCriterionId == readModule.TrialReadingCriterionId)) - .Select(x => new GetReadingClinicalDataListOutDto() - { - - ClinicalDataLevel = x.ClinicalDataLevel, - SubjectId = inDto.SubjectId, - ReadingId = default(Guid), - IsCRCApplicationRevoke= readModule.IsCRCApplicationRevoke, - IsCRCConfirm = readModule.IsCRCConfirm, - IsPMConfirm=readModule.IsPMConfirm, - - ClinicalDataSetName = x.ClinicalDataSetName.LanguageName(x.ClinicalDataSetEnName, _userInfo.IsEn_Us), - ClinicalDataSetEnName = x.ClinicalDataSetEnName, - ClinicalDataTrialSetId = x.Id, - IsSign = readModule.IsPMConfirm, - ClinicalUploadType = x.ClinicalUploadType, - Id = default(Guid), - UploadRole = x.UploadRole, - IsCRCUpload = x.UploadRole == UploadRole.CRC, - IsNeedMerge = true, - ReadModuleId = readModule.Id, - //FileCount = x.FileCount, - - //ReadingClinicalDataState = x.ReadingClinicalDataState, - - //FileList = x.ReadingClinicalDataPDFList.Select(y => new GetFileDto() - //{ - // Id = y.Id, - // FileName = y.FileName, - // Path = y.Path, - // CreateTime = y.CreateTime, - //}).ToList() - - }).ToListAsync(); - - clinicalresult.ForEach(x => - { - x.FileCount = moduleCriterionFromList.Where(y => y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).Count(); - x.ClinicalFromList = moduleCriterionFromList.Where(y => y.ClinicalDataTrialSetId == x.ClinicalDataTrialSetId).OrderBy(y => y.CheckDate).Select(x => new ClinicalFromData - { - CheckDate = x.CheckDate, - ClinicalFormId = x.ClinicalFormId - - }).ToList(); - - x.IsSign = readModule.IsPMConfirm ?true : false; - x.ReadingClinicalDataState = readModule.IsPMConfirm ? ReadingClinicalDataStatus.HaveSigned : ReadingClinicalDataStatus.HaveChecked; - - }); - - result.AddRange(clinicalresult); - } - - - if (inDto.GetClinicalType != null) - { - switch (inDto.GetClinicalType) + var addFileList = indto.AddFileList.Select(x => new ReadingConsistentClinicalDataPDF() { - case GetClinicalType.PMUpload: - result = result.Where(x => x.UploadRole == UploadRole.PM).ToList(); - break; - case GetClinicalType.CRCConfirm: - result = result.Where(x => x.UploadRole == UploadRole.CRC&&x.IsCRCConfirm&&!x.IsPMConfirm).ToList(); - break; - case GetClinicalType.HasSign: - result = result.Where(x => x.IsSign).ToList(); - break; + + FileName = x.FileName, + Path = x.Path, + Size = x.Size, + Type = x.Type, + ReadingConsistentClinicalDataId = entity.Id, + }).ToList(); + + + + _mapper.Map(indto, entity); + + //上传 或者删除了文件 核查状态需要重新确认 + + if (indto.AddFileList.Count > 0 || indto.AddFileList.Count > 0) + { + entity.IsComplete = true; + entity.IsBlind = false; + } + + await _readingConsistentClinicalDataPDFRepository.AddRangeAsync(addFileList); + + await _readingConsistentClinicalDataPDFRepository.SaveChangesAsync(); + + var fileCount = await _readingConsistentClinicalDataPDFRepository.Where(t => t.ReadingConsistentClinicalDataId == indto.Id).CountAsync(); + entity.ReadingClinicalDataState = ReadingClinicalDataStatus.HaveUploaded; + entity.IsSign = false; + entity.FileCount = fileCount; + var success = await _readingConsistentClinicalDataRepository.SaveChangesAsync(); + return ResponseOutput.Ok(entity.Id); + + } + + } + + + /// + /// 一致性分析临床数据签名 + /// + /// + /// + [HttpPost] + public async Task SignConsistencyAnalysisReadingClinicalData(SignConsistencyAnalysisReadingClinicalDataInDto inDto) + { + var pdfCount = await _readingConsistentClinicalDataPDFRepository.Where(x => x.ReadingConsistentClinicalDataId == inDto.ConsistentClinicalDataId).CountAsync(); + if (pdfCount == 0) + { + return ResponseOutput.NotOk(_localizer["ReadingClinicalData_NoHavePDF"]); + } + await _readingConsistentClinicalDataRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.ConsistentClinicalDataId, x => new ReadingConsistentClinicalData() + { + IsSign = true, + IsBlind = inDto.IsBlind, + IsComplete = inDto.IsComplete, + ReadingClinicalDataState= ReadingClinicalDataStatus.HaveSigned, + }); + await _readingClinicalDataPDFRepository.SaveChangesAsync(); + return ResponseOutput.Ok(pdfCount); + } + + /// + /// 一致性分析临床数据签名完设置任务为有效 + /// + /// + /// + [HttpPost] + public async Task SetTaskValid(SetTaskValidInDto inDto) + { + var visittask = await _visitTaskRepository.AsQueryable().Include(x=>x.TrialReadingCriterion).Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var readingId = visittask.SouceReadModuleId==null? visittask.SourceSubjectVisitId: visittask.SouceReadModuleId; + + if (await _readingConsistentClinicalDataRepository.AnyAsync(x => x.ReadingId == readingId + + && x.ClinicalDataTrialSet.TrialClinicalDataSetCriteriaList.Any(t => t.TrialReadingCriterionId == visittask.TrialReadingCriterionId) && x.IsSign == false)) + { + return ResponseOutput.NotOk(_localizer["ReadingClinicalData_HaveUnsignedClinicalData"]); + } + if(visittask.TrialReadingCriterion.IsReadingTaskViewInOrder== ReadingOrder.InOrder) + { + if(await _visitTaskRepository.Where(x=>x.SubjectId== visittask.SubjectId + &&x.TrialReadingCriterionId== visittask.TrialReadingCriterionId + &&x.VisitTaskNum< visittask.VisitTaskNum&&x.IsAnalysisCreate==visittask.IsAnalysisCreate + &&x.IsSelfAnalysis==visittask.IsSelfAnalysis&&x.ArmEnum==visittask.ArmEnum&&x.TaskState==TaskState.NotEffect).AnyAsync() + ) + { + return ResponseOutput.NotOk(_localizer["ReadingClinicalData_NeedSetBeforeTaskEffect"]); } } - - return result; + await _visitTaskRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.VisitTaskId, x => new VisitTask() + { + + TaskState = TaskState.Effect + }); + await _visitTaskRepository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } - #endregion - #region 阅片临床数据PDF /// diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs index fbe40bf2a..edf51866c 100644 --- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingClinicalDataDto.cs @@ -9,8 +9,30 @@ using System.Threading.Tasks; namespace IRaCIS.Core.Application.Service.Reading.Dto { + public class SetTaskValidInDto + { + public Guid VisitTaskId { get; set; } + } - public class AddOrUpdateReadingClinicalDataDto + + public class SignConsistencyAnalysisReadingClinicalDataInDto + { + public Guid ConsistentClinicalDataId { get; set; } + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } + + } + + + public class AddOrUpdateReadingClinicalDataDto { public Guid? Id { get; set; } @@ -184,12 +206,25 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// 路径 /// public string Path { get; set; } - } + + /// + /// 大小 + /// + public int Size { get; set; } = 0; + + /// + /// 文件类型 + /// + public string Type { get; set; } = string.Empty; + } public class GetReadingOrTaskClinicalDataListInDto { - [NotDefault] + + public Guid? TrialReadingCriterionId { get; set; } + + [NotDefault] public Guid SubjectId { get; set; } [NotDefault] @@ -220,12 +255,29 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// 只查询已经签名的临床数据 /// public bool SelectIsSign { get; set; } = true; - } - /// - /// 获取访视列表 - /// - public class GetReadingClinicalDataListIndto + /// + /// 是否查询所有的一致性分析临床数据 (为否只查询PDF) + /// + public bool IsGetAllConsistencyAnalysis { get; set; } = true; + + } + + public class GetConsistencyAnalysisReadingClinicalDataListInDto + { + [NotDefault] + public Guid SubjectId { get; set; } + [NotDefault] + public Guid VisitTaskId { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + } + + /// + /// 获取访视列表 + /// + public class GetReadingClinicalDataListIndto { [NotDefault] public Guid SubjectId { get; set; } @@ -489,10 +541,21 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public string FileName { get; set; } - /// - /// 上传时间 - /// - public DateTime CreateTime { get; set; } + + /// + /// 大小 + /// + public int Size { get; set; } = 0; + + /// + /// 文件类型 + /// + public string Type { get; set; } + + /// + /// 上传时间 + /// + public DateTime CreateTime { get; set; } } diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs index 153e78ebb..688df02f5 100644 --- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingImageTaskViewModel.cs @@ -1,5 +1,6 @@ using IRaCIS.Core.Domain.Share; using MassTransit; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; @@ -141,6 +142,13 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public string ReportMark { get; set; } = string.Empty; + /// + /// 高亮问题的答案 + /// + public string HighlightAnswer { get; set; } = "[]"; + + public List HighlightAnswerList { get; set; } = new List(); + /// /// 问题英文分组 @@ -503,6 +511,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// CreateUserId /// public Guid CreateUserId { get; set; } + + public int? NumberOfFrames { get; set; } } public class GetManualListInDto @@ -609,6 +619,8 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto { public string Answer { get; set; } + public string LastTaskAnswer { get; set; } + public bool IsFirstChangeTask { get; set; } = false; public List CrterionDictionaryGroup { get; set; } @@ -1701,12 +1713,12 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto } - public class ResetReadingTaskInDto + public class SetSkipReadingCacheInDto { public Guid VisitTaskId { get; set; } } - public class SetSkipReadingCacheInDto + public class ResetReadingTaskInDto { public Guid VisitTaskId { get; set; } } diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs index c63e757d2..feebe486e 100644 --- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingMedicalReviewDto.cs @@ -32,7 +32,21 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public bool IsApplyHeavyReading { get; set; } = false; } - public class GetIRMedicalFeedbackListInDto:PageInput + public class GetNextIRMedicalFeedbackInDto + { + /// + /// 项目Id + /// + [NotDefault] + public Guid TrialId { get; set; } + + public Guid TrialReadingCriterionId { get; set; } + + public Guid TaskMedicalReviewId { get; set; } + } + + + public class GetIRMedicalFeedbackListInDto:PageInput { /// /// 项目Id @@ -43,7 +57,10 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public bool? IsUrgent { get; set; } - public MedicalReviewAuditState? AuditState { get; set; } + public Guid? SubjectId { get; set; } + + + public MedicalReviewAuditState? AuditState { get; set; } public string SubjectCode { get; set; } = string.Empty; @@ -78,7 +95,13 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public bool? IsInvalid { get; set; } public Guid? TrialReadingCriterionId { get; set; } - } + + /// + /// 获取不加急的 + /// + public bool IsNotHaveSigned { get; set; } = false; + + } public class GetIRMedicalFeedbackListOutDto { diff --git a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs index e7cada86e..566e9679c 100644 --- a/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs +++ b/IRaCIS.Core.Application/Service/Reading/Dto/ReadingQuestionViewModel.cs @@ -1,7 +1,9 @@ using IRaCIS.Core.Domain.Share; +using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -892,11 +894,14 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// public string ParentDictionaryCode { get; set; } = string.Empty; + public List HighlightAnswerList { get; set; } = new List(); + - /// - /// 系统标准Id - /// - public Guid ReadingQuestionCriterionTrialId { get; set; } + + /// + /// 系统标准Id + /// + public Guid ReadingQuestionCriterionTrialId { get; set; } /// /// 是否复制病灶 @@ -1118,6 +1123,9 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public List ParentTriggerValueList { get; set; } = new List(); public List RelevanceValueList { get; set; } = new List(); + + public List HighlightAnswerList { get; set; } = new List(); + /// /// 备注 /// @@ -1660,10 +1668,14 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// public LimitShow LimitShow { get; set; } = LimitShow.AllShow; - /// - /// 最大答案长度 - /// - public int? MaxAnswerLength { get; set; } + + public List HighlightAnswerList { get; set; }= new List(); + + + /// + /// 最大答案长度 + /// + public int? MaxAnswerLength { get; set; } /// /// 文件类型 @@ -1728,10 +1740,15 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto /// public string RelevanceValue { get; set; } = string.Empty; - /// - /// 类型 - /// - public string Type { get; set; } + /// + /// 高亮问题的答案 + /// + public string HighlightAnswer { get; set; } = "[]"; + + /// + /// 类型 + /// + public string Type { get; set; } /// /// 父问题触发 @@ -1846,6 +1863,13 @@ namespace IRaCIS.Core.Application.Service.Reading.Dto public bool IsAdditional { get; set; } + /// + /// 高亮问题的答案 + /// + public string HighlightAnswer { get; set; } = "[]"; + + public List HighlightAnswerList { get; set; } = new List(); + /// /// 分类算法 /// diff --git a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs index ccbbc510e..521a9d843 100644 --- a/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs +++ b/IRaCIS.Core.Application/Service/Reading/Interface/IReadingClinicalDataService.cs @@ -22,7 +22,6 @@ namespace IRaCIS.Core.Application.Contracts // Task<(List, object)> GetReadingClinicalDataList(GetReadingClinicalDataListIndto inDto); - Task> GetReadingClinicalList(GetReadingClinicalDataListIndto inDto); Task> GetStudyClinicalData(GetStudyClinicalDataInDto inDto); @@ -30,5 +29,7 @@ namespace IRaCIS.Core.Application.Contracts Task DealVisiTaskClinicalDataSignedAsync(Guid trialId, Guid subjectId, Guid readingId, bool isVisit, Guid trialReadingCritrialId); + + Task SignConsistencyAnalysisReadingClinicalData(SignConsistencyAnalysisReadingClinicalDataInDto inDto); } } \ No newline at end of file diff --git a/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs index b59c45897..6d410e1f7 100644 --- a/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs +++ b/IRaCIS.Core.Application/Service/Reading/MedicalAudit/ReadingMedicalReviewService.cs @@ -780,22 +780,65 @@ namespace IRaCIS.Core.Application.Service return ResponseOutput.Result(result); } - /// - /// 获取IR医学审核信息 + /// 获取下一个IR医学反馈 /// /// /// + /// [HttpPost] + public async Task GetNextIRMedicalFeedback(GetNextIRMedicalFeedbackInDto inDto) + { + var list = await GetIRMedicalFeedbackList(new GetIRMedicalFeedbackListInDto() + { + TrialId=inDto.TrialId, + TrialReadingCriterionId=inDto.TrialReadingCriterionId, + PageIndex=1, + AuditState= MedicalReviewAuditState.Auditing, + PageSize =99999, + }); + + var index = list.CurrentPageData.ToList().FindIndex(x => x.Id == inDto.TaskMedicalReviewId); + + var result = new GetIRMedicalFeedbackListOutDto() { }; + + if (index + 1 == list.CurrentPageData.Count()) // 最后一个 + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_IRFinish"]); + } + else if (index == -1 || list.CurrentPageData.Count == 1) // 第一个或者只有一个 + { + if (list.CurrentPageData[0].Id == inDto.TaskMedicalReviewId) + { + throw new BusinessValidationFailedException(_localizer["MedicalReview_IRFinish"]); + } + result = list.CurrentPageData[0]; + } + else + { + result = list.CurrentPageData.Skip(index + 1).Take(1).First(); + + } + return result; + } + + /// + /// 获取IR医学审核信息 + /// + /// + /// + [HttpPost] public async Task> GetIRMedicalFeedbackList(GetIRMedicalFeedbackListInDto inDto) { var taskMedicalReviewquery = _taskMedicalReviewRepository.Where(x => x.TrialId == inDto.TrialId).Include(x => x.VisitTask) .Where(x => x.VisitTask.DoctorUserId == _userInfo.Id) .Where(x => x.IsHaveQuestion) .WhereIf(!inDto.TaskBlindName.IsNullOrEmpty(), x => x.VisitTask.TaskBlindName == inDto.TaskBlindName) - .WhereIf(inDto.IsUrgent != null, x => x.VisitTask.IsUrgent == inDto.IsUrgent!) + .WhereIf(inDto.SubjectId != null, x => x.VisitTask.SubjectId == inDto.SubjectId!) + .WhereIf(inDto.IsUrgent != null, x => x.VisitTask.IsUrgent == inDto.IsUrgent!) .WhereIf(inDto.AuditState != null, x => x.AuditState == inDto.AuditState!) - .WhereIf(inDto.ReadingCategory != null, x => x.VisitTask.ReadingCategory == inDto.ReadingCategory!) + .WhereIf(inDto.IsNotHaveSigned, x => x.AuditState != MedicalReviewAuditState.HaveSigned) + .WhereIf(inDto.ReadingCategory != null, x => x.VisitTask.ReadingCategory == inDto.ReadingCategory!) .WhereIf(inDto.AuditAdviceEnum != null, x => x.AuditAdviceEnum == inDto.AuditAdviceEnum!) .WhereIf(inDto.DoctorUserIdeaEnum != null, x => x.DoctorUserIdeaEnum == inDto.DoctorUserIdeaEnum!) .WhereIf(inDto.IsClosedDialog != null, x => x.IsClosedDialog == inDto.IsClosedDialog!) diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs index f4ce30663..4089e8676 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingCriterion/ReadingQuestionService.cs @@ -274,6 +274,7 @@ namespace IRaCIS.Application.Services { indto.ParentTriggerValue = string.Join(',', indto.ParentTriggerValueList); indto.RelevanceValue = string.Join(',', indto.RelevanceValueList); + indto.HighlightAnswer=JsonConvert.SerializeObject(indto.HighlightAnswerList); if (indto.Id != null) { var trialIdList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionSystemId == indto.Id && x.IsJudgeQuestion && x.JudgeType != JudgeTypeEnum.None) @@ -439,7 +440,7 @@ namespace IRaCIS.Application.Services } indto.ParentTriggerValue = string.Join(',', indto.ParentTriggerValueList); indto.RelevanceValue = string.Join(',', indto.RelevanceValueList); - + indto.HighlightAnswer =JsonConvert.SerializeObject(indto.HighlightAnswerList); if (indto.Id != null) { diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs index 5759c9ced..9c7371891 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingGlobalTaskService.cs @@ -34,6 +34,23 @@ namespace IRaCIS.Application.Services await VerifyTaskIsSign(inDto.GlobalTaskId); await this.SubmitTaskChangeState(inDto.GlobalTaskId); + var globalAnswerList = await _readingGlobalTaskInfoRepository.Where(x => + x.QuestionId!=null &&x.Answer!=string.Empty&& x.Answer != null && + x.GlobalTaskId == inDto.GlobalTaskId).ToListAsync(); + + foreach (var item in globalAnswerList) + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.TaskId && x.ReadingQuestionTrialId == item.QuestionId + && x.Answer != item.Answer + , x => new ReadingTaskQuestionAnswer() + { + GlobalChangeAnswer = item.Answer, + IsGlobalChange = true, + }); + } + + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + return ResponseOutput.Ok(true); } @@ -60,24 +77,15 @@ namespace IRaCIS.Application.Services if (criterionType == CriterionType.PCWG3) { await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == answer.QuestionId - && x.Answer != answer.Answer && answer.Answer != string.Empty && answer.Answer != null - , x => new ReadingTaskQuestionAnswer() - { + && x.Answer != answer.Answer && answer.Answer != string.Empty && answer.Answer != null + , x => new ReadingTaskQuestionAnswer() + { Answer= answer.Answer, GlobalChangeAnswer = answer.Answer, IsGlobalChange = true, - }); - } - else - { - await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == answer.QuestionId - && x.Answer != answer.Answer && answer.Answer != string.Empty && answer.Answer != null - , x => new ReadingTaskQuestionAnswer() - { - GlobalChangeAnswer = answer.Answer, - IsGlobalChange = true, - }); + }); } + } } diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs index 00af22d3d..c27c2e8bd 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs @@ -24,6 +24,7 @@ using IRaCIS.Core.Domain.Models; using Microsoft.Extensions.Options; using System.Linq; using NPOI.SS.Formula.Functions; +using static Microsoft.EntityFrameworkCore.DbLoggerCategory; namespace IRaCIS.Application.Services { @@ -873,10 +874,33 @@ namespace IRaCIS.Application.Services var answers = new List(); + var lastTaskAnswer = new List(); + if (visitTaskId != null) { answers = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == visitTaskId).ToListAsync(); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var laskTaskId = await _visitTaskRepository.Where(x => + (x.SubjectId == taskInfo.SubjectId && x.TaskState == TaskState.Effect + && x.IsAnalysisCreate == taskInfo.IsAnalysisCreate + && x.DoctorUserId == taskInfo.DoctorUserId + && x.IsSelfAnalysis == taskInfo.IsSelfAnalysis + && x.VisitTaskNum < taskInfo.VisitTaskNum + && x.ArmEnum == taskInfo.ArmEnum + && x.TrialReadingCriterionId == taskInfo.TrialReadingCriterionId + && x.ReadingCategory == ReadingCategory.Visit) || x.Id == taskInfo.BeforeConvertedTaskId + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).FirstOrDefaultAsync(); + if (criterionIdInfo.IsReadingTaskViewInOrder != ReadingOrder.InOrder) + { + // 无序的话 不要查 + laskTaskId = Guid.NewGuid(); + } + + lastTaskAnswer = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == laskTaskId).ToListAsync(); + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); if (taskinfo.VisitTaskNum == 0) @@ -921,24 +945,28 @@ namespace IRaCIS.Application.Services foreach (var item in result) { - GetDicomReadingAnswer(item, questions, answers); + GetDicomReadingAnswer(item, questions, answers, lastTaskAnswer); } return result; } - private void GetDicomReadingAnswer(DicomReadingQuestionAnswer item, List questions, List answers) + private void GetDicomReadingAnswer(DicomReadingQuestionAnswer item, List questions, List answers, List lastTaskAnswers) { var answer = answers.Where(x => x.ReadingQuestionTrialId == item.Id).Select(x => x.Answer).FirstIsNullReturnEmpty(); item.Answer = answer.IsNullOrEmpty() ? item.DefaultValue : answer; + var lastTaskAnswer = lastTaskAnswers.Where(x => x.ReadingQuestionTrialId == item.Id).Select(x => x.Answer).FirstIsNullReturnEmpty(); + item.LastTaskAnswer = lastTaskAnswer.IsNullOrEmpty() ? item.DefaultValue : lastTaskAnswer; + + item.Childrens = questions.Where(x => x.ParentId == item.Id || (x.GroupId == item.Id && x.ParentId == null)).ToList(); if (item.Childrens != null && item.Childrens.Count > 0) { foreach (var question in item.Childrens) { - GetDicomReadingAnswer(question, questions, answers); + GetDicomReadingAnswer(question, questions, answers, lastTaskAnswers); } } @@ -1212,6 +1240,7 @@ namespace IRaCIS.Application.Services var groupList = new List(); var qusetionIds = qusetionList.Select(x => x.Id).ToList(); + var tableQuestionList = await _readingTableQuestionTrialRepository .WhereIf(inDto.QuestionClassify != null, x => x.QuestionClassify == inDto.QuestionClassify) .Where(x => qusetionIds.Contains(x.ReadingQuestionId)) @@ -1489,6 +1518,8 @@ namespace IRaCIS.Application.Services answers.Add("LastTaskState", lastTaskTableAnswer.Where(n => n.QuestionId == item.Id && n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && n.RowIndex == x).Select(n => n.Answer).FirstOrDefault() ?? string.Empty); answers.Add("LastTaskMajorAxis", lastTaskTableAnswer.Where(n => n.QuestionId == item.Id && n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis && n.RowIndex == x).Select(n => n.Answer).FirstOrDefault() ?? string.Empty); answers.Add("LastTaskShortAxis", lastTaskTableAnswer.Where(n => n.QuestionId == item.Id && n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis && n.RowIndex == x).Select(n => n.Answer).FirstOrDefault() ?? string.Empty); + answers.Add("BaseLineMajorAxis", baseLineTableAnswer.Where(n => n.QuestionId == item.Id && n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis && n.RowIndex == x).Select(n => n.Answer).FirstOrDefault() ?? string.Empty); + answers.Add("BaseLineShortAxis", baseLineTableAnswer.Where(n => n.QuestionId == item.Id && n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis && n.RowIndex == x).Select(n => n.Answer).FirstOrDefault() ?? string.Empty); if (rowInfo.LesionType == LesionType.BaselineLesions) { answers.Add("BaseLineLesionNumber", baseLineTableAnswer.Where(n => n.ReadingTableQuestionTrial.QuestionMark == QuestionMark.LesionNumber && n.RowIndex == rowInfo.RowIndex).Select(x => x.Answer).FirstIsNullReturnEmpty()); @@ -2702,6 +2733,8 @@ namespace IRaCIS.Application.Services return ResponseOutput.Ok(true); } + + /// /// 清除跳过阅片的缓存 /// @@ -2714,6 +2747,7 @@ namespace IRaCIS.Application.Services return true; } + /// /// 设置跳过阅片的缓存 /// @@ -2764,8 +2798,6 @@ namespace IRaCIS.Application.Services throw new BusinessValidationFailedException(_localizer["ReadingImage_IDMust"]); } - var trialReadingCriterion = await _readingQuestionCriterionTrialRepository.FindAsync(trialReadingCriterionId ?? Guid.Empty); - #region 跳过阅片 var clearSkipReadingCacheKey = _userInfo.Id.ToString() + "SkipReadingCache"; @@ -2776,8 +2808,7 @@ namespace IRaCIS.Application.Services cacheSkipIds = JsonConvert.DeserializeObject>(clearSkipReadingCache); } #endregion - - + var trialReadingCriterion = await _readingQuestionCriterionTrialRepository.FindAsync(trialReadingCriterionId ?? Guid.Empty); if (inDto.VisitTaskId != null) { @@ -2797,7 +2828,6 @@ namespace IRaCIS.Application.Services trialReadingCriterionId = task.TrialReadingCriterionId; } - // 有序 else if (inDto.SubjectId != null && trialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.InOrder) { var subjectTaskList = (await _visitTaskService.GetSubjectReadingIQueryable(new GetReadingIQueryableInDto() @@ -2847,12 +2877,8 @@ namespace IRaCIS.Application.Services } - // 受试者无序 else if (inDto.SubjectId != null && trialReadingCriterion.IsReadingTaskViewInOrder == ReadingOrder.SubjectRandom) { - - - var subjectTaskList = (await _visitTaskService.GetSubjectReadingIQueryable(new GetReadingIQueryableInDto() { TrialId = inDto.TrialId, @@ -2865,10 +2891,6 @@ namespace IRaCIS.Application.Services })).CurrentPageData; - - - - if (subjectTaskList.Count() == 0) { throw new BusinessValidationFailedException(_localizer["ReadingImage_TaskFinish"], ApiResponseCodeEnum.CloseCurrentWindows); @@ -2904,14 +2926,11 @@ namespace IRaCIS.Application.Services }).FirstOrDefaultAsync(); } - // 完全随机 else { var query = _visitTaskRepository.Where(x => x.TrialId == inDto.TrialId && x.TrialReadingCriterionId == trialReadingCriterionId && x.ReadingTaskState != ReadingTaskState.HaveSigned && x.DoctorUserId == _userInfo.Id && x.TrialReadingCriterionId == trialReadingCriterionId - && x.TaskState == TaskState.Effect) - // 排除跳过的 - .Where(x => !cacheSkipIds.Contains(x.Id)); + && x.TaskState == TaskState.Effect); var count = await query.CountAsync(); if (count == 0) { @@ -2949,7 +2968,7 @@ namespace IRaCIS.Application.Services task.SubjectCode = await _subjectRepository.Where(x => x.Id == task.SubjectId).Select(x => x.Code).FirstNotNullAsync(); } - await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == task.VisitTaskId && x.FirstReadingTime == null, x => new VisitTask() + await _visitTaskRepository.BatchUpdateNoTrackingAsync(x => x.Id == task.VisitTaskId&&x.TaskState==TaskState.Effect&&x.ReadingTaskState!=ReadingTaskState.HaveSigned && x.FirstReadingTime == null, x => new VisitTask() { FirstReadingTime = DateTime.Now, }); @@ -3043,9 +3062,25 @@ namespace IRaCIS.Application.Services var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); if (taskinfo.ReadingCategory != ReadingCategory.Visit) { - + throw new BusinessValidationFailedException(_localizer["ReadingImage_CannotReset"]); } + await _readingTableAnswerRowInfoRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId); + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId); + await _readingTableQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId); + await _readingTaskQuestionMarkRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId); + await _readingCustomTagRepository.BatchDeleteNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId); + await _visitTaskRepository.UpdatePartialFromQueryAsync(x => x.Id == inDto.VisitTaskId, x => new VisitTask() + { + ReadingTaskState = ReadingTaskState.WaitReading + }); + await _visitTaskRepository.SaveChangesAsync(); + _userInfo.IsNotNeedInspection = true; + await _readingCalculateService.AddTaskLesionAnswerFromLastTask(new AddTaskLesionAnswerFromLastTaskInDto() + { + VisitTaskId = inDto.VisitTaskId + }); + await AddDefaultValueToTask(inDto.VisitTaskId); return new ResetReadingTaskOutDto() { }; } /// diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs index 594aa2137..e4e374694 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingOncologyTaskService.cs @@ -470,7 +470,7 @@ namespace IRaCIS.Application.Services if (finishReading) { // 获取临床数据 - var clinicalData = await _readingClinicalDataService.GetReadingClinicalList(new GetReadingClinicalDataListIndto() + var clinicalData = await _readingClinicalDataService.GetClinicalDataList(new GetReadingOrTaskClinicalDataListInDto() { SubjectId = readModuleInfo.SubjectId, ReadingId = readModuleInfo.Id, diff --git a/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs index e2a260e9f..fe279c652 100644 --- a/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Reading/_MapConfig.cs @@ -118,6 +118,7 @@ namespace IRaCIS.Core.Application.Service //.ForMember(d => d.SiteNames, u => u.MapFrom(s => s.ReadingPeriodSites.SelectMany(x => x.Site.SiteName).ToList())); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); CreateMap().ReverseMap(); diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/IRECIST1Point1CalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/IRECIST1Point1CalculateService.cs index 51c3433ad..0cfebf074 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/IRECIST1Point1CalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/IRECIST1Point1CalculateService.cs @@ -217,6 +217,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType= ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 分组 @@ -242,6 +244,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoCalculateService.cs index 1abb95c98..22d76219b 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoCalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoCalculateService.cs @@ -213,6 +213,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit = x.CustomUnit, ReportLayType = ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 分组 @@ -238,6 +240,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit = x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoWithoutPETCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoWithoutPETCalculateService.cs new file mode 100644 index 000000000..c7cccd3e0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/LuganoWithoutPETCalculateService.cs @@ -0,0 +1,3753 @@ +using IRaCIS.Core.Application.Service.Reading.Dto; +using IRaCIS.Core.Domain.Share; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using Panda.DynamicWebApi.Attributes; +using IRaCIS.Core.Infra.EFCore.Common; +using Microsoft.Extensions.Caching.Memory; + +using IRaCIS.Core.Infrastructure; +using MassTransit; +using System.Reflection.Metadata.Ecma335; +using System.Linq; +using NPOI.SS.Formula.Functions; +using DocumentFormat.OpenXml.Drawing.Charts; +using IRaCIS.Core.Application.Contracts; +using IRaCIS.Core.Application.Service.ReadingCalculate.Interface; +using DocumentFormat.OpenXml.Office2016.Drawing.ChartDrawing; + +namespace IRaCIS.Core.Application.Service.ReadingCalculate +{ + + [ApiExplorerSettings(GroupName = "Reading")] + public class LuganoWithoutPETCalculateService : BaseService, ICriterionCalculateService, ILuganoCalculateService + { + private readonly IRepository _readingTableQuestionAnswerRepository; + private readonly IRepository _visitTaskRepository; + private readonly IRepository _readingQuestionCriterionTrialRepository; + private readonly IRepository _readingTableQuestionTrialRepository; + private readonly IRepository _readingTaskQuestionMarkRepository; + private readonly IRepository _readingTableAnswerRowInfoRepository; + private readonly IRepository _readingGlobalTaskInfoRepository; + private readonly IRepository _readingQuestionTrialRepository; + private readonly IRepository _organInfoRepository; + private readonly IRepository _subjectVisitRepository; + private readonly IRepository _dicomStudyRepository; + private readonly IRepository _tumorAssessmentRepository; + private readonly ISubjectVisitService _subjectVisitService; + private readonly IGeneralCalculateService _generalCalculateService; + private readonly IRepository _readingTaskQuestionAnswerRepository; + + public LuganoWithoutPETCalculateService( + IRepository readingTableQuestionAnswerRepository, + IRepository visitTaskRepository, + IRepository readingQuestionCriterionTrialRepository, + IRepository readingTableQuestionTrialRepository, + IRepository readingTaskQuestionMarkRepository, + IRepository readingTableAnswerRowInfoRepository, + IRepository readingGlobalTaskInfoRepository, + IRepository readingQuestionTrialRepository, + IRepository organInfoRepository, + IRepository subjectVisitRepository, + IRepository dicomStudyRepository, + IRepository tumorAssessmentRepository, + ISubjectVisitService subjectVisitService, + IGeneralCalculateService generalCalculateService, + IRepository readingTaskQuestionAnswerRepository + ) + { + this._readingTableQuestionAnswerRepository = readingTableQuestionAnswerRepository; + this._visitTaskRepository = visitTaskRepository; + this._readingQuestionCriterionTrialRepository = readingQuestionCriterionTrialRepository; + this._readingTableQuestionTrialRepository = readingTableQuestionTrialRepository; + this._readingTaskQuestionMarkRepository = readingTaskQuestionMarkRepository; + this._readingTableAnswerRowInfoRepository = readingTableAnswerRowInfoRepository; + this._readingGlobalTaskInfoRepository = readingGlobalTaskInfoRepository; + this._readingQuestionTrialRepository = readingQuestionTrialRepository; + this._organInfoRepository = organInfoRepository; + this._subjectVisitRepository = subjectVisitRepository; + this._dicomStudyRepository = dicomStudyRepository; + this._tumorAssessmentRepository = tumorAssessmentRepository; + this._subjectVisitService = subjectVisitService; + this._generalCalculateService = generalCalculateService; + this._readingTaskQuestionAnswerRepository = readingTaskQuestionAnswerRepository; + } + + /// + /// 获取阅片的计算数据 + /// + /// + /// + public async Task GetReadingCalculationData(GetReadingCalculationDataInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + readingData.ComputationTrigger = ComputationTrigger.LiverBloodPool; + var baseLinePET5PS = 0m; + if (!readingData.IsBaseLine) + { + var baseLineTaskId = await GetBaseLineTaskId(readingData); + baseLinePET5PS = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + } + return new + { + BaseLinePET5PS = baseLinePET5PS, + //计算的5ps评分 + CalculatePET5PS=await GetPET5PS(readingData), + + }; + } + + #region 临时对象 单个请求的生命周期 避免重复查询数据库 + + private List visitTaskAnswerList; + + /// + /// 获取Sod的值 + /// + private decimal? sODData; + + /// + /// 基线任务Id + /// + private Guid? BaseLineTaskId; + + /// + /// 影像是否无法融合 + /// + private bool? isUnableFuse; + + + private string nAString = "NA"; + #endregion + + #region 删除病灶获取起始病灶序号 + /// + /// 删除病灶获取起始病灶序号(RECIST1Point1 固定是1) + /// + /// + public async Task GetDeleteLesionStatrIndex(DeleteReadingRowAnswerInDto inDto) + { + return 1; + } + #endregion + + #region 获取阅片报告 + /// + /// 获取阅片报告 + /// + /// + /// + [HttpPost] + public async Task GetReadingReportEvaluation(GetReadingReportEvaluationInDto indto) + { + GetReadingReportEvaluationOutDto result = new GetReadingReportEvaluationOutDto(); + + result.CalculateResult = await this.GetReportVerify(new GetReportVerifyInDto() + { + VisitTaskId = indto.VisitTaskId + }); + + + var visitTaskInfo = await _visitTaskRepository.Where(x => x.Id == indto.VisitTaskId).FirstNotNullAsync(); + + result.ReadingTaskState = visitTaskInfo.ReadingTaskState; + var taskInfoList = await _generalCalculateService.GetReadingReportTaskList(indto.VisitTaskId); + + result.VisitTaskList = taskInfoList; + + var visitTaskIds = taskInfoList.Select(x => x.VisitTaskId).ToList(); + + var criterionId = visitTaskInfo.TrialReadingCriterionId; + var questionList = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == criterionId && x.ShowQuestion != ShowQuestion.Hide).ToListAsync(); + var tableQuestionList = await _readingTableQuestionTrialRepository.Where(x => x.TrialCriterionId == criterionId).OrderBy(x => x.ShowOrder).ToListAsync(); + var tableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == indto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + result.LesionCountList = tableAnsweRowInfos.GroupBy(x => x.LesionType).Select(x => new LesionDto + { + LesionType = x.Key, + Count = x.ToList().Count() + }).ToList(); + var answers = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var tableAnswers = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + + var globalanswerList = await _readingGlobalTaskInfoRepository.Where(x => visitTaskIds.Contains(x.TaskId) && x.GlobalVisitTask.TaskState == TaskState.Effect && x.Answer != string.Empty).Select(x => new + { + x.TaskId, + x.GlobalVisitTask.VisitTaskNum, + x.QuestionId, + x.Answer + }).ToListAsync(); + + var alltableAnsweRowInfos = await _readingTableAnswerRowInfoRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId)).ToListAsync(); + var organIds = alltableAnsweRowInfos.Where(x => x.OrganInfoId != null).Select(x => x.OrganInfoId).Distinct().ToList(); + var organInfos = await _organInfoRepository.Where(x => organIds.Contains(x.Id)).ToListAsync(); + + var needChangeType = new List() { + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + }; + + // 第一级 + + #region 构造问题 + List questions = questionList.Where(x => x.Type == ReadingQestionType.Group).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + QuestionId = x.Id, + GroupName = x.GroupName, + GroupEnName = x.GroupEnName, + IsShowInDicom = x.IsShowInDicom, + Type = x.Type, + GroupId = x.GroupId, + QuestionType = x.QuestionType, + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + TypeValue = x.TypeValue, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + ReportLayType = ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, + }).ToList(); + + // 分组 + foreach (var item in questions) + { + item.Childrens = questionList.Where(x => x.GroupId == item.QuestionId).OrderBy(x => x.ShowOrder).Select(x => new ReadingReportDto() + { + GroupName = x.GroupName, + QuestionId = x.Id, + IsShowInDicom = x.IsShowInDicom, + GroupEnName = x.GroupEnName, + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + LesionType = x.LesionType, + QuestionGenre = x.QuestionGenre, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + Type = x.Type, + QuestionType = x.QuestionType, + TypeValue = x.TypeValue, + ShowOrder = x.ShowOrder, + OrderMark = x.OrderMark, + ValueType = x.ValueType, + Unit = x.Unit, + CustomUnit = x.CustomUnit, + ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, + }).ToList(); + + // 问题 + foreach (var question in item.Childrens) + { + + foreach (var task in taskInfoList) + { + + var globalAnswer = globalanswerList.Where(x => x.TaskId == task.VisitTaskId && x.QuestionId == question.QuestionId).OrderByDescending(x => x.VisitTaskNum).FirstOrDefault(); + var answer = answers.Where(x => x.VisitTaskId == task.VisitTaskId && x.ReadingQuestionTrialId == question.QuestionId).FirstOrDefault(); + question.Answer.Add(new TaskQuestionAnswer() + { + Answer = answer == null ? string.Empty : answer.Answer, + IsGlobalChange = globalAnswer == null ? false : true, + GlobalChangeAnswer = globalAnswer == null ? string.Empty : globalAnswer.Answer, + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + + }); + } + + // 构造表格行数据 + + + var rowlist = tableAnsweRowInfos.Where(x => x.QuestionId == question.QuestionId).OrderBy(x => x.RowIndex).ToList(); + + + question.Childrens = rowlist.Select(x => new ReadingReportDto() + { + QuestionName = question.OrderMark + x.RowIndex.GetLesionMark(), + SplitOrMergeLesionName = x.MergeName.IsNullOrEmpty() ? x.SplitName : x.MergeName, + SplitOrMergeType = x.SplitOrMergeType, + LesionType = question.LesionType, + IsShowInDicom = question.IsShowInDicom, + IsCanEditPosition = x.IsCanEditPosition, + RowIndex = x.RowIndex, + BlindName = x.BlindName, + ReportLayType = ReportLayType.Lesions, + }).ToList(); + + + foreach (var row in question.Childrens) + { + // tableQuestion + row.Childrens = tableQuestionList.Where(x => x.ReadingQuestionId == question.QuestionId).Select(x => new ReadingReportDto() + { + QuestionName = x.QuestionName.LanguageName(x.QuestionEnName, _userInfo.IsEn_Us), + QuestionId = x.ReadingQuestionId, + TableQuestionId = x.Id, + Type = x.Type, + LesionType = question.LesionType, + TableQuestionType = x.TableQuestionType, + RowId = row.RowId, + IsShowInDicom = question.IsShowInDicom, + DataSource = x.DataSource, + DictionaryCode = x.DictionaryCode, + QuestionMark = x.QuestionMark, + TypeValue = x.TypeValue, + RowIndex = row.RowIndex, + ShowOrder = x.ShowOrder, + ValueType = x.ValueType, + Unit = x.Unit, + ReportLayType = ReportLayType.TableQuestion, + }).ToList(); + + + foreach (var tableQuestion in row.Childrens) + { + foreach (var task in taskInfoList) + { + var rowinfo = alltableAnsweRowInfos.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex).FirstOrDefault(); + var taskQuestionAnswer = new TaskQuestionAnswer() + { + Answer = tableAnswers.Where(x => x.VisitTaskId == task.VisitTaskId && x.QuestionId == tableQuestion.QuestionId && x.RowIndex == tableQuestion.RowIndex && x.TableQuestionId == tableQuestion.TableQuestionId).Select(x => x.Answer).FirstIsNullReturnEmpty(), + TaskName = task.TaskName, + VisitTaskId = task.VisitTaskId, + }; + if (rowinfo != null && rowinfo.OrganInfoId != null) + { + var organInfo = organInfos.Where(x => x.Id == rowinfo.OrganInfoId).FirstOrDefault(); + + + if (organInfo != null && needChangeType.Contains(tableQuestion.QuestionMark)) + { + if (_userInfo.IsEn_Us) + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOCEN; + + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULATEN; + + } + break; + case QuestionMark.Part: + + taskQuestionAnswer.Answer = organInfo.PartEN; + + break; + + } + + } + else + { + switch (tableQuestion.QuestionMark) + { + case QuestionMark.Organ: + taskQuestionAnswer.Answer = organInfo.TULOC; + break; + case QuestionMark.Location: + if (organInfo.IsCanEditPosition) + { + + } + else + { + taskQuestionAnswer.Answer = organInfo.TULAT; + + } + break; + case QuestionMark.Part: + taskQuestionAnswer.Answer = organInfo.Part; + break; + + } + } + + } + } + tableQuestion.Answer.Add(taskQuestionAnswer); + } + } + + + } + + + }; + } + #endregion + + + + result.TaskQuestions = questions; + + + + return result; + + } + #endregion + + + /// + /// 获取是否是最大suvmax 病灶 + /// + /// + /// + [HttpPost] + public async Task GetIsSuvMaxLesion(GetIsSuvMaxLesionInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + + var maxSuv = await GetSuvMax(readingData); + var rowInfo = readingData.QuestionInfo.SelectMany(x => x.TableRowInfoList).ToList(); + var tableQuestions = rowInfo.SelectMany(x => x.TableQuestionList).Where(x => x.RowId == inDto.RowId).ToList(); + + var lesionMaxSuv = tableQuestions.Where(x => x.QuestionMark == QuestionMark.SUVmax).Select(x => x.Answer.IsNullOrEmptyReturn0()).MaxOrDefault(); + + + return new GetIsSuvMaxLesionOutDto() + { + IsSuvMaxLesion = maxSuv == lesionMaxSuv + }; + + + } + + /// + /// 获取是否可选择不能融合影像 + /// + /// + /// + [HttpPost] + public async Task GetCanChooseNotMerge(GetCanChooseNotMergeInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + + List questionTypes = new List() { + QuestionType.LiverSUVmax, + QuestionType.MediastinumSUVmax, + QuestionType.SUVmax, + QuestionType.SUVmaxLesion, + }; + + var pet5ps = readingData.QuestionInfo.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstOrDefault(); + + + GetCanChooseNotMergeOutDto getCanChooseNotMergeOutDto = new GetCanChooseNotMergeOutDto() + { + IsCanChooseNotMerge = !readingData.QuestionInfo.Any(x => questionTypes.Contains(x.QuestionType) && x.Answer != string.Empty) + + + }; + return getCanChooseNotMergeOutDto; + } + + /// + /// 测试计算 + /// + /// + /// + /// + [HttpPost] + public async Task TestCalculate(Guid visitTaskId, QuestionType type) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + await ReadingCalculate(readingData, new List() { type }); + } + + /// + /// 获取最低PDD信息 + /// + /// + /// + [HttpPost] + public async Task GetLowPPDInfo(GetPPDInfoInDto inDto) + { + GetPPDInfoOutDto result = new GetPPDInfoOutDto(); + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).ProjectTo(_mapper.ConfigurationProvider).FirstNotNullAsync(); + var visitTaskIds = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId == taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ArmEnum == taskinfo.ArmEnum + && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).ToListAsync(); + + var answerList = await _readingTableQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId) + && x.QuestionId == inDto.QuestionId + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.PPD && x.RowIndex == inDto.RowIndex) + .Select(x => new + { + x.Answer, + x.VisitTaskId, + }).ToListAsync(); + + var lowPddTaskid = answerList.Select(x => new + { + Answer = x.Answer.IsNullOrEmptyReturn0(), + x.VisitTaskId + }).OrderBy(x => x.Answer).Select(x => x.VisitTaskId).FirstOrDefault(); + + if (lowPddTaskid != default(Guid)) + { + var lowPPDAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == lowPddTaskid + && x.QuestionId == inDto.QuestionId + && x.RowIndex == inDto.RowIndex).Include(x => x.ReadingTableQuestionTrial) + .Select(x => new + { + x.Answer, + x.ReadingTableQuestionTrial.QuestionMark, + }).ToListAsync(); + + result.NadirPPD = lowPPDAnswerList.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + result.LowPPDLDi = lowPPDAnswerList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + result.LowPPDSDi = lowPPDAnswerList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + result.LowPPDVisit = await _visitTaskRepository.Where(x => x.Id == lowPddTaskid).Select(x => x.TaskBlindName).FirstOrDefaultAsync(); + } + + return result; + + } + + /// + /// 计算任务 + /// + /// + /// + [HttpPost] + public async Task CalculateTask(CalculateTaskInDto inDto) + { + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + readingData.IsChangeOtherTask = inDto.IsChangeOtherTask; + readingData.ComputationTrigger = inDto.ComputationTrigger; + await ReadingCalculate(readingData); + } + + /// + /// 获取报告验证的信息(这里每个标准可能不一样 返回用object) + /// + /// + /// + public async Task GetReportVerify(GetReportVerifyInDto inDto) + { + var calculateDto = await _generalCalculateService.GetReadingCalculateDto(inDto.VisitTaskId); + return new GetReportVerifyOutDto() + { + TumorEvaluate = calculateDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.ImgOncology).Select(x => x.Answer).FirstOrDefault(), + IsExistDisease = await this.GetReportIsExistDisease(inDto.VisitTaskId), + + }; + } + + + + /// + /// 自动计算 + /// + /// + /// + /// + public async Task ReadingCalculate(ReadingCalculateDto inDto, List? calculateType = null) + { + + #region 计算 这里顺序非常重要 后面计算的值要依赖前面计算的结果 + var needAddList = new List(); + + + List calculateList = new List() + { + // 是否存在Pet + new ReadingCalculateData (){QuestionType=QuestionType.ExistPET,GetStringFun=GetExistPET ,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, } }, + + //垂直径乘积之和(SPD) + new ReadingCalculateData (){QuestionType=QuestionType.SPD,GetDecimalFun=GetSPD,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.RemoveLesion,}}, + + // 与基线相比SPD变化的百分比 + new ReadingCalculateData (){QuestionType=QuestionType.SPDChange,GetStringFun=CompareBaselineSPD,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.RemoveLesion,}}, + + // 脾脏垂直径 + new ReadingCalculateData (){QuestionType=QuestionType.SpleenLength,GetDecimalNullFun=GetSpleenLength,ComputationTriggerList=new List(){ ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 与基线相比脾肿大增加的百分比 + new ReadingCalculateData (){QuestionType=QuestionType.SplenoncusChange,GetStringFun=GetSplenoncusChange,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 与最低点相比脾脏垂直径长度的增加值 + new ReadingCalculateData (){QuestionType=QuestionType.SplenoncusAdd,GetStringFun=GetSplenoncusAdd,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 与基线相比脾垂直径变化值 + new ReadingCalculateData (){QuestionType=QuestionType.SplenoncusDiameterChange,GetStringFun=GetSplenoncusDiameterChange,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 脾肿垂直径最低点访视 + new ReadingCalculateData (){QuestionType=QuestionType.LowestSplenoncusVisit,GetStringFun=GetLowestSplenoncusVisit,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 获取脾脏状态 + new ReadingCalculateData (){QuestionType=QuestionType.SplenicStatus,GetStringFun=GetSplenicStatus,ComputationTriggerList=new List(){ ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,}}, + + // 获取脾脏评估 + new ReadingCalculateData (){QuestionType=QuestionType.SplenicEvaluation,GetStringFun=GetSplenicEvaluation,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,ComputationTrigger.SplenicEvaluation,}}, + + //靶病灶评估 + new ReadingCalculateData (){QuestionType=QuestionType.TargetLesion,GetStringFun=GetTargetLesionEvaluate,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,ComputationTrigger.MergeLesion,}}, + + //非靶病灶评估 + new ReadingCalculateData (){QuestionType=QuestionType.NoTargetLesion,GetStringFun=GetNoTargetLesionEvaluate,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + //新病灶评估 + new ReadingCalculateData (){QuestionType=QuestionType.NewLesionEvaluation,GetStringFun=GetNewLesionEvaluate,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + //获取肝脏评估 + new ReadingCalculateData (){QuestionType=QuestionType.LiverAssessment,GetStringFun=GetLiverAssessment,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.LiverStatus,}}, + + // 骨髓中是否存在局灶性 FDG亲和病灶的证据 + new ReadingCalculateData (){QuestionType=QuestionType.EvidenceFocalFDG,GetStringFun=GetEvidenceFocalFDG,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.ImageQuality, }}, + + //CT/MRI + new ReadingCalculateData (){QuestionType=QuestionType.CTandMRI,GetStringFun=CTMRIEvaluation,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.LiverStatus,ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,ComputationTrigger.SplenicEvaluation,ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,ComputationTrigger.MergeLesion,}}, + + //SUVmax + new ReadingCalculateData (){QuestionType=QuestionType.SUVmax,GetDecimalNullFun=GetSuvMax,ComputationTriggerList=new List(){ ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + // PET 5PS评分 + new ReadingCalculateData (){QuestionType=QuestionType.PET5PS,GetStringFun=GetPET5PS,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.ImageQuality, ComputationTrigger.LiverBloodPool,ComputationTrigger.MediastinalPool,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + // 修改PET 5PS评分备注 + new ReadingCalculateData (){QuestionType=QuestionType.PSScoreRemarks,GetStringFun=GetPET5PSRemark,ComputationTriggerList=new List(){ ComputationTrigger.LiverBloodPool,ComputationTrigger.MediastinalPool,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + //与基线相比摄取值变化 + new ReadingCalculateData (){QuestionType=QuestionType.UptakeChange,GetStringFun=GetUptakeChange,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.ImageQuality, ComputationTrigger.LiverBloodPool,ComputationTrigger.MediastinalPool,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + // FDG-PET 评估结果 + new ReadingCalculateData (){QuestionType=QuestionType.FDGPET,GetStringFun=GetFDGPETOverallAssessment,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.ImageQuality, ComputationTrigger.LiverBloodPool,ComputationTrigger.MediastinalPool,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,ComputationTrigger.PDGPET,}}, + + // 上一次 FDG-PET 评估结果 + new ReadingCalculateData (){QuestionType=QuestionType.LastFDGPET,GetStringFun=GetLastFDGPETOverallAssessment,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + // 影像学整体肿瘤评估 + new ReadingCalculateData (){QuestionType=QuestionType.ImgOncology,GetStringFun=GetImgOncology,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.ImageQuality, ComputationTrigger.LiverStatus,ComputationTrigger.SplenicApexPosition,ComputationTrigger.PositionSpleenFloor,ComputationTrigger.SplenicEvaluation,ComputationTrigger.LiverBloodPool,ComputationTrigger.MediastinalPool,ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,ComputationTrigger.PDGPET,ComputationTrigger.MergeLesion,}}, + + + + // SUVmax所在病灶 + new ReadingCalculateData (){QuestionType=QuestionType.SUVmaxLesion,GetStringFun=GetSuvMaxFocus,ComputationTriggerList=new List(){ ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + + //是否存在疾病 + new ReadingCalculateData (){QuestionType=QuestionType.ExistDisease,GetStringFun=GetIsExistDisease,ComputationTriggerList=new List(){ ComputationTrigger.InitialCalculation, ComputationTrigger.CTSave,ComputationTrigger.MergeSave,ComputationTrigger.RemoveLesion,}}, + + + + ////靶病灶径线之和(SOD) + //new ReadingCalculateData (){QuestionType=QuestionType.SOD,GetDecimalNullFun=GetSODData}, + + ////非淋巴结靶病灶长径之和 + //new ReadingCalculateData (){QuestionType=QuestionType.SumOfDiameter,GetDecimalNullFun=GetSumOfDiameter}, + + ////与基线SOD相比变化量(mm) + // new ReadingCalculateData (){QuestionType=QuestionType.SODChange,GetDecimalNullFun=GetSODChange}, + + ////与基线访视相比SOD变化百分比 + // new ReadingCalculateData (){QuestionType=QuestionType.SODPercent,GetDecimalNullFun=GetSODPercent}, + + ////与整个访视期间SOD最低点相比增加的值(mm) //其他任务需要改 + // new ReadingCalculateData (){QuestionType=QuestionType.LowestIncrease,GetDecimalNullFun=GetLowestIncrease,/*ChangeAllTaskFun=ChangeAllLowestIncrease*/}, + + // //与整个访视期间SOD最低点相比增加的百分比 //其他任务需要改 + // new ReadingCalculateData (){QuestionType=QuestionType.LowPercent,GetDecimalNullFun=GetLowPercent,/*ChangeAllTaskFun=ChangeAllLowPercent*/}, + + ////整个访视期间SOD最低点访视名称 //其他任务需要改 + // new ReadingCalculateData (){QuestionType=QuestionType.LowVisit,GetStringFun=GetLowVisit,/*ChangeAllTaskFun=ChangeAllLowVisitName*/}, + + // //是否存在非淋巴结靶病灶 + // new ReadingCalculateData (){QuestionType=QuestionType.IsLymphTarget,GetStringFun=GetIsLymphTarget}, + + // //是否存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上 + // new ReadingCalculateData (){QuestionType=QuestionType.IsAddFive,GetStringFun=GetIsAddFive}, + + // //被评估为NE的单个靶病灶 + // new ReadingCalculateData (){QuestionType=QuestionType.NETarget,GetStringFun=GetNETarget}, + + + + // //脾脏评估 + //new ReadingCalculateData (){QuestionType=QuestionType.SplenicEvaluation,GetStringFun=GetNewLesionEvaluate}, + + // //整体肿瘤评估 + // new ReadingCalculateData (){QuestionType=QuestionType.Tumor,GetStringFun=GetTumor}, + + // //是否存在疾病 + // new ReadingCalculateData (){QuestionType=QuestionType.ExistDisease,GetStringFun=GetIsExistDisease}, + + }; + + // 没有靶病灶只计算最后几个 + //if (inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).Sum(x => x.TableRowInfoList.Count()) == 0) + //{ + + // List questionTypes = new List() + // { + // QuestionType.TargetLesion, + // QuestionType.NoTargetLesion, + // QuestionType.NewLesions, + // QuestionType.Tumor, + // QuestionType.ExistDisease, + // }; + + // // 没有靶病灶就删除其他几个答案的值 + // var isNeedDeleteTypes = calculateList.Where(x => !questionTypes.Contains(x.QuestionType)).Select(x => x.QuestionType).ToList(); + + + // var isNeedDeleteIds = inDto.QuestionInfo.Where(x => x.QuestionType != null && isNeedDeleteTypes.Contains(x.QuestionType.Value)).Select(x => x.QuestionId).ToList(); + + // await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == inDto.VisitTaskId && isNeedDeleteIds.Contains(x.ReadingQuestionTrialId), x => new ReadingTaskQuestionAnswer + // { + // Answer = string.Empty + // }); + + + + // calculateList = calculateList.Where(x => questionTypes.Contains(x.QuestionType)).ToList(); + + + + + + //} + + if (calculateType != null) + { + calculateList = calculateList.Where(x => calculateType.Contains(x.QuestionType)).ToList(); + } + + + calculateList = calculateList.Where(x => x.ComputationTriggerList.Contains(inDto.ComputationTrigger)).ToList(); + var typeNAList = new List + { + QuestionType.SODChange, + QuestionType.SODPercent, + QuestionType.LowestIncrease, + QuestionType.LowPercent, + }; + + foreach (var calculate in calculateList) + { + var item = inDto.QuestionInfo.FirstOrDefault(x => x.QuestionType == calculate.QuestionType); + + if (item != null) + { + //计算答案 + if (inDto.IsOnlyChangeAllTask == false) + { + + #region 计算答案 + if (calculate.GetDecimalFun != null) + { + item.Answer = (await calculate.GetDecimalFun(inDto)).ToString(); + + } + else if (calculate.GetDecimalNullFun != null) + { + var value = await calculate.GetDecimalNullFun(inDto); + item.Answer = value == null ? string.Empty : value.Value.ToString(); + } + else if (calculate.GetStringFun != null) + { + item.Answer = await calculate.GetStringFun(inDto); + } + #endregion + // 修改修约小数位数 + + List valueOfTypes = new List() { + + ValueOfType.Decimals, + ValueOfType.Percentage + }; + + if (inDto.DigitPlaces != -1) + { + try + { + + if (valueOfTypes.Contains(item.ValueType)) + { + item.Answer = decimal.Round(decimal.Parse(item.Answer ?? "0"), inDto.DigitPlaces).ToString("F" + inDto.DigitPlaces.ToString()); + } + } + catch (Exception) + { + + + } + } + + + + needAddList.Add(new ReadingTaskQuestionAnswer() + { + Answer = item.Answer, + ReadingQuestionTrialId = item.QuestionId, + }); + } + + // 修改全局 + if (inDto.IsChangeOtherTask && calculate.ChangeAllTaskFun != null) + { + await calculate.ChangeAllTaskFun(new ChangeAllTaskDto() + { + calculateDto = inDto, + QuestionId = item.QuestionId, + }); + } + + } + } + + + + var questionIds = needAddList.Select(x => x.ReadingQuestionTrialId).ToList(); + + await _readingTaskQuestionAnswerRepository.BatchDeleteNoTrackingAsync(x => questionIds.Contains(x.ReadingQuestionTrialId) && x.VisitTaskId == inDto.VisitTaskId); + needAddList.ForEach(x => + { + x.SubjectId = inDto.SubjectId; + x.ReadingQuestionCriterionTrialId = inDto.CriterionId; + x.VisitTaskId = inDto.VisitTaskId; + x.TrialId = inDto.TrialId; + x.SubjectId = inDto.SubjectId; + }); + + await _readingTaskQuestionAnswerRepository.AddRangeAsync(needAddList); + + await _readingTaskQuestionAnswerRepository.SaveChangesAsync(); + #endregion + + + } + + /// + /// 获取影像是否无法融合 + /// + /// + /// + public async Task ImageQualityIsUnableFuse(ReadingCalculateDto inDto) + { + if (isUnableFuse != null) + { + return isUnableFuse.Value; + } + else + { + var imageQualityProblem = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.ImageQualityProblem).Select(x => x.Answer).FirstOrDefault(); + isUnableFuse = imageQualityProblem == ((int)ImageQualityIssues.PETCTFailureFuse).ToString(); + return isUnableFuse.Value; + } + + } + + /// + /// 获取报告整体整体评估 + /// + /// + /// + public async Task GetReportTumor(Guid visitTaskId) + { + return await GetTumor(await _generalCalculateService.GetReadingCalculateDto(visitTaskId)); + } + + /// + /// 获取报告是否存在疾病 + /// + /// + /// + public async Task GetReportIsExistDisease(Guid visitTaskId) + { + return await GetIsExistDisease(await _generalCalculateService.GetReadingCalculateDto(visitTaskId)); + } + + /// + /// 验证访视提交 + /// + /// + /// + public async Task VerifyVisitTaskQuestions(VerifyVisitTaskQuestionsInDto inDto) + { + + if (await _readingTaskQuestionAnswerRepository.AnyAsync(x => x.ReadingQuestionTrial.QuestionType == QuestionType.ImageQualityAssessment && x.VisitTaskId == inDto.VisitTaskId && x.Answer == ImageQualityEvaluation.Abnormal.GetEnumInt())) + { + + return; + } + + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + + var tableAnswerList = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.Answer == string.Empty || x.Answer == null) + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + Answer = x.Answer, + + }).ToListAsync(); + + + string errorMassage = string.Empty; + + var rowAnswerList = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && (x.MeasureData == string.Empty || x.MeasureData == null)) + .Select(x => new + { + x.ReadingQuestionTrial.OrderMark, + x.RowIndex, + x.Id, + }).ToListAsync(); + + + var unableEvaluateRowIds = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && + (x.Answer == TargetState.UnableEvaluate.GetEnumInt() || x.Answer == TargetState.Loss.GetEnumInt()) + && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + ) + .Select(x => x.RowId).Distinct().ToListAsync(); + + + List measureDataList = rowAnswerList.Where(x => !unableEvaluateRowIds.Contains(x.Id)).Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + List stateIsNullList = tableAnswerList.Select(x => x.OrderMark + x.RowIndex.GetLesionMark()).ToList(); + + + List allExists = measureDataList.Intersect(stateIsNullList).ToList(); + measureDataList = measureDataList.Except(allExists).ToList(); + stateIsNullList = stateIsNullList.Except(allExists).ToList(); + + + if (measureDataList.Count() > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarker", string.Join(',', measureDataList)] + ","; + } + + + if (tableAnswerList.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_StatusIsEmpty", string.Join(',', stateIsNullList)] + ","; + } + + if (allExists.Count > 0) + { + errorMassage += _localizer["ReadingCalculate_NoMarkerEmpty", string.Join(',', stateIsNullList)] + ","; + } + + // 判断是否有pet + if (await _readingTaskQuestionAnswerRepository.AnyAsync(x => x.ReadingQuestionTrial.QuestionType == QuestionType.ExistPET && x.VisitTaskId == inDto.VisitTaskId && x.Answer == ReadingYesOrNo.Yes.GetEnumInt())) + { + + List required = new List() { + QuestionType.LiverSUVmax, + QuestionType.MediastinumSUVmax, + QuestionType.SUVmax, + QuestionType.SUVmaxLesion, + QuestionType.PET5PS, + }; + if (taskinfo.VisitTaskNum != 0m) + { + required.Add(QuestionType.UptakeChange); + required.Add(QuestionType.EvidenceFocalFDG); + } + + if ((await _readingTaskQuestionAnswerRepository.CountAsync(x => required.Contains(x.ReadingQuestionTrial.QuestionType) && x.VisitTaskId == inDto.VisitTaskId && x.Answer != string.Empty)) != required.Count()) + { + if (taskinfo.VisitTaskNum != 0m) + { + errorMassage += _localizer["ReadingCalculate_LuganoPetVerification"]; + } + else + { + errorMassage += _localizer["ReadingCalculate_LuganoBaseLinePetVerification"]; + } + + } + } + + + if (errorMassage != string.Empty) + { + //errorMassage = errorMassage; + throw new BusinessValidationFailedException(errorMassage); + } + + + } + + /// + /// 计算融合靶病灶的状态 + /// + /// + /// + public async Task CalculateMergeTargetLesionStatus(CalculateTargetLesionStatusInDto inDto) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + // 找到靶病灶问题 + var targetQuestion = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.LesionType == LesionType.TargetLesion).FirstOrDefaultAsync(); + if (targetQuestion != null) + { + // 找到状态问题 + var stateQuestion = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == targetQuestion.Id && x.QuestionMark == QuestionMark.State).FirstOrDefaultAsync(); + if (stateQuestion != null) + { + + //// 找到主病灶的状态 + //var state = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == (int)Math.Floor(inDto.RowNumber) && x.TableQuestionId == stateQuestion.Id).Select(x => x.Answer).FirstOrDefaultAsync(); + + // 长径 + var majorAxis = (await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex == inDto.RowNumber && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis && + x.QuestionId == targetQuestion.Id + ).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + // 短径 + var shortAxis = (await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex == inDto.RowNumber && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis && + x.QuestionId == targetQuestion.Id + ).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + // 找到ppd问题 + var ppdQuestion = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == targetQuestion.Id && x.QuestionMark == QuestionMark.PPD).FirstOrDefaultAsync(); + + if (ppdQuestion != null) + { + var pPdAnswer = (await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.PPD && + x.QuestionId == targetQuestion.Id && + x.RowIndex == inDto.RowNumber + ).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + + // 是否符合疾病进展 + var accord = false; + + var lowPPDInfo = await GetLowPPDInfo(new GetPPDInfoInDto() + { + RowIndex = inDto.RowNumber, + VisitTaskId = inDto.VisitTaskId, + QuestionId = inDto.QuestionId, + }); + + + if (lowPPDInfo.NadirPPD != 0) + { + //当前融合靶病灶LDi>15 mm && + //(当前融合靶病灶的ppd-最低点PPD)/最低点PPD ≥50 % + + if (majorAxis >= 15 && + (pPdAnswer - lowPPDInfo.NadirPPD) * 100 / lowPPDInfo.NadirPPD >= 50) + { + accord = true; + } + } + + // 符合疾病进展 + if (accord) + { + await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex == inDto.RowNumber && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && + x.QuestionId == targetQuestion.Id, x => new ReadingTableQuestionAnswer() + { + Answer = TargetState.DiseaseProgression.GetEnumInt() + } + ); + } + //else if (state == TargetState.DiseaseProgression.GetEnumInt()) + //{ + // //await _readingTableQuestionAnswerRepository.UpdatePartialFromQueryAsync(x => + // // x.VisitTaskId == inDto.VisitTaskId && + // // x.RowIndex == inDto.RowNumber && + // // x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && + // // x.QuestionId == targetQuestion.Id, x => new ReadingTableQuestionAnswer() + // // { + // // Answer = TargetState.Exist.GetEnumInt() + // // } + // // ); + //} + + + } + } + + } + } + + /// + /// 计算分裂靶病灶状态 + /// + /// + /// + public async Task CalculateTargetLesionStatus(CalculateTargetLesionStatusInDto inDto) + { + if (inDto.RowNumber % 1 != 0) + { + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + + // 找到靶病灶问题 + var targetQuestion = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.LesionType == LesionType.TargetLesion).FirstOrDefaultAsync(); + if (targetQuestion != null) + { + // 找到状态问题 + var stateQuestion = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == targetQuestion.Id && x.QuestionMark == QuestionMark.State).FirstOrDefaultAsync(); + if (stateQuestion != null) + { + + // 找到主病灶的状态 + var state = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && x.RowIndex == (int)Math.Floor(inDto.RowNumber) && x.TableQuestionId == stateQuestion.Id).Select(x => x.Answer).FirstOrDefaultAsync(); + + // 长径 + var majorAxis = (await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex >= (int)Math.Floor(inDto.RowNumber) && x.RowIndex < ((int)Math.Floor(inDto.RowNumber) + 1) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.MajorAxis && + x.QuestionId == targetQuestion.Id + ).Select(x => x.Answer).ToListAsync()).Select(x => x.IsNullOrEmptyReturn0()).Sum(); + + // 短径 + var shortAxis = (await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex >= (int)Math.Floor(inDto.RowNumber) && x.RowIndex < ((int)Math.Floor(inDto.RowNumber) + 1) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis && + x.QuestionId == targetQuestion.Id + ).Select(x => x.Answer).ToListAsync()).Select(x => x.IsNullOrEmptyReturn0()).Sum(); + + // 找到ppd问题 + var ppdQuestion = await _readingTableQuestionTrialRepository.Where(x => x.ReadingQuestionId == targetQuestion.Id && x.QuestionMark == QuestionMark.PPD).FirstOrDefaultAsync(); + + if (ppdQuestion != null) + { + var ppdAnswerList = await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.PPD && + x.QuestionId == targetQuestion.Id && + x.RowIndex >= (int)Math.Floor(inDto.RowNumber) && x.RowIndex < ((int)Math.Floor(inDto.RowNumber) + 1) + ).Select(x => x.Answer).ToListAsync(); + + var allPPd = ppdAnswerList.Select(x => x.IsNullOrEmptyReturn0()).Sum(); + + // 是否符合疾病进展 + var accord = false; + + var lowPPDInfo = await GetLowPPDInfo(new GetPPDInfoInDto() + { + RowIndex = (int)Math.Floor(inDto.RowNumber), + VisitTaskId = inDto.VisitTaskId, + QuestionId = inDto.QuestionId, + }); + + + if (lowPPDInfo.NadirPPD != 0) + { + //15mm < 当前靶病灶LDi≤20mm && + //&& 相比最低点PPD增加百分比 ≥50% && + //(相比PPD最低点LDi增加值 ≥5 mm + //or相比PPD最低点SDi增加值≥5 mm) + if (15 < majorAxis && majorAxis <= 20 && + (allPPd - lowPPDInfo.NadirPPD) * 100 / lowPPDInfo.NadirPPD >= 50 && + (majorAxis - lowPPDInfo.LowPPDLDi >= 5 || + shortAxis - lowPPDInfo.LowPPDSDi >= 5) + ) + { + accord = true; + } + + //当前靶病灶LDi>20 mm + //相比最低点PPD增加百分比 ≥50% + // (相比PPD最低点LDi增加值 ≥10 mm + // 或者相比PPD最低点SDi增加值Sdi ≥10 mm) + if (majorAxis > 20 && + (allPPd - lowPPDInfo.NadirPPD) * 100 / lowPPDInfo.NadirPPD >= 50 && + (majorAxis - lowPPDInfo.LowPPDLDi >= 10 || + shortAxis - lowPPDInfo.LowPPDSDi >= 10) + ) + { + accord = true; + } + + } + + // 符合疾病进展 + if (accord) + { + await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex == (int)Math.Floor(inDto.RowNumber) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && + x.QuestionId == targetQuestion.Id, x => new ReadingTableQuestionAnswer() + { + Answer = TargetState.DiseaseProgression.GetEnumInt() + } + ); + } + else if (state == TargetState.DiseaseProgression.GetEnumInt()) + { + await _readingTableQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => + x.VisitTaskId == inDto.VisitTaskId && + x.RowIndex == (int)Math.Floor(inDto.RowNumber) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State && + x.QuestionId == targetQuestion.Id, x => new ReadingTableQuestionAnswer() + { + Answer = TargetState.Exist.GetEnumInt() + } + ); + } + + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + } + } + + } + + + } + } + + + + + + + /// + /// 获取分裂病灶的PPd之和 不包括当前的主病灶 + /// + /// + [HttpPost] + public async Task> GetSplitPPdSum(GetSplitPPdInDto inDto) + { + List result = new List(); + var taskInfo = await _visitTaskRepository.Where(x => x.Id == inDto.VisitTaskId).FirstNotNullAsync(); + var targetQuestion = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskInfo.TrialReadingCriterionId && x.LesionType == LesionType.TargetLesion).FirstOrDefaultAsync(); + if (targetQuestion != null) + { + var ppdAnswerList = await _readingTableQuestionAnswerRepository.Where(x => + x.VisitTaskId == inDto.VisitTaskId && + //x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.PPD && + x.QuestionId == targetQuestion.Id) + .Select(x => new { + x.RowId, + x.RowIndex, + x.Answer, + x.ReadingTableQuestionTrial.QuestionMark, + }).ToListAsync(); + var answerList = ppdAnswerList.Select(x => new { + x.RowId, + x.RowIndex, + Answer = x.Answer.IsNullOrEmptyReturn0(), + x.QuestionMark, + }).ToList(); + var maxRowIndex = answerList.MaxOrDefault(x => x.RowIndex); + for (int i = 1; i < maxRowIndex; i++) + { + result.Add(new GetSplitPPdOutDto() + { + RowId = answerList.Where(x => x.RowIndex == i).Select(x => x.RowId).FirstOrDefault(), + RowIndex = answerList.Where(x => x.RowIndex == i).Select(x => x.RowIndex).FirstOrDefault().ToString(), + AllPPD = answerList.Where(x => x.QuestionMark == QuestionMark.PPD).Where(x => x.RowIndex > i && x.RowIndex < (i + 1)).Sum(x => x.Answer), + AllLDi = answerList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Where(x => x.RowIndex > i && x.RowIndex < (i + 1)).Sum(x => x.Answer), + AllSDi = answerList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Where(x => x.RowIndex > i && x.RowIndex < (i + 1)).Sum(x => x.Answer), + }); + } + } + return result; + } + + #region 将上一次的访视病灶添加到这一次 + + /// + /// 将上一次的访视病灶添加到这一次 + /// + /// + /// + public async Task AddTaskLesionAnswerFromLastTask(AddTaskLesionAnswerFromLastTaskInDto inDto) + { + var visitTaskId = inDto.VisitTaskId; + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var baseLineVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == taskinfo.SubjectId && x.IsBaseLine).Select(x => x.Id).FirstOrDefaultAsync(); + + + ReadingCalculateDto readingData = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + + await ReadingCalculate(readingData, new List() { QuestionType.ExistPET }); + + + // 判断当前任务是否是基线 + if (taskinfo.SourceSubjectVisitId != baseLineVisitId) + { + // 判断当前任务是是否有表格问题答案 + if (!(await _readingTableQuestionAnswerRepository.AnyAsync(x => x.VisitTaskId == visitTaskId))) + { + + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId == taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect && x.ArmEnum == taskinfo.ArmEnum + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).FirstOrDefaultAsync(); + + + + var copyTableAnswers = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId).Select(x => new CopyTableAnswerDto() + { + Answer = x.Answer, + QuestionId = x.QuestionId, + RowId = x.RowId, + QuestionMark = x.ReadingTableQuestionTrial.QuestionMark, + TableQuestionId = x.TableQuestionId, + RowIndex = x.RowIndex, + TrialId = x.TrialId + }).ToListAsync(); + + var tableRowAnswers = await _readingTableAnswerRowInfoRepository.Where(x => x.VisitTaskId == LastVisitTaskId).OrderBy(x => x.RowIndex).ProjectTo(_mapper.ConfigurationProvider).ToListAsync(); + + + tableRowAnswers.ForEach(x => + { + + switch (x.SplitOrMergeType) + { + case SplitOrMergeType.Merge: + case SplitOrMergeType.Merged: + x.SplitOrMergeType = SplitOrMergeType.Merged; + break; + case SplitOrMergeType.MergeMain: + case SplitOrMergeType.MergeMained: + x.SplitOrMergeType = SplitOrMergeType.MergeMained; + break; + default: + x.SplitOrMergeType = null; + break; + } + x.VisitTaskId = visitTaskId; + x.IsCurrentTaskAdd = false; + x.Id = NewId.NextGuid(); + x.SeriesId = null; + x.InstanceId = null; + x.MeasureData = string.Empty; + x.PicturePath = string.Empty; + + + }); + + tableRowAnswers.ForEach(x => + { + x.SplitRowId = tableRowAnswers.Where(y => y.OriginalId == x.SplitRowId).FirstOrDefault()?.Id; + x.MergeRowId = tableRowAnswers.Where(y => y.OriginalId == x.MergeRowId).FirstOrDefault()?.Id; + + }); + + List needCopyMarks = new List() + { + + QuestionMark.IsLymph, + QuestionMark.Lesion, + QuestionMark.Organ, + QuestionMark.Location, + QuestionMark.Part, + QuestionMark.BodyPartDescription, + QuestionMark.LowPPDLDi, + QuestionMark.LowPPDSDi, + QuestionMark.NadirPPD, + QuestionMark.LowPPDVisit, + }; + + + + var rowIndexList = copyTableAnswers.Select(x => x.RowIndex).Distinct().ToList(); + // 找到靶病灶问题Id + var questionId = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId && x.LesionType == LesionType.TargetLesion).Select(x => x.Id).FirstNotNullAsync(); + + foreach (var item in rowIndexList) + { + var lowPPD = await this.GetLowPPDInfo(new GetPPDInfoInDto() { + + VisitTaskId = visitTaskId, + QuestionId = questionId, + RowIndex = item + }); + + copyTableAnswers.Where(x => x.RowIndex == item).ForEach(x => + { + + + switch (x.QuestionMark) + { + case QuestionMark.LowPPDLDi: + x.Answer = lowPPD.LowPPDLDi == null ? string.Empty : lowPPD.LowPPDLDi.Value.ToString(); + break; + case QuestionMark.LowPPDSDi: + x.Answer = lowPPD.LowPPDSDi == null ? string.Empty : lowPPD.LowPPDSDi.Value.ToString(); + break; + case QuestionMark.NadirPPD: + x.Answer = lowPPD.NadirPPD == null ? string.Empty : lowPPD.NadirPPD.Value.ToString(); + break; + case QuestionMark.LowPPDVisit: + x.Answer = lowPPD.LowPPDVisit == null ? string.Empty : lowPPD.LowPPDVisit.ToString(); + break; + + + } + + + }); + } + + var tableAnswers = new List(); + // 处理状态 + + var mergedRowIds = tableRowAnswers.Where(x => x.SplitOrMergeType == SplitOrMergeType.Merged).Select(x => x.Id).ToList(); + copyTableAnswers.ForEach(x => + { + var tableAnswer = new ReadingTableQuestionAnswer() + { + Id = NewId.NextGuid(), + Answer = needCopyMarks.Contains(x.QuestionMark) ? x.Answer : string.Empty, + QuestionId = x.QuestionId, + RowIndex = x.RowIndex, + RowId = tableRowAnswers.Where(y => y.OriginalId == x.RowId).Select(x => x.Id).FirstOrDefault(), + TableQuestionId = x.TableQuestionId, + TrialId = x.TrialId, + VisitTaskId = visitTaskId, + }; + if (mergedRowIds.Contains(tableAnswer.RowId) && x.QuestionMark == QuestionMark.State) + { + tableAnswer.Answer = TargetState.Loss.GetEnumInt(); + + } + tableAnswers.Add(tableAnswer); + }); + + var questionMarkList = await _readingTaskQuestionMarkRepository.Where(x => x.VisitTaskId == LastVisitTaskId).Select(x => new ReadingTaskQuestionMark() + { + VisitTaskId = visitTaskId, + FirstAddTaskId = x.FirstAddTaskId, + QuestionId = x.QuestionId, + QuestionType = x.QuestionType, + OrderMarkName = x.OrderMarkName, + + }).ToListAsync(); + questionMarkList.ForEach(x => { + x.Id = NewId.NextGuid(); + }); + + + var addList = _mapper.Map>(tableRowAnswers).OrderBy(x => x.RowIndex).ToList(); + + foreach (var item in addList) + { + await _readingTableAnswerRowInfoRepository.AddAsync(item); + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + } + await _readingTaskQuestionMarkRepository.AddRangeAsync(questionMarkList); + //await _readingTableAnswerRowInfoRepository.AddRangeAsync(addList); + await _readingTableQuestionAnswerRepository.AddRangeAsync(tableAnswers); + //addList.ForEach(x => + //{ + // x.MergeRow = null; + // x.SplitRow = null; + + //}); + await _readingTableQuestionAnswerRepository.SaveChangesAsync(); + + } + } + + return new AddTaskLesionAnswerFromLastTaskOutDto() + { + + IsBaseLine = taskinfo.SourceSubjectVisitId == baseLineVisitId, + }; + + } + #endregion + + #region 获取SOD + + /// + /// 获取SOD + /// + /// + /// 靶病灶径线之和(SOD) + /// 非淋巴结的长径 和淋巴结的短径 + /// + /// + public async Task GetSODData(ReadingCalculateDto inDto) + { + if (sODData != null) + { + return sODData.Value; + } + + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + if (tableQuestion.Count() == 0) + { + return null; + } + + decimal result = 0; + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 淋巴结的短径 + result += (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0(); + } + + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.No))) + { + // 非淋巴结的长径 + result += item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + } + } + + sODData = result; + + return sODData.Value; + + + + } + #endregion + + #region 非淋巴结靶病灶长径之和 + /// + /// 非淋巴结靶病灶长径之和 + /// + /// + /// + public async Task GetSumOfDiameter(ReadingCalculateDto inDto) + { + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + if (tableQuestion.Count() == 0) + { + return null; + } + + decimal result = 0; + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.No))) + { + // 非淋巴结的长径 + result += item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.MajorAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + } + } + + + return result; + } + #endregion + + #region 与基线SOD相比变化量(mm) + /// + /// 与基线SOD相比变化量(mm) + /// + /// + /// + public async Task GetSODChange(ReadingCalculateDto inDto) + { + + var value = await GetSODData(inDto); + + if (value == null || inDto.IsBaseLine) + { + return null; + } + return value.NullChange0() - await GetBaseLineSOD(inDto); + } + #endregion + + #region 与基线访视相比SOD变化百分比 + /// + /// 与基线访视相比SOD变化百分比 + /// + /// + /// + public async Task GetSODPercent(ReadingCalculateDto inDto) + { + var thisSOD = await GetSODData(inDto); + + if (thisSOD == null || inDto.IsBaseLine) + { + return null; + } + + var baseLineSOD = await GetBaseLineSOD(inDto); + + if (baseLineSOD == 0) + { + return 100; + } + else + { + return (thisSOD.NullChange0() - baseLineSOD) * 100 / baseLineSOD; + } + } + #endregion + + #region 与整个访视期间SOD最低点相比增加的值(mm) + /// + /// 与整个访视期间SOD最低点相比增加的值(mm) + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowestIncrease(ReadingCalculateDto inDto) + { + var value = await GetSODData(inDto); + if (value == null || inDto.IsBaseLine) + { + return null; + } + var decimalAnswerList = await GetLowSODVisit(inDto); + var minSOD = decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.SOD).FirstOrDefault(); + return value.NullChange0() - minSOD; + } + #endregion + + #region 与整个访视期间SOD最低点相比增加的百分比 + /// + /// 与整个访视期间SOD最低点相比增加的百分比 + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowPercent(ReadingCalculateDto inDto) + { + var thisSOD = await GetSODData(inDto); + if (thisSOD == null || inDto.IsBaseLine) + { + return null; + } + var decimalAnswerList = await GetLowSODVisit(inDto); + var minSOD = decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.SOD).FirstOrDefault(); + + if (minSOD == 0) + { + return 100; + } + else + { + return (thisSOD.NullChange0() - minSOD) * 100 / minSOD; + } + + + } + #endregion + + #region 整个访视期间SOD最低点访视名称 + /// + /// 整个访视期间SOD最低点访视名称 + /// + /// + /// + /// 要更新之前的 + /// + /// + public async Task GetLowVisit(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return nameof(YesOrNoOrNa.NA); + } + var targetCount = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).Count(); + if (targetCount == 0) + { + return nameof(YesOrNoOrNa.NA); + } + + var decimalAnswerList = await GetLowSODVisit(inDto); + return decimalAnswerList.OrderBy(x => x.SOD).Select(x => x.BlindName).FirstOrDefault() ?? string.Empty; + } + #endregion + + #region 是否存在非淋巴结靶病灶 + /// + /// 是否存在非淋巴结靶病灶 + /// + /// + /// + public async Task GetIsLymphTarget(ReadingCalculateDto inDto) + { + var result = IsLymph.No.GetEnumInt(); + var tableQuestion = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + foreach (var item in tableQuestion) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(IsLymph.No))) + { + result = IsLymph.Yes.GetEnumInt(); + } + } + + + + return result; + } + #endregion + + #region 是否存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上 + /// + /// 是否存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上 + /// + /// + /// + public async Task GetIsAddFive(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return YesOrNoOrNa.NA.GetEnumInt(); + } + var addFiveList = await GetIsAddFiveRowIndexs(inDto); + var isExists = addFiveList.Count() > 0 ? true : false; + + return isExists ? YesOrNoOrNa.Yes.GetEnumInt() : YesOrNoOrNa.No.GetEnumInt(); + + } + + /// + /// 获取存在淋巴结靶病灶且该病灶比上一访视短径增加5MM以上的病灶 + /// + /// + /// + public async Task> GetIsAddFiveRowIndexs(ReadingCalculateDto inDto) + { + List result = new List(); + var LastVisitTaskId = await this.GetLastVisitTaskId(inDto); + var questionIds = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).Select(x => x.QuestionId).ToList(); + var lastQuestionAsnwer = await _readingTableQuestionAnswerRepository.Where(x => x.VisitTaskId == LastVisitTaskId && questionIds.Contains(x.QuestionId)).Include(x => x.ReadingQuestionTrial).Include(x => x.ReadingTableQuestionTrial).ToListAsync(); + var rowIndexs = lastQuestionAsnwer.Where(x => x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes)).Select(x => x.RowIndex).Distinct().OrderBy(x => x).ToList(); + var thisQuestionAsnwer = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + foreach (var item in rowIndexs) + { + var lastValue = lastQuestionAsnwer.Where(x => x.RowIndex == item && x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + var thisRowData = thisQuestionAsnwer.Where(x => x.RowIndex == item).SelectMany(x => x.TableQuestionList).ToList(); + var thisValue = thisRowData.Where(x => x.QuestionMark == QuestionMark.ShortAxis).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + if (thisValue - lastValue >= 5) + { + result.Add(item); + } + } + + return result; + } + #endregion + + #region 被评估为NE的单个靶病灶 + /// + /// 被评估为NE的单个靶病灶 + /// + /// + /// + public async Task GetNETarget(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return ExistOrNA.NA.GetEnumInt(); + } + + var result = inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.TargetLesion && x.Answer.EqEnum(TargetAssessment.NE)); + + return result ? ExistOrNA.Exist.GetEnumInt() : ExistOrNA.NotExist.GetEnumInt(); + } + #endregion + + #region 整体肿瘤评估 + + /// + /// 整体肿瘤评估 + /// + /// + /// + public async Task GetTumor(ReadingCalculateDto inDto) + { + + if (inDto.IsBaseLine) + { + return OverallAssessment.NA.GetEnumInt(); + } + + var targetLesion = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.TargetLesion).Select(x => x.Answer).FirstOrDefault(); + var noTargetLesion = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NoTargetLesion).Select(x => x.Answer).FirstOrDefault(); + var newLesions = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesions).Select(x => x.Answer).FirstOrDefault(); + var result = await _tumorAssessmentRepository.Where(x => + x.TargetLesion == (TargetAssessment)int.Parse(targetLesion.IsNullOrEmpty() ? TargetAssessment.NA.GetEnumInt() : targetLesion) && + x.NonTargetLesions == (NoTargetAssessment)int.Parse(noTargetLesion.IsNullOrEmpty() ? NoTargetAssessment.NA.GetEnumInt() : noTargetLesion) && + x.NewLesion == (NewLesionAssessment)int.Parse(newLesions.IsNullOrEmpty() ? NewLesionAssessment.NA.GetEnumInt() : newLesions)).Select(x => x.OverallEfficacy).ToListAsync(); + + return result.Count == 0 ? OverallAssessment.NE.GetEnumInt() : result[0].GetEnumInt(); + } + #endregion + + #region 是否存在疾病 + /// + /// 是否存在疾病 + /// + /// + /// + public async Task GetIsExistDisease(ReadingCalculateDto inDto) + { + if (!inDto.IsBaseLine) + { + return string.Empty; + } + + var lesionCount = inDto.QuestionInfo.SelectMany(x => x.TableRowInfoList).Count(); + + return lesionCount > 0 ? ExistDisease.Yes.GetEnumInt() : ExistDisease.No.GetEnumInt(); + } + #endregion + + + #region 修改其他标准 + + + + + #region 修改整个访视期间SOD最低点相比增加的百分比 + + /// + /// 修改整个访视期间SOD最低点相比增加的百分比 + /// + /// + /// + //public async Task ChangeAllLowPercent(ChangeAllTaskDto inDto) + //{ + // var visitTaskList = await GetVisitTaskAnswerList(inDto.calculateDto); + + // var lowSod = (await GetLowSODVisit(inDto.calculateDto)).Select(x => x.SOD).OrderBy(x => x).FirstOrDefault(); + + // foreach (var item in visitTaskList.Where(x => x.VisitTaskId != inDto.calculateDto.BaseLineTaskId)) + // { + // decimal percent = 0; + // if (lowSod == 0) + // { + // percent = 100; + // } + // else + // { + // percent = decimal.Round((item.SOD - lowSod) * 100 / lowSod, 2); + // } + + // await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => x.VisitTaskId == item.VisitTaskId && x.ReadingQuestionTrialId == inDto.QuestionId, x => new ReadingTaskQuestionAnswer() + // { + // Answer = percent.ToString() + // }); + // } + //} + + #endregion + + #region 修改最低方式点名称 + /// + /// 修改最低方式点名称 + /// + /// + /// + public async Task ChangeAllLowVisitName(ChangeAllTaskDto inDto) + { + // 找到所有访视任务的Id + + var visitTaskIds = await _visitTaskRepository.Where(x => x.IsAnalysisCreate == inDto.IsAnalysisCreate && + x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == inDto.calculateDto.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ArmEnum == inDto.calculateDto.ArmEnum).Select(x => x.Id).ToListAsync(); + + var answer = (await GetLowSODVisit(inDto.calculateDto)).OrderBy(x => x.SOD).Select(x => x.BlindName).FirstOrDefault(); + visitTaskIds = visitTaskIds.Where(x => x != inDto.calculateDto.BaseLineTaskId).ToList(); + await this.ChangeAllVisitTaskAnswer(visitTaskIds, inDto.QuestionId, answer); + + } + #endregion + + + #endregion + + #region 通用方法 + + #region 修改所有访视任务的答案 + /// + /// 修改所有访视任务的答案 + /// + /// + /// + /// + /// + private async Task ChangeAllVisitTaskAnswer(List visitTaskGuids, Guid questionId, string answer) + { + await _readingTaskQuestionAnswerRepository.BatchUpdateNoTrackingAsync(x => visitTaskGuids.Contains(x.VisitTaskId) && x.ReadingQuestionTrialId == questionId, x => new ReadingTaskQuestionAnswer() + { + Answer = answer + }); + } + #endregion + + + #region 获取基线任务ID + + /// + /// 获取基线任务的Id + /// + /// + /// + private async Task GetBaseLineTaskId(ReadingCalculateDto inDto) + { + if (this.BaseLineTaskId == null) + { + this.BaseLineTaskId = await _visitTaskRepository.Where(x => x.SubjectId == inDto.SubjectId && x.ReadingCategory == ReadingCategory.Visit + && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + + x.SourceSubjectVisit.IsBaseLine && x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.DoctorUserId == inDto.DoctorUserId + && x.IsSelfAnalysis == inDto.IsSelfAnalysis && x.ArmEnum == inDto.ArmEnum) + .Select(x => x.Id).FirstOrDefaultAsync(); + } + + return this.BaseLineTaskId.Value; + + } + #endregion + + #region 获取基线SOD + /// + /// 获取基线SOD + /// + /// + /// + private async Task GetBaseLineSOD(ReadingCalculateDto inDto) + { + if (await _visitTaskRepository.AnyAsync(x => x.Id == inDto.VisitTaskId && x.SourceSubjectVisit.IsBaseLine && x.IsAnalysisCreate == inDto.IsAnalysisCreate && x.ArmEnum == inDto.ArmEnum)) + { + return 0; + } + + // 先找到基线的任务 + var baseLineTaskId = await _visitTaskRepository.Where(x => x.SubjectId == inDto.SubjectId && x.ReadingCategory == ReadingCategory.Visit + && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + + x.SourceSubjectVisit.IsBaseLine && x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.DoctorUserId == inDto.DoctorUserId + && x.IsSelfAnalysis == inDto.IsSelfAnalysis && x.ArmEnum == inDto.ArmEnum) + .Select(x => x.Id).FirstOrDefaultAsync(); + + + var baseLineSOD = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SOD).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + return baseLineSOD; + } + #endregion + + + /// + /// 获取最低方式 + /// + /// + /// + public async Task> GetLowSODVisit(ReadingCalculateDto inDto) + { + var taskAnswerList = await GetVisitTaskAnswerList(inDto); + + taskAnswerList = taskAnswerList.Where(x => x.VisitTaskId != inDto.VisitTaskId).ToList(); + + var taskIds = taskAnswerList.Select(x => x.VisitTaskId).ToList(); + + // 排除无法评估 + var unableEvaluateTaskIds = await _readingTableQuestionAnswerRepository.Where(x => taskIds.Contains(x.VisitTaskId) && + x.ReadingTableQuestionTrial.QuestionMark == QuestionMark.State + && x.ReadingQuestionTrial.LesionType == LesionType.TargetLesion + && x.Answer == TargetState.UnableEvaluate.GetEnumInt() + ) + .Select(x => x.VisitTaskId).Distinct().ToListAsync(); + + taskAnswerList = taskAnswerList.Where(x => !unableEvaluateTaskIds.Contains(x.VisitTaskId)).ToList(); + return taskAnswerList.OrderBy(x => x.SOD).ToList(); + } + + #region 获取访视任务信息 + /// + /// 获取访视任务信息 + /// + /// + /// + public async Task> GetVisitTaskAnswerList(ReadingCalculateDto inDto) + { + if (visitTaskAnswerList == null) + { + // 查询的时候要把自己排除 因为查询出来的可能不是计算出的最新的 + visitTaskAnswerList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId != inDto.VisitTaskId && x.VisitTask.ReadingCategory == ReadingCategory.Visit + && x.VisitTask.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.VisitTask.IsSelfAnalysis == inDto.IsSelfAnalysis + && x.VisitTask.VisitTaskNum < inDto.VisitTaskNum + && x.ReadingQuestionCriterionTrialId == inDto.TrialReadingCriterionId + && x.SubjectId == inDto.SubjectId && x.VisitTask.ReadingTaskState == ReadingTaskState.HaveSigned && x.VisitTask.ArmEnum == inDto.ArmEnum && x.VisitTask.TaskState == TaskState.Effect && x.ReadingQuestionTrial.QuestionType == QuestionType.SpleenLength) + .Select(x => new VisitTaskAnswerInfo + { + VisitTaskId = x.VisitTaskId, + QuestionId = x.ReadingQuestionTrialId, + VisitName = x.VisitTask.SourceSubjectVisit.VisitName, + BlindName = x.VisitTask.TaskBlindName, + + SpleenLength = x.Answer.IsNullOrEmptyReturn0(), + }).ToListAsync(); + + //// 这里是需要加上自己的 基线不用管 + //if (visitTaskAnswerList.Count > 0) + //{ + // visitTaskAnswerList.Add(new VisitTaskAnswerInfo() + // { + // VisitTaskId = inDto.VisitTaskId, + // BlindName = inDto.TaskBlindName, + // QuestionId = visitTaskAnswerList[0].QuestionId, + // VisitName = inDto.VisitName, + // SOD = (await GetSODData(inDto)).ToString().IsNullOrEmptyReturn0(), + // }); + //} + + + } + + return visitTaskAnswerList; + } + #endregion + + /// + /// 获取上一个访视任务Id + /// + /// + private async Task GetLastVisitTaskId(ReadingCalculateDto inDto) + { + // 拿到这一个访视 + var thisNum = await _subjectVisitRepository.Where(x => x.Id == inDto.SubjectVisitId).Select(x => x.VisitNum).FirstOrDefaultAsync(); + + // 先找到上一个访视 + var lastVisitId = await _subjectVisitRepository.Where(x => x.SubjectId == inDto.SubjectId && !x.IsLostVisit && x.VisitNum < thisNum).OrderByDescending(x => x.VisitNum).Select(x => x.Id).FirstOrDefaultAsync(); + + // 找到访视任务Id + + var LastVisitTaskId = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.SourceSubjectVisitId == lastVisitId && x.ArmEnum == inDto.ArmEnum).Select(x => x.Id).FirstOrDefaultAsync(); + + return LastVisitTaskId; + } + #endregion + + #region 计算阅片问题 外层问题 + + #region 获取SPD + + /// + /// 获取SPD + /// + /// + /// + public async Task GetSPD(ReadingCalculateDto inDto) + { + decimal result = 0; + var rowInfo = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + var tableQuestionList = rowInfo.SelectMany(x => x.TableQuestionList).ToList(); + + result = tableQuestionList.Where(x => x.QuestionMark == QuestionMark.PPD).Sum(x => x.Answer.IsNullOrEmptyReturn0()); + return result; + + } + + #endregion + + #region 脾脏垂直径 + /// + /// 获取脾脏垂直径 + /// + /// + /// + public async Task GetSpleenLength(ReadingCalculateDto inDto) + { + + // 脾尖答案 + var splenicTopAnswer = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SplenicTopPosition).Select(x => x.Answer).FirstOrDefault(); + // 脾底答案 + var splenicBottomAnswer = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SplenicBottomPosition).Select(x => x.Answer).FirstOrDefault(); + + decimal? spleenLength = null; + if (splenicTopAnswer != null && splenicBottomAnswer != null&& splenicTopAnswer != string.Empty && splenicBottomAnswer != string.Empty) + { + spleenLength = splenicTopAnswer.IsNullOrEmptyReturn0() - splenicBottomAnswer.IsNullOrEmptyReturn0(); + if (spleenLength < 0) + { + spleenLength = 0 - spleenLength; + } + return spleenLength; + } + + return spleenLength; + } + #endregion + + #region 与基线相比SPD变化的百分比 + /// + /// 与基线相比SPD变化的百分比 + /// + /// + /// + public async Task CompareBaselineSPD(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return "NA"; + } + var baseLineTaskId = await GetBaseLineTaskId(inDto); + + var baseLineSpd = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SPD).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + var presentSpd = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SPD).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + if (baseLineSpd != 0) + { + return ReserveDecimal((presentSpd - baseLineSpd) * 100 / baseLineSpd, inDto.DigitPlaces); + } + else + { + return "NA"; + } + } + #endregion + + #region 与基线相比脾肿大增加的百分比 + /// + /// 与基线相比脾肿大增加的百分比 + /// [(当前垂直径-130)-(基线垂直径-130)]/(基线垂直径-130) + /// + /// + /// + public async Task GetSplenoncusChange(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return "NA"; + } + + + var baseLineSpleenLength = await GetBaseLineSpleenLength(inDto); + + var presentSpleenLength = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + if (baseLineSpleenLength - 130 > 0 && presentSpleenLength > 130) + { + return ReserveDecimal((presentSpleenLength - 130 - (baseLineSpleenLength - 130)) * 100 / (baseLineSpleenLength - 130), inDto.DigitPlaces); + } + else + { + return "NA"; + } + } + #endregion + + #region 与最低点相比脾脏垂直径长度的增加值 + + /// + /// 与最低点相比脾脏垂直径长度的增加值 + /// + /// + /// + public async Task GetSplenoncusAdd(ReadingCalculateDto inDto) + { + var TaskAnswer = await GetVisitTaskAnswerList(inDto); + if (inDto.IsBaseLine) + { + return "NA"; + } + if (TaskAnswer.Count() == 0) + { + return "0"; + } + var presentSpd = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + var lowSplenoncus = TaskAnswer.OrderBy(x => x.SpleenLength).Select(x => x.SpleenLength).FirstOrDefault(); + return (presentSpd - lowSplenoncus).ToString(); + + + } + #endregion + + + #region 脾肿垂直径最低点访视 + + /// + /// 脾肿垂直径最低点访视 + /// + /// + /// + public async Task GetLowestSplenoncusVisit(ReadingCalculateDto inDto) + { + var taskAnswer = await GetVisitTaskAnswerList(inDto); + if (inDto.IsBaseLine) + { + return "NA"; + } + if (taskAnswer.Count() == 0) + { + return "NA"; + } + + var lowSplenoncus = taskAnswer.OrderBy(x => x.SpleenLength).FirstOrDefault(); + return lowSplenoncus.BlindName; + + + } + #endregion + + #region 获取脾脏状态 + + /// + /// 获取脾脏状态 + /// + /// + /// + /// + [HttpPost] + public async Task GetSplenicState(Guid visitTaskId, decimal spleenLength) + { + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + ReadingCalculateDto inDto = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + var result = SpleenAssessment.NotEvaluable; + if (spleenLength < 0) + { + return SpleenAssessment.NotEvaluable.GetEnumInt(); + } + else if (0 < spleenLength && spleenLength <= 130) + { + return SpleenAssessment.Normal.GetEnumInt(); + + } + else if (inDto.IsBaseLine) + { + return SpleenAssessment.Swelling.GetEnumInt(); + } + + + var lowSpleenLength = await this.GetLowSpleenLength(taskinfo); + + + + + + var presentSpd = spleenLength; + var baseLineSpleenLength = await GetBaseLineSpleenLength(inDto); + + var baseLineTaskId = await GetBaseLineTaskId(inDto); + var baseLineState = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SplenicStatus).Select(x => x.Answer).FirstOrDefaultAsync(); + + + var differenceValue = presentSpd - baseLineSpleenLength; + + var differenceLowValue = presentSpd - lowSpleenLength; + + + decimal getPercentage() + { + decimal percentage = 0; + if (baseLineSpleenLength != 0) + { + percentage = differenceValue * 100 / (baseLineSpleenLength - 130); + } + return percentage; + } + + + + // 1、基线 垂直径> 130 mm + //2、与基线相比脾垂直径变化值≥10 mm + // 当前垂直径>130 mm + //与基线相比脾肿大增加的百分比 > 50% + if (baseLineSpleenLength > 130 && differenceValue >= 10 && spleenLength > 130 && getPercentage() > 50) + { + result = SpleenAssessment.Increase; + } + //1、基线垂直径≤130mm + //2、与基线相比脾垂直径变化值≥20 mm + //当前垂直径 > 130 mm + else if (baseLineSpleenLength <= 130 && differenceValue >= 20 && presentSpd > 130) + { + result = SpleenAssessment.Increase; + } + //1、基线 垂直径> 130 mm + //2、当前访视的前面访视中 存在垂直径≤130mm + //3、与最低点相比脾脏垂直径的增加值≥20 mm + //4、当前垂直径 > 130 mm + else if (baseLineSpleenLength > 130 && lowSpleenLength <= 130 && differenceLowValue >= 20 && presentSpd > 130) + { + result = SpleenAssessment.Increase; + } + // 当前垂直径≤130mm + else if (spleenLength <= 130) + { + result = SpleenAssessment.Normal; + } + //1、基线期 状态为“肿大” + // 当前垂直径>130 mm + //与基线相比脾肿大增加的百分比 < -50% + else if (baseLineState.EqEnum(SpleenAssessment.Swelling) && spleenLength > 130 && getPercentage() < -50) + { + result = SpleenAssessment.Remission; + } + else + { + result = SpleenAssessment.Stabilization; + } + return result.GetEnumInt(); + } + #endregion + + /// + /// 获取最低垂直径 + /// + /// + private async Task GetLowSpleenLength(VisitTask taskinfo) + { + var visitTaskIds = await _visitTaskRepository.Where(x => x.ReadingCategory == ReadingCategory.Visit && + x.TrialReadingCriterionId == taskinfo.TrialReadingCriterionId && + x.IsAnalysisCreate == taskinfo.IsAnalysisCreate && + x.DoctorUserId == taskinfo.DoctorUserId && + x.IsSelfAnalysis == taskinfo.IsSelfAnalysis && + x.SubjectId == taskinfo.SubjectId && x.ReadingTaskState == ReadingTaskState.HaveSigned && x.ArmEnum == taskinfo.ArmEnum + && x.VisitTaskNum < taskinfo.VisitTaskNum && x.TaskState == TaskState.Effect + ).OrderByDescending(x => x.VisitTaskNum).Select(x => x.Id).ToListAsync(); + + var questionId = await _readingQuestionTrialRepository.Where(x => x.ReadingQuestionCriterionTrialId == taskinfo.TrialReadingCriterionId && x.QuestionType == QuestionType.SpleenLength).Select(x => x.Id).FirstNotNullAsync(); + var answerList = await _readingTaskQuestionAnswerRepository.Where(x => visitTaskIds.Contains(x.VisitTaskId) + && x.ReadingQuestionTrialId == questionId) + .Select(x => new + { + x.Answer, + x.VisitTaskId, + }).ToListAsync(); + + var lowSpleenLength = answerList.Select(x => new + { + Answer = x.Answer.IsNullOrEmptyReturn0(), + x.VisitTaskId + }).OrderBy(x => x.Answer).Select(x => x.Answer).FirstOrDefault(); + + return lowSpleenLength; + } + + /// + /// 获取脾脏验证 + /// + /// + /// + [HttpPost] + public async Task GetSplenicVerify(Guid visitTaskId) + { + + ReadingCalculateDto inDto = await _generalCalculateService.GetReadingCalculateDto(visitTaskId); + + // 基线垂直径 + var baseLineSpleenLength = await GetBaseLineSpleenLength(inDto); + + var taskinfo = await _visitTaskRepository.Where(x => x.Id == visitTaskId).FirstNotNullAsync(); + + var baseLineTaskId = await GetBaseLineTaskId(inDto); + + // 最低垂直经 + var lowSpleenLength = await this.GetLowSpleenLength(taskinfo); + + // 基线状态 + var baseLineState = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SplenicStatus).Select(x => x.Answer).FirstOrDefaultAsync(); + + return new + { + + //基线垂直径 + BaseLineSpleenLength = baseLineSpleenLength, + // 最低垂直经 + LowSpleenLength = lowSpleenLength, + // 基线状态 + BaseLineState = baseLineState, + + // 脾脏状态 + SplenicStatus = await GetSplenicStatus(inDto), + }; + + } + + /// + /// 获取脾脏状态 + /// + /// + /// + public async Task GetSplenicStatus(ReadingCalculateDto inDto) + { + var spleenLength = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefault(); + if (spleenLength == string.Empty) + { + return string.Empty; + } + else + { + return await GetSplenicState(inDto.VisitTaskId, spleenLength.IsNullOrEmptyReturn0()); + } + } + + #region 获取脾脏评估 + + /// + /// 获取脾脏评估 + /// + /// + /// + public async Task GetSplenicEvaluation(ReadingCalculateDto inDto) + { + + return inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SplenicStatus).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + //if (inDto.IsBaseLine) + //{ + // return SpleenAssessment.Stabilization.GetEnumInt(); + //} + + //var result = SpleenAssessment.Stabilization; + + + //var presentSpd = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + //var baseLineSpleenLength = await GetBaseLineSpleenLength(inDto); + + //var baseLineTaskId = await GetBaseLineTaskId(inDto); + //var baseLineState = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SplenicStatus).Select(x => x.Answer).FirstOrDefaultAsync(); + + + //var differenceValue = presentSpd - baseLineSpleenLength; + //decimal percentage = 0; + //if (baseLineSpleenLength != 0) + //{ + // percentage = differenceValue * 100 / baseLineSpleenLength; + //} + + //// 1、基线 垂直径> 130 mm + ////2、与基线相比脾垂直径变化值≥10 mm + ////与基线相比脾肿大增加的百分比 > 50% + //if (baseLineSpleenLength > 130 && differenceValue >= 10 && percentage > 50) + //{ + // result = SpleenAssessment.Increase; + //} + ////1、基线垂直径≤130mm + ////2、与基线相比脾垂直径变化值≥20 mm + ////当前垂直径 > 130 mm + //else if (baseLineSpleenLength <= 130 && differenceValue >= 20 && presentSpd > 130) + //{ + // result = SpleenAssessment.Increase; + //} + ////1、基线 垂直径> 130 mm + ////2、当前访视的前面访视中 存在垂直径≤130mm + ////3、与最低点相比脾脏垂直径的增加值≥20 mm + ////4、当前垂直径 > 130 mm + //else if (baseLineSpleenLength > 130 && presentSpd <= 130 && differenceValue >= 20 && presentSpd > 130) + //{ + // result = SpleenAssessment.Increase; + //} + //// 基线垂直径≤130mm + //else if (baseLineSpleenLength <= 130) + //{ + // result = SpleenAssessment.Normal; + //} + ////1、基线期 状态为“肿大” + ////与基线相比脾肿大增加的百分比 > 50% + //else if (baseLineState.EqEnum(SpleenState.Swelling) && percentage > 50) + //{ + // result = SpleenAssessment.Remission; + //} + //else + //{ + // result = SpleenAssessment.Remission; + //} + //return result.GetEnumInt(); + + + } + #endregion + + + #region PET 5PS + + /// + /// 获取PET5PS评分 + /// + /// + /// + public async Task GetPET5PS(ReadingCalculateDto inDto) + { + + // 如果是保存影像进来 PET-CT不能融合 就是NE + if (inDto.ComputationTrigger == ComputationTrigger.ImageQuality) + { + if (await ImageQualityIsUnableFuse(inDto)) + { + return PET5PSScore.NE.GetEnumInt(); + } + } + + + // 先在数据库查这几个值 + List needSearchTypes = new List() + { + QuestionType.SUVmax, + QuestionType.PET5PS, + }; + + var dataBaseDataList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && needSearchTypes.Contains(x.ReadingQuestionTrial.QuestionType)).Select(x => new + { + x.Answer, + x.ReadingQuestionTrial.QuestionType + }).ToListAsync(); + + + // 数据库中最大SUVmax + var dataBaseSUVmax = dataBaseDataList.Where(x => x.QuestionType == QuestionType.SUVmax).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + // 如果不是保存肝脏血池和纵隔血池 + if (inDto.ComputationTrigger != ComputationTrigger.InitialCalculation && inDto.ComputationTrigger != ComputationTrigger.LiverBloodPool && inDto.ComputationTrigger != ComputationTrigger.MediastinalPool&& dataBaseSUVmax == (await GetSuvMax(inDto)).ToString()) + { + + + return dataBaseDataList.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + } + + + + //if (inDto.IsBaseLine) + //{ + // return SpleenAssessment.Stabilization.GetEnumInt(); + //} + + else if (inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.ExistPET && x.Answer == ReadingYesOrNo.No.GetEnumInt())) + { + return PET5PSScore.NE.GetEnumInt(); + } + + + var existPET = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.ExistPET).Select(x => x.Answer).FirstOrDefault(); + if (existPET.EqEnum(ReadingYesOrNo.No)) + { + return PET5PSScore.NE.GetEnumInt(); + } + + + PET5PSScore result = PET5PSScore.X; + + // 最大Suvmax + var maxSUVmax = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SUVmax).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + // 肝脏血池Suvmax + var LiverSUVmax = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LiverSUVmax).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + // 纵膈血池 + var MediastinumSUVmax = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.MediastinumSUVmax).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + + + if (existPET.EqEnum(ReadingYesOrNo.No)) + { + result = PET5PSScore.NE; + } + + // 本访视病灶的 max SUVmax(所有病灶中最大的)> 2 * 肝脏血池SUVmax + else if (maxSUVmax > 2 * LiverSUVmax && LiverSUVmax != 0m) + { + result = PET5PSScore.Five; + } + //本访视病灶的SUVmax(所有病灶中最大的)>肝脏血池SUVmax + else if (maxSUVmax > LiverSUVmax && LiverSUVmax != 0) + { + result = PET5PSScore.Four; + } + //纵隔血池SUVmax<本访视点病灶的max SUVmax(所有病灶中最大的)≤1*肝脏血池SUVmax + else if (MediastinumSUVmax < maxSUVmax && maxSUVmax <= LiverSUVmax && MediastinumSUVmax != 0) + { + result = PET5PSScore.Three; + } + //本访视点病灶的SUVmax(所有病灶中最大的)<=纵隔血池SUVmax + else if (maxSUVmax <= MediastinumSUVmax && maxSUVmax != 0) + { + result = PET5PSScore.Two; + } + //无需标记,自主选择 + else + { + return string.Empty; + } + + return result.GetEnumInt(); + } + #endregion + + #region 修改PET 5PS评分备注 + + /// + /// 获取PET5PS评分备注 + /// + /// + /// + public async Task GetPET5PSRemark(ReadingCalculateDto inDto) + { + List needSearchTypes = new List() + { + QuestionType.PET5PS, + QuestionType.PSScoreRemarks, + }; + + var dataBaseDataList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && needSearchTypes.Contains(x.ReadingQuestionTrial.QuestionType)).Select(x => new + { + x.Answer, + x.ReadingQuestionTrial.QuestionType + }).ToListAsync(); ; + + + // 数据库中PET5PS + var pET5PS = dataBaseDataList.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + // 如果PET5PS没变 就不重新计算 + if (pET5PS == (await GetPET5PS(inDto)).ToString()) + { + return dataBaseDataList.Where(x => x.QuestionType == QuestionType.PSScoreRemarks).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + } + else + { + return string.Empty; + } + + } + #endregion + + + #region 与基线相比摄取值变化 + + /// + /// 与基线相比摄取值变化 + /// + /// + /// + public async Task GetUptakeChange(ReadingCalculateDto inDto) + { + var existPET = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.ExistPET).Select(x => x.Answer).FirstOrDefault(); + if (await ImageQualityIsUnableFuse(inDto)|| existPET.EqEnum(ReadingYesOrNo.No)) + { + if (inDto.IsBaseLine) + { + return string.Empty; + } + else + { + return SUVChangeVSBaseline.NotEvaluable.GetEnumInt(); + } + + } + + //PET5ps评分改变时,才计算 + + List needSearchTypes = new List() + { + QuestionType.PET5PS, + QuestionType.UptakeChange, + }; + + var dataBaseDataList = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == inDto.VisitTaskId && needSearchTypes.Contains(x.ReadingQuestionTrial.QuestionType)).Select(x => new + { + x.Answer, + x.ReadingQuestionTrial.QuestionType + }).ToListAsync(); ; + + + // 数据库中PET5ps评分 + var dataBasePET5ps = dataBaseDataList.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + + if (dataBasePET5ps == await GetPET5PS(inDto)) + { + return dataBaseDataList.Where(x => x.QuestionType == QuestionType.UptakeChange).Select(x => x.Answer).FirstIsNullReturnEmpty(); + + } + + + if (inDto.IsBaseLine) + { + return SUVChangeVSBaseline.NA.GetEnumInt(); + } + + var result = SUVChangeVSBaseline.NotEvaluable; + var baseLineTaskId = await GetBaseLineTaskId(inDto); + + var PET5PS = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + if (PET5PS == 0) + { + return string.Empty; + } + + var baseLinePET5PSValue = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstOrDefaultAsync(); + + + var baseLinePET5PS = baseLinePET5PSValue.IsNullOrEmptyReturn0(); + + + + + + // 基线为NE或者X 为空 + if (baseLinePET5PSValue.EqEnum(PET5PSScore.X) || baseLinePET5PSValue.EqEnum(PET5PSScore.NE)) + { + return string.Empty; + } + + //本访视PET评分>基线PET评分 + else if (PET5PS > baseLinePET5PS) + { + result = SUVChangeVSBaseline.Increase; + } + + //本访视PET评分<基线PET评分 + else if (PET5PS < baseLinePET5PS) + { + result = SUVChangeVSBaseline.Decrease; + } + + //访视PET评分 等于 基线PET评分 + else if (PET5PS == baseLinePET5PS) + { + return string.Empty; + //result = SUVChangeVSBaseline.DidNotChange; + } + + return result.GetEnumInt(); + + } + #endregion + + /// + /// 获取上一次FDGPET 评估 + /// + /// + /// + public async Task GetLastFDGPETOverallAssessment(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return FDGPETOverallAssessment.NA.GetEnumInt(); + } + + var lastTaskId = await GetLastVisitTaskId(inDto); + + + var answer = await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == lastTaskId).Include(x => x.ReadingQuestionTrial).Where(x => x.ReadingQuestionTrial.QuestionType == QuestionType.FDGPET).Select(x => x.Answer).FirstOrDefaultAsync(); + return answer ?? string.Empty; + } + + #region FDG-PET总体评估结果 + + /// + /// FDG-PET总体评估结果 + /// + /// + /// + public async Task GetFDGPETOverallAssessment(ReadingCalculateDto inDto) + { + + // 如果是保存影像进来 PET-CT不能融合 就是NE + if (inDto.ComputationTrigger == ComputationTrigger.ImageQuality) + { + if (await ImageQualityIsUnableFuse(inDto)) + { + if (inDto.IsBaseLine) + { + return FDGPETOverallAssessment.NA.GetEnumInt(); + } + else + { + return FDGPETOverallAssessment.NE.GetEnumInt(); + } + } + } + + if (inDto.IsBaseLine) + { + return FDGPETOverallAssessment.NA.GetEnumInt(); + } + + var notExistPET = inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.ExistPET && x.Answer == ReadingYesOrNo.No.GetEnumInt()); + + + + if (notExistPET || (await ImageQualityIsUnableFuse(inDto))) + { + return FDGPETOverallAssessment.NE.GetEnumInt(); + } + + //FDGPETOverallAssessment result = FDGPETOverallAssessment.NA; + + // PET5PS + var pET5PS = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.PET5PS).Select(x => x.Answer).FirstOrDefault(); + + // UptakeChange + var uptakeChange = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.UptakeChange).Select(x => x.Answer).FirstOrDefault(); + + // EvidenceFocalFDG + var evidenceFocalFDG = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.EvidenceFocalFDG).Select(x => x.Answer).FirstOrDefault(); + + + return CalculationFDGPETOverallAssessment(pET5PS, uptakeChange, evidenceFocalFDG); + } + + + /// + /// 计算FDG-PET总体评估结果【测试】 + /// + /// PET5PS评分 + /// 与基线相比摄取值变化 + /// 骨髓中是否存在局灶性 FDG亲和病灶的证据 + /// + public string CalculationFDGPETOverallAssessment(string? pET5PS, string uptakeChange, string? evidenceFocalFDG) + { + List data = new List() { + + //NE NE NE NE + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.NE }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.NotEvaluable }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.NE}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + }, + + //NE/5分/4分/3分/2分/1分/X NE/增大/减少/无明显变化 是,存在新的/复发的FDG高亲和性病灶 PMD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.NE,PET5PSScore.Five,PET5PSScore.Four,PET5PSScore.Three,PET5PSScore.Two,PET5PSScore.One,PET5PSScore.X, }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.NotEvaluable,SUVChangeVSBaseline.Increase,SUVChangeVSBaseline.Decrease,SUVChangeVSBaseline.DidNotChange, }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.YesHaveNew,}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD }), + }, + + //5分/4分 增大 NE/(是,存在新的/复发的FDG高亲和性病灶)/(是,存在持续的局灶性变化)/否 PMD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.Five,PET5PSScore.Four, }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.Increase }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.NE, FDGAffinityFociInBM.YesHaveNew, FDGAffinityFociInBM.YesSustain, FDGAffinityFociInBM.No}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD }), + }, + + //3分/2分/1分/X NE/增大/减少/无明显变化 否 CMR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.Three,PET5PSScore.Two,PET5PSScore.One,PET5PSScore.X, }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.NotEvaluable,SUVChangeVSBaseline.Increase,SUVChangeVSBaseline.Decrease,SUVChangeVSBaseline.DidNotChange, }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.No}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.CMR }), + }, + + //3分/2分/1分/X NE/增大/减少/无明显变化 是,存在持续的局灶性变化 PMR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.Three,PET5PSScore.Two,PET5PSScore.One,PET5PSScore.X, }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.NotEvaluable,SUVChangeVSBaseline.Increase,SUVChangeVSBaseline.Decrease,SUVChangeVSBaseline.DidNotChange, }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.YesSustain }), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMR }), + }, + + //5分/4分 减少 否/是,存在持续的局灶性变化 PMR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.Five,PET5PSScore.Four }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.Decrease }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.No, FDGAffinityFociInBM.YesSustain}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMR }), + }, + + //5分/4分 无明显变化 否/是,存在持续的局灶性变化 NMR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { PET5PSScore.Five,PET5PSScore.Four }), + Column2=ReadingCommon.EnumToString(new List() { SUVChangeVSBaseline.DidNotChange }), + Column3=ReadingCommon.EnumToString(new List() { FDGAffinityFociInBM.No, FDGAffinityFociInBM.YesSustain}), + Column4=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NMR }), + }, + }; + + + var resultdata = data.Where(x => + (x.NotEq.Contains(1) ? !x.Column1.Contains(pET5PS) : x.Column1.Contains(pET5PS) || x.Column1.Count() == 0) && + (x.NotEq.Contains(2) ? !x.Column2.Contains(uptakeChange) : x.Column2.Contains(uptakeChange) || x.Column2.Count() == 0) && + (x.NotEq.Contains(3) ? !x.Column3.Contains(evidenceFocalFDG) : x.Column3.Contains(evidenceFocalFDG) || x.Column3.Count() == 0)) + .Select(x => x.Column4.FirstOrDefault()) + .FirstOrDefault(); + return resultdata ?? string.Empty; + } + + #endregion + #region 骨髓中是否存在局灶性 FDG亲和病灶的证据 + + /// + /// 骨髓中是否存在局灶性 FDG亲和病灶的证据 + /// + /// + /// + public async Task GetEvidenceFocalFDG(ReadingCalculateDto inDto) + { + + if (await ImageQualityIsUnableFuse(inDto)|| inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.ExistPET && x.Answer == ReadingYesOrNo.No.GetEnumInt())) + { + if (inDto.IsBaseLine) + { + return string.Empty; + } + else + { + return FDGAffinityFociInBM.NE.GetEnumInt(); + } + } + else + { + return inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.EvidenceFocalFDG).Select(x => x.Answer).FirstOrDefault()??string.Empty; + } + } + #endregion + + #region 肝脏评估 + /// + /// 获取肝脏评估 + /// + /// + /// + public async Task GetLiverAssessment(ReadingCalculateDto inDto) + { + return inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LiverState).Select(x => x.Answer).FirstIsNullReturnEmpty(); + } + #endregion + + #region SuvMax所在病灶 + + /// + /// SuvMax所在病灶 + /// + /// + /// + public async Task GetSuvMaxFocus(ReadingCalculateDto inDto) + { + var rowInfo = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + + if (inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.ExistPET && x.Answer == ReadingYesOrNo.No.GetEnumInt()) + || !inDto.QuestionInfo.SelectMany(x => x.TableRowInfoList).Any(x => x.OtherMeasureData != null && x.OtherMeasureData != string.Empty)) + { + return string.Empty; + } + + var tableQuestions = rowInfo.SelectMany(x => x.TableQuestionList).ToList(); + + var maxSuv = tableQuestions.Where(x => x.QuestionMark == QuestionMark.SUVmax).Select(x =>new { + x.RowIndex, + Answer= x.Answer.IsNullOrEmptyReturn0(), + + + }).OrderByDescending(x=>x.Answer).FirstOrDefault(); + if (maxSuv == null|| maxSuv.Answer == 0m) + { + return "NE"; + } + else + { + var orderMark = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).FirstOrDefault()!.OrderMark; + return orderMark+ maxSuv.RowIndex.GetLesionMark(); + } + + + } + #endregion + + #region SuvMax + /// + /// 最大sum + /// + /// + /// + public async Task GetSuvMax(ReadingCalculateDto inDto) + { + + + + if (inDto.QuestionInfo.Any(x => x.QuestionType == QuestionType.ExistPET && x.Answer == ReadingYesOrNo.No.GetEnumInt()) + || !inDto.QuestionInfo.SelectMany(x => x.TableRowInfoList).Any(x => x.OtherMeasureData != null && x.OtherMeasureData != string.Empty)) + { + return null; + } + var rowInfo = inDto.QuestionInfo.SelectMany(x => x.TableRowInfoList).ToList(); + var tableQuestions = rowInfo.SelectMany(x => x.TableQuestionList).ToList(); + + var maxSuv = tableQuestions.Where(x => x.QuestionMark == QuestionMark.SUVmax).Select(x => x.Answer.IsNullOrEmptyReturn0()).MaxOrDefault(); + return maxSuv; + } + #endregion + + /// + /// 是否存在PET + /// + /// + /// + public async Task GetExistPET(ReadingCalculateDto inDto) + { + //var studyList = await this._subjectVisitService.GetReadingVisitStudyList(new Contracts.GetReadingVisitStudyListIndto() + //{ + // TrialId = inDto.TrialId, + // SujectVisitId = inDto.SubjectVisitId, + // VisitTaskId=inDto.VisitTaskId, + //}); + + var existPet=await _dicomStudyRepository.Where(x => x.TrialId == inDto.TrialId && x.SubjectVisitId == inDto.SubjectVisitId).AnyAsync(x => x.Modalities.Contains("PT")); + + return existPet ? ReadingYesOrNo.Yes.GetEnumInt() : ReadingYesOrNo.No.GetEnumInt(); + } + + #region 影像学整体肿瘤评估 + /// + /// 影像学整体肿瘤评估 + /// + /// + /// + public async Task GetImgOncology(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return ImagingOverallAssessment_Lugano.NA.GetEnumInt(); + } + + //var imageQualityEvaluation = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.ImageQualityAssessment&&x.Answer.EqEnum(ImageQualityEvaluation.Abnormal)).FirstOrDefault(); + //if (imageQualityEvaluation != null) + //{ + // return ImagingOverallAssessment_Lugano.NE.GetEnumInt(); + //} + + // CTandMRI + var cTandMRIData = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.CTandMRI).Select(x => x.Answer).FirstOrDefault(); + + // FDGPET + var fDGPETData = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.FDGPET).Select(x => x.Answer).FirstOrDefault(); + + var baseLineTaskId = await GetBaseLineTaskId(inDto); + // lastFDGPET + var lastFDGPETData = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LastFDGPET).Select(x => x.Answer).FirstOrDefault(); + + return CalculationGetImgOncology(cTandMRIData, fDGPETData, lastFDGPETData); + + } + + /// + /// 计算整体肿瘤评估 【测试】 + /// + /// CT/MRI总体评估 + /// FDG-PET总体评估 + /// 上一次 FDG-PET总体评估 + /// + public string CalculationGetImgOncology(string? cTandMRIData,string? fDGPETData,string? lastFDGPETData) + { + List data = new List() { + + //1、ND NE NE/NA ND + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.ND }), + }, + //2、ND/PD/CR/NE/PR/SD PMD PMD/CMR/PMR/NMR/NE/NA PMD/PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND, CTMRIOverallAssessment.PD,CTMRIOverallAssessment.CR,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD}), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD, }), + Column3=ReadingCommon.EnumToString(new List() {FDGPETOverallAssessment.PMD, FDGPETOverallAssessment.CMR,FDGPETOverallAssessment.PMR, FDGPETOverallAssessment.NMR, FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMDPD }), + }, + //3、ND/PD/CR/NE/PR/SD NE PMD PMD/PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND, CTMRIOverallAssessment.PD,CTMRIOverallAssessment.CR,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD}), + Column2=ReadingCommon.EnumToString(new List() {FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD, }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMDPD }), + }, + //4、PD NE CMR/PMR/NMR/NE/NA PMD/PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD}), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.CMR,FDGPETOverallAssessment.PMR, FDGPETOverallAssessment.NMR, FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMDPD }), + }, + //5、NE NE NE/NA NE + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.NE }), + }, + + //6、CR、PR、SD、NE、ND、PD CMR CMR/PMR/NMR/PMD/NE/NA CMR/CR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.ND,CTMRIOverallAssessment.PD }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.CMR }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.CMR,FDGPETOverallAssessment.PMR, FDGPETOverallAssessment.NMR, FDGPETOverallAssessment.PMD, FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.CMRCR }), + }, + //7、CR NE NE/NA CMR/CR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA,}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.CMRCR }), + }, + + //8、CR、PR、SD、NE、ND NE CMR CMR/CR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.CMR,}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.CMRCR }), + }, + + + + + //9、ND/PD/CR/NE/PR/SD PMR PMD/CMR/PMR/NMR/NE PMR/PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND, CTMRIOverallAssessment.PD,CTMRIOverallAssessment.CR,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMR }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD, FDGPETOverallAssessment.CMR,FDGPETOverallAssessment.PMR, FDGPETOverallAssessment.NMR, FDGPETOverallAssessment.NE,FDGPETOverallAssessment.NA, }), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMRPR }), + }, + //10、PR NE NE/NA PMR/PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA,}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMRPR }), + }, + //11、CR/PR/SD/NE/ND NE PMR PMR/PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMR}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.PMRPR }), + }, + //12、ND/PD/CR/NE/PR/SD NMR PMD/CMR/PMR/NMR/NE NMR/SD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND,CTMRIOverallAssessment.PD,CTMRIOverallAssessment.CR,CTMRIOverallAssessment.NE,CTMRIOverallAssessment.PR,CTMRIOverallAssessment.SD }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NMR }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.PMD, FDGPETOverallAssessment.CMR, FDGPETOverallAssessment.PMR, FDGPETOverallAssessment.NMR, FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA,}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.NMRSD }), + }, + //13、CR/PR/SD/ND/NE NE NMR NMR/SD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR, CTMRIOverallAssessment.PR, CTMRIOverallAssessment.SD, CTMRIOverallAssessment.ND, CTMRIOverallAssessment.NE }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NMR}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.NMRSD }), + }, + //14、SD NE NE NMR/SD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.SD }), + Column2=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { FDGPETOverallAssessment.NE, FDGPETOverallAssessment.NA,}), + Column4=ReadingCommon.EnumToString(new List() { ImagingOverallAssessment_Lugano.NMRSD }), + }, + }; + + var resultdata = data.Where(x => + (x.NotEq.Contains(1) ? !x.Column1.Contains(cTandMRIData) : x.Column1.Contains(cTandMRIData) || x.Column1.Count() == 0) && + (x.NotEq.Contains(2) ? !x.Column2.Contains(fDGPETData) : x.Column2.Contains(fDGPETData) || x.Column2.Count() == 0) && + (x.NotEq.Contains(3) ? !x.Column3.Contains(lastFDGPETData) : x.Column3.Contains(lastFDGPETData) || x.Column3.Count() == 0)) + .Select(x => x.Column4.FirstOrDefault()) + .FirstOrDefault(); + return resultdata ?? string.Empty; + } + + #endregion + + #region 获取基线脾脏长度 + /// + /// 获取基线脾脏长度 + /// + /// + /// + public async Task GetBaseLineSpleenLength(ReadingCalculateDto inDto) + { + var baseLineTaskId = await GetBaseLineTaskId(inDto); + + var baseLineSpleenLength = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + return baseLineSpleenLength; + } + + + #endregion + + #region 与最低点相比脾脏垂直径长度的增加值 + /// + /// 与最低点相比脾脏垂直径长度的增加值 + /// + /// + /// + public async Task GetSplenoncusDiameterChange(ReadingCalculateDto inDto) + { + + if (inDto.IsBaseLine) + { + return "NA"; + } + + var baseLineTaskId = await GetBaseLineTaskId(inDto); + var presentSpleenLength = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefault().IsNullOrEmptyReturn0(); + var baseLineSpleenLength = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SpleenLength).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + + return (presentSpleenLength - baseLineSpleenLength).ToString(); + + + } + #endregion + + + #region 获取靶病灶评估 + /// + /// 获取靶病灶评估 + /// + /// + /// + public async Task GetTargetLesionEvaluate(ReadingCalculateDto inDto) + { + + + + var rowInfo = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.TargetLesion).SelectMany(x => x.TableRowInfoList).ToList(); + if (inDto.IsBaseLine) + { + return TargetAssessment.NA.GetEnumInt(); + } + var tableQuestions = rowInfo.SelectMany(x => x.TableQuestionList).ToList(); + + TargetAssessment result = TargetAssessment.SD; + + //或者当前访视非淋巴结靶病灶全部消失; + //并且 2.当前访视淋巴结靶病灶的状态全部变为“消失”。 + + var eqCR = true; + //当前访视中,靶病灶的Σ PPD; + decimal spd = 0; + foreach (var item in rowInfo) + { + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.No))) + { + // 或者当前访视非淋巴结靶病灶全部消失; + eqCR = eqCR && item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(TargetState.Loss)); + } + spd += (item.TableQuestionList.Where(x => x.QuestionMark == QuestionMark.PPD).Select(x => x.Answer).FirstOrDefault()).IsNullOrEmptyReturn0(); + if (item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.IsLymph && x.Answer.EqEnum(YesOrNoOrNa.Yes))) + { + // 当前访视淋巴结靶病灶的状态全部变为“消失” + eqCR = eqCR && item.TableQuestionList.Any(x => x.QuestionMark == QuestionMark.State && (x.Answer.EqEnum(TargetState.Loss)|| x.Answer.EqEnum(TargetState.TooSmall))); + } + } + // 1、与基线相比SPD变化的百分比 ≥50%,; + var eqPR = false; + + if (inDto.IsBaseLine) + { + eqPR = false; + } + else + { + // 先找到基线的任务 + var baseLineTaskId = await _visitTaskRepository.Where(x => x.SubjectId == inDto.SubjectId && x.ReadingCategory == ReadingCategory.Visit + && x.TrialReadingCriterionId == inDto.TrialReadingCriterionId && + x.SourceSubjectVisit.IsBaseLine && x.TaskState == TaskState.Effect && + x.IsAnalysisCreate == inDto.IsAnalysisCreate + && x.DoctorUserId == inDto.DoctorUserId + && x.IsSelfAnalysis == inDto.IsSelfAnalysis && x.ArmEnum == inDto.ArmEnum) + .Select(x => x.Id).FirstOrDefaultAsync(); + + var baseLineSPD = (await _readingTaskQuestionAnswerRepository.Where(x => x.VisitTaskId == baseLineTaskId && x.ReadingQuestionTrial.QuestionType == QuestionType.SPD).Select(x => x.Answer).FirstOrDefaultAsync()).IsNullOrEmptyReturn0(); + var presentSPD = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SPD).Select(x => x.Answer).FirstIsNullReturnEmpty().IsNullOrEmptyReturn0(); + + if (baseLineSPD > 0) + { + eqPR = (presentSPD - baseLineSPD) / baseLineSPD <=- 0.5m; + } + } + + + + //基线未选择靶病灶 + if (rowInfo.Count() == 0) + { + result = TargetAssessment.ND; + } + // 任一单个病灶进展即可 + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(TargetState.DiseaseProgression))) + { + result = TargetAssessment.PD; + } + + //当前访视存在至少一个状态为“不可评估”的靶病灶。 + else if ( + tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(TargetState.UnableEvaluate))) + { + result = TargetAssessment.NE; + } + //当前访视非淋巴结靶病灶全部消失 && (当前访视淋巴结靶病灶的状态全部变为“消失” 或者 "太小" ) + else if (eqCR) + { + result = TargetAssessment.CR; + } + // 与基线相比SPD变化的百分比 <=-50%,; + else if (eqPR) + { + result = TargetAssessment.PR; + } + + + return result.GetEnumInt(); + } + #endregion + + #region 获取非靶病灶评估 + + /// + /// 获取非靶病灶评估 + /// + /// + /// + public async Task GetNoTargetLesionEvaluate(ReadingCalculateDto inDto) + { + + NoTargetAssessment result = NoTargetAssessment.PD; + + if (inDto.IsBaseLine) + { + return NoTargetAssessment.NA.GetEnumInt(); + } + + var tableRows = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NonTargetLesions).SelectMany(x => x.TableRowInfoList).ToList(); + + var tableQuestions = tableRows.SelectMany(x => x.TableQuestionList).ToList(); + + //基线未选择非靶病灶 + if (tableQuestions.Count() == 0) + { + result = NoTargetAssessment.ND; + } + // 随访至少存在一个状态为“显著增大”的非靶病灶 + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Increase))) + { + result = NoTargetAssessment.PD; + } + + // 随访存在至少一个状态为“不可评估”的非靶病灶 + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.UnableEvaluate))) + { + result = NoTargetAssessment.NE; + } + //所有单个病灶/病灶组状态评估状态为“消失” + else if (tableQuestions.Count(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NoTargetState.Loss))== tableQuestions.Count(x=> x.QuestionMark == QuestionMark.State) && tableQuestions.Count(x => x.QuestionMark == QuestionMark.State)>0) + { + result = NoTargetAssessment.CR; + } + else + { + result = NoTargetAssessment.PRSD; + } + + return result.GetEnumInt(); + + } + + #endregion + + #region 获取新病灶评估 + /// + /// 获取新病灶评估 + /// + /// + /// + public async Task GetNewLesionEvaluate(ReadingCalculateDto inDto) + { + + NewLesionAssessment result = NewLesionAssessment.No; + if (inDto.IsBaseLine) + { + return NewLesionAssessment.NA.GetEnumInt(); + } + + var tableRows = inDto.QuestionInfo.Where(x => x.LesionType == LesionType.NewLesions).SelectMany(x => x.TableRowInfoList).ToList(); + + var tableQuestions = tableRows.SelectMany(x => x.TableQuestionList).ToList(); + + + + // 当前访视存在至少一个明确新病灶 + if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Exist))) + { + result = NewLesionAssessment.Yes; + } + //只要有任何一个新病灶状态为“无法评估” + else if (tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.UnableEvaluate))) + { + result = NewLesionAssessment.NE; + } + //当前访视不存在明确新病灶且存在至少一个疑似新病灶 + else if (!tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Exist)) && + tableQuestions.Any(x => x.QuestionMark == QuestionMark.State && x.Answer.EqEnum(NewLesionState.Suspected)) + ) + { + result = NewLesionAssessment.Suspected; + } + + + else + { + result = NewLesionAssessment.No; + } + return result.GetEnumInt(); + + } + #endregion + + + #region CTMRI 总体评估 + + /// + /// CTMRI 总体评估 + /// + /// + /// + public async Task CTMRIEvaluation(ReadingCalculateDto inDto) + { + if (inDto.IsBaseLine) + { + return CTMRIOverallAssessment.NA.GetEnumInt(); + } + // 靶病灶评估 + var targetEvaluation = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.TargetLesion).Select(x => x.Answer).FirstOrDefault(); + + // 非靶病灶评估 + var noTargetEvaluation = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NoTargetLesion).Select(x => x.Answer).FirstOrDefault(); + + // 存在新病灶 + var existsNewTarget = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.NewLesionEvaluation).Select(x => x.Answer).FirstOrDefault(); + + // 肝脏评估 + var liverEvaluation = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.LiverAssessment).Select(x => x.Answer).FirstOrDefault(); + + // 脾脏评估 + var spleenEvaluation = inDto.QuestionInfo.Where(x => x.QuestionType == QuestionType.SplenicEvaluation).Select(x => x.Answer).FirstOrDefault(); + + return CalculationCTMRIEvaluation(targetEvaluation, noTargetEvaluation, existsNewTarget, liverEvaluation, spleenEvaluation); + } + + /// + /// 计算CTMRI 总体评估 【测试】 + /// + /// 靶病灶评估 + /// 非靶病灶评估 + /// 存在新病灶 + /// 肝脏评估 + /// 脾脏评估 + /// + [HttpPost] + public string CalculationCTMRIEvaluation(string? targetEvaluation,string? noTargetEvaluation,string? existsNewTarget,string? liverEvaluation,string? spleenEvaluation) + { + List data = new List() { + + //ND ND 否/疑似 正常 正常 ND + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No, NewLesionAssessment.Suspected }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.ND }), + }, + + //PD 任一结果 任一结果 任一结果 任一结果 PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.PD }), + Column2=ReadingCommon.EnumToString(new List() { }), + Column3=ReadingCommon.EnumToString(new List() { }), + Column4=ReadingCommon.EnumToString(new List() { }), + Column5=ReadingCommon.EnumToString(new List() { }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD }), + }, + //任一结果 PD 任一结果 任一结果 任一结果 PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.PD }), + Column3=ReadingCommon.EnumToString(new List() {}), + Column4=ReadingCommon.EnumToString(new List() { }), + Column5=ReadingCommon.EnumToString(new List() { }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD }), + }, + //任一结果 任一结果 是 任一结果 任一结果 PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { }), + Column2=ReadingCommon.EnumToString(new List() { }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.Yes }), + Column4=ReadingCommon.EnumToString(new List() { }), + Column5=ReadingCommon.EnumToString(new List() { }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD }), + }, + //任一结果 任一结果 任一结果 显著增大 任一结果 PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { }), + Column2=ReadingCommon.EnumToString(new List() { }), + Column3=ReadingCommon.EnumToString(new List() { }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Increase }), + Column5=ReadingCommon.EnumToString(new List() { }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD }), + }, + //任一结果 任一结果 任一结果 任一结果 显著增大 PD + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { }), + Column2=ReadingCommon.EnumToString(new List() { }), + Column3=ReadingCommon.EnumToString(new List() {}), + Column4=ReadingCommon.EnumToString(new List() { }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Increase }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PD }), + }, + //CR CR/ND 否 正常 正常 CR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR, NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR }), + }, + //ND CR 否 正常 正常 CR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.CR }), + }, + //NE 非PD 否/疑似/无法评估 非显著增大 非显著增大 NE + new CalculationDto(){ + NotEq=new List(){ 2,4,5}, + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.NE }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.PD }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No,NewLesionAssessment.Suspected, NewLesionAssessment.NE}), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Increase }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Increase }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + }, + //ND NE 否/疑似/无法评估(不为是) 正常/稳定/无法评估/部分缓解(非显著增大) 正常/稳定/无法评估/部分缓解(非显著增大) NE + new CalculationDto(){ + NotEq=new List(){ 3,4,5}, + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.Yes }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Increase }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Increase }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + }, + //ND ND 无法评估 正常/稳定/无法评估/部分缓解(非显著增大) 正常/稳定/无法评估/部分缓解(非显著增大) NE + new CalculationDto(){ + NotEq=new List(){ 4,5}, + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.NE }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Increase }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Increase }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + }, + //ND ND 否/疑似 无法评估 正常/稳定/无法评估/部分缓解(非显著增大) NE + new CalculationDto(){ + NotEq=new List(){ 5}, + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No,NewLesionAssessment.Suspected }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.NotEvaluable }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Increase }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + }, + // ND ND 否/疑似 正常/稳定 NE NE + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No,NewLesionAssessment.Suspected }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal,LiverAssessment.Stabilization }), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.NE }), + }, + //1 PR CR或PR/SD或NE或ND 否/疑似/无法评估NE 正常/稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.PR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR,NoTargetAssessment.PRSD,NoTargetAssessment.NE,NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No,NewLesionAssessment.Suspected,NewLesionAssessment.NE }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal,LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + //2 CR (PR/SD)或者NE 否/疑似/无法评估 正常/稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.PRSD,NoTargetAssessment.NE }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No,NewLesionAssessment.Suspected ,NewLesionAssessment.NE}), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal,LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission ,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable}), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + //3 CR CR/ND 疑似/无法评估 正常/稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR,NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.Suspected, NewLesionAssessment.NE }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal,LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission ,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + //4 CR CR/ND 否 稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR,NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() {LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission ,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + //5 CR CR/ND 否 正常 部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.CR }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR,NoTargetAssessment.ND }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() {LiverAssessment.Normal}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Remission ,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + // 6 ND CR 疑似/无法评估 正常/稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.Suspected, NewLesionAssessment.NE }), + Column4=ReadingCommon.EnumToString(new List() { LiverAssessment.Normal,LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + // 7 ND CR 否 稳定/NE 正常/部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() {LiverAssessment.Stabilization,LiverAssessment.NotEvaluable}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Normal,SpleenAssessment.Remission,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + // 8 ND CR 否 正常 部分缓解/稳定/NE PR + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { TargetAssessment.ND }), + Column2=ReadingCommon.EnumToString(new List() { NoTargetAssessment.CR }), + Column3=ReadingCommon.EnumToString(new List() { NewLesionAssessment.No }), + Column4=ReadingCommon.EnumToString(new List() {LiverAssessment.Normal}), + Column5=ReadingCommon.EnumToString(new List() { SpleenAssessment.Remission,SpleenAssessment.Stabilization,SpleenAssessment.NotEvaluable }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.PR }), + }, + new CalculationDto(){ + Column1=ReadingCommon.EnumToString(new List() { }), + Column2=ReadingCommon.EnumToString(new List() { }), + Column3=ReadingCommon.EnumToString(new List() { }), + Column4=ReadingCommon.EnumToString(new List() { }), + Column5=ReadingCommon.EnumToString(new List() { }), + Column6=ReadingCommon.EnumToString(new List() { CTMRIOverallAssessment.SD }), + }, + }; + + var index = data.FindIndex(x => + (x.NotEq.Contains(1) ? !x.Column1.Contains(targetEvaluation) : x.Column1.Contains(targetEvaluation) || x.Column1.Count() == 0) && + (x.NotEq.Contains(2) ? !x.Column2.Contains(noTargetEvaluation) : x.Column2.Contains(noTargetEvaluation) || x.Column2.Count() == 0) && + (x.NotEq.Contains(3) ? !x.Column3.Contains(existsNewTarget) : x.Column3.Contains(existsNewTarget) || x.Column3.Count() == 0) && + (x.NotEq.Contains(4) ? !x.Column4.Contains(liverEvaluation) : x.Column4.Contains(liverEvaluation) || x.Column4.Count() == 0) && + (x.NotEq.Contains(5) ? !x.Column5.Contains(spleenEvaluation) : x.Column5.Contains(spleenEvaluation) || x.Column5.Count() == 0)) + ; + var result = data.Where(x => + (x.NotEq.Contains(1) ? !x.Column1.Contains(targetEvaluation) : x.Column1.Contains(targetEvaluation) || x.Column1.Count() == 0) && + (x.NotEq.Contains(2) ? !x.Column2.Contains(noTargetEvaluation) : x.Column2.Contains(noTargetEvaluation) || x.Column2.Count() == 0) && + (x.NotEq.Contains(3) ? !x.Column3.Contains(existsNewTarget) : x.Column3.Contains(existsNewTarget) || x.Column3.Count() == 0) && + (x.NotEq.Contains(4) ? !x.Column4.Contains(liverEvaluation) : x.Column4.Contains(liverEvaluation) || x.Column4.Count() == 0) && + (x.NotEq.Contains(5) ? !x.Column5.Contains(spleenEvaluation) : x.Column5.Contains(spleenEvaluation) || x.Column5.Count() == 0)) + .Select(x => x.Column6.FirstOrDefault()) + .FirstOrDefault(); + return result ?? string.Empty; + } + + #endregion + + /// + /// 保留小数 + /// + /// + /// + /// + public string ReserveDecimal (decimal answer, int digitPlaces) + { + return decimal.Round(answer, digitPlaces).ToString(); + } + + #region 脾脏评估 + + ///// + ///// 获取脾脏评估 + ///// + ///// + ///// + //public async Task GetSplenicEvaluation(ReadingCalculateDto inDto) + //{ + + //} + #endregion + + #endregion + + } + +} diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs index 543d30833..e13ced660 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/PCWG3CalculateService.cs @@ -178,6 +178,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate CustomUnit=x.CustomUnit, Unit = x.Unit, ReportLayType=ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 分组 @@ -204,6 +206,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs index 289356dd4..51cc7ff8f 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1CalculateService.cs @@ -171,6 +171,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType= ReportLayType.Group, + HighlightAnswer=x.HighlightAnswer, + HighlightAnswerList=x.HighlightAnswerList, }).ToList(); // 分组 @@ -196,6 +198,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1_BMCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1_BMCalculateService.cs index d19fb0e0b..e0c335572 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1_BMCalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/RECIST1Point1_BMCalculateService.cs @@ -167,6 +167,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType= ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 分组 @@ -192,6 +194,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs b/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs index 6d08584c1..55cfa4e3d 100644 --- a/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs +++ b/IRaCIS.Core.Application/Service/ReadingCalculate/SelfDefineCalculateService.cs @@ -175,6 +175,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Group, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 分组 @@ -203,6 +205,8 @@ namespace IRaCIS.Core.Application.Service.ReadingCalculate Unit = x.Unit, CustomUnit=x.CustomUnit, ReportLayType = ReportLayType.Question, + HighlightAnswer = x.HighlightAnswer, + HighlightAnswerList = x.HighlightAnswerList, }).ToList(); // 问题 diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs index 957bdba54..2665746ff 100644 --- a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs +++ b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteSurveyService.cs @@ -117,6 +117,10 @@ namespace IRaCIS.Core.Application.Contracts } else //验证码正确 并且 没有超时 { + + //删除验证码历史记录 + await _repository.BatchDeleteAsync(t => t.Id == verificationRecord.Id); + var dockerInfo = await _repository.Where(t => t.EMail == inDto.EmailOrPhone || t.Phone == inDto.EmailOrPhone).FirstOrDefaultAsync(); if (dockerInfo != null) @@ -192,6 +196,9 @@ namespace IRaCIS.Core.Application.Contracts } else { + //删除验证码历史记录 + await _repository.BatchDeleteAsync(t => t.Id == verifyRecord.Id); + //验证码正确 不处理 } @@ -611,48 +618,8 @@ namespace IRaCIS.Core.Application.Contracts } - var builder = new BodyBuilder(); - var trialInfo = await _repository.FirstOrDefaultAsync(t => t.Id == trialSiteSubmitBackCommand.TrialId); - - var siteInfo = await _trialSiteRepository.FirstOrDefaultAsync(t => t.TrialId == trialSiteSubmitBackCommand.TrialId && t.Id == survey.TrialSiteId, true); - - - //主题 - // $"[来自展影IRC] [{trialInfo.ResearchProgramNo}] 关于中心调研审批的提醒"; - messageToSend.Subject = _localizer["TrialSiteSurvey_IRCNotification", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN, trialInfo.ResearchProgramNo]; - - var pathToFile = _hostEnvironment.WebRootPath - + Path.DirectorySeparatorChar.ToString() - + "EmailTemplate" - + Path.DirectorySeparatorChar.ToString() - + (_userInfo.IsEn_Us ? "TrialSiteSurveyReject_US.html" : "TrialSiteSurveyReject.html"); - - - using (StreamReader SourceReader = System.IO.File.OpenText(pathToFile)) - { - var templateInfo = SourceReader.ReadToEnd(); - - templateInfo = templateInfo.Replace("{company}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyName : _systemEmailConfig.CompanyNameCN) - .Replace("{company abbreviation}", _userInfo.IsEn_Us ? _systemEmailConfig.CompanyShortName : _systemEmailConfig.CompanyShortNameCN); - - builder.HtmlBody = string.Format(templateInfo, - (user == null ? survey.UserName : user.LastName + "/ " + user.FirstName), - trialInfo.TrialCode, - trialInfo.ResearchProgramNo, - trialInfo.ExperimentName, - siteInfo.TrialSiteCode, - siteInfo.TrialSiteAliasName, - survey.LatestBackReason, - trialSiteSubmitBackCommand.RouteUrl, - (survey.State == TrialSiteSurveyEnum.ToSubmit ? "inline - block" : "none") - - ); - } - messageToSend.Body = builder.ToMessageBody(); - - - await _IMailVerificationService.SiteSurveyRejectEmail(messageToSend); + await _IMailVerificationService.SiteSurveyRejectEmail(messageToSend, survey , trialSiteSubmitBackCommand.RouteUrl, user); await _trialSiteSurveyRepository.SaveChangesAsync(); diff --git a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs index 1a842aad9..1174921f8 100644 --- a/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs +++ b/IRaCIS.Core.Application/Service/SiteSurvey/TrialSiteUserSurveyService.cs @@ -95,7 +95,7 @@ namespace IRaCIS.Core.Application.Contracts var verifyExp1 = new EntityVerifyExp() { VerifyExp = u => u.UserTypeId == addOrEditTrialSiteUserSurvey.UserTypeId && u.Email == addOrEditTrialSiteUserSurvey.Email && u.TrialSiteSurveyId == addOrEditTrialSiteUserSurvey.TrialSiteSurveyId, - VerifyMsg = "同一邮箱同一用户类型,生成账号的数据只允许存在一条!", + VerifyMsg = _localizer["TrialSiteUser_SingleAccountPerEmailAndUserType"],// "同一邮箱同一用户类型,生成账号的数据只允许存在一条!", IsVerify = addOrEditTrialSiteUserSurvey.IsGenerateAccount }; diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs new file mode 100644 index 000000000..cd9cbd134 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/DicomAEViewModel.cs @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:37 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +namespace IRaCIS.Core.Application.ViewModel +{ + /// DicomAEView 列表视图模型 + public class DicomAEView : DicomAEAddOrEdit + { + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + public DateTime? LatestTestTime { get; set; } + + + //public bool IsPACSConnect { get; set; } + + //public bool IsTrialPACSConfirmed { get; set; } + + } + + ///DicomAEQuery 列表查询参数模型 + public class DicomAEQuery : PageInput + { + public Guid? TrialId { get; set; } + + public string? CalledAE { get; set; } + + public string? IP { get; set; } + + + public int? Port { get; set; } + + + public string? Modality { get; set; } + + public string? Description { get; set; } + } + + /// DicomAEAddOrEdit 列表查询参数模型 + public class DicomAEAddOrEdit + { + public Guid? Id { get; set; } + + [NotDefault] + public Guid TrialId { get; set; } + + public string CalledAE { get; set; } = string.Empty; + public string IP { get; set; } = string.Empty; + public int? Port { get; set; } + public string Modality { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; + + + public bool IsPACSConnect { get; set; } + + public bool? IsTestOK { get; set; } + } + + public class TestAECommand + { + public string CalledAE { get; set; } = string.Empty; + public string IP { get; set; } = string.Empty; + public int Port { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs index bb5102c50..c833214ef 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/PersonalWorkstationViewModel.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using System.Web; using IRaCIS.Application.Contracts; +using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Domain.Share; namespace IRaCIS.Core.Application.Contracts @@ -289,6 +290,7 @@ namespace IRaCIS.Core.Application.Contracts public string Sponsor { get; set; } + [DictionaryTranslateAttribute("TrialStatusEnum")] public string TrialStatusStr { get; set; } #region PM diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs index f88b288dc..6cb000dfb 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialConfigDTO.cs @@ -224,6 +224,10 @@ namespace IRaCIS.Core.Application.Contracts public TrialQCProcess QCProcessEnum { get; set; } = TrialQCProcess.DoubleAudit; public bool IsImageConsistencyVerification { get; set; } = true; + + public bool IsPACSConnect { get; set; } + + public bool IsTrialPACSConfirmed { get; set; } } public class TrialTaskConfigView : TrialTaskConfig @@ -313,6 +317,16 @@ namespace IRaCIS.Core.Application.Contracts } + public class TrialPACSConfig + { + [NotDefault] + public Guid TrialId { get; set; } + + public bool IsPACSConnect { get; set; } + + public bool IsTrialPACSConfirmed { get; set; } = true; + } + public class TrialStateChangeDTO { public Guid Id { get; set; } @@ -1106,6 +1120,9 @@ namespace IRaCIS.Core.Application.Contracts [NotDefault] public Guid TrialId { get; set; } + + + public bool IsHandAdd { get; set; } = true; } public class TrialBodyPartView @@ -1114,5 +1131,9 @@ namespace IRaCIS.Core.Application.Contracts public string Name { get; set; } + + public Guid Id { get; set; } + + public bool IsHandAdd { get; set; } } } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs new file mode 100644 index 000000000..568583c26 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialSiteDicomAEViewModel.cs @@ -0,0 +1,60 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:52 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- +using System; +using IRaCIS.Core.Domain.Share; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +namespace IRaCIS.Core.Application.ViewModel +{ + /// TrialSiteDicomAEView 列表视图模型 + public class TrialSiteDicomAEView : TrialSiteDicomAEAddOrEdit + { + + public Guid UpdateUserId { get; set; } + public Guid? DeleteUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime UpdateTime { get; set; } + + } + + ///TrialSiteDicomAEQuery 列表查询参数模型 + public class TrialSiteDicomAEQuery /*: PageInput*/ + { + [NotDefault] + + public Guid TrialSiteId { get; set; } + + public string? CallingAE { get; set; } + + public string? IP { get; set; } + + public string? Port { get; set; } + + public string? Description { get; set; } + + } + + /// TrialSiteDicomAEAddOrEdit 列表查询参数模型 + public class TrialSiteDicomAEAddOrEdit + { + public Guid? Id { get; set; } + public Guid TrialId { get; set; } + + + public Guid TrialSiteId { get; set; } + public string CallingAE { get; set; } + public string IP { get; set; } + public string Port { get; set; } + public string Description { get; set; } + + //public bool IsDeleted { get; set; } + } + + +} + + diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs index a25cf7e19..93469cbcf 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/TrialViewModel.cs @@ -159,6 +159,7 @@ namespace IRaCIS.Application.Contracts //public int? StudyCount { get; set; } = 0; //public int? SiteCount { get; set; } = 0; + public int? UserFeedBackUnDealedCount { get; set; } } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs index a3cd5fa36..a6d85a694 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/DTO/UserTrialViewModel.cs @@ -259,7 +259,7 @@ namespace IRaCIS.Application.Contracts //public string ContactPhone { get; set; } = String.Empty; //public string Address { get; set; } = String.Empty; - + public List CallingAEList { get; set; } public List UserNameList { get; set; } = new List(); public int? VisitCount { get; set; } @@ -321,7 +321,7 @@ namespace IRaCIS.Application.Contracts public class TrialUserScreeningDTO : TrialUserAddCommand { - public int Sex { get; set; } // 1-男 2-女 + public int? Sex { get; set; } // 1-男 2-女 public string Phone { get; set; } = string.Empty; public string EMail { get; set; } = string.Empty; diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs new file mode 100644 index 000000000..71f56aeb4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/IDicomAEService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:27 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// IDicomAEService + /// + public interface IDicomAEService + { + + Task>> GetDicomAEList(DicomAEQuery inQuery); + + Task AddOrUpdateDicomAE(DicomAEAddOrEdit addOrEditDicomAE); + + Task DeleteDicomAE(Guid dicomAEId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs index 7a06d0fb5..1de70d035 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialConfigService.cs @@ -18,7 +18,7 @@ namespace IRaCIS.Application.Interfaces Task ConfigTrialUrgentInfo(TrialUrgentConfig trialConfig); - + Task ConfigTrialPACSInfo(TrialPACSConfig trialConfig); Task TrialConfigSignatureConfirm(SignConfirmDTO signConfirmDTO); Task AsyncTrialCriterionDictionary(AsyncTrialCriterionDictionaryInDto inDto); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs new file mode 100644 index 000000000..c6c6fc713 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/Interface/ITrialSiteDicomAEService.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:55 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Interfaces +{ + /// + /// ITrialSiteDicomAEService + /// + public interface ITrialSiteDicomAEService + { + + Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery); + + Task AddOrUpdateTrialSiteDicomAE(TrialSiteDicomAEAddOrEdit addOrEditTrialSiteDicomAE); + + Task DeleteTrialSiteDicomAE(Guid trialSiteDicomAEId); + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs index 2159ae68e..936d470b7 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/PersonalWorkstation.cs @@ -880,7 +880,7 @@ namespace IRaCIS.Core.Application // 前序 不存在 未一致性核查未通过的 .Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum)) //前序 不存在 未生成任务的访视 - .Where(t => c.IsAutoCreate ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true) + .Where(t => c.IsAutoCreate == false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true) .Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)) .Count(t => t.IsUrgent), @@ -891,7 +891,7 @@ namespace IRaCIS.Core.Application .Where(t => !t.Subject.SubjectVisitList.Any(sv => sv.CheckState != CheckStateEnum.CVPassed && t.VisitTaskNum > sv.VisitNum)) //前序 不存在 未生成任务的访视 //.WhereIf(g.Key.IsAutoCreate == false, t => !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum)) - .Where(t => c.IsAutoCreate ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true) + .Where(t => c.IsAutoCreate==false ? !t.Subject.SubjectCriteriaEvaluationVisitFilterList.Where(d => d.TrialReadingCriterionId == t.TrialReadingCriterionId).Any(f => f.IsGeneratedTask == false && t.VisitTaskNum > f.SubjectVisit.VisitNum) : true) .Where(y => y.IsFrontTaskNeedSignButNotSign == false && (y.IsNeedClinicalDataSign == false || y.IsClinicalDataSign == true)) .Count(), @@ -1248,7 +1248,8 @@ namespace IRaCIS.Core.Application //} - + //防止生成sql生成GETDATE() 时区导致的问题 + var appDateTimeNow = DateTime.Now; var record = new UserToBeDoneRecord() { @@ -1264,7 +1265,7 @@ namespace IRaCIS.Core.Application SysNoticeUnReadCount= await _systemNoticeRepository.Where(t => t.NoticeUserTypeList.Any(t => t.UserTypeId == _userInfo.UserTypeId) && t.NoticeStateEnum == Domain.Share.Management.SystemNotice_NoticeStateEnum.HavePublished && !t.NoticeUserReadList.Any(t => t.CreateUserId == _userInfo.Id)) - .Where(t => t.EndDate == null || t.EndDate != null && t.EndDate > DateTime.Now) + .Where(t => t.EndDate == null || t.EndDate != null && t.EndDate > appDateTimeNow) .CountAsync(), #region PM diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs index e104e19d7..b130638a7 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialConfigService.cs @@ -22,6 +22,7 @@ using IRaCIS.Core.Domain.Models; using IRaCIS.Application.Contracts; using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors; using Newtonsoft.Json; +using Microsoft.Extensions.Options; namespace IRaCIS.Core.Application { @@ -1152,6 +1153,25 @@ namespace IRaCIS.Core.Application return ResponseOutput.Ok(await _repository.SaveChangesAsync()); } + /// + /// 配置pacs信息 + /// + /// + /// + [HttpPut] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })] + public async Task ConfigTrialPACSInfo(TrialPACSConfig trialConfig) + { + var trialInfo = (await _trialRepository.FirstOrDefaultAsync(t => t.Id == trialConfig.TrialId)).IfNullThrowException(); + trialInfo.IsPACSConnect = trialConfig.IsPACSConnect; + trialInfo.IsTrialPACSConfirmed = trialConfig.IsTrialPACSConfirmed; + trialInfo.UpdateTime = DateTime.Now; + await _trialRepository.SaveChangesAsync(); + + return ResponseOutput.Ok(); + } + + [HttpGet("{trialId:guid}")] public async Task IfTrialCanOngoing(Guid trialId) { @@ -1319,7 +1339,7 @@ namespace IRaCIS.Core.Application public async Task>> GetTrialBodyPartList(Guid trialId) { - var list = await _trialRepository.Where(t => t.Id == trialId).SelectMany(t => t.TrialBodyPartList).Select(t => new TrialBodyPartView() { Code = t.Code, Name = _userInfo.IsEn_Us ? t.Name : t.NameCN }).ToListAsync(); + var list = await _trialRepository.Where(t => t.Id == trialId).SelectMany(t => t.TrialBodyPartList).Select(t => new TrialBodyPartView() { Code = t.Code, Name = _userInfo.IsEn_Us ? t.Name : t.NameCN, Id = t.Id, IsHandAdd = t.IsHandAdd }).ToListAsync(); return ResponseOutput.Ok(list); } @@ -1327,12 +1347,15 @@ namespace IRaCIS.Core.Application public async Task AddOrUpdateTrialBodyPart(AddOrUpdateTrialBodyPartCommand incommand) { - var codeList = await _repository.Where(t => t.TrialId == incommand.TrialId).Select(t => t.Code).ToListAsync(); + var codeList = await _repository.Where(t => t.TrialId == incommand.TrialId) + .WhereIf(incommand.Id != null, t => t.Id != incommand.Id) + .Select(t => t.Code).ToListAsync(); var newString = incommand.Code; // 检查字符串是否在集合中存在,如果存在,则在后面加上一个数字 int count = 1; + while (codeList.Contains(newString)) { newString = $"{newString}{count}"; @@ -1356,12 +1379,18 @@ namespace IRaCIS.Core.Application } - [AllowAnonymous] public async Task GetTrialExtralConfig(Guid trialId) { var extralConfig = _trialRepository.Where(t => t.Id == trialId).Select(t => t.TrialExtraConfigJsonStr).FirstOrDefault() ?? string.Empty; return JsonConvert.DeserializeObject(extralConfig) ?? new TrialExtraConfig(); } + + public async Task GetTrialPacsConfigInfo(Guid trialId, [FromServices] IOptionsMonitor optionsMonitor) + { + var trialCode = await _trialRepository.Where(t => t.Id == trialId).Select(t => t.TrialCode).FirstOrDefaultAsync(); + + return new TrialPacsInfo() { Ip=optionsMonitor.CurrentValue.IP,Port=optionsMonitor.CurrentValue.Port,TrialCalledAE=$"EI{trialCode}" }; + } } } diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs new file mode 100644 index 000000000..9c59315b4 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialDicomAEService.cs @@ -0,0 +1,169 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:31 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +using FellowOakDicom.Network.Client; +using FellowOakDicom.Network; +using IRaCIS.Application.Contracts; +using IRaCIS.Core.Domain.Share; +using DocumentFormat.OpenXml.InkML; +namespace IRaCIS.Core.Application.Service +{ + /// + /// DicomAEService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialDicomAEService : BaseService, IDicomAEService + { + + private readonly IRepository _dicomAERepository; + private readonly IRepository _trialRepository; + + public TrialDicomAEService(IRepository dicomAERepository, IRepository trialRepository) + { + _trialRepository = trialRepository; + _dicomAERepository = dicomAERepository; + } + + [HttpPost] + public async Task>> GetDicomAEList(DicomAEQuery inQuery) + { + + var dicomAEQueryable = _dicomAERepository + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) + .WhereIf(inQuery.Port != null, t => t.Port == inQuery.Port) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.CalledAE.Contains(inQuery.CalledAE)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Description), t => t.Description.Contains(inQuery.Description)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Modality), t => t.Modality.Contains(inQuery.Modality)) + .ProjectTo(_mapper.ConfigurationProvider); + + + + + var pageList = await dicomAEQueryable.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(DicomAEView.CalledAE) : inQuery.SortField, inQuery.Asc); + + + return ResponseOutput.Ok(pageList); + } + + + /// + /// 获取项目dicom AE 配置信息,otherinfo里面有IsPACSConnect IsTrialPACSConfirmed + /// + /// + /// + public async Task> GetTrialDicomAE(Guid trialId) + { + var dicomAE = _dicomAERepository.Where(t => t.TrialId == trialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefault(); + var trialConfig = await _trialRepository.Where(t => t.Id == trialId).Select(t => new { t.IsPACSConnect, t.IsTrialPACSConfirmed }).FirstOrDefaultAsync(); + return ResponseOutput.Ok(dicomAE, trialConfig); + + } + + + public async Task AddOrUpdateDicomAE(DicomAEAddOrEdit addOrEditDicomAE) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.IP == addOrEditDicomAE.IP && u.Port == addOrEditDicomAE.Port && u.TrialId == addOrEditDicomAE.TrialId, + + //"不允许添加相同的IP和端口的记录" + VerifyMsg = _localizer["TrialDicomAE_RepeatIPAndPort"] + }; + + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = u => u.CalledAE == addOrEditDicomAE.CalledAE , + + //"AE名称不能与其他项目相同" + VerifyMsg = _localizer["TrialDicomAE_RepeatCalledAE"] + }; + + var verifyExp3 = new EntityVerifyExp() + { + VerifyExp = u => u.TrialId == addOrEditDicomAE.TrialId, + + //"AE名称不能与其他项目相同" + VerifyMsg = "该项目只允许添加一条dicom AE记录(前端对接有bug时出现)" + }; + + //var verifyExp2 = new EntityVerifyExp() + //{ + // VerifyExp = u => u.TrialId == addOrEditDicomAE.TrialId, + + // VerifyMsg = "只允许配置一条记录", + // IsVerify=addOrEditDicomAE.Id==null + //}; + + await _trialRepository.UpdatePartialFromQueryAsync(t => t.Id == addOrEditDicomAE.TrialId, u => new Trial() { IsPACSConnect = addOrEditDicomAE.IsPACSConnect }, true); + + + if (addOrEditDicomAE.IsPACSConnect) + { + // 在此处拷贝automapper 映射 + var entity = await _dicomAERepository.InsertOrUpdateAsync(addOrEditDicomAE, true, verifyExp3, verifyExp1, verifyExp2); + + return ResponseOutput.Ok(entity.Id.ToString()); + } + else + { + return ResponseOutput.Ok(); + } + + + } + + + [HttpDelete("{dicomAEId:guid}")] + public async Task DeleteDicomAE(Guid dicomAEId) + { + var success = await _dicomAERepository.DeleteFromQueryAsync(t => t.Id == dicomAEId, true); + return ResponseOutput.Ok(); + } + + + /// + /// 测试scp server 是否可以连接 + /// + /// + [HttpPost] + public async Task TestSCPServerConnect(TestAECommand inCommand) + { + try + { + var client = DicomClientFactory.Create(inCommand.IP, inCommand.Port, false, "test-callingAE", inCommand.CalledAE); + + client.NegotiateAsyncOps(); + + await client.AddRequestAsync(new DicomCEchoRequest()); + + // 创建一个超时任务,设置超时时间为1秒 + var timeoutTask = Task.Delay(TimeSpan.FromSeconds(3)); + + // 发送 DICOM 请求 + var sendTask = client.SendAsync(); + + // 等待任务完成,若超时任务先完成则抛出超时异常 + if (await Task.WhenAny(sendTask, timeoutTask) == timeoutTask) + { + throw new TimeoutException("DICOM 请求超时。"); + } + return true; + } + catch (Exception ex) + { + return false; + } + + + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs index 0629f5579..9e060eb76 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialMaintenanceService.cs @@ -121,8 +121,9 @@ namespace IRaCIS.Application.Services { var trialType = _trialRepository.Where(t => t.Id == trialUserQuery.TrialId).Select(t => t.TrialType).FirstOrDefault(); - var userTypeEnums = new List() { UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA, UserTypeEnum.MW }; - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) + var userTypeEnums = new List() { UserTypeEnum.ClinicalResearchCoordinator, UserTypeEnum.CRA, UserTypeEnum.IQC, UserTypeEnum.APM, UserTypeEnum.MIM, UserTypeEnum.QA, UserTypeEnum.MW, UserTypeEnum.MC }; + + //if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.SuperAdmin) { userTypeEnums.Add(UserTypeEnum.ProjectManager); } @@ -138,7 +139,7 @@ namespace IRaCIS.Application.Services .WhereIf(trialType == TrialType.NoneOfficial, t => t.IsTestUser == true || (t.IsTestUser == false && t.IsZhiZhun)) - + .Where(t=>userTypeEnums.Contains(t.UserTypeEnum)) .WhereIf(!string.IsNullOrWhiteSpace(trialUserQuery.UserRealName), t => (t.FullName).Contains(trialUserQuery.UserRealName)) diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs new file mode 100644 index 000000000..5ed3452e0 --- /dev/null +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/TrialSiteDicomAEService.cs @@ -0,0 +1,85 @@ +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:58 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +//-------------------------------------------------------------------- + +using IRaCIS.Core.Domain.Models; +using Microsoft.AspNetCore.Mvc; +using IRaCIS.Core.Application.Interfaces; +using IRaCIS.Core.Application.ViewModel; +namespace IRaCIS.Core.Application.Service +{ + /// + /// TrialSiteDicomAEService + /// + [ApiExplorerSettings(GroupName = "Trial")] + public class TrialSiteDicomAEService : BaseService, ITrialSiteDicomAEService + { + + private readonly IRepository _trialSiteDicomAERepository; + + public TrialSiteDicomAEService(IRepository trialSiteDicomAERepository) + { + _trialSiteDicomAERepository = trialSiteDicomAERepository; + } + + [HttpPost] + public async Task> GetTrialSiteDicomAEList(TrialSiteDicomAEQuery inQuery) + { + + var trialSiteDicomAEQueryable = + + _trialSiteDicomAERepository.Where(t=>t.TrialSiteId==inQuery.TrialSiteId) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.IP), t => t.IP.Contains(inQuery.IP)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Port), t => t.Port.Contains(inQuery.Port)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Description), t => t.Description.Contains(inQuery.Description)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.CallingAE.Contains(inQuery.CallingAE)) + .ProjectTo(_mapper.ConfigurationProvider); + + //var pageList = await trialSiteDicomAEQueryable + //.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(TrialSiteDicomAEView.Id) : inQuery.SortField, + //inQuery.Asc); + + var list = await trialSiteDicomAEQueryable.ToListAsync(); + + return list; + } + + + public async Task AddOrUpdateTrialSiteDicomAE(TrialSiteDicomAEAddOrEdit addOrEditTrialSiteDicomAE) + { + var verifyExp1 = new EntityVerifyExp() + { + VerifyExp = u => u.CallingAE == addOrEditTrialSiteDicomAE.CallingAE && u.TrialId == addOrEditTrialSiteDicomAE.TrialId + && u.TrialSiteId == addOrEditTrialSiteDicomAE.TrialSiteId, + + //"不允许添加相同的记录" + VerifyMsg = _localizer["TrialSiteDicomAE_RepeatRecord"] + }; + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = u => u.CallingAE == addOrEditTrialSiteDicomAE.CallingAE && u.TrialId == addOrEditTrialSiteDicomAE.TrialId && u.TrialSiteId!=addOrEditTrialSiteDicomAE.TrialSiteId, + + // "其他中心已有该CallingAE" + VerifyMsg = _localizer["TrialSiteDicomAE_OtherSiteExistCallingAE"] + }; + + + var entity = await _trialSiteDicomAERepository.InsertOrUpdateAsync(addOrEditTrialSiteDicomAE, true, verifyExp1, verifyExp2); + + return ResponseOutput.Ok(entity.Id.ToString()); + + } + + + [HttpDelete("{trialSiteDicomAEId:guid}")] + public async Task DeleteTrialSiteDicomAE(Guid trialSiteDicomAEId) + { + var success = await _trialSiteDicomAERepository.DeleteFromQueryAsync(t => t.Id == trialSiteDicomAEId, true); + return ResponseOutput.Ok(); + } + + + } +} diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs index 2e38d4476..9a3326db6 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig.cs @@ -77,6 +77,9 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.ReviewMode, u => u.MapFrom(s => isEn_Us ? s.ReviewMode.Value : s.ReviewMode.ValueCN)) //.ForMember(d => d.ReviewType, u => u.MapFrom(s => s.ReviewType.Value)) .ForMember(d => d.IsLocked, u => u.MapFrom(s => s.WorkloadList.Any(u => u.DataFrom == (int)WorkLoadFromStatus.FinalConfirm))) + .ForMember(d => d.UserFeedBackUnDealedCount, u => u.MapFrom(s => s.UserFeedBackList.Count(t=>t.State==0))) + + //.ForMember(d => d.SiteCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.TrialSiteUserList.Count(k => k.UserId == userId) : s.TrialSiteList.Count())) //.ForMember(d => d.StudyCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.StudyList.Count(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)) : s.StudyList.Count())) //.ForMember(d => d.SubjectCount, u => u.MapFrom(s => userTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator ? s.SubjectList.Count(t => t.TrialSite.CRCUserList.Any(t => t.UserId == userId)) : s.SubjectList.Count())) @@ -141,7 +144,8 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.UserCount, u => u.MapFrom(s => s.CRCUserList.Count())) .ForMember(d => d.VisitCount, u => u.MapFrom(s => s.SubjectVisitList.Count())) .ForMember(d => d.SubjectCount, u => u.MapFrom(s => s.SubjectList.Count())) - .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))); + .ForMember(d => d.UserNameList, u => u.MapFrom(s => s.CRCUserList.Where(t => t.IsDeleted == false).Select(u => u.User.FullName))) + .ForMember(d => d.CallingAEList, u => u.MapFrom(s => s.TrialSiteDicomAEList.Select(u => u.CallingAE))); //CreateMap(); diff --git a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs index b608fb229..84c15094d 100644 --- a/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs +++ b/IRaCIS.Core.Application/Service/TrialSiteUser/_MapConfig2.cs @@ -45,7 +45,18 @@ namespace IRaCIS.Core.Application.Service CreateMap(); - + + CreateMap(); + CreateMap().ReverseMap(); + + CreateMap(); + + CreateMap(); + + + + + } } diff --git a/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs index fc1155c32..bd0178751 100644 --- a/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs +++ b/IRaCIS.Core.Application/Service/Visit/DTO/ClinicalStudySubjects.cs @@ -2,7 +2,6 @@ using IRaCIS.Core.Infrastructure.Extention; using IRaCIS.Core.Domain.Share; using System.ComponentModel.DataAnnotations; -using Org.BouncyCastle.Bcpg.OpenPgp; namespace IRaCIS.Application.Contracts { 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..05d8d3fcc --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/DTO/PatientViewModel.cs @@ -0,0 +1,1174 @@ +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 + { + [NotDefault] + public Guid TrialId { 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 string? CalledAE { get; set; } + + public DateTime? BeginPushTime { get; set; } + public DateTime? EndPushTime { get; set; } + + public string SubejctCode { get; set; } + + public string TrialSiteKeyInfo { get; set; } + + } + + public class PatientSubjectView : PatientQueryView + { + + public int? StudyCount { get; set; } + + + + public Guid? SubejctId { get; set; } + + public Guid TrialId { get; set; } + + public string? SubjectCode { get; set; } + + public string? TrialSiteCode { get; set; } + + public string? TrialSiteName { get; set; } + + public string? TrialSiteAliasName { 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; } + + [NotDefault] + public Guid SubjectVisitId { get; set; } + + public List SCPStudyIdList { 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 + { + [NotDefault] + public Guid TrialId { get; set; } + public string TrialSiteKeyInfo { get; set; } + + 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 Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + + public string TrialSiteCode { get; set; } + + public string TrialSiteName { get; set; } + + public string TrialSiteAliasName { 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 VisitPatientStudyFilterQuery : PageInput + { + [NotDefault] + public Guid SubjectId { get; set; } + + [NotDefault] + public Guid SubjectVisitId { get; set; } + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + public string? Modalities { get; set; } + + public string? PatientInfo { get; set; } + } + public class VisitPatientStudyFilterView + { + 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 string BodyPartExamined { get; set; } = string.Empty; + public string AccessionNumber { get; set; } = string.Empty; + 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 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 Guid? SubjectVisitId { get; set; } + public string? VisitName { get; set; } + + public string? BlindName { get; set; } = string.Empty; + + + //public SubjectVisitInfo SubejectVisit { get; set; } + + } + + public class SubjectVisitInfo + { + public Guid Id { get; set; } + + public string VisitName { get; set; } + + public decimal VisitNum { get; set; } + public string BlindName { 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..1d98ce922 --- /dev/null +++ b/IRaCIS.Core.Application/Service/Visit/PatientService.cs @@ -0,0 +1,396 @@ +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 _scpStudyRepository; + 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) + { + _scpStudyRepository = studyRepository; + _trialRepository = trialRepository; + _patientRepository = patientRepository; + _subjectRepository = subjectRepository; + _subjectVisitRepository = subjectVisitRepository; + _distributedLockProvider = distributedLockProvider; + } + + + /// + /// scp 影像推送记录表 + /// + /// + /// + [HttpPost] + public async Task>> GetSCPImageUploadList(SCPImageUploadQuery inQuery) + { + var query = _repository.Where(t=>t.TrialId==inQuery.TrialId) + .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) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) + || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) + .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); + } + + + #region 患者检查管理 + + /// + ///影像检查列表-患者为维度组织 + /// + /// + /// + [HttpPost] + public async Task>> GetPatientList(PatientTrialQuery inQuery) + { + + + #region new ok + var query = _patientRepository.Where(t => t.TrialId == inQuery.TrialId) + .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.SubejctCode), t => t.Subject.Code.Contains(inQuery.SubejctCode)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.TrialSiteKeyInfo), t => t.TrialSite.TrialSiteCode.Contains(inQuery.TrialSiteKeyInfo) + || t.TrialSite.TrialSiteAliasName.Contains(inQuery.TrialSiteKeyInfo) || t.TrialSite.TrialSiteName.Contains(inQuery.TrialSiteKeyInfo)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CallingAE), t => t.SCPStudyList.Any(t => t.CallingAE == inQuery.CallingAE)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.CalledAE), t => t.SCPStudyList.Any(t => t.CalledAE == inQuery.CalledAE)) + .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 PatientSubjectView() + { + 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(), + + TrialId = patient.TrialId, + SubejctId = patient.SubjectId, + SubjectCode = patient.Subject.Code, + TrialSiteAliasName = patient.TrialSite.TrialSiteAliasName, + TrialSiteCode = patient.TrialSite.TrialSiteCode, + TrialSiteName = patient.TrialSite.TrialSiteName + + }; + + + + + 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 _scpStudyRepository.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, + + + + + SubjectVisitId = scpStudy.SubjectVisitId, + VisitName = scpStudy.SubjectVisit.VisitName, + BlindName = scpStudy.SubjectVisit.BlindName + }; + + + //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(); + + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField, inQuery.Asc); + + return pageList; + } + + + public async Task> GetDicomCalledAEList(Guid trialId) + { + var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).Select(t => t.CalledAE).Distinct().ToListAsync(); + + return list; + } + + public async Task> GetDicomCallingAEList(Guid trialId) + { + var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).Select(t => t.CallingAE).Distinct().ToListAsync(); + + return list; + } + + public async Task> GetDicomModalityList(Guid trialId) + { + var list = await _scpStudyRepository.Where(t => t.TrialId == trialId).SelectMany(t => t.SeriesList).Select(t => t.Modality).Distinct().ToListAsync(); + + return list; + } + + /// + /// 影像访视上传 检查列表 + /// + /// + /// + [HttpPost] + public async Task> GetVisitPatientStudyFilterList(VisitPatientStudyFilterQuery inQuery) + { + + var trialSiteId = _subjectRepository.Where(t => t.Id == inQuery.SubjectId).Select(t => t.TrialSiteId).FirstOrDefault(); + + var query = from scpStudy in _scpStudyRepository + //未绑定的患者,或者自己已绑定但是未绑定访视的 + .Where(t => t.Patient.SubjectId == null || (t.Patient.SubjectId == inQuery.SubjectId && t.SubjectVisitId == null)) + //中心 + .Where(t => t.TrialSiteId == trialSiteId) + .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)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.PatientInfo), t => t.PatientIdStr.Contains(inQuery.PatientInfo) || t.PatientName.Contains(inQuery.PatientInfo) || t.PatientSex.Contains(inQuery.PatientInfo)) + select new VisitPatientStudyFilterView() + { + 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, + BodyPartExamined = scpStudy.BodyPartExamined, + AccessionNumber = scpStudy.AccessionNumber, + + PatientBirthDate = scpStudy.PatientBirthDate, + PatientAge = scpStudy.PatientAge, + PatientIdStr = scpStudy.PatientIdStr, + PatientName = scpStudy.PatientName, + PatientSex = scpStudy.PatientSex, + }; + + + var pageList = await query.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? nameof(PatientStudySimpleView.StudyTime) : inQuery.SortField, inQuery.Asc); + + return pageList; + } + + + + /// + /// 提交 患者检查和访视的绑定 + /// + /// + /// + [HttpPost] + [UnitOfWork] + [TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })] + public async Task SubmitVisitStudyBinding(SubmitVisitStudyBindingCommand inCommand) + { + + var subjectId = inCommand.SubjectId; + var subjectVisitId = inCommand.SubjectVisitId; + var trialId = inCommand.TrialId; + + + + 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 scpStudyId in inCommand.SCPStudyIdList) + { + + var find = _scpStudyRepository.Where(t => t.Id == scpStudyId).Select(t => new { SCPStudy = t, t.SeriesList, t.InstanceList }).FirstOrDefault(); + + if (find != null) + { + + var newStuty = _mapper.Map(find.SCPStudy); + + await _repository.AddAsync(newStuty); + + newStuty.SeqId = Guid.Empty; + newStuty.Code = currentNextCodeInt; + newStuty.StudyCode = AppSettings.GetCodeStr(currentNextCodeInt, nameof(DicomStudy)); + newStuty.IsFromPACS = true; + newStuty.TrialId = trialId; + newStuty.SubjectId = subjectId; + newStuty.SubjectVisitId = subjectVisitId; + + var newSeriesList = _mapper.Map>(find.SeriesList); + + foreach (var series in newSeriesList) + { + + series.SeqId = Guid.Empty; + series.TrialId = trialId; + series.SubjectId = subjectId; + series.SubjectVisitId = subjectVisitId; + } + + await _repository.AddRangeAsync(newSeriesList); + + var newInstanceList = _mapper.Map>(find.InstanceList); + + foreach (var instance in newInstanceList) + { + instance.SeqId = Guid.Empty; + instance.TrialId = trialId; + instance.SubjectId = subjectId; + instance.SubjectVisitId = subjectVisitId; + + } + await _repository.AddRangeAsync(newInstanceList); + } + + currentNextCodeInt++; + + await _repository.BatchUpdateAsync(t => t.Id == find.SCPStudy.PatientId, u => new SCPPatient() { SubjectId = subjectId }); + await _repository.BatchUpdateAsync(t => t.Id == scpStudyId, u => new SCPStudy() { SubjectVisitId = subjectVisitId }); + + + } + + + } + + + + await _repository.SaveChangesAsync(); + + + return ResponseOutput.Ok(); + } + + #endregion + + + + + + + + + + + + + + + + } + + + +} diff --git a/IRaCIS.Core.Application/Service/Visit/SubjectService.cs b/IRaCIS.Core.Application/Service/Visit/SubjectService.cs index 0f56423cb..cb21db1dc 100644 --- a/IRaCIS.Core.Application/Service/Visit/SubjectService.cs +++ b/IRaCIS.Core.Application/Service/Visit/SubjectService.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Authorization; using IRaCIS.Core.Application.Auth; using IRaCIS.Core.Domain.Models; +using IRaCIS.Core.Infrastructure; namespace IRaCIS.Application.Services { @@ -108,27 +109,27 @@ namespace IRaCIS.Application.Services } /// 分页获取受试者列表[New] - /// /// state:1-访视中,2-出组。0-全部 + /// /// state:1-访视中,2-出组。0-全部 [HttpPost] - public async Task>> GetSubjectList(SubjectQueryParam param) + public async Task>> GetSubjectList(SubjectQueryParam inQuery) { - var subjectQuery = _subjectRepository.Where(u => u.TrialId == param.TrialId) - .WhereIf(!string.IsNullOrWhiteSpace(param.Code), t => t.Code.Contains(param.Code)) - .WhereIf(!string.IsNullOrWhiteSpace(param.Name), t => t.ShortName.Contains(param.Name)) - .WhereIf(!string.IsNullOrWhiteSpace(param.Sex), t => t.Sex.Contains(param.Sex)) - .WhereIf(param.Status != null, t => t.Status == param.Status) + var subjectQuery = _subjectRepository.Where(u => u.TrialId == inQuery.TrialId) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Code), t => t.Code.Contains(inQuery.Code)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Name), t => t.ShortName.Contains(inQuery.Name)) + .WhereIf(!string.IsNullOrWhiteSpace(inQuery.Sex), t => t.Sex.Contains(inQuery.Sex)) + .WhereIf(inQuery.Status != null, t => t.Status == inQuery.Status) - .WhereIf(param.TrialSiteId != null, t => t.TrialSiteId == param.TrialSiteId) + .WhereIf(inQuery.TrialSiteId != null, t => t.TrialSiteId == inQuery.TrialSiteId) // CRC 只负责他管理site的受试者 .WhereIf(_userInfo.UserTypeEnumInt == (int)UserTypeEnum.ClinicalResearchCoordinator || _userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA, t => t.TrialSite.CRCUserList.Any(t => t.UserId == _userInfo.Id)) .ProjectTo(_mapper.ConfigurationProvider) - .WhereIf(param.IsMissingImages != null, t => t.IsMissingImages == param.IsMissingImages); + .WhereIf(inQuery.IsMissingImages != null, t => t.IsMissingImages == inQuery.IsMissingImages); - var pageList = await subjectQuery.ToPagedListAsync(param.PageIndex, param.PageSize, param.SortField == string.Empty ? "Code" : param.SortField, param.Asc); + var pageList = await subjectQuery.ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, inQuery.SortField == string.Empty ? "Code" : inQuery.SortField, inQuery.Asc); - var trialConfig = await _repository.Where(t => t.Id == param.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); + var trialConfig = await _repository.Where(t => t.Id == inQuery.TrialId).ProjectTo(_mapper.ConfigurationProvider).FirstOrDefaultAsync().IfNullThrowException(); return ResponseOutput.Ok(pageList, trialConfig); diff --git a/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs index 5d435d1e2..071d8bf8b 100644 --- a/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs +++ b/IRaCIS.Core.Application/Service/Visit/SubjectVisitService.cs @@ -584,6 +584,7 @@ namespace IRaCIS.Core.Application.Services SeriesId = x.SeriesId, StudyId = x.StudyId, InstanceId = x.InstanceId, + NumberOfFrames=x.NumberOfFrames, }).ToListAsync(); @@ -595,6 +596,7 @@ namespace IRaCIS.Core.Application.Services SeriesId = x.SeriesId, StudyId = x.StudyId, InstanceId = x.InstanceId, + NumberOfFrames = x.NumberOfFrames, }).ToListAsync(); rowInfoList.AddRange(customoList); @@ -635,7 +637,8 @@ namespace IRaCIS.Core.Application.Services HtmlPath = k.HtmlPath, Path = k.Path, InstanceNumber = k.InstanceNumber, - + StudyId= k.StudyId, + SeriesId= k.SeriesId, }).ToListAsync(); item.InstanceInfoList.ForEach(x => diff --git a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs index 35a01056d..9909f3eba 100644 --- a/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Visit/_MapConfig.cs @@ -112,6 +112,20 @@ namespace IRaCIS.Core.Application.Service .ForMember(d => d.InstanceInfoList, u => u.MapFrom(s => s.InstanceList)); CreateMap(); + + + CreateMap() + .ForMember(d => d.TrialSiteCode, u => u.MapFrom(s => s.TrialSite.TrialSiteCode)) + .ForMember(d => d.TrialSiteAliasName, u => u.MapFrom(s => s.TrialSite.TrialSiteAliasName)) + .ForMember(d => d.TrialSiteName, u => u.MapFrom(s => s.TrialSite.TrialSiteName)) +; + + + CreateMap(); + CreateMap(); + CreateMap(); + + } } diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index 2a4c7eb64..6d617ec9c 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -1,7 +1,5 @@ using Aliyun.OSS; -using BeetleX; -using BeetleX.BNR; -using Castle.DynamicProxy.Generators.Emitters.SimpleAST; +using DocumentFormat.OpenXml.Wordprocessing; using IP2Region.Net.XDB; using IRaCIS.Application.Contracts; using IRaCIS.Core.Application.Contracts; @@ -21,16 +19,15 @@ using Microsoft.Extensions.Options; using MiniExcelLibs; using Minio; using Minio.DataModel.Args; -using NPOI.HPSF; -using NPOI.POIFS.Crypt; +using NPOI.XWPF.UserModel; using SharpCompress.Common; using Spire.Doc; using System.Linq.Expressions; using System.Reflection.Metadata; -using System.Security.AccessControl; -using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; +using Xceed.Document.NET; +using Xceed.Words.NET; namespace IRaCIS.Application.Services @@ -72,6 +69,30 @@ namespace IRaCIS.Application.Services //_cache = cache; } + /// + /// 清理一致性分析任务 + /// + /// + /// + public async Task DeleteConsistentDate(Guid trialReadingCriterionId) + { + + + var consistentSubjectIdList = _repository.Where(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == true).Select(t => t.SubjectId).ToList(); + + await _repository.BatchDeleteAsync(t => t.TrialReadingCriterionId == trialReadingCriterionId); + + await _repository.BatchDeleteAsync(t => t.TrialReadingCriterionId == trialReadingCriterionId && t.IsAnalysisCreate == true); + + + await _repository.BatchDeleteAsync(t => consistentSubjectIdList.Contains(t.ReadingConsistentClinicalData.SubjectId)); + + await _repository.BatchDeleteAsync(t => consistentSubjectIdList.Contains(t.SubjectId)); + + + await _repository.SaveChangesAsync(); + return ResponseOutput.Ok(); + } public async Task TestEFcore8() @@ -120,11 +141,14 @@ namespace IRaCIS.Application.Services public async Task TestMinIO([FromServices] IOSSService oSSService) { - await oSSService.UploadToOSSAsync("C:\\Users\\Administrator\\Desktop\\TrialSiteUserImportTemplate.xlsx", "myfolder"); + var str = await oSSService.GetSignedUrl("/01000000-c0a8-0242-1c98-08dc7ebcd37d/Read/01000000-c0a8-0242-1c98-08dc7ebcd37d/Visit/1716872544006_1716872544003.png"); - await oSSService.DownLoadFromOSSAsync("/myfolder/TrialSiteUserImportTemplate.xlsx", "C:\\Users\\Administrator\\Desktop\\aws.xlsx"); - return ResponseOutput.Ok(); + //await oSSService.UploadToOSSAsync("C:\\Users\\Administrator\\Desktop\\TrialSiteUserImportTemplate.xlsx", "myfolder"); + + //await oSSService.DownLoadFromOSSAsync("/myfolder/TrialSiteUserImportTemplate.xlsx", "C:\\Users\\Administrator\\Desktop\\aws.xlsx"); + + return ResponseOutput.Ok(str); } [AllowAnonymous] @@ -175,82 +199,32 @@ namespace IRaCIS.Application.Services public async Task Get() { - //Expression> visitTaskLambda = x => x.TrialId == Guid.Empty && x.SubjectId == Guid.Empty && x.TrialReadingCriterionId == Guid.Empty; + await _repository.Where(t => t.SubjectId == Guid.Empty).Include(t => t.ReadingClinicalDataPDFList).ExecuteDeleteAsync(); - //var visitTaskIdQueryable = _visitTaskRepositoryy.Where(visitTaskLambda).Where(t => t.Subject.SubjectVisitTaskList.AsQueryable().Where(visitTaskLambda).Any(c => c.IsNeedClinicalDataSign == true && c.IsClinicalDataSign == false && c.VisitTaskNum < t.VisitTaskNum)).Select(t => t.Id); + // Generate RSA keys + var keyPair = RSAHelper.GenerateRSAKeyPair(2048); - //await _visitTaskRepositoryy.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable.Contains(t.Id), u => new VisitTask() - //{ - // IsFrontTaskNeedSignButNotSign = true - //}); + // Export the public and private keys to PEM format + string publicKey = RSAHelper.ExportPublicKey(keyPair.Public); + string privateKey = RSAHelper.ExportPrivateKey(keyPair.Private); + Console.WriteLine("Public Key:"); + Console.WriteLine(publicKey); + Console.WriteLine("\nPrivate Key:"); + Console.WriteLine(privateKey); - //var a = ((Decimal)1.00).ToString().TrimEnd(new char[] { '.', '0' }); - //var b = ((Decimal)1.01).ToString().TrimEnd(new char[] { '.', '0' }); - //var c = ((Decimal)100).ToString().TrimEnd(new char[] { '.', '0' }); - //var subject1 = Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391693"); - //var subject2 = Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391694"); + // Data to encrypt + string dataToEncrypt = "Hello, RSA!"; + Console.WriteLine("\nOriginal Data: " + dataToEncrypt); - // var subjectList = new List() { Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391693") , - // Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391694") , - // Guid.Parse("431D0C58-ABC5-4166-B9BC-08DA0E391695") - // }; + // Encrypt the data + var encryptedData = RSAHelper.Encrypt(publicKey, dataToEncrypt); + Console.WriteLine("\nEncrypted Data: " + encryptedData); - //string[] citys = new string[] { "广州", "深圳", "上海", "北京" }; - //foreach (var item in subjectList) - //{ - // Console.WriteLine(await BNRFactory.Default.Create($"[CN:{item}][N:[CN:{item}]/0000000]")); - //} - //foreach (var item in subjectList) - //{ - // Console.WriteLine(await BNRFactory.Default.Create($"[N:[CN:{item}]/0000000]")); - //} + // Decrypt the data + string decryptedData = RSAHelper.Decrypt(privateKey, encryptedData); + Console.WriteLine("\nDecrypted Data: " + decryptedData); - //foreach (var item in subjectList) - //{ - // Console.WriteLine(await BNRFactory.Default.Create($"[CN:{item}][redis:city/0000000]")); - //} - - //var needAddVisitList = await _repository.Where(t => t.TrialId == Guid.Empty).DistinctBy(t => t.VisitTaskNum).ToListAsync(); - - - //await _repository.BatchUpdateAsync(t => t.Id == Guid.Empty, u => new VisitTask() - //{ - // SuggesteFinishedTime = u.IsUrgent ? DateTime.Now.AddDays(2) : DateTime.Now.AddDays(7), - - // Code = u.Code + 1 - //}); - - //var query = from item1 in _repository.Where() - // join item2 in _repository.Where() on item1.ValueType equals item2.ValueType - // select new - // { - // item1.ValueType, - // dd = item2.ValueType - // }; - - //var list2 = query.ToList(); - - //await Task.CompletedTask; - - //var list = await _repository.Where(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74")).ToListAsync(); - - ////await _repository.BatchDeleteAsync(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74")); - - //await _repository.AddRangeAsync(list, true); - - //await _repository.SaveChangesAsync(); - - //await _repository.BatchUpdateAsync(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74") && t.EntityName== "ClinicalDataTrialSet", t => new DataInspection() { CreateTime= DateTime.Now.AddMonths(-2) } ); - - //await _visitTaskRepositoryy.UpdatePartialFromQueryAsync( Guid.Parse("78360000-3E2C-0016-9B53-08DA6A002040"), c => new VisitTask() { UpdateTime = DateTime.Now }); - - //await _visitTaskRepositoryy.UpdatePartialFromQueryAsync( Guid.Parse("78360000-3E2C-0016-9B53-08DA6A002040"), c => new VisitTask() { UpdateTime = DateTime.Now.AddMinutes(1) }); - - //var a = _userInfo.IsTestUser; - - //var list1 = await _repository.Where().Select(t => t.TranslateValue(t.Value, t.ValueCN, true)).ToListAsync(); - //var list2 = await _repository.Where().Select(t => t.TranslateValue(t.Value, t.ValueCN, false)).ToListAsync(); return "测试自动发布--再次提交"; } @@ -258,27 +232,126 @@ namespace IRaCIS.Application.Services [AllowAnonymous] - public async Task testEmail([FromServices] IWebHostEnvironment env ,string email) + public async Task testDoc([FromServices] IWebHostEnvironment env, string email) { + #region DocX 测试 + //using (DocX document = DocX.Load("C:\\Users\\hang\\Desktop\\test.docx")) + //{ + // // 查找书签 + // var bookmarkCn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == "zh_cn"); + // var bookmarkEn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == "en_us"); - var hiddenEmail = EmailMaskHelper.MaskEmail(email); + // if (bookmarkCn_Start != null && bookmarkEn_Start != null) + // { + // // 获取书签的起始位置 + // int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex; - return hiddenEmail; + // var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex; + + // // // 创建一个要删除段落的列表 + // List paragraphsToRemove = new List(); + + // foreach (var item in document.Paragraphs) + // { + + // //中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的 + // //_userInfo.IsEn_Us? item.EndIndex< bookmarkENStartPos :item.StartIndex>= bookmarkENStartPos + // if (item.StartIndex>= bookmarkENStartPos) + // { + // paragraphsToRemove.Add(item); + + // } + // } + + // foreach (var paragraph in paragraphsToRemove) + // { + // document.RemoveParagraph(paragraph); + // } + + // } + + // // 保存修改 + // document.SaveAs("C:\\Users\\hang\\Desktop\\test1.docx"); + //} + #endregion + + using (FileStream fs = new FileStream("C:\\Users\\hang\\Desktop\\test.docx", FileMode.Open, FileAccess.Read)) + { + XWPFDocument doc = new XWPFDocument(fs); + + + // 查找包含指定书签的段落及其索引 + var bookmarkParagraph = doc.Paragraphs + .FirstOrDefault(p => p.GetCTP().GetBookmarkStartList().Any(b => b.name == "en_us")); + + + if (bookmarkParagraph != null) + { + int bookmarkIndex = doc.Paragraphs.IndexOf(bookmarkParagraph); + + // 删除书签之后的所有段落 + for (int i = doc.Paragraphs.Count - 1; i >= bookmarkIndex; i--) + { + doc.RemoveBodyElement(i); + } + + } + else + { + throw new BusinessValidationFailedException("word 模板没有英文书签"); + } + + // 创建一个要删除段落的列表 + + //XWPFParagraph bookmarkParagraph = null; + + //foreach (var paragraph in doc.Paragraphs) + //{ + + // foreach (var bookmark in paragraph.GetCTP().GetBookmarkStartList()) + // { + // if (bookmark.name == "en_us") + // { + // bookmarkParagraph = paragraph; + // break; + // } + // } + //} + + + + + //// 从书签所在段落开始,删除之前的所有段落 + //for (int i = bookmarkIndex - 1; i >= 0; i--) + //{ + // doc.RemoveBodyElement(i); + //} + + + + using (FileStream outStream = new FileStream("C:\\Users\\hang\\Desktop\\test1.docx", FileMode.Create, FileAccess.Write)) + { + doc.Write(outStream); + } + } + + + return "hiddenEmail"; } [AllowAnonymous] public async Task GetEnvironmentName([FromServices] IWebHostEnvironment env) { - var a = IdentifierHelper.CreateGuid("123456"); + //var a = IdentifierHelper.CreateGuid("123456"); - var k = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("123456")); + //var k = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("123456")); - var c = MD5Helper.Md5("123456"); + //var c = MD5Helper.Md5("123456"); - return new { env.EnvironmentName, EMailConfig = _systemEmailConfig.CurrentValue, BasicConfig = _basicConfig.CurrentValue }; + //return new { env.EnvironmentName, EMailConfig = _systemEmailConfig.CurrentValue, BasicConfig = _basicConfig.CurrentValue }; // Load a document. diff --git a/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs b/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs index 5df0c112c..5159dd58a 100644 --- a/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs +++ b/IRaCIS.Core.Domain.Share/Allocation/AllocationRelation.cs @@ -279,7 +279,7 @@ namespace IRaCIS.Core.Domain.Share Effect = 0, //未生效 - //NotEffect = 1, + NotEffect = 1, // 失效 Adbandon = 3, diff --git a/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs index 1d4ecdf1a..2520324b9 100644 --- a/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs +++ b/IRaCIS.Core.Domain.Share/Common/EmailScenarioEnum.cs @@ -37,19 +37,62 @@ namespace IRaCIS.Core.Domain.Share public enum EmailBusinessScenario { None=-1, + EnrollConfirmed = 1, PDConfirmed = 2, - Trial=3, + //Trial=3, - Reviewer=4, + //Reviewer=4, QCTask = 5, QCQuestion = 6, - ImageQuestion = 7 + ImageQuestion = 7, + + + + MFALogin = 10, + + MFAUnlock = 11, + + + //医生简历信息录入入口登录 + ReviewerLogin=12, + + + + UserResetEmail = 13, + + + SiteSurveyLogin = 14, + + SiteUseOrExternalUserFirstrJoinTrial = 15, + + SiteUserOrExternalUserExistJoinTrial = 16, + + SiteSurveyReject=17, + + DoctorUserFirstJoinTrial=19, + + DoctorUserExistJoinTrial=20, + + + SysCreateUser = 21, + + SysResetPassword = 22, + + //不登陆通过邮箱重置密码 + UnloginUseEmailResetPassword = 23, + + IRImageError=24, + + SysFeedBack=26, + + TrialFeedBack=27 + } diff --git a/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs index 506830ba6..dcdb9c795 100644 --- a/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs +++ b/IRaCIS.Core.Domain.Share/Trial/TrialExpedited.cs @@ -64,6 +64,15 @@ namespace IRaCIS.Core.Domain.Share } + public class TrialPacsInfo + { + public int Port { get; set; } + + public string Ip { get; set; } + + public string TrialCalledAE { get; set; } + } + public class SiteSurveyModifyFiled { public string NeedModifyFiled { get; set; } diff --git a/IRaCIS.Core.Domain/Allocation/VisitTask.cs b/IRaCIS.Core.Domain/Allocation/VisitTask.cs index f1eb0a159..5a45dd3c4 100644 --- a/IRaCIS.Core.Domain/Allocation/VisitTask.cs +++ b/IRaCIS.Core.Domain/Allocation/VisitTask.cs @@ -368,9 +368,14 @@ namespace IRaCIS.Core.Domain.Models public bool IsNeedClinicalDataSign { get; set; } + /// + /// 临床数据是否签名 + /// public bool IsClinicalDataSign { get; set; } - //前序任务需要签名 但是未签名 + /// + /// 前序任务需要签名 但是未签名 + /// public bool IsFrontTaskNeedSignButNotSign { get; set; } [JsonIgnore] @@ -431,5 +436,8 @@ namespace IRaCIS.Core.Domain.Models public int ImageStudyState { get; set; } + [JsonIgnore] + public List UserFeedBackList { get; set; } + } } diff --git a/IRaCIS.Core.Domain/BaseModel/Entity.cs b/IRaCIS.Core.Domain/BaseModel/Entity.cs index 939bf9f13..8482a17d4 100644 --- a/IRaCIS.Core.Domain/BaseModel/Entity.cs +++ b/IRaCIS.Core.Domain/BaseModel/Entity.cs @@ -17,4 +17,52 @@ namespace IRaCIS.Core.Domain.Models abstract TKey Id { get; set; } } + #region 减少实体属性,增加基类 + + public abstract class BaseAuditAddEntity : Entity, IAuditAdd + { + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } + + public abstract class BaseFullAuditEntity : Entity, IAuditUpdate, IAuditAdd + { + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + } + + + public abstract class BaseFullAuditDeleteEntity : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + public Guid? DeleteUserId { get; set; } + public bool IsDeleted { get; set; } + public DateTime? DeletedTime { get; set; } + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + } + + + + public abstract class BaseAuditAddEntityWithUserName : Entity, IAuditAddWithUserName + { + public string CreateUser { get; set; } + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } + } + + public abstract class BaseAuditUpdateEntity : Entity, IAuditUpdate + { + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } + } + + + #endregion + } diff --git a/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs b/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs index 70cd25e2b..4786c8e6d 100644 --- a/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs +++ b/IRaCIS.Core.Domain/BaseModel/IAuditAdd.cs @@ -19,7 +19,9 @@ namespace IRaCIS.Core.Domain.Models public interface IAuditAddWithUserName : IAuditAdd { - string CreateUser { get; set; } + public string CreateUser { get; set; } } + + } diff --git a/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs b/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs index e154ccecc..05d422a16 100644 --- a/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs +++ b/IRaCIS.Core.Domain/BaseModel/IAuditUpdate.cs @@ -5,7 +5,6 @@ namespace IRaCIS.Core.Domain.Models public interface IAuditUpdate where TKey : struct { public TKey UpdateUserId { get; set; } - //string UpdateUserName { get; set; } public DateTime UpdateTime { get; set; } } @@ -13,4 +12,9 @@ namespace IRaCIS.Core.Domain.Models { } + + + + + } diff --git a/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs b/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs index 8cfa535b1..e0549348b 100644 --- a/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs +++ b/IRaCIS.Core.Domain/BaseModel/ISoftDelete.cs @@ -2,16 +2,18 @@ namespace IRaCIS.Core.Domain.Models { - - - public interface ISoftDelete + public interface ISoftDelete where TKey : struct { - bool IsDeleted { get; set; } + public TKey? DeleteUserId { get; set; } + public bool IsDeleted { get; set; } public DateTime? DeletedTime { get; set; } - - public Guid? DeleteUserId { get; set; } } - + public interface ISoftDelete : ISoftDelete + { + + } + + } diff --git a/IRaCIS.Core.Domain/Common/CommonDocument.cs b/IRaCIS.Core.Domain/Common/CommonDocument.cs index 451b0f51c..579fb3da8 100644 --- a/IRaCIS.Core.Domain/Common/CommonDocument.cs +++ b/IRaCIS.Core.Domain/Common/CommonDocument.cs @@ -9,80 +9,30 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace IRaCIS.Core.Domain.Models { - /// - ///CommonDocument - /// - [Table("CommonDocument")] - public class CommonDocument : Entity, IAuditUpdate, IAuditAdd,ISoftDelete - { + /// + ///CommonDocument + /// + [Table("CommonDocument")] + public class CommonDocument : BaseFullAuditDeleteEntity + { + + public string Code { get; set; } = String.Empty; - [Required] - public string Code { get; set; } = String.Empty; + public string Name { get; set; } = String.Empty; - [Required] - public string Name { get; set; } = String.Empty; + public string NameCN { get; set; } = string.Empty; - /// - /// Path - /// - [Required] - public string Path { get; set; } = String.Empty; - - /// - /// CreateTime - /// - [Required] - public DateTime CreateTime { get; set; } - - /// - /// CreateUserId - /// - [Required] - public Guid CreateUserId { get; set; } - - /// - /// UpdateTime - /// - [Required] - public DateTime UpdateTime { get; set; } - - /// - /// UpdateUserId - /// - [Required] - public Guid UpdateUserId { get; set; } - - /// - /// Description - /// - [Required] - public string Description { get; set; } = String.Empty; - - /// - /// IsDeleted - /// - [Required] - public bool IsDeleted { get; set; } - - public DateTime? DeletedTime { get; set; } - - public Guid? DeleteUserId { get; set; } + public string Path { get; set; } = String.Empty; - public CriterionType? CriterionTypeEnum { get; set; } - public CommonDocumentFileType FileTypeEnum { get; set; } - public EmailBusinessScenario BusinessScenarioEnum { get; set; } + public string Description { get; set; } = String.Empty; + + public CriterionType? CriterionTypeEnum { get; set; } + public CommonDocumentFileType FileTypeEnum { get; set; } + public EmailBusinessScenario BusinessScenarioEnum { get; set; } - //[Required] - //public Guid FileTypeId { get; set; } - //public Guid ModuleTypeId { get; set; } + } - - //public Dictionary FileType { get; set; } - //public Dictionary ModuleType { get; set; } - - } - -} +} diff --git a/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs b/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs index 74ab2ac60..613bc008e 100644 --- a/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs +++ b/IRaCIS.Core.Domain/Common/EmailNoticeConfig.cs @@ -69,6 +69,7 @@ namespace IRaCIS.Core.Domain.Models + public SysEmailLevel SystemLevel { get; set; } /// /// 是否区分标准 @@ -122,4 +123,16 @@ namespace IRaCIS.Core.Domain.Models public EmailUserType EmailUserType { get; set; } } + + + public enum SysEmailLevel + { + not_sys=0, + + //系统 不配置角色 + sys_not_role=1, + + //系统需要配置角色的 + sys_Config_role=2 + } } diff --git a/IRaCIS.Core.Domain/Common/ExploreRecommend.cs b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs new file mode 100644 index 000000000..b58cd06b5 --- /dev/null +++ b/IRaCIS.Core.Domain/Common/ExploreRecommend.cs @@ -0,0 +1,55 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 09:26:43 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///ExploreRecommend + /// + [Table("ExploreRecommend")] + public class ExploreRecommend : Entity, IAuditUpdate, IAuditAdd,ISoftDelete + { + public string ExploreType { get; set; } = string.Empty; + + + public string Version { get; set; }=string.Empty; + + + public string Title { get; set; } = string.Empty; + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public DateTime UpdateTime { get; set; } + + + public bool IsDeleted { get; set; } + + + public string DownloadUrl { get; set; } = string.Empty; + + + public string Path { get; set; } = string.Empty; + + public string FileName { get; set; } = string.Empty; + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Common/Internationalization.cs b/IRaCIS.Core.Domain/Common/Internationalization.cs index e4a7be5e9..1f6b63cc7 100644 --- a/IRaCIS.Core.Domain/Common/Internationalization.cs +++ b/IRaCIS.Core.Domain/Common/Internationalization.cs @@ -29,7 +29,9 @@ namespace IRaCIS.Core.Domain.Models public Guid UpdateUserId { get; set; } - + /// + /// 0 1 2 预翻译 已确认 废除 + /// public int State { get; set; } @@ -45,6 +47,16 @@ namespace IRaCIS.Core.Domain.Models public string FrontType { get; set; }=string.Empty; + + public string Module { get; set; } = string.Empty; + //关联版本历史记录表Id + public Guid? PublishLogId { get; set; } + + [JsonIgnore] + public PublishLog PublishLog { get; set; } + + + } } diff --git a/IRaCIS.Core.Domain/Common/PublishLog.cs b/IRaCIS.Core.Domain/Common/PublishLog.cs index 082edaef3..a93c7f9a5 100644 --- a/IRaCIS.Core.Domain/Common/PublishLog.cs +++ b/IRaCIS.Core.Domain/Common/PublishLog.cs @@ -17,48 +17,38 @@ namespace IRaCIS.Core.Domain.Models { - /// - /// Version - /// + [Required] public string Version { get; set; } - /// - /// PublishTime - /// - [Required] - public DateTime PublishTime { get; set; } + public DateTime? PublishTime { get; set; } - /// - /// UpdateContent - /// + [Required] public string UpdateContent { get; set; } - /// - /// CreateTime - /// + [Required] public DateTime CreateTime { get; set; } - /// - /// CreateUserId - /// + [Required] public Guid CreateUserId { get; set; } - /// - /// UpdateUserId - /// + [Required] public Guid UpdateUserId { get; set; } - /// - /// UpdateTime - /// + [Required] public DateTime UpdateTime { get; set; } - - } + + //0 开发中 ,已发布 + public int State { get; set; } + + public bool IsCurrentVersion { get; set; } + + + } } diff --git a/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj b/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj index ac7815099..80a3e8702 100644 --- a/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj +++ b/IRaCIS.Core.Domain/IRaCIS.Core.Domain.csproj @@ -10,6 +10,7 @@ + diff --git a/IRaCIS.Core.Domain/Image/DicomStudy.cs b/IRaCIS.Core.Domain/Image/DicomStudy.cs index e5193846b..2fa295e25 100644 --- a/IRaCIS.Core.Domain/Image/DicomStudy.cs +++ b/IRaCIS.Core.Domain/Image/DicomStudy.cs @@ -89,15 +89,20 @@ namespace IRaCIS.Core.Domain.Models public List ReadingClinicalDataList { get; set; } + [JsonIgnore] + public List ReadingConsistentClinicalDataList { get; set; } - //软删除 - public bool IsDeleted { get; set; } + //软删除 + public bool IsDeleted { get; set; } public DateTime? DeletedTime { get; set; } public Guid? DeleteUserId { get; set; } + public bool IsFromPACS { get; set; } + + } } diff --git a/IRaCIS.Core.Domain/Image/SCPImageUpload.cs b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs new file mode 100644 index 000000000..10e6b4ef0 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPImageUpload.cs @@ -0,0 +1,74 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-05-24 14:31:45 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///SCPImageUpload + /// + [Table("SCPImageUpload")] + public class SCPImageUpload : Entity, IAuditAdd + { + + + + [Required] + public DateTime CreateTime { get; set; } + + + [Required] + public Guid CreateUserId { get; set; } + + + [Required] + public string CallingAE { get; set; }=string.Empty; + + [Required] + public string CalledAE { get; set; } = string.Empty; + + + [Required] + public string CallingAEIP { get; set; } = string.Empty; + + + [Required] + public DateTime StartTime { get; set; } + + [Required] + public DateTime EndTime { get; set; } + + + [Required] + public int FileCount { get; set; } + + + [Required] + public long FileSize { get; set; } + + + public int StudyCount { get; set; } + + + + + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + [JsonIgnore] + + public Trial Trial { get; set; } + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + + } + + +} diff --git a/IRaCIS.Core.Domain/Image/SCPInstance.cs b/IRaCIS.Core.Domain/Image/SCPInstance.cs new file mode 100644 index 000000000..f0eb643f6 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPInstance.cs @@ -0,0 +1,53 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPInstance")] + public class SCPInstance : Entity, IAuditAdd, IAuditUpdate + { + [JsonIgnore] + [ForeignKey("SeriesId")] + public SCPSeries SCPSeries { get; set; } + + [JsonIgnore] + [ForeignKey("StudyId")] + public SCPStudy SCPStudy { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public Guid SeriesId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public string SopInstanceUid { get; set; } + public int InstanceNumber { get; set; } + public DateTime? InstanceTime { get; set; } + public bool CPIStatus { get; set; } + public int ImageRows { get; set; } + public int ImageColumns { get; set; } + public int SliceLocation { get; set; } + + + public string SliceThickness { get; set; } + public int NumberOfFrames { get; set; } + public string PixelSpacing { get; set; } + + public string ImagerPixelSpacing { get; set; } + public string FrameOfReferenceUID { get; set; } + public string WindowCenter { get; set; } + public string WindowWidth { get; set; } + + public bool Anonymize { get; set; } + public string Path { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPPatient.cs b/IRaCIS.Core.Domain/Image/SCPPatient.cs new file mode 100644 index 000000000..0c75de6d4 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPPatient.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPPatient")] + public class SCPPatient : Entity, IAuditUpdate, IAuditAdd + { + public List SCPStudyList { 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 Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + + + public DateTime? EarliestStudyTime { get; set; } + + public DateTime? LatestStudyTime { get; set; } + + public DateTime LatestPushTime { get; set; } + + + public Guid? SubjectId { get; set; } + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + + [JsonIgnore] + public Subject Subject { get; set; } + [JsonIgnore] + public Trial Trial { get; set; } + [JsonIgnore] + public TrialSite TrialSite { get; set; } + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPSeries.cs b/IRaCIS.Core.Domain/Image/SCPSeries.cs new file mode 100644 index 000000000..5c2213c20 --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPSeries.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPSeries")] + public class SCPSeries : Entity, IAuditAdd, IAuditUpdate, ISoftDelete + { + [JsonIgnore] + [ForeignKey("StudyId")] + public SCPStudy SCPStudy { get; set; } + + [JsonIgnore] + public List SCPInstanceList { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + public Guid StudyId { get; set; } + public string StudyInstanceUid { get; set; } + public string SeriesInstanceUid { get; set; } + public int SeriesNumber { get; set; } + public DateTime? SeriesTime { get; set; } + public string Modality { get; set; } + public string Description { get; set; } + public int InstanceCount { get; set; } + public string SliceThickness { get; set; } + + public string ImagePositionPatient { get; set; } + public string ImageOrientationPatient { get; set; } + public string BodyPartExamined { get; set; } + public string SequenceName { get; set; } + public string ProtocolName { get; set; } + public string ImagerPixelSpacing { get; set; } + + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { get; set; } = string.Empty; + + public string BodyPartForEdit { get; set; } = string.Empty; + + public Guid CreateUserId { get; set; } + public DateTime CreateTime { get; set; } = DateTime.Now; + public Guid UpdateUserId { get; set; } + public DateTime UpdateTime { get; set; } = DateTime.Now; + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + public bool IsDeleted {get;set;} + public bool IsReading { get; set; } = true; + + public string ImageResizePath { get; set; }=string.Empty; + + } +} diff --git a/IRaCIS.Core.Domain/Image/SCPStudy.cs b/IRaCIS.Core.Domain/Image/SCPStudy.cs new file mode 100644 index 000000000..2fff33c5f --- /dev/null +++ b/IRaCIS.Core.Domain/Image/SCPStudy.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + [Table("SCPStudy")] + public class SCPStudy : Entity, IAuditUpdate, IAuditAdd, ISoftDelete + { + + + + + + //0 未知 1 单重 2 双重 + public bool IsDoubleReview { get; set; } + + [JsonIgnore] + public List InstanceList { get; set; } + + [JsonIgnore] + public List SeriesList { get; set; } + + + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public Guid SeqId { get; set; } + + [ForeignKey("PatientId")] + [JsonIgnore] + public SCPPatient Patient { get; set; } + public Guid PatientId { get; set; } + + + + + public string StudyInstanceUid { get; set; } = string.Empty; + public DateTime? StudyTime { get; set; } + public string Modalities { get; set; } = string.Empty; + + public string Description { get; set; } = string.Empty; + public int SeriesCount { get; set; } = 0; + public int InstanceCount { get; set; } = 0; + + + public string InstitutionName { get; set; } = string.Empty; + 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 StudyId { get; set; } = string.Empty; + public string AccessionNumber { get; set; } = string.Empty; + public string PatientBirthDate { get; set; } = string.Empty; + public string AcquisitionTime { get; set; } = string.Empty; + public string AcquisitionNumber { get; set; } = string.Empty; + public string TriggerTime { 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; + + + + 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 bool IsDeleted { get; set; } + + public DateTime? DeletedTime { get; set; } + + public Guid? DeleteUserId { get; set; } + + public string CallingAE { get; set; } = string.Empty; + + public string CalledAE { get; set; } = string.Empty; + + public bool IsUploadFinished { get; set; } + + + public Guid TrialId { get; set; } + public Guid TrialSiteId { get; set; } + + public Guid? SubjectVisitId { get; set; } + + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + + [JsonIgnore] + + public Trial Trial { get; set; } + + [JsonIgnore] + public TrialSite TrialSite { get; set; } + + + } +} diff --git a/IRaCIS.Core.Domain/Image/TrialDicomAE.cs b/IRaCIS.Core.Domain/Image/TrialDicomAE.cs new file mode 100644 index 000000000..3a1f963ab --- /dev/null +++ b/IRaCIS.Core.Domain/Image/TrialDicomAE.cs @@ -0,0 +1,52 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-03-22 15:44:11 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///DicomAE + /// + [Table("TrialDicomAE")] + public class TrialDicomAE : Entity, IAuditUpdate, IAuditAdd + { + + public Guid TrialId { get; set; } + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public DateTime UpdateTime { get; set; } + + public string CalledAE { get; set; } = string.Empty; + + public string IP { get; set; } + + + public int Port { get; set; } + + + public string Modality { get; set; } = string.Empty; + + + public string Description { get; set; }=string.Empty; + + + public DateTime? LatestTestTime { get; set; } + + public bool? IsTestOK { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/Management/User.cs b/IRaCIS.Core.Domain/Management/User.cs index 775c56fd5..099b0a9f9 100644 --- a/IRaCIS.Core.Domain/Management/User.cs +++ b/IRaCIS.Core.Domain/Management/User.cs @@ -36,7 +36,7 @@ namespace IRaCIS.Core.Domain.Models public string Phone { get; set; } = string.Empty; public string EMail { get; set; } = string.Empty; - public int Sex { get; set; } + public int? Sex { get; set; } public UserStateEnum Status { get; set; } = UserStateEnum.Enable; public DateTime? LastLoginTime { get; set; } diff --git a/IRaCIS.Core.Domain/Management/UserFeedBack.cs b/IRaCIS.Core.Domain/Management/UserFeedBack.cs new file mode 100644 index 000000000..a9674c176 --- /dev/null +++ b/IRaCIS.Core.Domain/Management/UserFeedBack.cs @@ -0,0 +1,83 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-30 10:39:01 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///UserFeedBack + /// + [Table("UserFeedBack")] + public class UserFeedBack : Entity, IAuditUpdate, IAuditAdd + { + [JsonIgnore] + public Trial Trial { get; set; } + [JsonIgnore] + public Subject Subject { get; set; } + [JsonIgnore] + public SubjectVisit SubjectVisit { get; set; } + [JsonIgnore] + public TrialSite TrialSite { get; set; } + [JsonIgnore] + public User CreateUser { get; set; } + + [JsonIgnore] + + public VisitTask VisitTask { get; set; } + + public Guid? VisitTaskId { get; set; } + + public Guid? SubjectId { get; set; } + + + public Guid? SubjectVisitId { get; set; } + + + public int QuestionType { get; set; } + + public string QuestionDescription { get; set; } + + + public Guid CreateUserId { get; set; } + + + public DateTime CreateTime { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public DateTime UpdateTime { get; set; } + + + public int State { get; set; } + + + public Guid? TrialSiteId { get; set; } + + + public Guid? TrialId { get; set; } + + public string ScreenshotListStr { get; set; } + + // + [NotMapped] + public List ScreenshotList => JsonConvert.DeserializeObject>(ScreenshotListStr); + + + + //public class ScreenshotInfo + //{ + // public string Path { get; set; } + + // public string Name { get; set; } + //} + + } + } diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs index a42e9c86a..b8213b6d9 100644 --- a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingClinicalDataPDF.cs @@ -44,8 +44,16 @@ namespace IRaCIS.Core.Domain.Models /// public Guid CreateUserId { get; set; } + /// + /// 大小 + /// + public int Size { get; set; } = 0; - } + /// + /// 文件类型 + /// + public string Type { get; set; } + } diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalData.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalData.cs new file mode 100644 index 000000000..4de50e36a --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalData.cs @@ -0,0 +1,120 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Collections.Generic; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 一致性分析临床数据 + /// + [Table("ReadingConsistentClinicalData")] + public class ReadingConsistentClinicalData : Entity, IAuditAdd + { + /// + /// 项目ID + /// + public Guid TrialId { get; set; } + + /// + /// 访视Id 或者模块Id + /// + public Guid ReadingId { get; set; } + + public Guid? StudyId { get; set; } + + /// + /// 受试者ID + /// + public Guid SubjectId { get; set; } + + /// + /// 临床数据类型Id + /// + public Guid ClinicalDataTrialSetId { get; set; } + + /// + /// 是否为访视 + /// xiu + public bool IsVisit { get; set; } + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 是否签名 + /// + public bool IsSign { get; set; } + + /// + /// 是否盲化 + /// + public bool? IsBlind { get; set; } = false; + + /// + /// 是否完整 + /// + public bool? IsComplete { get; set; } = true; + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + public int FileCount { get; set; } + + + //临床数据状态 + public ReadingClinicalDataStatus ReadingClinicalDataState { get; set; } + + [JsonIgnore] + [ForeignKey("ClinicalDataTrialSetId")] + + public ClinicalDataTrialSet ClinicalDataTrialSet { get; set; } + + [JsonIgnore] + + public DicomStudy? DicomStudy { get; set; } + + [JsonIgnore] + [ForeignKey("TrialId")] + + public Trial Trial { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingId")] + + public SubjectVisit SubjectVisit { get; set; } + + [JsonIgnore] + [ForeignKey("SubjectId")] + + public Subject Subject { get; set; } + + [JsonIgnore] + [ForeignKey("ReadingId")] + + public ReadModule ReadModule { get; set; } + + /// + /// PDF文件 + /// + [JsonIgnore] + public List ReadingClinicalDataPDFList { get; set; } + + + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalDataPDF.cs b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalDataPDF.cs new file mode 100644 index 000000000..ab442df64 --- /dev/null +++ b/IRaCIS.Core.Domain/Reading/ClinicalData/ReadingConsistentClinicalDataPDF.cs @@ -0,0 +1,65 @@ + + +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace IRaCIS.Core.Domain.Models +{ + /// + /// 一致性分析临床数据 + /// + [Table("ReadingConsistentClinicalDataPDF")] + public class ReadingConsistentClinicalDataPDF : Entity, IAuditAdd + { + [JsonIgnore] + [ForeignKey("ReadingConsistentClinicalDataId")] + public ReadingConsistentClinicalData ReadingConsistentClinicalData { get; set; } + /// + /// 阅片临床数据ID + /// + public Guid ReadingConsistentClinicalDataId { get; set; } + + /// + /// Path + /// + [Required] + public string Path { get; set; } + + /// + /// FileName + /// + [Required] + public string FileName { get; set; } + + + /// + /// 创建时间 + /// + public DateTime CreateTime { get; set; } + + /// + /// 创建人 + /// + public Guid CreateUserId { get; set; } + + /// + /// 大小 + /// + public int Size { get; set; } = 0; + + /// + /// 文件类型 + /// + public string Type { get; set; } + + + } + + + + + + +} diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs index 1de10ce86..bc02ee3b4 100644 --- a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionSystem.cs @@ -217,7 +217,12 @@ namespace IRaCIS.Core.Domain.Models /// public QuestionClassify? QuestionClassify { get; set; } - [JsonIgnore] + /// + /// 高亮问题的答案 + /// + public string HighlightAnswer { get; set; } = "[]"; + + [JsonIgnore] [ForeignKey("GroupId")] public ReadingQuestionSystem GroupInfo { get; set; } @@ -272,6 +277,26 @@ namespace IRaCIS.Core.Domain.Models } + [NotMapped] + public List HighlightAnswerList + { + get + { + + try + { + var result = JsonConvert.DeserializeObject>(this.HighlightAnswer); + return result == null ? new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + } diff --git a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs index 5c315bf9b..7bf00b885 100644 --- a/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs +++ b/IRaCIS.Core.Domain/Reading/ReadingCriterionQuestion/ReadingQuestionTrial.cs @@ -302,10 +302,15 @@ namespace IRaCIS.Core.Domain.Models [ForeignKey("GroupId")] public ReadingQuestionTrial GroupInfo { get; set; } - /// - /// 分页标准 - /// - [ForeignKey("ReadingCriterionPageId")] + /// + /// 高亮问题的答案 + /// + public string HighlightAnswer { get; set; } = "[]"; + + /// + /// 分页标准 + /// + [ForeignKey("ReadingCriterionPageId")] [JsonIgnore] public ReadingCriterionPage ReadingCriterionPage { get; set; } [JsonIgnore] @@ -380,7 +385,27 @@ namespace IRaCIS.Core.Domain.Models } } - } + [NotMapped] + public List HighlightAnswerList + { + get + { + + try + { + var result = JsonConvert.DeserializeObject>(this.HighlightAnswer); + return result == null ? new List() : result; + } + catch (Exception) + { + + return new List(); + } + + } + } + + } public class CalculateInfo diff --git a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingCustomTag.cs b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingCustomTag.cs index 1409e8f3f..88f59ce88 100644 --- a/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingCustomTag.cs +++ b/IRaCIS.Core.Domain/Reading/ReadingFormAnswer/ReadingCustomTag.cs @@ -49,8 +49,10 @@ namespace IRaCIS.Core.Domain.Models /// CreateUserId /// public Guid CreateUserId { get; set; } - - } + + public int? NumberOfFrames { get; set; } + + } } diff --git a/IRaCIS.Core.Domain/Trial/Trial.cs b/IRaCIS.Core.Domain/Trial/Trial.cs index ba3279e52..f2dc183c9 100644 --- a/IRaCIS.Core.Domain/Trial/Trial.cs +++ b/IRaCIS.Core.Domain/Trial/Trial.cs @@ -59,8 +59,8 @@ namespace IRaCIS.Core.Domain.Models public List TrialSiteUserList { get; set; } = new List(); [JsonIgnore] public List ReadModuleList { get; set; } = new List(); - - + [JsonIgnore] + public List UserFeedBackList { get; set; } = new List(); public Guid IndicationTypeId { get; set; } = Guid.Empty; public Guid? PhaseId { get; set; } = Guid.Empty; @@ -400,7 +400,9 @@ namespace IRaCIS.Core.Domain.Models #endregion + public bool IsPACSConnect { get; set; } + public bool IsTrialPACSConfirmed { get; set; } ///// ///// 图像是否有标注 diff --git a/IRaCIS.Core.Domain/Trial/TrialBodyPart.cs b/IRaCIS.Core.Domain/Trial/TrialBodyPart.cs index b69845dca..7dc64d2ab 100644 --- a/IRaCIS.Core.Domain/Trial/TrialBodyPart.cs +++ b/IRaCIS.Core.Domain/Trial/TrialBodyPart.cs @@ -35,5 +35,8 @@ namespace IRaCIS.Core.Domain.Models [JsonIgnore] public Trial Trial { get; set; } + + public bool IsHandAdd { get; set; } + } } diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs index 63da78434..8812dd62b 100644 --- a/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSite.cs @@ -55,7 +55,9 @@ namespace IRaCIS.Core.Domain.Models [JsonIgnore] public List SubjectList { get; set; } + [JsonIgnore] + public List TrialSiteDicomAEList { get; set; } } } \ No newline at end of file diff --git a/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs new file mode 100644 index 000000000..1ef1446f5 --- /dev/null +++ b/IRaCIS.Core.Domain/TrialSiteUser/TrialSiteDicomAE.cs @@ -0,0 +1,63 @@ + +//-------------------------------------------------------------------- +// 此代码由T4模板自动生成 byzhouhang 20210918 +// 生成时间 2024-07-02 16:53:49 +// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +using System; +using IRaCIS.Core.Domain.Share; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +namespace IRaCIS.Core.Domain.Models +{ + /// + ///TrialSiteDicomAE + /// + [Table("TrialSiteDicomAE")] + public class TrialSiteDicomAE : Entity, IAuditUpdate, IAuditAdd + { + + + + public DateTime? DeletedTime { get; set; } + + + public Guid TrialId { get; set; } + + + public Guid UpdateUserId { get; set; } + + + public Guid? DeleteUserId { get; set; } + + + public DateTime CreateTime { get; set; } + + + public Guid CreateUserId { get; set; } + + public bool IsDeleted { get; set; } + + public DateTime UpdateTime { get; set; } + + + public Guid TrialSiteId { get; set; } + + + public string CallingAE { get; set; } + + + public string IP { get; set; } + + + public string Port { get; set; } + + + public string Description { get; set; } + + + + public TrialSite TrialSite { get; set; } + + } + +} diff --git a/IRaCIS.Core.Domain/_Config/_AppSettings.cs b/IRaCIS.Core.Domain/_Config/_AppSettings.cs index 80aed90e8..fac748828 100644 --- a/IRaCIS.Core.Domain/_Config/_AppSettings.cs +++ b/IRaCIS.Core.Domain/_Config/_AppSettings.cs @@ -1,6 +1,7 @@ using IRaCIS.Core.Domain.Models; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.Json; +using System.Collections.Generic; namespace IRaCIS.Core.Domain.Share { @@ -19,7 +20,7 @@ namespace IRaCIS.Core.Domain.Share public int LoginMaxFailCount { get; set; } - public int LoginFailLockMinutes { get; set; } + public int LoginFailLockMinutes { get; set; } public int AutoLoginOutMinutes { get; set; } @@ -35,13 +36,24 @@ namespace IRaCIS.Core.Domain.Share /// public int ReadingRestTimeMin { get; set; } + + /// + /// 是否需要修改密码 + /// + public bool IsNeedChangePassWord { get; set; } + + /// + /// 修改密码的天数 + /// + public int ChangePassWordDays { get; set; } + } public class SystemEmailSendConfig { - public int Port { get; set; } + public int Port { get; set; } - public string Host { get; set; } + public string Host { get; set; } public string FromEmail { get; set; } @@ -66,6 +78,22 @@ namespace IRaCIS.Core.Domain.Share } + public class SystemPacsConfig + { + public int Port { get; set; } + + public string IP { get; set; } + } + + + + public class EncreptResponseOption + { + public bool IsEnable { get; set; } + + public List ApiPathList { get; set; } + } + /// /// 项目基础配置规则 @@ -82,14 +110,13 @@ namespace IRaCIS.Core.Domain.Share public static string NoneDicomStudyCodePrefix { get; set; } - public static string DefaultInternalOrganizationName { get; set; } public static int ImageShareExpireDays { get; set; } = 7; public static string SystemSiteCodePrefix { get; set; } - public static string BlindTaskPrefix { get; set; } + public static string BlindTaskPrefix { get; set; } /// /// 用户默认密码 @@ -104,7 +131,7 @@ namespace IRaCIS.Core.Domain.Share { Path = "appsettings.json", ReloadOnChange = true - }) + }) .Build(); DoctorCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("DoctorCodePrefix"); @@ -112,10 +139,9 @@ namespace IRaCIS.Core.Domain.Share QCChallengeCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("QCChallengeCodePrefix"); NoneDicomStudyCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("NoneDicomStudyCodePrefix"); DicomStudyCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("DicomStudyCodePrefix"); - DefaultPassword= configuration.GetSection("IRaCISBasicConfig").GetValue("DefaultPassword"); + DefaultPassword = configuration.GetSection("IRaCISBasicConfig").GetValue("DefaultPassword"); SystemSiteCodePrefix = configuration.GetSection("IRaCISBasicConfig").GetValue("SystemSiteCodePrefix"); - DefaultInternalOrganizationName = configuration.GetSection("IRaCISBasicConfig").GetValue("DefaultInternalOrganizationName"); ImageShareExpireDays = configuration.GetSection("IRaCISBasicConfig").GetValue("ImageShareExpireDays"); @@ -125,7 +151,7 @@ namespace IRaCIS.Core.Domain.Share //获取实体编码字符串 - public static string GetCodeStr(int codeInt ,string typeStr) + public static string GetCodeStr(int codeInt, string typeStr) { switch (typeStr) { @@ -139,7 +165,7 @@ namespace IRaCIS.Core.Domain.Share case nameof(QCChallenge): - return QCChallengeCodePrefix+ codeInt.ToString("D5"); + return QCChallengeCodePrefix + codeInt.ToString("D5"); case nameof(NoneDicomStudy): @@ -164,5 +190,5 @@ namespace IRaCIS.Core.Domain.Share } - + } \ No newline at end of file diff --git a/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs index ec1e86542..742a51a72 100644 --- a/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs +++ b/IRaCIS.Core.Infra.EFCore/Common/AuditingData.cs @@ -328,6 +328,34 @@ namespace IRaCIS.Core.Infra.EFCore.Common }); } + + // PACS直连 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialSiteDicomAE))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialSiteDicomAE; + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + ObjectRelationParentId=entity.TrialSiteId, + }); + } + + // PACS直连 + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(TrialDicomAE))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as TrialDicomAE; + + + await InsertInspection(entity, type, x => new InspectionConvertDTO() + { + }); + } + //系统标准问题 foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingQuestionSystem))) { @@ -2314,6 +2342,27 @@ namespace IRaCIS.Core.Infra.EFCore.Common }, new { FileCountViewStr = config?.ClinicalDataLevel == ClinicalLevel.Subject && config?.ClinicalUploadType == ClinicalUploadType.Table ? "NA" : entity.FileCount.ToString() }); } + foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingConsistentClinicalData))) + { + var type = GetEntityAuditOpt(item); + + var entity = item.Entity as ReadingConsistentClinicalData; + + var config = await _dbContext.ClinicalDataTrialSet.FindAsync(entity.ClinicalDataTrialSetId); + + await InsertInspection(item.Entity as ReadingConsistentClinicalData, type, x => new InspectionConvertDTO() + { + + IsDistinctionInterface = type == AuditOpt.Update ? true : false, + + SubjectVisitId = x.IsVisit ? x.ReadingId : null, + + ObjectRelationParentId = entity.ClinicalDataTrialSetId, + + //ObjectRelationParentId2 = x.IsVisit == false?x.ReadingId:null + }, new { FileCountViewStr = config?.ClinicalDataLevel == ClinicalLevel.Subject && config?.ClinicalUploadType == ClinicalUploadType.Table ? "NA" : entity.FileCount.ToString() }); + } + //阅片期计划 foreach (var item in entitys.Where(x => x.Entity.GetType() == typeof(ReadingPeriodSet))) { @@ -2870,7 +2919,6 @@ namespace IRaCIS.Core.Infra.EFCore.Common { Unit = (ValueUnit)int.Parse(x.Code), - UnitName = x.Value.ToString(), }).ToList(); //获取表格问题名称 组合成数组 diff --git a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs index c5dd471f2..9985806ed 100644 --- a/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs +++ b/IRaCIS.Core.Infra.EFCore/Context/IRaCISDBContext.cs @@ -21,36 +21,45 @@ using Microsoft.Identity.Client; using EntityFramework.Exceptions.Common; using System.Data; using IRaCIS.Core.Infrastructure; +using System.Reflection.Metadata; namespace IRaCIS.Core.Infra.EFCore { + /// + /// 报错,添加subject 报错,重复添加访视 + /// + //public class IRaCISDBScopedFactory : IDbContextFactory + //{ + + // private readonly IDbContextFactory _pooledFactory; + // private readonly IUserInfo _userInfo; + + // public IRaCISDBScopedFactory(IDbContextFactory pooledFactory,IUserInfo userInfo) + // { + // _pooledFactory = pooledFactory; + // _userInfo = userInfo; + // } + + // public IRaCISDBContext CreateDbContext() + // { + // var context = _pooledFactory.CreateDbContext(); + // context._userInfo = _userInfo; + // return context; + // } + //} + public class IRaCISDBContext : DbContext { - public readonly IUserInfo _userInfo; + private IUserInfo _userInfo; - public readonly ILogger _logger; - // 在控制台 - //public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); - // 调试窗口 - public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddDebug(); }); + private readonly ILogger _logger; public IRaCISDBContext(DbContextOptions options, IUserInfo userInfo, ILogger logger ) : base(options) { - _logger= logger; _userInfo = userInfo; - - } - - - //比数据库上下文构造函数先执行 不能构造函数注入的方式使用配置文件 - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseLoggerFactory(MyLoggerFactory); - - optionsBuilder.UseExceptionProcessor(); - + _logger = logger; } @@ -64,7 +73,7 @@ namespace IRaCIS.Core.Infra.EFCore }); - modelBuilder.Entity().HasQueryFilter(p => p.IsAdditional==false); + modelBuilder.Entity().HasQueryFilter(p => p.IsAdditional == false); modelBuilder.Entity().HasMany(t => t.VisitTaskList).WithOne(t => t.DoctorUser).HasForeignKey(t => t.DoctorUserId).IsRequired(false); @@ -90,11 +99,12 @@ namespace IRaCIS.Core.Infra.EFCore modelBuilder.Entity().HasMany(t => t.TaskInfluenceList).WithOne(s => s.OriginalTask).HasForeignKey(t => t.OriginalTaskId); - modelBuilder.Entity().HasMany(t => t.GlobalVisitResultList).WithOne(s => s.GlobalVisitTask).HasForeignKey(t => t.GlobalTaskId); + modelBuilder.Entity().HasMany(t => t.GlobalVisitResultList).WithOne(s => s.GlobalVisitTask).HasForeignKey(t => t.GlobalTaskId); modelBuilder.Entity().HasMany(t => t.ChildList).WithOne(t => t.Parent); modelBuilder.Entity().HasMany(t => t.EarlierSubjectUserList).WithOne(t => t.OrignalSubjectUser); + modelBuilder.Entity().HasQueryFilter(b => b.Subject.IsDeleted == false); //遍历实体模型手动配置 var typesToRegister = Assembly.GetExecutingAssembly().GetTypes().Where(q => q.GetInterface(typeof(IEntityTypeConfiguration<>).FullName) != null); @@ -208,6 +218,8 @@ namespace IRaCIS.Core.Infra.EFCore //public virtual DbSet ReadingClinicalDataView { get; set; } public virtual DbSet ReadingClinicalDataPDF { get; set; } + public virtual DbSet ReadingConsistentClinicalData { get; set; } + public virtual DbSet ReadingConsistentClinicalDataPDF { get; set; } public virtual DbSet ReadingJudgeInfo { get; set; } @@ -229,18 +241,18 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet CriterionNidusSystem { get; set; } - public virtual DbSet CriterionNidusTrial { get; set; } + public virtual DbSet CriterionNidusTrial { get; set; } - public virtual DbSet ReadingTrialCriterionDictionary { get; set; } + public virtual DbSet ReadingTrialCriterionDictionary { get; set; } - public virtual DbSet ReadingTableQuestionTrial { get; set; } + public virtual DbSet ReadingTableQuestionTrial { get; set; } public virtual DbSet TumorAssessment_RECIST1Point1BM { get; set; } public virtual DbSet TumorAssessment_RECIST1Point1 { get; set; } public virtual DbSet TumorAssessment_IRECIST1Point1 { get; set; } - + public virtual DbSet TrialClinicalDataSetCriterion { get; set; } public virtual DbSet TrialCriterionAdditionalAssessmentType { get; set; } @@ -309,7 +321,7 @@ namespace IRaCIS.Core.Infra.EFCore #region Trial public virtual DbSet Trial { get; set; } - + public virtual DbSet TrialDictionary { get; set; } public virtual DbSet TrialDetail { get; set; } public virtual DbSet UserTrial { get; set; } @@ -323,9 +335,9 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet User { get; set; } - public virtual DbSet UserPassWordLog { get; set; } + public virtual DbSet UserPassWordLog { get; set; } - public virtual DbSet TrialSiteUserSurvey { get; set; } + public virtual DbSet TrialSiteUserSurvey { get; set; } public virtual DbSet TrialSiteEquipmentSurvey { get; set; } public virtual DbSet TrialSiteSurvey { get; set; } @@ -381,8 +393,8 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet ClinicalTableAnswer { get; set; } - public virtual DbSet ReadModuleCriterionFrom { get; set; } - public virtual DbSet ClinicalForm { get; set; } + public virtual DbSet ReadModuleCriterionFrom { get; set; } + public virtual DbSet ClinicalForm { get; set; } #endregion @@ -438,8 +450,8 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet ShortcutKey { get; set; } - public virtual DbSet UserWLTemplate { get; set; } - public virtual DbSet EmailNoticeConfig { get; set; } + public virtual DbSet UserWLTemplate { get; set; } + public virtual DbSet EmailNoticeConfig { get; set; } public virtual DbSet SystemBasicData { get; set; } public virtual DbSet TrialSign { get; set; } @@ -479,6 +491,23 @@ namespace IRaCIS.Core.Infra.EFCore public virtual DbSet TrialBodyPart { get; set; } + public virtual DbSet ExploreRecommend { get; set; } + + public virtual DbSet SCPPatient { get; set; } + public virtual DbSet SCPStudy { get; set; } + public virtual DbSet SCPSeries { get; set; } + public virtual DbSet SCPInstance { get; set; } + public virtual DbSet TrialDicomAE { get; set; } + + + public virtual DbSet TrialSiteDicomAE { get; set; } + + + public virtual DbSet SCPImageUpload { get; set; } + + public virtual DbSet UserFeedBack { get; set; } + + public override async Task SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken()) { // 采用触发器的方式 设置 CreateUserId CreateTime UpdateTime UpdateUserId 稽查实体里面没有这四个字段的值 因为先后顺序的原因 @@ -492,7 +521,7 @@ namespace IRaCIS.Core.Infra.EFCore } catch (UniqueConstraintException ex) { - _logger.LogError(ex.InnerException is null? ex.Message :ex.InnerException?.Message); + _logger.LogError(ex.InnerException is null ? ex.Message : ex.InnerException?.Message); throw new DBSaveFailedException("该唯一键已经存在于数据库中。"); @@ -540,7 +569,7 @@ namespace IRaCIS.Core.Infra.EFCore } } - + public async Task AddAudit() @@ -562,7 +591,7 @@ namespace IRaCIS.Core.Infra.EFCore await auditingData.InsertAddEntitys(entities); } - + } diff --git a/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs index dfed9328a..c1b0133e8 100644 --- a/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs +++ b/IRaCIS.Core.Infra.EFCore/EntityConfigration/StudyConfigration.cs @@ -19,6 +19,13 @@ namespace IRaCIS.Core.Infra.EFCore.EntityConfigration .HasForeignKey(s => new { s.StudyId }) .HasPrincipalKey(c => new { c.Id }); - } + + builder + .HasMany(s => s.ReadingConsistentClinicalDataList) + .WithOne(c => c.DicomStudy) + .HasForeignKey(s => new { s.StudyId }) + .HasPrincipalKey(c => new { c.Id }); + + } } } diff --git a/IRaCIS.Core.Infrastructure/Extention/RandomExtensions.cs b/IRaCIS.Core.Infrastructure/Extention/RandomExtensions.cs new file mode 100644 index 000000000..db395d9b4 --- /dev/null +++ b/IRaCIS.Core.Infrastructure/Extention/RandomExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infrastructure.Extention +{ + public static class RandomExtensions + { + private static Random _random = new Random(); + + public static List GetRandomCountList(this List list, int count) + { + if (count > list.Count) + { + throw new ArgumentException("Sample size cannot be greater than list size."); + } + + // 使用 Fisher-Yates 洗牌算法来随机选择元素 + List result = list.ToList(); // 复制原始列表,以避免修改原始列表 + int n = result.Count; + + while (n > 1) + { + n--; + int k = _random.Next(n + 1); + T value = result[k]; + result[k] = result[n]; + result[n] = value; + } + + // 返回所需数量的元素 + return result.Take(count).ToList(); + } + } +} diff --git a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj index f2772d9ce..324ac91a0 100644 --- a/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj +++ b/IRaCIS.Core.Infrastructure/IRaCIS.Core.Infrastructure.csproj @@ -9,13 +9,15 @@ - - + + + + - - + + - + diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs index cbe9ec767..1939d57cd 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Exception/BusinessValidationFailedException.cs @@ -1,4 +1,5 @@ using IRaCIS.Core.Infrastructure.Extention; +using Microsoft.Extensions.Localization; using System; namespace IRaCIS.Core.Infrastructure @@ -8,15 +9,24 @@ namespace IRaCIS.Core.Infrastructure public ApiResponseCodeEnum Code { get; set; } - public BusinessValidationFailedException() + public string LocalizedKey { get; set; }=string.Empty; + + public BusinessValidationFailedException() { } + public BusinessValidationFailedException(LocalizedString message, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) : base(message) + { + Code = code; + LocalizedKey=message.Name; + + } + public BusinessValidationFailedException(string message, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) : base(message) { - Code = code; + Code = code; - } + } } } diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs index 459ea65fb..a2d88b1c8 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ApiResponseCodeEnum.cs @@ -54,7 +54,9 @@ namespace IRaCIS.Core.Infrastructure.Extention NoToken = 10, //带了Token,但是没有相应权限(该用户类型不能做) - HaveTokenNotAccess = 11 + HaveTokenNotAccess = 11, + + ResultEncrepted=12 } diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs index 9c713a8a0..a0edd84d0 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/IResponseOutput.cs @@ -16,6 +16,9 @@ /// 消息 /// string ErrorMessage { get; } + + + public string LocalizedInfo { get; set; } } /// @@ -27,16 +30,7 @@ /// /// 返回数据 /// - T Data { get; } + T Data { get; set; } } - //public interface IResponseOutput : IResponseOutput - //{ - // /// - // /// 返回数据 - // /// - // T Data { get; } - - // T2 OtherInfo { get; } - //} } diff --git a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs index af255eeb2..9023c87e5 100644 --- a/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/Output/ResponseOutput.cs @@ -1,4 +1,8 @@ -using Newtonsoft.Json; +using IRaCIS.Core.Domain.Share; +using Microsoft.Extensions.Localization; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace IRaCIS.Core.Infrastructure.Extention { @@ -7,10 +11,10 @@ namespace IRaCIS.Core.Infrastructure.Extention /// public class ResponseOutput : IResponseOutput { - /// - /// 是否成功标记 - /// - public bool IsSuccess { get; private set; } + /// + /// 是否成功标记 + /// + public bool IsSuccess { get; private set; } public ApiResponseCodeEnum Code { get; set; } = ApiResponseCodeEnum.OK; @@ -25,34 +29,20 @@ namespace IRaCIS.Core.Infrastructure.Extention /// 数据 兼顾以前 Json序列化的时候返回属性名为“Result” /// [JsonProperty("Result")] - public T Data { get; private set; } + public T Data { get; set; } [JsonProperty("OtherInfo")] public object OtherData { get; set; } + public string LocalizedInfo { get; set; } - /// - /// 成功 - /// - /// 数据 - /// 消息 - //public ResponseOutput Ok(T data, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) - //{ - // IsSuccess = true; - // Code = code; - // Data = data; - // ErrorMessage = msg; - - // return this; - //} - - public ResponseOutput Ok(T data, object otherData, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) + public ResponseOutput Ok(T data, object otherData, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) { IsSuccess = true; Code = code; Data = data; - OtherData=otherData; + OtherData = otherData; ErrorMessage = msg; return this; @@ -64,12 +54,13 @@ namespace IRaCIS.Core.Infrastructure.Extention /// 提示消息 /// 数据 /// - public ResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) + public ResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.OK, string localizedInfo = "") { IsSuccess = false; - Code = code; + Code = code; ErrorMessage = msg; Data = data; + LocalizedInfo = localizedInfo; return this; } @@ -77,33 +68,6 @@ namespace IRaCIS.Core.Infrastructure.Extention } - //public class ResponseOutput : IResponseOutput - //{ - // [JsonProperty("Result")] - // public T Data { get; private set; } - - // public T2 OtherInfo { get; private set; } - - // public bool IsSuccess { get; private set; } - - // public ApiResponseCodeEnum Code { get; set; } - - - // public string ErrorMessage { get; private set; } - - - // public ResponseOutput Ok(T data, T2 otherInfo, string msg = "") - // { - // IsSuccess = true; - // Data = data; - // OtherInfo = otherInfo; - // ErrorMessage = msg; - - // return this; - // } - //} - - /// /// 响应数据静态输出 为了代码简洁 不用每处都New /// @@ -111,23 +75,6 @@ namespace IRaCIS.Core.Infrastructure.Extention { - //public static IResponseOutput Ok(T data, T2 otherInfo, string msg = "") - //{ - // return new ResponseOutput().Ok(data, otherInfo); - //} - - - /// - /// 成功 -----适合查询 - /// - /// 数据 - /// 消息 - /// - //public static IResponseOutput Ok(T data = default, string msg = "") - //{ - // return new ResponseOutput().Ok(data, msg); - //} - public static IResponseOutput Ok(T data = default, object otherData = default, string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.OK) { return new ResponseOutput().Ok(data, otherData, msg, code); @@ -147,15 +94,11 @@ namespace IRaCIS.Core.Infrastructure.Extention /// 消息 /// 数据 /// - public static IResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) + public static IResponseOutput NotOk(string msg = "", T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed, string localizedInfo = "") { - return new ResponseOutput().NotOk(msg, data, code); + return new ResponseOutput().NotOk(msg, data, code, localizedInfo); } - //public static IResponseOutput NotOk( T data = default, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) - //{ - // return new ResponseOutput().NotOk("", data, code); - //} /// /// 失败 @@ -164,7 +107,22 @@ namespace IRaCIS.Core.Infrastructure.Extention /// public static IResponseOutput NotOk(string msg = "", ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) { - return new ResponseOutput().NotOk(msg,code:code); + return new ResponseOutput().NotOk(msg, code: code); + } + + public static IResponseOutput NotOk(LocalizedString msg, ApiResponseCodeEnum code = ApiResponseCodeEnum.BusinessValidationFailed) + { + + var key = msg.Name; + + var info = string.Empty; + + if (!string.IsNullOrWhiteSpace(key) && StaticData.Log_Locoalize_Dic.ContainsKey(key)) + { + info = $"[{key}]:{StaticData.Log_Locoalize_Dic[key]}"; + } + + return new ResponseOutput().NotOk(msg, code: code, localizedInfo: info); } public static IResponseOutput DBNotExistIfNUll(object businessObject) @@ -172,7 +130,7 @@ namespace IRaCIS.Core.Infrastructure.Extention return new ResponseOutput().NotOk($"The business object{businessObject.GetType().Name} does not exist in the database, or was deleted by someone else, or an incorrect parameter query caused"); } - + /// /// 根据布尔值返回结果 --适合删除 /// @@ -190,7 +148,7 @@ namespace IRaCIS.Core.Infrastructure.Extention /// public static IResponseOutput Result(bool success, T data = default) { - return success ? Ok(data) : NotOk("Saved failed",data); + return success ? Ok(data) : NotOk("Saved failed", data); } ///// diff --git a/IRaCIS.Core.Domain/_Config/_StaticData.cs b/IRaCIS.Core.Infrastructure/_IRaCIS/_Config/_StaticData.cs similarity index 92% rename from IRaCIS.Core.Domain/_Config/_StaticData.cs rename to IRaCIS.Core.Infrastructure/_IRaCIS/_Config/_StaticData.cs index ddcc620c9..42ab4a9ca 100644 --- a/IRaCIS.Core.Domain/_Config/_StaticData.cs +++ b/IRaCIS.Core.Infrastructure/_IRaCIS/_Config/_StaticData.cs @@ -12,11 +12,21 @@ public static class StaticData public static Dictionary Zh_CN_Dic = new Dictionary(); + public static Dictionary Log_Locoalize_Dic = new Dictionary(); + public static readonly string En_US_Json = "en-US.json"; public static readonly string Zh_CN_Json = "zh-CN.json"; + public static class CultureInfo + { + public static readonly string zh_CN = "zh-CN"; + public static readonly string en_US = "en-US"; + + public static readonly string en_US_bookMark = "en_us"; + } + /// /// 获取国际化 /// @@ -208,6 +218,10 @@ public static class StaticData /// public static class Export { + public const string TrialList_Export = "TrialList_Export"; + + public const string TrialTrainingRecordList_Export = "TrialTrainingRecordList_Export"; + public const string TrialSiteUserList_Export = "TrialSiteUserList_Export"; public const string TrialSiteUserSummary_Export = "TrialSiteUserSummary_Export"; @@ -216,6 +230,8 @@ public static class StaticData public const string TrialCRCUploadImageList_Export = "TrialCRCUploadImageList_Export"; + public const string TrialQCVisitImageList_Export = "TrialQCVisitImageList_Export"; + public const string TrialQCImageChanllengeList_Export = "TrialQCImageChanllengeList_Export"; public const string TrialSubjectList_Export = "TrialSubjectList_Export"; diff --git a/IRaCIS.Core.Test/DbHelper.ttinclude b/IRaCIS.Core.Test/DbHelper.ttinclude index 93d6bd2d6..d33e52c37 100644 --- a/IRaCIS.Core.Test/DbHelper.ttinclude +++ b/IRaCIS.Core.Test/DbHelper.ttinclude @@ -4,7 +4,7 @@ public static readonly string ConnectionString = "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true"; public static readonly string DbDatabase = "Test_IRC"; //表名称用字符串,拼接 - public static readonly string TableName = "TrialBodyPart"; + public static readonly string TableName = "UserFeedBack"; //具体文件里面 例如service 可以配置是否分页 } #> diff --git a/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj b/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj index dae984eb7..2d9915d1d 100644 --- a/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj +++ b/IRaCIS.Core.Test/IRaCIS.Core.Test.csproj @@ -77,11 +77,6 @@ True IRaCIS.Core.Dto.tt - - True - True - IRaCIS.Core.Entity.tt - True True @@ -102,12 +97,18 @@ True IRaCIS.Core.Services.tt + + True + True + IRaCIS.Core.Entity.tt + + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt index fa5c367b7..5a1998a61 100644 --- a/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS .Core.ServiceAsync.tt @@ -65,6 +65,7 @@ namespace IRaCIS.Core.Application.Service } <# if(isPage){#> + [HttpPost] public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) { @@ -74,13 +75,13 @@ namespace IRaCIS.Core.Application.Service .ProjectTo<<#=tableName#>View>(_mapper.ConfigurationProvider); var pageList= await <#=char.ToLower(tableName[0]) + tableName.Substring(1)#>Queryable - .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? "Id" : inQuery.SortField, + .ToPagedListAsync(inQuery.PageIndex, inQuery.PageSize, string.IsNullOrWhiteSpace(inQuery.SortField) ? nameof(<#=tableName#>View.Id) : inQuery.SortField, inQuery.Asc); return pageList; } <# } else {#> - + [HttpPost] public async TaskView>> Get<#=tableName#>List(<#=tableName#>Query inQuery) { diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt index bf59c18a7..128807500 100644 --- a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Dto.tt @@ -42,30 +42,34 @@ using IRaCIS.Core.Domain.Share; using System.Collections.Generic; namespace IRaCIS.Core.Application.ViewModel { - /// <#=tableName#>View 列表视图模型 - public class <#=tableName#>View + <# var excludedColumns = new[] { "CreateUserId", "UpdateUserId", "CreateTime", "UpdateTime" };#> + + /// <#=tableName#>View 列表视图 + public class <#=tableName#>View:<#=tableName#>AddOrEdit { <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> + <# if (excludedColumns.Contains(column.ColumnName)){ #> public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <# }#> <# }#> } - ///<#=tableName#>Query 列表查询参数模型 + ///<#=tableName#>Query 列表查询参数 public class <#=tableName#>Query { - <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#><# if(column.CSharpType=="string"){#> - /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n","") #> - public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } - - <# }#> - <# }#> + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#><# if(column.CSharpType=="string"){#> + public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <# }#> + <# }#> } - /// <#=tableName#>AddOrEdit 列表查询参数模型 + /// <#=tableName#>AddOrEdit 添加编辑 public class <#=tableName#>AddOrEdit { <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> + <# if (!excludedColumns.Contains(column.ColumnName)){ #> public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <#}#> <# }#> } diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs index 171fad3cb..b28b04f64 100644 --- a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.cs @@ -1,3 +1,3 @@ - + diff --git a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt index c15229ffb..248a2d82a 100644 --- a/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt +++ b/IRaCIS.Core.Test/TT_Template/IRaCIS.Core.Entity.tt @@ -1,4 +1,4 @@ -<#@ template debug="false" hostspecific="true" language="C#" #> +<#@ template debug="false" hostspecific="true" language="C#" #> <#@ output extension=".cs" #> <#@ assembly name="System.Core.dll" #> <#@ assembly name="System.Data.dll" #> @@ -27,13 +27,13 @@ <# foreach (var item in DbHelper.GetDbTablesNew(config.ConnectionString, config.DbDatabase,config.TableName)) { var tableName=item.ToString(); - manager.StartBlock(tableName+".cs",OutputPath1);//文件名 + manager.StartBlock(tableName+".cs",OutputPath1);//ļ #> //-------------------------------------------------------------------- -// 此代码由T4模板自动生成 byzhouhang 20210918 -// 生成时间 <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> -// 对此文件的更改可能会导致不正确的行为,并且如果重新生成代码,这些更改将会丢失。 +// ˴T4ģԶ byzhouhang 20210918 +// ʱ <#=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")#> +// ԴļĸĿܻᵼ²ȷΪɴ룬ЩĽᶪʧ using System; using IRaCIS.Core.Domain.Share; using System.ComponentModel.DataAnnotations; @@ -44,19 +44,15 @@ namespace IRaCIS.Core.Domain.Models ///<#=tableName#> /// [Table("<#=tableName#>")] - public class <#=tableName#> : Entity, IAuditUpdate, IAuditAdd + public class <#=tableName#> : BaseFullAuditEntity { - <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){#> - - /// - /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n"," ") #> - /// - <# - if(column.IsPrimaryKey) - {#>[Key] - <#}#><# if(!column.IsNullable) {#>[Required] - <# }#>public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } - <#}#> + <# var excludedColumns = new[] { "CreateUserId", "Id", "UpdateUserId", "CreateTime", "UpdateTime", "DeleteUserId", "IsDeleted", "DeletedTime" };#> + <# foreach(DbColumn column in DbHelper.GetDbColumns(config.ConnectionString, config.DbDatabase, tableName)){ #> + <# if (!excludedColumns.Contains(column.ColumnName)){ #> + /// <#= column.Remark == "" ? column.ColumnName : column.Remark.Replace("\r\n"," ") #> + public <#= column.CSharpType#><# if(column.CommonType.IsValueType && column.IsNullable){#>?<#}#> <#=column.ColumnName#> { get; set; } + <#}#> + <#}#> } public virtual DbSet<<#=tableName#>> <#=tableName#> { get; set; } diff --git a/irc_api.drone.yml b/irc_api.drone.yml index b157b6e1b..290fa2b13 100644 --- a/irc_api.drone.yml +++ b/irc_api.drone.yml @@ -19,7 +19,7 @@ steps: - name: build-uat-irc commands: - cd /opt/1panel/xc-deploy/Uat_IRC/devops-build-publish/Uat-To-Uat - - sh uat-branch-latest-build-image-publish.sh v${DRONE_BUILD_NUMBER} + - sh pull-build-uat-irc-image.sh v${DRONE_BUILD_NUMBER} trigger: branch: @@ -48,6 +48,34 @@ steps: commands: - cd /opt/1panel/xc-deploy/Test_IRC/devops-build-publish;sh test-irc-update-or-create-stack.sh v${DRONE_BUILD_NUMBER} +trigger: + branch: + - Test_IRC_Net8 + +--- +kind: pipeline +type: ssh +name: ssh-linux-test-irc-scp-publish + +platform: + os: Linux + arch: 386 + +clone: + disable: true + +server: + host: 106.14.89.110 + user: root + password: + from_secret: test_ssh_pwd + +steps: +- name: publish-test-irc-scp + commands: + - cd /opt/1panel/xc-deploy/Test_IRC_SCP/devops-build-publish;sh pull-build-test-irc-scp-image.sh v${DRONE_BUILD_NUMBER} + - cd /opt/1panel/xc-deploy/Test_IRC_SCP ;sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER} + trigger: branch: - Test_IRC_Net8 @@ -73,12 +101,12 @@ server: steps: - name: publish-test-study commands: - - cd /opt/1panel/xc-deploy/Test_Study/devops-build-publish - - sh test-study.sh v${DRONE_BUILD_NUMBER} + - cd /opt/1panel/xc-deploy/Test_Study/devops-build-publish ; sh pull-build-test-study-image.sh v${DRONE_BUILD_NUMBER} + - cd /opt/1panel/xc-deploy/Test_Study ;sh update-image-if-need-then-pull-aliyun.sh v${DRONE_BUILD_NUMBER} trigger: branch: - - Test.Study + - Test_Study_Net8 --- kind: pipeline