diff --git a/IRaCIS.Core.API/Program.cs b/IRaCIS.Core.API/Program.cs deleted file mode 100644 index 38f1a2ee5..000000000 --- a/IRaCIS.Core.API/Program.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using Autofac.Extensions.DependencyInjection; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Configuration; -using Serilog; -using MediatR; -using IRaCIS.Core.Application.MediatR.Handlers; -using System.Threading.Tasks; -using MassTransit; -using MassTransit.NewIdProviders; -using System.IO; -using IRaCIS.Core.Domain.Share; -using IRaCIS.Core.Infra.EFCore; -using IRaCIS.Core.Application.Helper; -using System.Runtime.InteropServices; - -namespace IRaCIS.Core.API -{ - public class Program - { - public readonly string environment; - public static async Task Main(string[] args) - { - try - { - //以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) - var config = new ConfigurationBuilder() - .AddEnvironmentVariables() - .Build(); - - var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; - - if (string.IsNullOrWhiteSpace(enviromentName)) - { - - var index = Array.IndexOf(args, "--env"); - enviromentName = index > -1 - ? args[index + 1] - : "Development"; - } - - //Dicom 浏览 - //ImageManager.SetImplementation(WinFormsImageManager.Instance); - - var host = CreateHostBuilder(args) - .UseEnvironment(enviromentName) //命令行传入环境 - .ConfigureAppConfiguration((hostContext, config) => - { - - //Console.WriteLine(hostContext.HostingEnvironment.EnvironmentName); - config.AddJsonFile("appsettings.json", false, true) - .AddJsonFile($"appsettings.{enviromentName}.json", false, true); - }) - .Build(); - - - //// Serilog - SerilogExtension.AddSerilogSetup(enviromentName, host.Services); - - 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"); - } - - NewId.SetProcessIdProvider(new CurrentProcessIdProvider()); - - - host.Run(); - - - - } - catch (Exception e) - { - - Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace); - } - - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .UseWindowsService() - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureKestrel((context, options) => - { - //设置应用服务器Kestrel请求体最大为1GB // if don't set default value is: 30 MB - options.Limits.MaxRequestBodySize = long.MaxValue; - options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(30); - options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(20); - - }); - //webBuilder.UseSerilog();//在宿主机启动的时候配置serilog,与微软ILogger进行整合 - webBuilder.UseStartup(); - }).UseSerilog() - .UseServiceProviderFactory(new AutofacServiceProviderFactory()); - - - private static async Task InitCache(IHost host) - { - var _repository = host.Services.GetService(typeof(IRepository)) as IRepository; - - //初始化 国际化数据,并且监测国际化文件变更 - //await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_repository); - - } - - - - - - } -} diff --git a/IRaCIS.Core.API/Progranm.cs b/IRaCIS.Core.API/Progranm.cs new file mode 100644 index 000000000..87fab940c --- /dev/null +++ b/IRaCIS.Core.API/Progranm.cs @@ -0,0 +1,306 @@ +using System; +using Autofac.Extensions.DependencyInjection; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; +using MediatR; +using IRaCIS.Core.Application.MediatR.Handlers; +using System.Threading.Tasks; +using MassTransit; +using MassTransit.NewIdProviders; +using System.IO; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infra.EFCore; +using IRaCIS.Core.Application.Helper; +using System.Runtime.InteropServices; +using Microsoft.AspNetCore.Builder; +using IRaCIS.Core.API; +using Autofac; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.DependencyInjection; +using IRaCIS.Core.Application.Filter; +using Microsoft.AspNetCore.HttpOverrides; +using IRaCIS.Application.Services.BackGroundJob; +using LogDashboard; +using OfficeOpenXml.Utils; + + + +#region 获取环境变量 +//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数) +var config = new ConfigurationBuilder() + .AddEnvironmentVariables() + .Build(); + +var enviromentName = config["ASPNETCORE_ENVIRONMENT"]; + +if (string.IsNullOrWhiteSpace(enviromentName)) +{ + + var index = Array.IndexOf(args, "--env"); + enviromentName = index > -1 + ? args[index + 1] + : "Development"; +} +#endregion + +var builder = WebApplication.CreateBuilder(new WebApplicationOptions +{ + EnvironmentName = enviromentName +}); + +#region 兼容windows 服务命令行的方式 + +//foreach (var arg in args) +//{ +// Console.WriteLine(arg); +//} + +int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--urls")); + +if (urlsIndex > -1) +{ + var url = args[urlsIndex].Substring("--urls=".Length); + Console.WriteLine(url); + builder.WebHost.UseUrls(url); +} + +#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(); + }) + .UseWindowsService().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(); + + if (_configuration.GetSection("BasicSystemConfig").GetValue("OpenLoginLimit")) + { + options.Filters.Add(); + } + +}) + .AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理 + +builder.Services.AddOptions().Configure(_configuration.GetSection("SystemEmailSendConfig")); +builder.Services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); +builder.Services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); +builder.Services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); + + +//动态WebApi + UnifiedApiResultFilter 省掉控制器代码 +builder.Services.AddDynamicWebApiSetup(); +//AutoMapper +builder.Services.AddAutoMapperSetup(); +//EF ORM QueryWithNoLock +builder.Services.AddEFSetup(_configuration); +//Http 响应压缩 +builder.Services.AddResponseCompressionSetup(); +//Swagger Api 文档 +builder.Services.AddSwaggerSetup(); +//JWT Token 验证 +builder.Services.AddJWTAuthSetup(_configuration); + +// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系 +builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining()); +// EasyCaching 缓存 +builder.Services.AddEasyCachingSetup(_configuration); + +// hangfire 定时任务框架 有界面,更友好~ +builder.Services.AddhangfireSetup(_configuration); + +// +builder.Services.AddQuartZSetup(_configuration); + +//Serilog 日志可视化 LogDashboard日志 +builder.Services.AddLogDashboardSetup(); + + +builder.Services.AddJsonConfigSetup(_configuration); +//转发头设置 获取真实IP +builder.Services.Configure(options => +{ + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; +}); +//Dicom影像渲染图片 跨平台 +builder.Services.AddDicomSetup(); + +// 实时应用 +builder.Services.AddSignalR(); + +builder.Services.AddSingleton(); + +//builder.Services.AddMemoryCache(); + + + +#region 历史废弃配置 + +////上传限制 配置 +//builder.Services.Configure(options => +//{ +// options.MultipartBodyLengthLimit = int.MaxValue; +// options.ValueCountLimit = int.MaxValue; +// options.ValueLengthLimit = int.MaxValue; +//}); +//IP 限流 可设置白名单 或者黑名单 +//services.AddIpPolicyRateLimitSetup(_configuration); +// 用户类型 策略授权 +//services.AddAuthorizationPolicySetup(_configuration); +#endregion + +#endregion + +var app = builder.Build(); +var env = app.Environment; + +#region 配置中间件 + + +// Configure the HTTP request pipeline. + +//本地化 +app.UseLocalization(); + +app.UseForwardedHeaders(); + +//响应压缩 +app.UseResponseCompression(); + +//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + +//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication. +app.UseStaticFiles(); + +//app.UseMiddleware(); + +//LogDashboard +app.UseLogDashboard("/LogDashboard"); + +//hangfire +app.UseHangfireConfig(env); + + +////限流 中间件 +//app.UseIpRateLimiting(); + + +if (env.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} +else +{ + //app.UseHsts(); +} + +// 特殊异常处理 比如 404 +app.UseStatusCodePagesWithReExecute("/Error/{0}"); + +SwaggerSetup.Configure(app, env); + + +////serilog 记录请求的用户信息 +app.UseSerilogConfig(env); + +app.UseRouting(); + +app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + +//app.UseIRacisHostStaticFileStore(env); + + +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapControllers(); + +app.MapHub("/UploadHub"); +app.MapHealthChecks("/health"); + + +// Serilog +SerilogExtension.AddSerilogSetup(enviromentName, app.Services); + + +var hangfireJobService = app.Services.GetRequiredService(); + +await hangfireJobService.InitHangfireJobTaskAsync(); + + +#endregion + +try +{ + #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 + + + Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}"); + + + string parentDirectory = Path.GetFullPath(Path.Combine(env.ContentRootPath, "..")); + + + Log.Logger.Warning($"ContentRootPath——parentDirectory:{parentDirectory}"); + + //Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}"); + //Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}"); + + app.Run(); + +} +catch (Exception e) +{ + + Log.Logger.Error(e.InnerException is null ? e.Message + e.StackTrace : e.InnerException?.Message + e.InnerException?.StackTrace); +} + + + diff --git a/IRaCIS.Core.API/Startup.cs b/IRaCIS.Core.API/Startup.cs deleted file mode 100644 index 9d7a01577..000000000 --- a/IRaCIS.Core.API/Startup.cs +++ /dev/null @@ -1,262 +0,0 @@ -using Autofac; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http.Features; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using System; -using IRaCIS.Core.Application.Filter; -using LogDashboard; -using MediatR; -using IRaCIS.Core.Application.MediatR.Handlers; -using Microsoft.Extensions.Logging; -using AspNetCoreRateLimit; -using Microsoft.AspNetCore.HttpOverrides; -using IRaCIS.Core.Infra.EFCore; -using System.Globalization; -using Microsoft.AspNetCore.Localization; -using Localization; -using Magicodes.ExporterAndImporter.Core.Filters; -using IRaCIS.Core.Application.MediatR.CommandAndQueries; -using IRaCIS.Core.Infra.EFCore.Common; -using Invio.Extensions.Authentication.JwtBearer; -using Microsoft.AspNetCore.SignalR; -using IRaCIS.Core.Domain.Share; -using Microsoft.AspNetCore.StaticFiles; -using IRaCIS.Application.Services.BackGroundJob; -using IRaCIS.Core.Application.Helper; -using Microsoft.AspNetCore.Http; -using Autofac.Core; -using DocumentFormat.OpenXml.InkML; -using EasyCaching.Core; - -namespace IRaCIS.Core.API -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - _configuration = configuration; - } - public ILogger _logger { get; } - - public IConfiguration _configuration { get; } - - //// ConfigureContainer is where you can register things directly - //// with Autofac. This runs after ConfigureServices so the things - //// here will override registrations made in ConfigureServices. - //// Don't build the container; that gets done for you by the factory. - // for castle - public void ConfigureContainer(ContainerBuilder containerBuilder) - { - containerBuilder.RegisterModule(); - - #region Test - //containerBuilder.RegisterType().PropertiesAutowired().InstancePerLifetimeScope();//注册仓储 - - //var container = containerBuilder.Build(); - - //// Now you can resolve services using Autofac. For example, - //// this line will execute the lambda expression registered - //// to the IConfigReader service. - //using (var scope = container.BeginLifetimeScope()) - //{ - // var reader = scope.Resolve(); - - // var test = scope.Resolve(); - // var test2 = scope.Resolve(); - - // var test3 = scope.Resolve>(); - //} - #endregion - } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - //健康检查 - services.AddHealthChecks(); - //本地化 - services.AddJsonLocalization(options => options.ResourcesPath = "Resources"); - - // 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim() - services.AddControllers(options => - { - //options.Filters.Add(); - options.Filters.Add(); - options.Filters.Add(); - options.Filters.Add(); - - if (_configuration.GetSection("BasicSystemConfig").GetValue("OpenLoginLimit")) - { - options.Filters.Add(); - } - - - }) - .AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理 - - services.AddOptions().Configure(_configuration.GetSection("SystemEmailSendConfig")); - services.AddOptions().Configure(_configuration.GetSection("BasicSystemConfig")); - services.AddOptions().Configure(_configuration.GetSection("AliyunOSS")); - services.AddOptions().Configure(_configuration.GetSection("ObjectStoreService")); - - - //动态WebApi + UnifiedApiResultFilter 省掉控制器代码 - services.AddDynamicWebApiSetup(); - //AutoMapper - services.AddAutoMapperSetup(); - //EF ORM QueryWithNoLock - services.AddEFSetup(_configuration); - //Http 响应压缩 - services.AddResponseCompressionSetup(); - //Swagger Api 文档 - services.AddSwaggerSetup(); - //JWT Token 验证 - services.AddJWTAuthSetup(_configuration); - // MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系 - services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining()); - // EasyCaching 缓存 - services.AddEasyCachingSetup(_configuration); - - //services.AddDistributedMemoryCache(); - - // hangfire 定时任务框架 有界面,更友好~ - services.AddhangfireSetup(_configuration); - - // QuartZ 定时任务框架 使用了hangfire 暂时不用,后续需要可以打开,已经配好 - services.AddQuartZSetup(_configuration); - - // 保护上传文件 - //services.AddStaticFileAuthorizationSetup(); - - - ////HttpReports 暂时废弃 - //services.AddHttpReports().AddHttpTransport(); - //Serilog 日志可视化 LogDashboard日志 - services.AddLogDashboardSetup(); - //上传限制 配置 - services.Configure(options => - { - options.MultipartBodyLengthLimit = int.MaxValue; - options.ValueCountLimit = int.MaxValue; - options.ValueLengthLimit = int.MaxValue; - }); - //IP 限流 可设置白名单 或者黑名单 - //services.AddIpPolicyRateLimitSetup(_configuration); - // 用户类型 策略授权 - //services.AddAuthorizationPolicySetup(_configuration); - - services.AddJsonConfigSetup(_configuration); - //转发头设置 获取真实IP - services.Configure(options => - { - options.ForwardedHeaders = - ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; - }); - //Dicom影像渲染图片 跨平台 - services.AddDicomSetup(); - - // 实时应用 - services.AddSignalR(); - - - services.AddSingleton(); - - //services.AddSingleton(); - - services.AddMemoryCache(); - - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public async void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - //app.UsePathBase(PathString.FromUriComponent("/api")); - - //本地化 - app.UseLocalization(); - - app.UseForwardedHeaders(); - - //响应压缩 - app.UseResponseCompression(); - - //app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); - - //不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication. - app.UseStaticFiles(); - - - //LogDashboard - app.UseLogDashboard("/back/logs"); - - //hangfire - app.UseHangfireConfig(env); - - ////暂时废弃 - //app.UseHttpReports(); - - ////限流 中间件 - //app.UseIpRateLimiting(); - - - - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - //app.UseHsts(); - } - Console.WriteLine("当前环境: " + env.EnvironmentName); - - //app.UseMiddleware(); - - // 特殊异常处理 比如 404 - app.UseStatusCodePagesWithReExecute("/Error/{0}"); - - SwaggerSetup.Configure(app, env); - - - - ////serilog 记录请求的用户信息 - app.UseSerilogConfig(env); - - app.UseRouting(); - - app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); - - //app.UseIRacisHostStaticFileStore(env); - app.UseMiddleware(); - - - app.UseAuthentication(); - //app.UseJwtBearerQueryString(); - app.UseAuthorization(); - - ////文件伺服 必须带Token 访问 - ////app.UseIRacisHostStaticFileStore(env); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - - endpoints.MapHub("/UploadHub"); - - endpoints.MapHealthChecks("/health"); - }); - - - var hangfireJobService = app.ApplicationServices.GetRequiredService(); - - await hangfireJobService.InitHangfireJobTaskAsync(); - //有的时候每调用 - //HangfireJobHelper.NotImmediatelyOnceOnlyJob(t => t.InitHangfireJobTaskAsync(),TimeSpan.FromSeconds(1)); - - - } - } -} diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs index c5af7adbe..1f88b7746 100644 --- a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogConfig.cs @@ -17,7 +17,7 @@ namespace IRaCIS.Core.API app.UseSerilogRequestLogging(opts => { - opts.MessageTemplate = "{TokenUserRealName} {TokenUserType} {ClientIp} {RequestIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; + opts.MessageTemplate = "{TokenUserRealName} {TokenUserTypeShortName} {ClientIp} {LocalIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms"; opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest; }); diff --git a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs index 1bb30e7bd..6699cb74b 100644 --- a/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs +++ b/IRaCIS.Core.API/_PipelineExtensions/Serilog/SerilogHelper.cs @@ -1,4 +1,5 @@ -using IRaCIS.Core.Infrastructure.Extention; +using IRaCIS.Core.Domain.Share; +using IRaCIS.Core.Infrastructure.Extention; using Microsoft.AspNetCore.Http; using Serilog; using System; @@ -41,9 +42,9 @@ namespace IRaCIS.Core.API // Set the content-type of the Response at this point diagnosticContext.Set("ContentType", httpContext.Response.ContentType); - diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst("realName")?.Value); + diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value); - diagnosticContext.Set("TokenUserType", httpContext?.User?.FindFirst("userTypeEnumName")?.Value); + diagnosticContext.Set("TokenUserTypeShortName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value); // Retrieve the IEndpointFeature selected for the request var endpoint = httpContext.GetEndpoint(); diff --git a/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs b/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs index a27de1505..6cd35a1ae 100644 --- a/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs +++ b/IRaCIS.Core.API/_ServiceExtensions/Serilog/SerilogSetup.cs @@ -26,24 +26,26 @@ namespace IRaCIS.Core.API //控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型 .WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning, - outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3} ] {LocalIP} {ClientIp} {TokenUserRealName} {TokenUserType} {Message:lj} {Properties:j}{NewLine} {Exception}") + 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} || {SourceContext:l} || {Message} ||{Exception} ||end {NewLine}"); + outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}"); + //.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。 - if (environment == "Production") - { - config.WriteTo.Email(new EmailConnectionInfo() - { - EmailSubject = "系统警告,请速速查看!",//邮件标题 - FromEmail = "test@extimaging.com",//发件人邮箱 - MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址 - NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码 - Port = 465,//端口号 - ToEmail = "872297557@qq.com"//收件人 - }, restrictedToMinimumLevel: LogEventLevel.Error, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})"); - } + //if (environment == "Production") + //{ + // config.WriteTo.Email(new EmailConnectionInfo() + // { + // EmailSubject = "系统警告,请速速查看!",//邮件标题 + // FromEmail = "test@extimaging.com",//发件人邮箱 + // MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址 + // NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码 + // Port = 465,//端口号 + // ToEmail = "872297557@qq.com"//收件人 + // }, restrictedToMinimumLevel: LogEventLevel.Error, + // outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})"); + //} //扩展方法 获取上下文的ip 用户名 用户类型 Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger(); diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html index ba530acb4..728dde6fc 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminAddUser.html @@ -31,7 +31,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html index ad1130293..045b2320f 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/AdminResetUser.html @@ -28,7 +28,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html index 368b1884c..7ba12b97a 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress.html @@ -18,7 +18,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress_US.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress_US.html index 4607c5af2..52fd9841e 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress_US.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/SubjectEnrollConfirmOrPDProgress_US.html @@ -14,7 +14,7 @@ Thank you for using our IRC imaging system.
- {0}。 + {0}.
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html index 4152662c4..46ea36656 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorExistJoin.html @@ -42,7 +42,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html index 9f7422603..159216984 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialDoctorFirstJoin.html @@ -43,7 +43,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html index a2b8f771c..3c182eddc 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject.html @@ -40,7 +40,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject_US.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject_US.html index caa38e3b7..e1a3b132e 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject_US.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialSiteSurveyReject_US.html @@ -8,7 +8,7 @@
- Dear {0} , + Dear {0} ,
The site survey questionnaire you filled in has been rejected. Details are as follows: diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html index 3b91510ab..8a0ae7fe1 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserExistJoin.html @@ -43,7 +43,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html index 958d8fe83..32b64456d 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/TrialUserFirstJoin.html @@ -43,7 +43,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html b/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html index d6bfd963e..d99e1d76d 100644 --- a/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html +++ b/IRaCIS.Core.API/wwwroot/EmailTemplate/UserOptCommon.html @@ -18,7 +18,7 @@
-
祝您顺利!/Best Regards
+
祝您顺利!
展影医疗
diff --git a/IRaCIS.Core.Application/Helper/OSSService.cs b/IRaCIS.Core.Application/Helper/OSSService.cs index 4cacd52f1..26cbd2b3c 100644 --- a/IRaCIS.Core.Application/Helper/OSSService.cs +++ b/IRaCIS.Core.Application/Helper/OSSService.cs @@ -163,7 +163,8 @@ namespace IRaCIS.Core.Application.Helper var putObjectArgs = new PutObjectArgs() .WithBucket(minIOConfig.bucketName) .WithObject(ossRelativePath) - .WithStreamData(memoryStream); + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); await minioClient.PutObjectAsync(putObjectArgs); } @@ -179,7 +180,8 @@ namespace IRaCIS.Core.Application.Helper var putObjectArgs = new PutObjectArgs() .WithBucket(minIOConfig.bucketName) .WithObject(ossRelativePath) - .WithStreamData(memoryStream); + .WithStreamData(memoryStream) + .WithObjectSize(memoryStream.Length); await minioClient.PutObjectAsync(putObjectArgs); } diff --git a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml index 8e31348c2..16d18b105 100644 --- a/IRaCIS.Core.Application/IRaCIS.Core.Application.xml +++ b/IRaCIS.Core.Application/IRaCIS.Core.Application.xml @@ -547,6 +547,32 @@ InternationalizationService + + + 前端国际化内容接口 + + + + + + 前端批量提交,后端判断不存在就添加,存在就更新 + + + + + + 后端之前批量添加接口 + + + + + + + 前后端添加的时候,区分了,前端判断重复多了多了一个路由 路由+标识唯一 + + + + PublishLogService @@ -12413,6 +12439,12 @@ + + + 维护OSS 影像数据 + + + Reviewer 列表查询参数 diff --git a/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs index a1a708029..1df7e3627 100644 --- a/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs +++ b/IRaCIS.Core.Application/Service/Allocation/VisitTaskService.cs @@ -1547,7 +1547,10 @@ namespace IRaCIS.Core.Application.Service.Allocation #region 方式二 - var origenalTask = influenceTaskList.Where(t => t.Id == task.Id).FirstOrDefault(); + //var origenalTask = influenceTaskList.Where(t => t.Id == task.Id).FirstOrDefault(); + + var origenalTask = await _visitTaskRepository.FindAsync(task.Id); + foreach (var influenceTask in influenceTaskList) { @@ -1610,7 +1613,9 @@ namespace IRaCIS.Core.Application.Service.Allocation await SetMedicalReviewInvalidAsync(currentVisitList); - var origenalTask = currentVisitList.Where(t => t.Id == task.Id).First(); + //var origenalTask = currentVisitList.Where(t => t.Id == task.Id).First(); + + var origenalTask = await _visitTaskRepository.FindAsync(task.Id); foreach (var influenceTask in currentVisitList) { diff --git a/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs b/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs index 16ed714b1..c6b7567b3 100644 --- a/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs +++ b/IRaCIS.Core.Application/Service/Common/DTO/InternationalizationViewModel.cs @@ -42,6 +42,8 @@ namespace IRaCIS.Core.Application.ViewModel public string Value { get; set; } = string.Empty; public string ValueCN { get; set; } = string.Empty; + public string FrontType { get; set; } = string.Empty; + public int InternationalizationType { get; set; } } @@ -55,19 +57,23 @@ namespace IRaCIS.Core.Application.ViewModel } - public class BatchAddInternationalizationDto + public class BatchInternationalizationDto { public string Description { get; set; } = string.Empty; public string Code { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; + public string FrontType { get; set; } = string.Empty; public string ValueCN { get; set; } = string.Empty; } - public class InternationalizationSimpleDto + public class BatchAddInternationalizationDto : BatchInternationalizationDto { - public string Code { get; set; } = string.Empty; - public string Value { get; set; } = string.Empty; - public string ValueCN { get; set; } = string.Empty; + + } + + public class InternationalizationSimpleDto: BatchInternationalizationDto + { + } } diff --git a/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs b/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs index efdcd51e5..46ed1e447 100644 --- a/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs +++ b/IRaCIS.Core.Application/Service/Common/InternationalizationService.cs @@ -10,6 +10,9 @@ using IRaCIS.Core.Application.Interfaces; using IRaCIS.Core.Application.ViewModel; using Microsoft.AspNetCore.Authorization; using IRaCIS.Core.Application.Helper; +using EasyCaching.Core; +using IRaCIS.Core.Domain.Share; +using OfficeOpenXml.FormulaParsing.Utilities; namespace IRaCIS.Core.Application.Service { @@ -19,28 +22,77 @@ namespace IRaCIS.Core.Application.Service [ApiExplorerSettings(GroupName = "Common")] public class InternationalizationService : BaseService, IInternationalizationService { - + private readonly IEasyCachingProvider _provider; private readonly IRepository _internationalizationRepository; - public InternationalizationService(IRepository internationalizationRepository) + public InternationalizationService(IRepository internationalizationRepository, IEasyCachingProvider provider) { _internationalizationRepository = internationalizationRepository; + _provider = provider; } - + /// + /// 前端国际化内容接口 + /// + /// [AllowAnonymous] - - public async Task> GetFrontInternationalizationList() + [HttpGet] + public async Task> GetFrontInternationalizationList() { + var cacheList= _provider.Get>(StaticData.InternationalData.Front).Value; - var list = await _internationalizationRepository.Where(t => t.InternationalizationType == 0).Select(t => new InternationalizationSimpleDto() + if(cacheList != null && cacheList.Count!=0) { - Code = t.Code, - Value = t.Value, - ValueCN = t.ValueCN - }).ToListAsync(); + return cacheList; + } + else + { + var list = await _internationalizationRepository.Where(t => t.InternationalizationType == 0).Select(t => new InternationalizationSimpleDto() + { + Code = t.Code, + Value = t.Value, + ValueCN = t.ValueCN, + FrontType = t.FrontType, + Description = t.Description, + }).ToListAsync(); - return list; + _provider.Set>(StaticData.InternationalData.Front, list, TimeSpan.FromDays(1)); + + return list; + } + + } + + /// + /// 前端批量提交,后端判断不存在就添加,存在就更新 + /// + /// + [AllowAnonymous] + public async Task BatchAddOrUpdateFrontInternationalization(List batchList) + { + foreach (var item in batchList) + { + var find = await _internationalizationRepository.FirstOrDefaultAsync(t => t.Code == item.Code && t.Description == item.Description && t.InternationalizationType == 0); + + if (find != null) + { + _mapper.Map(item, find); + } + else + { + var mapItem = _mapper.Map(item); + mapItem.InternationalizationType = 0; + mapItem.State = 1; + + await _internationalizationRepository.AddAsync(mapItem); + } + } + await _internationalizationRepository.SaveChangesAsync(); + + //清理缓存 + _provider.Set>(StaticData.InternationalData.Front, new List(), TimeSpan.FromDays(1)); + + return ResponseOutput.Ok(); } [HttpPost] @@ -63,6 +115,11 @@ namespace IRaCIS.Core.Application.Service return pageList; } + /// + /// 后端之前批量添加接口 + /// + /// + /// [HttpPost] public async Task BatchAddInternationalization(BatchAddInternationalization batchAdd) { @@ -91,17 +148,34 @@ namespace IRaCIS.Core.Application.Service return ResponseOutput.Ok(); } + /// + /// 前后端添加的时候,区分了,前端判断重复多了多了一个路由 路由+标识唯一 + /// + /// + /// public async Task AddOrUpdateInternationalization(InternationalizationAddOrEdit addOrEditInternationalization) { + var internationalizationType = addOrEditInternationalization.InternationalizationType; + + //后端验证标识重复与否 var verifyExp1 = new EntityVerifyExp() { VerifyExp = t => t.Code == addOrEditInternationalization.Code && t.InternationalizationType == addOrEditInternationalization.InternationalizationType, VerifyMsg = $"该类型已有{addOrEditInternationalization.Code}名称的国际化标识", - IsVerify = true + IsVerify = internationalizationType == 1 }; - var entity = await _internationalizationRepository.InsertOrUpdateAsync(addOrEditInternationalization, true, verifyExp1); + //前端验证标识重复与否 + var verifyExp2 = new EntityVerifyExp() + { + VerifyExp = t => t.Code == addOrEditInternationalization.Code && t.InternationalizationType == addOrEditInternationalization.InternationalizationType && t.Description == addOrEditInternationalization.Description, + + VerifyMsg = $"该类型已有{addOrEditInternationalization.Description}下的{addOrEditInternationalization.Code}名称的国际化标识", + IsVerify = internationalizationType == 0 + }; + + var entity = await _internationalizationRepository.InsertOrUpdateAsync(addOrEditInternationalization, true, verifyExp1, verifyExp2); if (addOrEditInternationalization.InternationalizationType == 1) { diff --git a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs index f157924cb..c8c297ede 100644 --- a/IRaCIS.Core.Application/Service/Common/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/Common/_MapConfig.cs @@ -74,6 +74,9 @@ namespace IRaCIS.Core.Application.Service CreateMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap(); diff --git a/IRaCIS.Core.Application/Service/Document/EmailSendService.cs b/IRaCIS.Core.Application/Service/Document/EmailSendService.cs index d38d3726a..0e92fb158 100644 --- a/IRaCIS.Core.Application/Service/Document/EmailSendService.cs +++ b/IRaCIS.Core.Application/Service/Document/EmailSendService.cs @@ -356,7 +356,7 @@ namespace IRaCIS.Core.Application.Service .Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync(); - if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false) + if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable==false) { return; } @@ -477,7 +477,7 @@ namespace IRaCIS.Core.Application.Service .Include(t => t.TrialEmailNoticeUserList).Include(t => t.TrialEmailBlackUserList).FirstOrDefaultAsync(); - if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false) + if (trialEmailConfig == null || trialEmailConfig.IsAutoSend == false || trialEmailConfig.IsEnable==false) { return (null, null); } diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs index 75f6d7d81..572b71ba1 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/DTO/UnionStudyViewDodel.cs @@ -42,6 +42,7 @@ namespace IRaCIS.Core.Application.Contracts //[JsonIgnore] //public int NoneDicomCode { get; set; } + public string RecordPath { get; set; } = string.Empty; public bool IsDicom { get; set; } } @@ -173,7 +174,16 @@ namespace IRaCIS.Core.Application.Contracts public string[]? VisitPlanArray { get; set; } - } + public Guid? VisitTaskId { get; set; } + + public bool? IsDicom { get; set; } + + public string? Uploader { get; set; } + + public bool? IsSuccess { get; set; } + + public string? StudyCode { get; set; } + } public class PreArchiveDicomStudyCommand @@ -215,6 +225,8 @@ namespace IRaCIS.Core.Application.Contracts public int FailedFileCount { get; set; } + public string RecordPath { get; set; } + public AddOrUpdateStudyDto Study { get; set; } diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs index 54880b1ed..e9f5a7e53 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/StudyService.cs @@ -151,7 +151,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc studyMonitor.UploadFinishedTime = DateTime.Now; studyMonitor.ArchiveFinishedTime = DateTime.Now; studyMonitor.FailedFileCount = incommand.FailedFileCount; - studyMonitor.IsSuccess = true; + studyMonitor.IsSuccess = incommand.FailedFileCount==0; + studyMonitor.RecordPath=incommand.RecordPath; //上传 if (studyMonitor.IsDicomReUpload == false) @@ -209,6 +210,9 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc series.SubjectId = incommand.SubjectId; series.SubjectVisitId = incommand.SubjectVisitId; + //前端传递的数量不准,上传的时候,把失败的也加进来了,以实际数组的数字为准 + series.InstanceCount = seriesItem.InstanceList.Count; + await _dicomSeriesRepository.AddAsync(series); foreach (var instanceItem in seriesItem.InstanceList) @@ -241,6 +245,8 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc var study = await _dicomstudyRepository.FirstOrDefaultAsync(t => t.Id == studyId); + //重传的时候也要赋值检查Id + studyMonitor.StudyId = study.Id; //特殊处理逻辑 study.Modalities = string.Join("、", incommand.Study.SeriesList.Select(t => t.Modality).Union(study.Modalities.Split("、", StringSplitOptions.RemoveEmptyEntries)).Distinct()); @@ -457,6 +463,10 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc .WhereIf(studyQuery.SubjectId != null, t => t.SubjectId == studyQuery.SubjectId) .WhereIf(studyQuery.SubjectVisitId != null, t => t.SubjectId == studyQuery.SubjectVisitId) .WhereIf(studyQuery.SiteId != null, t => t.SiteId == studyQuery.SiteId) + .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)) .Select(t => new UnionStudyMonitorModel() { TrialId = t.TrialId, @@ -489,7 +499,7 @@ namespace IRaCIS.Core.Application.Service.ImageAndDoc ArchiveFinishedTime = t.ArchiveFinishedTime, - + RecordPath=t.RecordPath, IsDicomReUpload = t.IsDicomReUpload, StudyId = t.Id, diff --git a/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs b/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs index ef437c37a..5fb7e3610 100644 --- a/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/ImageAndDoc/_MapConfig.cs @@ -62,6 +62,7 @@ namespace IRaCIS.Core.Application.Service .ForMember(o => o.UploadedTime, t => t.MapFrom(u => u.CreateTime)) .ForMember(o => o.Uploader, t => t.MapFrom(u => u.Uploader.LastName + " / " + u.Uploader.FirstName)) .ForMember(o => o.StudyId, t => t.MapFrom(u => u.Id)) + .ForMember(o => o.IsHaveUploadFailed, t => t.MapFrom(u => u.DicomStudyMonitorList.Any(t=>t.FailedFileCount>0))) .ForMember(o => o.Modalities, t => t.MapFrom(u => string.Join('、', u.SeriesList.Select(t => t.Modality).Distinct()) )); diff --git a/IRaCIS.Core.Application/Service/Institution/SiteService.cs b/IRaCIS.Core.Application/Service/Institution/SiteService.cs index 4a78b453a..9d77f48ed 100644 --- a/IRaCIS.Core.Application/Service/Institution/SiteService.cs +++ b/IRaCIS.Core.Application/Service/Institution/SiteService.cs @@ -67,7 +67,7 @@ namespace IRaCIS.Application.Services siteCommand.Code = await _siteRepository.Select(t => t.Code).DefaultIfEmpty().MaxAsync() + 1; - siteCommand.SiteCode = AppSettings.GetCodeStr(siteCommand.Code, nameof(User)); + siteCommand.SiteCode = AppSettings.GetCodeStr(siteCommand.Code, nameof(Site)); } var site = await _siteRepository.InsertOrUpdateAsync(siteCommand, true, exp); diff --git a/IRaCIS.Core.Application/Service/Management/UserService.cs b/IRaCIS.Core.Application/Service/Management/UserService.cs index b5b0a3f03..9fc51f6b4 100644 --- a/IRaCIS.Core.Application/Service/Management/UserService.cs +++ b/IRaCIS.Core.Application/Service/Management/UserService.cs @@ -12,6 +12,7 @@ using Microsoft.Identity.Client; using static IRaCIS.Core.Domain.Share.StaticData; using IRaCIS.Core.Application.ViewModel; using Medallion.Threading; +using EasyCaching.Core; namespace IRaCIS.Application.Services { @@ -25,7 +26,7 @@ namespace IRaCIS.Application.Services private readonly IRepository _userLogRepository; private readonly IDistributedLockProvider _distributedLockProvider; - private readonly IMemoryCache _cache; + private readonly IEasyCachingProvider _cache; private readonly IOptionsMonitor _verifyConfig; @@ -34,7 +35,7 @@ namespace IRaCIS.Application.Services IMailVerificationService mailVerificationService, IRepository verificationCodeRepository, - IMemoryCache cache, + IEasyCachingProvider cache, IRepository userTrialRepository, IOptionsMonitor verifyConfig, IRepository userLogRepository @@ -629,7 +630,7 @@ namespace IRaCIS.Application.Services string cacheKey = $"{cachePrefix}{userName}"; // 从缓存中获取登录失败次数 - int? failCount = _cache.Get(cacheKey); + int? failCount = _cache.Get(cacheKey).Value; if (failCount == null) { diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs index 490f23911..33157b4d2 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QARecordViewModel.cs @@ -386,7 +386,7 @@ namespace IRaCIS.Core.Application.Contracts.DTO public string ModalityForEdit { get; set; } = string.Empty; - + public bool IsHaveUploadFailed { get; set; } } public class QASeriesInfoDto diff --git a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs index 44786acb2..9b768d332 100644 --- a/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs +++ b/IRaCIS.Core.Application/Service/QC/DTO/QCListViewModel.cs @@ -1099,6 +1099,8 @@ namespace IRaCIS.Core.Application.Contracts public int? Age { get; set; } public string Sex { get; set; } = string.Empty; + public bool IsHaveUploadFailed { get; set; } + } diff --git a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs index 6ab146a12..afde027f9 100644 --- a/IRaCIS.Core.Application/Service/QC/_MapConfig.cs +++ b/IRaCIS.Core.Application/Service/QC/_MapConfig.cs @@ -395,6 +395,8 @@ namespace IRaCIS.Core.Application.Service || t.ReadingClinicalDataList.Any(x => x.ClinicalDataTrialSet.UploadRole == Domain.Share.UploadRole.CRC && x.ReadingClinicalDataPDFList.Count() > 0) || t.PreviousSurgeryList.Any())) + .ForMember(d => d.IsHaveUploadFailed, u => u.MapFrom(t => t.StudyList.SelectMany(c=>c.DicomStudyMonitorList).Any(h => h.FailedFileCount>0) )) + //.ForMember(d => d.VisitName, u => u.MapFrom(t =>t.InPlan? t.VisitStage.VisitName : t.VisitName)) //.ForMember(d => d.VisitNum, u => u.MapFrom(t => t.InPlan ? t.VisitStage.VisitNum : t.VisitNum)) //.ForMember(d => d.VisitDay, u => u.MapFrom(t => t.InPlan ? t.VisitStage.VisitDay : t.VisitDay)) diff --git a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs index c78247062..184132d88 100644 --- a/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs +++ b/IRaCIS.Core.Application/Service/Reading/ReadingImageTask/ReadingImageTaskService.cs @@ -89,7 +89,6 @@ namespace IRaCIS.Application.Services IRepository userRepository, IEasyCachingProvider provider, IRepository readingCustomTagRepository, - IMemoryCache cache, IRepository readingCriterionDictionaryRepository, IRepository readingTrialCriterionDictionaryRepository, IRepository tumorAssessmentRepository, diff --git a/IRaCIS.Core.Application/TestService.cs b/IRaCIS.Core.Application/TestService.cs index 5f36aecbe..ffd97ded6 100644 --- a/IRaCIS.Core.Application/TestService.cs +++ b/IRaCIS.Core.Application/TestService.cs @@ -1,4 +1,6 @@ -using DocumentFormat.OpenXml.Office2010.Excel; +using Aliyun.OSS; +using Castle.DynamicProxy.Generators.Emitters.SimpleAST; +using IRaCIS.Core.Application.Helper; using IRaCIS.Core.Application.Service; using IRaCIS.Core.Application.ViewModel; using IRaCIS.Core.Domain.Share; @@ -12,8 +14,15 @@ using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MiniExcelLibs; +using Minio; +using Minio.DataModel.Args; +using NPOI.HPSF; +using NPOI.POIFS.Crypt; +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; @@ -25,7 +34,7 @@ namespace IRaCIS.Application.Services public class TestService : BaseService { - public static int IntValue = 100; + public static int IntValue = 100; private readonly IRepository _dicRepository; private readonly IRepository _trialRepository; @@ -40,7 +49,7 @@ namespace IRaCIS.Application.Services private readonly ILogger _logger; - public TestService(IRepository dicRepository, IRepository trialRepository,ILogger logger + public TestService(IRepository dicRepository, IRepository trialRepository, ILogger logger , IOptionsMonitor systemEmailConfig, IOptionsMonitor basicConfig, IRepository visitTaskRepository, IDistributedLockProvider distributedLockProvider) { @@ -52,31 +61,56 @@ namespace IRaCIS.Application.Services _dicRepository = dicRepository; _trialRepository = trialRepository; - _distributedLockProvider= distributedLockProvider; + _distributedLockProvider = distributedLockProvider; - _logger= logger; + _logger = logger; //_cache = cache; } - [AllowAnonymous] - public async Task TestDistributedLock( ) + public async Task TestEFcore8() { - await _repository.Where().Select(t => t.FullName).FirstNotNullAsync(); + + //var aa= _dicRepository._dbContext.Subject.Where(t => t.Id == Guid.Empty).ExecuteUpdate("FirstName","ddd"); + + await _repository.BatchUpdateAsync(t => t.Id == Guid.Empty, u => new Subject() { FirstName = "fddd", LastName = "sss", UpdateTime = DateTime.Now }); + + await _repository.Where().ExecuteUpdateAsync(t => t.SetProperty(t => t.UpdateTime, u => DateTime.Now)); + + return ResponseOutput.Ok(); + } + // 设置 Ne + + [AllowAnonymous] + public async Task TestMinIO([FromServices] IOSSService oSSService) + { + + 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(); + } + + [AllowAnonymous] + public async Task TestDistributedLock() + { + + await _repository.Where().Select(t => t.FullName).FirstNotNullAsync(); Console.WriteLine($"我进来了当前值是:" + IntValue); _logger.LogWarning($"我进来了当前值是:" + IntValue); var @lock = _distributedLockProvider.CreateLock($"UserAccount"); - using (await @lock.AcquireAsync()) + using (await @lock.AcquireAsync()) { await Task.Delay(4); IntValue--; - _logger.LogWarning( IntValue.ToString()); + _logger.LogWarning(IntValue.ToString()); Console.WriteLine(IntValue); } @@ -85,86 +119,126 @@ namespace IRaCIS.Application.Services [AllowAnonymous] - public async Task GetMemoryStoreData() + public async Task InternationazitionInit() { - + var rows = await MiniExcel.QueryAsync(@"C:\Users\Administrator\Desktop\i18n-new2.xlsx"); - return ResponseOutput.Ok(new { StaticData.En_US_Dic , StaticData.Zh_CN_Dic }); + rows = rows.Where(t => !string.IsNullOrEmpty(t.Code)).ToList(); + + foreach (var row in rows) + { + await _repository.InsertOrUpdateAsync(row); + + } + + await _repository.SaveChangesAsync(); + + return ResponseOutput.Ok(); } - - //[AllowAnonymous] - //public async Task InternationazitionInit() - //{ - - - // var rows = await MiniExcel.QueryAsync(@"C:\Users\Administrator\Desktop\Export\vue.xlsx"); - - // foreach (var row in rows) - // { - // await _repository.InsertOrUpdateAsync(row); - - // } - - // await _repository.SaveChangesAsync(); - - // return ResponseOutput.Ok(); - //} - [AllowAnonymous] [UnitOfWork] public async Task Get() { - - return "修改服务器时间自动发布测试--我又修改了"; + + //Expression> visitTaskLambda = x => x.TrialId == Guid.Empty && x.SubjectId == Guid.Empty && x.TrialReadingCriterionId == Guid.Empty; + + //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); + + //await _visitTaskRepositoryy.BatchUpdateNoTrackingAsync(t => visitTaskIdQueryable.Contains(t.Id), u => new VisitTask() + //{ + // IsFrontTaskNeedSignButNotSign = true + //}); + + + //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"); + + // 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") + // }; + + //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]")); + //} + + //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 "测试自动发布--再次提交"; } - private static Dictionary _replacePatterns = new Dictionary() - { - { "test", "Atlanta Knight" }, - { "GAME_TIME", "7:30pm" }, - { "GAME_NUMBER", "161" }, - { "DATE", "October 18 2018" }, - }; - - private static string ReplaceFunc(string findStr) - { - if (_replacePatterns.ContainsKey(findStr)) - { - return _replacePatterns[findStr]; - } - return findStr; - } - - [AllowAnonymous] - public async Task> testwwwww([FromServices] IWebHostEnvironment env) + public async Task testwwwww([FromServices] IWebHostEnvironment env) { - int count = 200; - - var list=new List(); - - for (int i = 0; i < count; i++) - { - Guid guid = NewId.NextGuid(); - list.Add(guid); - } - - return list; + await Task.CompletedTask; } [AllowAnonymous] public async Task GetEnvironmentName([FromServices] IWebHostEnvironment env) { - var a = IdentifierHelper.CreateGuid("1.2.840.113619.2.416.3358551739363725609857319676326094825431.2.840.113619.2.80.2338912612.50499.1563432834.1.4.11.2.840.113619.2.80.2338912612.50499.1563432835.4b8340000-3e2c-0016-fbdd-08db883b137f"); + var a = IdentifierHelper.CreateGuid("123456"); + var k = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes("123456")); - var c = MD5Helper.Md5("1.2.840.113619.2.416.3358551739363725609857319676326094825431.2.840.113619.2.80.2338912612.50499.1563432834.1.4.11.2.840.113619.2.80.2338912612.50499.1563432835.4b8340000-3e2c-0016-fbdd-08db883b137f"); - + var c = MD5Helper.Md5("123456"); //update DicomInstance set Path = '/IRaCISData/TrialData/' + cast(DicomInstance.TrialId as varchar) + '/' + DicomInstance.SiteId + '/' + DicomInstance.SubjectId + '/' + DicomInstance.SubjectVisitId + '/Dicom/' + DicomInstance.StudyId + '/' + DicomInstance.Id + '.dcm' @@ -255,11 +329,7 @@ namespace IRaCIS.Application.Services - public string PostData(TestModel testModelList) - { - return String.Empty; - } - + #region 历史维护 /// /// 维护临床数据 --一定要在同步表前同步数据才行 /// @@ -299,6 +369,89 @@ namespace IRaCIS.Application.Services } + /// + /// 维护OSS 影像数据 + /// + /// + [AllowAnonymous] + [UnitOfWork] + public async Task OldLocalImageResizeJpg([FromServices] IOSSService oSSService, [FromServices] IRepository _repository, [FromServices] IWebHostEnvironment _hostEnvironment) + { + + var aliConfig = oSSService.ObjectStoreServiceOptions.AliyunOSS; + var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret); + + var rootPath = FileStoreHelper.GetIRaCISRootDataFolder(_hostEnvironment); + + var studyList = _repository.Where(t => t.SeriesList.Any(t => t.ImageResizePath.Length < 10)).Select(t => new { t.TrialId, t.SiteId, t.SubjectId, t.SubjectVisitId, t.Id }).ToList(); + + foreach (var studyitem in studyList) + { + + var relativePath = $"{studyitem.TrialId}/{studyitem.SiteId}/{studyitem.SubjectId}/{studyitem.SubjectVisitId}/{StaticData.Folder.DicomFolder}/{studyitem.Id}/"; + + try + { + string nextMarker = null; + do + { + // 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker + var objectListing = _ossClient.ListObjects(new ListObjectsRequest(aliConfig.bucketName) + { + Prefix = relativePath, + MaxKeys = 1000, + Marker = nextMarker + }); + + var jpgInfoList = objectListing.ObjectSummaries + .Where(summary => summary.Key.EndsWith(".jpg")) + .Select(summary => + { + string fileName = summary.Key.Split('/').Last(); // 提取文件夹名 + return new + { + + Key = summary.Key, + InstanceId = Guid.TryParse( + fileName.Split('.')[0], + out Guid instanceId) + ? instanceId + : Guid.Empty + }; + }) + .Where(info => info.InstanceId != Guid.Empty) + .ToList(); + + foreach (var jpg in jpgInfoList) + { + var seriesId = _repository.Where(t => t.Id == jpg.InstanceId).Select(t => t.SeriesId).FirstOrDefault(); + + await _repository.BatchUpdateAsync(t => t.Id == seriesId, t => new DicomSeries() { ImageResizePath = "/" + jpg.Key }); + } + + // 设置 NextMarker 以获取下一页的数据 + nextMarker = objectListing.NextMarker; + + } while (!string.IsNullOrEmpty(nextMarker)); + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex.Message}"); + } + + await _repository.SaveChangesAsync(); + } + + + + return ResponseOutput.Ok(); + } + + + #endregion + + + } public class TestModel diff --git a/IRaCIS.Core.Domain/Common/Internationalization.cs b/IRaCIS.Core.Domain/Common/Internationalization.cs index dc3292224..e4a7be5e9 100644 --- a/IRaCIS.Core.Domain/Common/Internationalization.cs +++ b/IRaCIS.Core.Domain/Common/Internationalization.cs @@ -33,16 +33,18 @@ namespace IRaCIS.Core.Domain.Models public int State { get; set; } - public string Description { get; set; } + public string Description { get; set; } = string.Empty; - public string Code { get; set; } + public string Code { get; set; } = string.Empty; - public string Value { get; set; } + public string Value { get; set; } = string.Empty; - public string ValueCN { get; set; } + public string ValueCN { get; set; } = string.Empty; public int InternationalizationType { get; set; } + public string FrontType { get; set; }=string.Empty; + } } diff --git a/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs b/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs index 39d940765..734865204 100644 --- a/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs +++ b/IRaCIS.Core.Domain/Image/DicomStudyMonitor.cs @@ -85,7 +85,9 @@ namespace IRaCIS.Core.Domain.Models public bool IsSuccess { get; set; } - public string Note = string.Empty; + public string Note { get; set; } = string.Empty; + + public string RecordPath { get; set; }=string.Empty; } diff --git a/IRaCIS.Core.Domain/_Config/_StaticData.cs b/IRaCIS.Core.Domain/_Config/_StaticData.cs index 83954751b..2791b6f50 100644 --- a/IRaCIS.Core.Domain/_Config/_StaticData.cs +++ b/IRaCIS.Core.Domain/_Config/_StaticData.cs @@ -183,7 +183,10 @@ public static class StaticData - + public static class InternationalData + { + public const string Front = "Front"; + } /// /// 匿名化配置 key diff --git a/IRaCIS.Core.Infra.EFCore/Repository/DynamicRelationalExtensions.cs b/IRaCIS.Core.Infra.EFCore/Repository/DynamicRelationalExtensions.cs new file mode 100644 index 000000000..b2ebb10f0 --- /dev/null +++ b/IRaCIS.Core.Infra.EFCore/Repository/DynamicRelationalExtensions.cs @@ -0,0 +1,120 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace IRaCIS.Core.Infra.EFCore +{ + + public static class DynamicRelationalExtensions + { + static MethodInfo UpdateMethodInfo = + typeof(RelationalQueryableExtensions).GetMethod(nameof(RelationalQueryableExtensions.ExecuteUpdate)); + + static MethodInfo UpdateAsyncMethodInfo = + typeof(RelationalQueryableExtensions).GetMethod(nameof(RelationalQueryableExtensions.ExecuteUpdateAsync)); + + #region 避免使用 + + public static int ExecuteUpdate(this IQueryable query, string fieldName, object? fieldValue) + { + var updateBody = BuildUpdateBody(query.ElementType, + new Dictionary { { fieldName, fieldValue } }); + + return (int)UpdateMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody }); + } + + public static int ExecuteUpdate(this IQueryable query, IReadOnlyDictionary fieldValues) + { + var updateBody = BuildUpdateBody(query.ElementType, fieldValues); + + return (int)UpdateMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody }); + } + public static Task ExecuteUpdateAsync(this IQueryable query, string fieldName, object? fieldValue, CancellationToken cancellationToken = default) + { + var updateBody = BuildUpdateBody(query.ElementType, + new Dictionary { { fieldName, fieldValue } }); + + return (Task)UpdateAsyncMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody, cancellationToken })!; + } + #endregion + + public static Dictionary ExtractFieldValues(this Expression> updateFactory) + { + var dic = new Dictionary(); + var obj = (TSource)Activator.CreateInstance(typeof(TSource)); + + Func func = updateFactory.Compile(); + + TSource applyObj = func(obj); + + var propList = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name) + .Select(propName => typeof(TSource).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList(); + + + foreach (PropertyInfo prop in propList) + { + object value = prop.GetValue(applyObj); + dic.Add(prop.Name, value); + } + + return dic; + } + + + + public static Task ExecuteUpdateAsync(this IQueryable query, IReadOnlyDictionary fieldValues, CancellationToken cancellationToken = default) + { + var updateBody = BuildUpdateBody(query.ElementType, fieldValues); + + return (Task)UpdateAsyncMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody, cancellationToken })!; + } + + + static LambdaExpression BuildUpdateBody(Type entityType, IReadOnlyDictionary fieldValues) + { + var setParam = Expression.Parameter(typeof(SetPropertyCalls<>).MakeGenericType(entityType), "s"); + var objParam = Expression.Parameter(entityType, "e"); + + Expression setBody = setParam; + + foreach (var pair in fieldValues) + { + var propExpression = Expression.PropertyOrField(objParam, pair.Key); + var valueExpression = ValueForType(propExpression.Type, pair.Value); + + // s.SetProperty(e => e.SomeField, value) + setBody = Expression.Call(setBody, nameof(SetPropertyCalls.SetProperty), + new[] { propExpression.Type }, Expression.Lambda(propExpression, objParam), valueExpression); + + } + + // s => s.SetProperty(e => e.SomeField, value) + var updateBody = Expression.Lambda(setBody, setParam); + + return updateBody; + } + + static Expression ValueForType(Type desiredType, object? value) + { + if (value == null) + { + return Expression.Default(desiredType); + } + + if (value.GetType() != desiredType) + { + return Expression.Convert(Expression.Constant(value), desiredType); + } + + return Expression.Constant(value); + } + } + +} diff --git a/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs b/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs index 5428578f6..c20c0704c 100644 --- a/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs +++ b/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs @@ -157,7 +157,7 @@ namespace IRaCIS.Core.Infra.EFCore { if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter)); - return await _dbContext.Set().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0; + return await _dbContext.Set().IgnoreQueryFilters().Where(deleteFilter).ExecuteDeleteAsync() > 0; } @@ -166,34 +166,64 @@ namespace IRaCIS.Core.Infra.EFCore { if (where == null) throw new ArgumentNullException(nameof(where)); - var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList(); - var hasPropNameList = bindings.Select(t => t.Member.Name).ToList(); + #region history 使用扩展删除包,同时自动赋值更新人 更新时间 + + //var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList(); + + //var hasPropNameList = bindings.Select(t => t.Member.Name).ToList(); - if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T))) + //if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T))) + //{ + + // if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime))) + // { + // bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now))); + + // } + + // if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId))) + // { + // bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(updateUserId))); + + // } + //} + + //var member = Expression.MemberInit(Expression.New(typeof(T)), bindings); + + //var factory = Expression.Lambda>(member, Expression.Parameter(typeof(T), "x")); + + //return await _dbContext.Set().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0; + + #endregion + + + #region efcore 7 & 8 { + var fieldValues = updateFactory.ExtractFieldValues(); - if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime))) + var hasPropNameList = ((MemberInitExpression)updateFactory.Body).Bindings.Select(t => t.Member.Name).ToList(); + + if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T))) { - bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now))); + if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime))) + { + fieldValues.Add(nameof(IAuditUpdate.UpdateTime), DateTime.Now); + } + + if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId))) + { + fieldValues.Add(nameof(IAuditUpdate.UpdateUserId), updateUserId); + } } - if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId))) - { - bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(updateUserId))); - - } + return await _dbContext.Set().IgnoreQueryFilters().Where(where).ExecuteUpdateAsync(fieldValues).ConfigureAwait(false) > 0; } - var member = Expression.MemberInit(Expression.New(typeof(T)), bindings); - - var factory = Expression.Lambda>(member, Expression.Parameter(typeof(T), "x")); - - - return await _dbContext.Set().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0; + #endregion