Merge branch 'Test.Study' of http://192.168.3.68:2000/XCKJ/irc-netcore-api into Test.Study
continuous-integration/drone/push Build is passing Details

Uat_Study
he 2024-01-25 16:00:35 +08:00
commit efe96203d9
39 changed files with 926 additions and 546 deletions

View File

@ -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<Startup>();
}).UseSerilog()
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
private static async Task InitCache(IHost host)
{
var _repository = host.Services.GetService(typeof(IRepository)) as IRepository;
//初始化 国际化数据,并且监测国际化文件变更
//await InternationalizationHelper.InitInternationlizationDataAndWatchJsonFileAsync(_repository);
}
}
}

306
IRaCIS.Core.API/Progranm.cs Normal file
View File

@ -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 =>
{
containerBuilder.RegisterModule<AutofacModuleSetup>();
})
.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<LogActionFilter>();
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
{
options.Filters.Add<LimitUserRequestAuthorization>();
}
})
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_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<ConsistencyVerificationHandler>());
// 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<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
//Dicom影像渲染图片 跨平台
builder.Services.AddDicomSetup();
// 实时应用
builder.Services.AddSignalR();
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
//builder.Services.AddMemoryCache();
#region 历史废弃配置
////上传限制 配置
//builder.Services.Configure<FormOptions>(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<MultiDiskStaticFilesMiddleware>();
//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>("/UploadHub");
app.MapHealthChecks("/health");
// Serilog
SerilogExtension.AddSerilogSetup(enviromentName, app.Services);
var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
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);
}

View File

@ -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<Startup> _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<AutofacModuleSetup>();
#region Test
//containerBuilder.RegisterType<ClinicalDataService>().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<BaseService>();
// var test = scope.Resolve<ClinicalDataService>();
// var test2 = scope.Resolve<IClinicalDataService>();
// var test3 = scope.Resolve<IEFUnitOfWork<IRaCISDBContext>>();
//}
#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<LogActionFilter>();
options.Filters.Add<ModelActionFilter>();
options.Filters.Add<ProjectExceptionFilter>();
options.Filters.Add<UnitOfWorkFilter>();
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
{
options.Filters.Add<LimitUserRequestAuthorization>();
}
})
.AddNewtonsoftJsonSetup(); // NewtonsoftJson 序列化 处理
services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
services.AddOptions().Configure<ObjectStoreServiceOptions>(_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<ConsistencyVerificationHandler>());
// 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<FormOptions>(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<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
//Dicom影像渲染图片 跨平台
services.AddDicomSetup();
// 实时应用
services.AddSignalR();
services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
//services.AddSingleton<IImportResultFilter, ImportResultFilteTest>();
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<AuthMiddleware>();
// 特殊异常处理 比如 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<MultiDiskStaticFilesMiddleware>();
app.UseAuthentication();
//app.UseJwtBearerQueryString();
app.UseAuthorization();
////文件伺服 必须带Token 访问
////app.UseIRacisHostStaticFileStore(env);
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<UploadHub>("/UploadHub");
endpoints.MapHealthChecks("/health");
});
var hangfireJobService = app.ApplicationServices.GetRequiredService<IIRaCISHangfireJob>();
await hangfireJobService.InitHangfireJobTaskAsync();
//有的时候每调用
//HangfireJobHelper.NotImmediatelyOnceOnlyJob<IIRaCISHangfireJob>(t => t.InitHangfireJobTaskAsync(),TimeSpan.FromSeconds(1));
}
}
}

View File

@ -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;
});

View File

@ -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();

View File

@ -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();

View File

@ -31,7 +31,7 @@
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -28,7 +28,7 @@
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -18,7 +18,7 @@
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -14,7 +14,7 @@
Thank you for using our IRC imaging system.
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
{0}
{0}.
</div>
<div style='line-height: 24px;font-size: 16px;color:#333;margin-top: 20px;padding-bottom: 40px;'>

View File

@ -42,7 +42,7 @@
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -43,7 +43,7 @@
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -40,7 +40,7 @@
</a>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -8,7 +8,7 @@
<div style='padding-left: 40px;background: #f6f6f6;font-size: 16px'>
<div style='padding-top: 20px;font-size: 16px'>
<div style='line-height: 40px;font-size: 16px'>
Dear {0} ,
Dear {0} ,
</div>
<div style='line-height: 40px;padding-left: 40px;margin-bottom: 10px;font-size: 16px'>
The site survey questionnaire you filled in has been rejected. Details are as follows:

View File

@ -43,7 +43,7 @@
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -43,7 +43,7 @@
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -18,7 +18,7 @@
</div>
<div style='line-height: 24px;font-size: 14px;color:#333;margin-top: 20px;padding-bottom: 40px;'>
<div>祝您顺利!/Best Regards</div>
<div>祝您顺利!</div>
<div style="font-size: 14px;">展影医疗</div>
</div>
</div>

View File

@ -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);
}

View File

@ -547,6 +547,32 @@
InternationalizationService
</summary>
</member>
<member name="M:IRaCIS.Core.Application.Service.InternationalizationService.GetFrontInternationalizationList">
<summary>
前端国际化内容接口
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.InternationalizationService.BatchAddOrUpdateFrontInternationalization(System.Collections.Generic.List{IRaCIS.Core.Application.ViewModel.BatchInternationalizationDto})">
<summary>
前端批量提交,后端判断不存在就添加,存在就更新
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.InternationalizationService.BatchAddInternationalization(IRaCIS.Core.Application.ViewModel.BatchAddInternationalization)">
<summary>
后端之前批量添加接口
</summary>
<param name="batchAdd"></param>
<returns></returns>
</member>
<member name="M:IRaCIS.Core.Application.Service.InternationalizationService.AddOrUpdateInternationalization(IRaCIS.Core.Application.ViewModel.InternationalizationAddOrEdit)">
<summary>
前后端添加的时候,区分了,前端判断重复多了多了一个路由 路由+标识唯一
</summary>
<param name="addOrEditInternationalization"></param>
<returns></returns>
</member>
<member name="T:IRaCIS.Core.Application.Service.PublishLogService">
<summary>
PublishLogService
@ -12413,6 +12439,12 @@
</summary>
<returns></returns>
</member>
<member name="M:IRaCIS.Application.Services.TestService.OldLocalImageResizeJpg(IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository,Microsoft.AspNetCore.Hosting.IWebHostEnvironment)">
<summary>
维护OSS 影像数据
</summary>
<returns></returns>
</member>
<member name="T:IRaCIS.Application.Contracts.DoctorSearchDTO">
<summary>
Reviewer 列表查询参数

View File

@ -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)
{

View File

@ -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
{
}
}

View File

@ -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<Internationalization> _internationalizationRepository;
public InternationalizationService(IRepository<Internationalization> internationalizationRepository)
public InternationalizationService(IRepository<Internationalization> internationalizationRepository, IEasyCachingProvider provider)
{
_internationalizationRepository = internationalizationRepository;
_provider = provider;
}
/// <summary>
/// 前端国际化内容接口
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public async Task<List<InternationalizationSimpleDto>> GetFrontInternationalizationList()
[HttpGet]
public async Task<IEnumerable<InternationalizationSimpleDto>> GetFrontInternationalizationList()
{
var cacheList= _provider.Get<List<InternationalizationSimpleDto>>(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<List<InternationalizationSimpleDto>>(StaticData.InternationalData.Front, list, TimeSpan.FromDays(1));
return list;
}
}
/// <summary>
/// 前端批量提交,后端判断不存在就添加,存在就更新
/// </summary>
/// <returns></returns>
[AllowAnonymous]
public async Task<IResponseOutput> BatchAddOrUpdateFrontInternationalization(List<BatchInternationalizationDto> 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<Internationalization>(item);
mapItem.InternationalizationType = 0;
mapItem.State = 1;
await _internationalizationRepository.AddAsync(mapItem);
}
}
await _internationalizationRepository.SaveChangesAsync();
//清理缓存
_provider.Set<List<InternationalizationSimpleDto>>(StaticData.InternationalData.Front, new List<InternationalizationSimpleDto>(), TimeSpan.FromDays(1));
return ResponseOutput.Ok();
}
[HttpPost]
@ -63,6 +115,11 @@ namespace IRaCIS.Core.Application.Service
return pageList;
}
/// <summary>
/// 后端之前批量添加接口
/// </summary>
/// <param name="batchAdd"></param>
/// <returns></returns>
[HttpPost]
public async Task<IResponseOutput> BatchAddInternationalization(BatchAddInternationalization batchAdd)
{
@ -91,17 +148,34 @@ namespace IRaCIS.Core.Application.Service
return ResponseOutput.Ok();
}
/// <summary>
/// 前后端添加的时候,区分了,前端判断重复多了多了一个路由 路由+标识唯一
/// </summary>
/// <param name="addOrEditInternationalization"></param>
/// <returns></returns>
public async Task<IResponseOutput> AddOrUpdateInternationalization(InternationalizationAddOrEdit addOrEditInternationalization)
{
var internationalizationType = addOrEditInternationalization.InternationalizationType;
//后端验证标识重复与否
var verifyExp1 = new EntityVerifyExp<Internationalization>()
{
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<Internationalization>()
{
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)
{

View File

@ -74,6 +74,9 @@ namespace IRaCIS.Core.Application.Service
CreateMap<Internationalization, InternationalizationView>();
CreateMap<Internationalization, InternationalizationAddOrEdit>().ReverseMap();
CreateMap<Internationalization, BatchInternationalizationDto>().ReverseMap();
CreateMap<BatchAddInternationalizationDto, InternationalizationAddOrEdit>();

View File

@ -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);
}

View File

@ -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; }

View File

@ -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,

View File

@ -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()) ));

View File

@ -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);

View File

@ -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<UserLog> _userLogRepository;
private readonly IDistributedLockProvider _distributedLockProvider;
private readonly IMemoryCache _cache;
private readonly IEasyCachingProvider _cache;
private readonly IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig;
@ -34,7 +35,7 @@ namespace IRaCIS.Application.Services
IMailVerificationService mailVerificationService,
IRepository<VerificationCode> verificationCodeRepository,
IMemoryCache cache,
IEasyCachingProvider cache,
IRepository<TrialUser> userTrialRepository,
IOptionsMonitor<ServiceVerifyConfigOption> verifyConfig,
IRepository<UserLog> userLogRepository
@ -629,7 +630,7 @@ namespace IRaCIS.Application.Services
string cacheKey = $"{cachePrefix}{userName}";
// 从缓存中获取登录失败次数
int? failCount = _cache.Get<int?>(cacheKey);
int? failCount = _cache.Get<int?>(cacheKey).Value;
if (failCount == null)
{

View File

@ -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

View File

@ -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; }
}

View File

@ -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))

View File

@ -89,7 +89,6 @@ namespace IRaCIS.Application.Services
IRepository<User> userRepository,
IEasyCachingProvider provider,
IRepository<ReadingCustomTag> readingCustomTagRepository,
IMemoryCache cache,
IRepository<ReadingSystemCriterionDictionary> readingCriterionDictionaryRepository,
IRepository<ReadingTrialCriterionDictionary> readingTrialCriterionDictionaryRepository,
IRepository<TumorAssessment_RECIST1Point1> tumorAssessmentRepository,

View File

@ -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<Dictionary> _dicRepository;
private readonly IRepository<Trial> _trialRepository;
@ -40,7 +49,7 @@ namespace IRaCIS.Application.Services
private readonly ILogger<TestService> _logger;
public TestService(IRepository<Dictionary> dicRepository, IRepository<Trial> trialRepository,ILogger<TestService> logger
public TestService(IRepository<Dictionary> dicRepository, IRepository<Trial> trialRepository, ILogger<TestService> logger
, IOptionsMonitor<SystemEmailSendConfig> systemEmailConfig, IOptionsMonitor<ServiceVerifyConfigOption> basicConfig, IRepository<VisitTask> 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<IResponseOutput> TestDistributedLock( )
public async Task<IResponseOutput> TestEFcore8()
{
await _repository.Where<User>().Select(t => t.FullName).FirstNotNullAsync();
//var aa= _dicRepository._dbContext.Subject.Where(t => t.Id == Guid.Empty).ExecuteUpdate("FirstName","ddd");
await _repository.BatchUpdateAsync<Subject>(t => t.Id == Guid.Empty, u => new Subject() { FirstName = "fddd", LastName = "sss", UpdateTime = DateTime.Now });
await _repository.Where<Subject>().ExecuteUpdateAsync(t => t.SetProperty(t => t.UpdateTime, u => DateTime.Now));
return ResponseOutput.Ok();
}
// 设置 Ne
[AllowAnonymous]
public async Task<IResponseOutput> 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<IResponseOutput> TestDistributedLock()
{
await _repository.Where<User>().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<IResponseOutput> GetMemoryStoreData()
public async Task<IResponseOutput> InternationazitionInit()
{
var rows = await MiniExcel.QueryAsync<InternationalizationAddOrEdit>(@"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<Internationalization, InternationalizationAddOrEdit>(row);
}
await _repository.SaveChangesAsync();
return ResponseOutput.Ok();
}
//[AllowAnonymous]
//public async Task<IResponseOutput> InternationazitionInit()
//{
// var rows = await MiniExcel.QueryAsync<InternationalizationAddOrEdit>(@"C:\Users\Administrator\Desktop\Export\vue.xlsx");
// foreach (var row in rows)
// {
// await _repository.InsertOrUpdateAsync<Internationalization, InternationalizationAddOrEdit>(row);
// }
// await _repository.SaveChangesAsync();
// return ResponseOutput.Ok();
//}
[AllowAnonymous]
[UnitOfWork]
public async Task<string> Get()
{
return "修改服务器时间自动发布测试--我又修改了";
//Expression<Func<VisitTask, bool>> 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>() { 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<VisitTask>(t => t.TrialId == Guid.Empty).DistinctBy(t => t.VisitTaskNum).ToListAsync();
//await _repository.BatchUpdateAsync<VisitTask>(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<ReadingQuestionTrial>()
// join item2 in _repository.Where<ReadingQuestionTrial>() 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<ClinicalDataTrialSet>(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74")).ToListAsync();
////await _repository.BatchDeleteAsync<ClinicalDataTrialSet>(t => t.TrialId == Guid.Parse("40400000-3e2c-0016-239b-08da581f0e74"));
//await _repository.AddRangeAsync(list, true);
//await _repository.SaveChangesAsync();
//await _repository.BatchUpdateAsync<DataInspection>(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<Dictionary>().Select(t => t.TranslateValue(t.Value, t.ValueCN, true)).ToListAsync();
//var list2 = await _repository.Where<Dictionary>().Select(t => t.TranslateValue(t.Value, t.ValueCN, false)).ToListAsync();
return "测试自动发布--再次提交";
}
private static Dictionary<string, string> _replacePatterns = new Dictionary<string, string>()
{
{ "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<List<Guid>> testwwwww([FromServices] IWebHostEnvironment env)
public async Task testwwwww([FromServices] IWebHostEnvironment env)
{
int count = 200;
var list=new List<Guid>();
for (int i = 0; i < count; i++)
{
Guid guid = NewId.NextGuid();
list.Add(guid);
}
return list;
await Task.CompletedTask;
}
[AllowAnonymous]
public async Task<object> 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 历史维护
/// <summary>
/// 维护临床数据 --一定要在同步表前同步数据才行
/// </summary>
@ -299,6 +369,89 @@ namespace IRaCIS.Application.Services
}
/// <summary>
/// 维护OSS 影像数据
/// </summary>
/// <returns></returns>
[AllowAnonymous]
[UnitOfWork]
public async Task<IResponseOutput> 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<DicomStudy>(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<DicomInstance>(t => t.Id == jpg.InstanceId).Select(t => t.SeriesId).FirstOrDefault();
await _repository.BatchUpdateAsync<DicomSeries>(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

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -183,7 +183,10 @@ public static class StaticData
public static class InternationalData
{
public const string Front = "Front";
}
/// <summary>
/// 匿名化配置 key

View File

@ -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<string, object?> { { fieldName, fieldValue } });
return (int)UpdateMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody });
}
public static int ExecuteUpdate(this IQueryable query, IReadOnlyDictionary<string, object?> fieldValues)
{
var updateBody = BuildUpdateBody(query.ElementType, fieldValues);
return (int)UpdateMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody });
}
public static Task<int> ExecuteUpdateAsync(this IQueryable query, string fieldName, object? fieldValue, CancellationToken cancellationToken = default)
{
var updateBody = BuildUpdateBody(query.ElementType,
new Dictionary<string, object?> { { fieldName, fieldValue } });
return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody, cancellationToken })!;
}
#endregion
public static Dictionary<string, object?> ExtractFieldValues<TSource>(this Expression<Func<TSource, TSource>> updateFactory)
{
var dic = new Dictionary<string, object?>();
var obj = (TSource)Activator.CreateInstance(typeof(TSource));
Func<TSource, TSource> 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<int> ExecuteUpdateAsync(this IQueryable query, IReadOnlyDictionary<string, object?> fieldValues, CancellationToken cancellationToken = default)
{
var updateBody = BuildUpdateBody(query.ElementType, fieldValues);
return (Task<int>)UpdateAsyncMethodInfo.MakeGenericMethod(query.ElementType).Invoke(null, new object?[] { query, updateBody, cancellationToken })!;
}
static LambdaExpression BuildUpdateBody(Type entityType, IReadOnlyDictionary<string, object?> 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<object>.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);
}
}
}

View File

@ -157,7 +157,7 @@ namespace IRaCIS.Core.Infra.EFCore
{
if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter));
return await _dbContext.Set<T>().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0;
return await _dbContext.Set<T>().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<Func<T, T>>(member, Expression.Parameter(typeof(T), "x"));
//return await _dbContext.Set<T>().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<T>().IgnoreQueryFilters().Where(where).ExecuteUpdateAsync(fieldValues).ConfigureAwait(false) > 0;
}
var member = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var factory = Expression.Lambda<Func<T, T>>(member, Expression.Parameter(typeof(T), "x"));
return await _dbContext.Set<T>().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0;
#endregion