增加pacs 直连 scp项目
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
d094f22ab2
commit
9f66a932fc
|
@ -0,0 +1,37 @@
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
|
||||||
|
{
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
public ModelActionFilter(IStringLocalizer localizer)
|
||||||
|
{
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
if (!context.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
|
||||||
|
var validationErrors = context.ModelState
|
||||||
|
.Keys
|
||||||
|
.SelectMany(k => context.ModelState[k]!.Errors)
|
||||||
|
.Select(e => e.ErrorMessage)
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
//---提供给接口的参数无效。
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ModelAction_InvalidAPIParameter"] + JsonConvert.SerializeObject( validationErrors)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
#region snippet_DisableFormValueModelBindingAttribute
|
||||||
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
||||||
|
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
|
||||||
|
{
|
||||||
|
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var factories = context.ValueProviderFactories;
|
||||||
|
//factories.RemoveType<FormValueProviderFactory>();
|
||||||
|
factories.RemoveType<FormFileValueProviderFactory>();
|
||||||
|
factories.RemoveType<JQueryFormValueProviderFactory>();
|
||||||
|
context.HttpContext.Request.EnableBuffering();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Filter
|
||||||
|
{
|
||||||
|
public class ProjectExceptionFilter : Attribute, IExceptionFilter
|
||||||
|
{
|
||||||
|
private readonly ILogger<ProjectExceptionFilter> _logger;
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer;
|
||||||
|
|
||||||
|
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_localizer = localizer;
|
||||||
|
}
|
||||||
|
public void OnException(ExceptionContext context)
|
||||||
|
{
|
||||||
|
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
|
||||||
|
|
||||||
|
if (!context.ExceptionHandled)
|
||||||
|
{
|
||||||
|
if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
|
||||||
|
{
|
||||||
|
//---并发更新,当前不允许该操作
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
|
||||||
|
{
|
||||||
|
var error = context.Exception as BusinessValidationFailedException;
|
||||||
|
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, error!.Code));
|
||||||
|
}
|
||||||
|
else if(context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk( context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["Project_ExceptionContactDeveloper"] + (context.Exception.InnerException is null ? (context.Exception.Message /*+ context.Exception.StackTrace*/)
|
||||||
|
: (context.Exception.InnerException?.Message /*+ context.Exception.InnerException?.StackTrace*/)), ApiResponseCodeEnum.ProgramException));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
_logger.LogError(context.Exception.InnerException is null ? (context.Exception.Message + context.Exception.StackTrace) : (context.Exception.InnerException?.Message + context.Exception.InnerException?.StackTrace));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//继续
|
||||||
|
}
|
||||||
|
context.ExceptionHandled = true;//标记当前异常已经被处理过了
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
|
||||||
|
namespace IRaCIS.Application.Services.BusinessFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码
|
||||||
|
/// by zhouhang 2021.09.12 周末
|
||||||
|
/// </summary>
|
||||||
|
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 异步版本
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="next"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (context.Result is ObjectResult objectResult)
|
||||||
|
{
|
||||||
|
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
|
||||||
|
|
||||||
|
//是200 并且没有包装 那么包装结果
|
||||||
|
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//if (objectResult.Value == null)
|
||||||
|
//{
|
||||||
|
// var apiResponse = ResponseOutput.DBNotExist();
|
||||||
|
|
||||||
|
// objectResult.Value = apiResponse;
|
||||||
|
// objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
var type = objectResult.Value?.GetType();
|
||||||
|
|
||||||
|
|
||||||
|
if ( type!=null&& type.IsGenericType&&(type.GetGenericTypeDefinition()==typeof(ValueTuple<,>)|| type.GetGenericTypeDefinition()==typeof(Tuple<,>)))
|
||||||
|
{
|
||||||
|
|
||||||
|
//报错
|
||||||
|
//var tuple = (object, object))objectResult.Value;
|
||||||
|
|
||||||
|
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
|
||||||
|
//var apiResponse = ResponseOutput.Ok(val1, val2);
|
||||||
|
|
||||||
|
//OK
|
||||||
|
var tuple = (dynamic)objectResult.Value;
|
||||||
|
var apiResponse = ResponseOutput.Ok(tuple.Item1, tuple.Item2);
|
||||||
|
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var apiResponse = ResponseOutput.Ok(objectResult.Value);
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
//如果不是200 是IResponseOutput 不处理
|
||||||
|
else if (statusCode != 200 && (objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(statusCode != 200&&!(objectResult.Value is IResponseOutput))
|
||||||
|
{
|
||||||
|
//---程序错误,请联系开发人员。
|
||||||
|
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
|
||||||
|
|
||||||
|
objectResult.Value = apiResponse;
|
||||||
|
objectResult.DeclaredType = apiResponse.GetType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
await next.Invoke();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
throw new ArgumentNullException(nameof(type));
|
||||||
|
|
||||||
|
if (type == typeof(Tuple))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
while (type != null)
|
||||||
|
{
|
||||||
|
if (type.IsGenericType)
|
||||||
|
{
|
||||||
|
var genType = type.GetGenericTypeDefinition();
|
||||||
|
if (genType == typeof(Tuple<>)
|
||||||
|
|| genType == typeof(Tuple<,>)
|
||||||
|
|| genType == typeof(Tuple<,>))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!checkBaseTypes)
|
||||||
|
break;
|
||||||
|
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Application.Helper;
|
||||||
|
|
||||||
|
public static class ImageHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
// 采用ImageSharp组件 跨平台,不依赖windows 平台图像处理组件,这里大坑(net 6 下,之前由于Dicom处理最新组件 依赖了这个库的一个旧版本,导致缩略图bug,单独升级依赖组件才解决)
|
||||||
|
public static void ResizeSave(string filePath, string? fileStorePath)
|
||||||
|
{
|
||||||
|
|
||||||
|
fileStorePath = fileStorePath ?? filePath + ".preview.jpeg";
|
||||||
|
|
||||||
|
using (var image = SixLabors.ImageSharp.Image.Load(filePath))
|
||||||
|
{
|
||||||
|
|
||||||
|
image.Mutate(x => x.Resize(500, 500));
|
||||||
|
|
||||||
|
image.Save(fileStorePath);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static Stream RenderPreviewJpeg(string filePath)
|
||||||
|
{
|
||||||
|
string jpegPath = filePath + ".preview.jpg";
|
||||||
|
|
||||||
|
if (!File.Exists(jpegPath))
|
||||||
|
{
|
||||||
|
using (Stream stream = new FileStream(jpegPath, FileMode.Create))
|
||||||
|
{
|
||||||
|
DicomImage image = new DicomImage(filePath);
|
||||||
|
|
||||||
|
var sharpimage = image.RenderImage().AsSharpImage();
|
||||||
|
|
||||||
|
sharpimage.Save(stream, new JpegEncoder());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileStream(jpegPath, FileMode.Open);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemovePreviewJpeg(string filePath)
|
||||||
|
{
|
||||||
|
string jpegPath = filePath + ".preview.jpg";
|
||||||
|
if (File.Exists(jpegPath)) File.Delete(jpegPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
using Autofac;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Application.Services;
|
||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
// ReSharper disable once IdentifierTypo
|
||||||
|
public class AutofacModuleSetup : Autofac.Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder containerBuilder)
|
||||||
|
{
|
||||||
|
|
||||||
|
#region byzhouhang 20210917 此处注册泛型仓储 可以减少Domain层 和Infra.EFcore 两层 空的仓储接口定义和 仓储文件定义
|
||||||
|
|
||||||
|
containerBuilder.RegisterGeneric(typeof(Repository<>))
|
||||||
|
.As(typeof(IRepository<>)).InstancePerLifetimeScope();//注册泛型仓储
|
||||||
|
|
||||||
|
containerBuilder.RegisterType<Repository>().As<IRepository>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 指定控制器也由autofac 来进行实例获取 https://www.cnblogs.com/xwhqwer/p/15320838.html
|
||||||
|
|
||||||
|
//获取所有控制器类型并使用属性注入
|
||||||
|
containerBuilder.RegisterAssemblyTypes(typeof(BaseService).Assembly)
|
||||||
|
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||||
|
.PropertiesAutowired();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.SCP.dll");
|
||||||
|
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
|
.PropertiesAutowired().AsImplementedInterfaces();
|
||||||
|
|
||||||
|
|
||||||
|
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
|
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using Medallion.Threading.SqlServer;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class EFSetup
|
||||||
|
{
|
||||||
|
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
//services.AddScoped<DbContext, IRaCISDBContext>();
|
||||||
|
|
||||||
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
|
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||||
|
services.AddDbContext<IRaCISDBContext>(options =>
|
||||||
|
{
|
||||||
|
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||||
|
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
|
|
||||||
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
|
|
||||||
|
options.UseProjectables();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||||
|
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||||
|
{
|
||||||
|
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
||||||
|
|
||||||
|
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
||||||
|
});
|
||||||
|
|
||||||
|
//services.AddAssemblyTriggers(typeof(SubjectVisitImageDateTrigger).Assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
|
||||||
|
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public static class NewtonsoftJsonSetup
|
||||||
|
{
|
||||||
|
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddHttpContextAccessor();
|
||||||
|
services.AddScoped<IOSSService,OSSService>();
|
||||||
|
|
||||||
|
builder.AddNewtonsoftJson(options =>
|
||||||
|
{
|
||||||
|
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||||
|
// 忽略循环引用
|
||||||
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||||
|
|
||||||
|
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||||
|
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||||
|
// 设置时间格式
|
||||||
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
|
||||||
|
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddControllersAsServices()//动态webApi属性注入需要
|
||||||
|
.ConfigureApiBehaviorOptions(o =>
|
||||||
|
{
|
||||||
|
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
Newtonsoft.Json.JsonSerializerSettings setting = new Newtonsoft.Json.JsonSerializerSettings();
|
||||||
|
JsonConvert.DefaultSettings = new Func<JsonSerializerSettings>(() =>
|
||||||
|
{
|
||||||
|
//日期类型默认格式化处理
|
||||||
|
setting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
|
setting.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
|
||||||
|
return setting;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
|
{
|
||||||
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
var list= type.GetProperties()
|
||||||
|
.Select(p =>
|
||||||
|
{
|
||||||
|
var jp = base.CreateProperty(p, memberSerialization);
|
||||||
|
jp.ValueProvider = new NullToEmptyStringValueProvider(p);
|
||||||
|
return jp;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
var uu = list.Select(t => t.PropertyName).ToList();
|
||||||
|
|
||||||
|
//获取复杂对象属性
|
||||||
|
properties = properties.TakeWhile(t => !uu.Contains(t.PropertyName)).ToList();
|
||||||
|
|
||||||
|
list.AddRange(properties);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
|
||||||
|
public class NullToEmptyStringValueProvider : IValueProvider
|
||||||
|
{
|
||||||
|
PropertyInfo _MemberInfo;
|
||||||
|
public NullToEmptyStringValueProvider(PropertyInfo memberInfo)
|
||||||
|
{
|
||||||
|
_MemberInfo = memberInfo;
|
||||||
|
}
|
||||||
|
public object GetValue(object target)
|
||||||
|
{
|
||||||
|
object result = _MemberInfo.GetValue(target);
|
||||||
|
if (_MemberInfo.PropertyType == typeof(string) && result == null) result = "";
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(String[]) && result == null) result = new string[] { };
|
||||||
|
//else if (_MemberInfo.PropertyType == typeof(Nullable<Int32>) && result == null) result = 0;
|
||||||
|
else if (_MemberInfo.PropertyType == typeof(Nullable<Decimal>) && result == null) result = 0.00M;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
public void SetValue(object target, object value)
|
||||||
|
{
|
||||||
|
|
||||||
|
if(_MemberInfo.PropertyType == typeof(string))
|
||||||
|
{
|
||||||
|
//去掉前后空格
|
||||||
|
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_MemberInfo.SetValue(target, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.13.0" />
|
||||||
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
|
<PackageReference Include="DistributedLock.Core" Version="1.0.6" />
|
||||||
|
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.4" />
|
||||||
|
<PackageReference Include="fo-dicom" Version="5.1.2" />
|
||||||
|
<PackageReference Include="fo-dicom.Codecs" Version="5.12.0" />
|
||||||
|
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.1.2" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.4" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||||
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
|
<PackageReference Include="Minio" Version="6.0.2" />
|
||||||
|
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
||||||
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="NewId" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
||||||
|
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj" />
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,208 @@
|
||||||
|
|
||||||
|
using Autofac;
|
||||||
|
using Autofac.Extensions.DependencyInjection;
|
||||||
|
using AutoMapper.EquivalencyExpression;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using FellowOakDicom.Imaging.NativeCodec;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.SCP;
|
||||||
|
using IRaCIS.Core.SCP.Filter;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using MassTransit;
|
||||||
|
using MassTransit.NewIdProviders;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
|
||||||
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||||
|
|
||||||
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
|
{
|
||||||
|
EnvironmentName = enviromentName
|
||||||
|
});
|
||||||
|
|
||||||
|
#region 兼容windows 服务命令行的方式
|
||||||
|
|
||||||
|
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--port"));
|
||||||
|
|
||||||
|
if (urlsIndex > -1)
|
||||||
|
{
|
||||||
|
var port = args[urlsIndex].Substring("--port=".Length);
|
||||||
|
Console.WriteLine(port);
|
||||||
|
builder.WebHost.UseUrls($"http://0.0.0.0:{port}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 主机配置
|
||||||
|
|
||||||
|
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||||
|
|
||||||
|
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||||
|
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||||
|
builder.Host
|
||||||
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
|
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||||
|
{
|
||||||
|
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||||
|
})
|
||||||
|
.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>();
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
|
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
builder.Services.AddOptions().Configure<DicomSCPServiceOption>(_configuration.GetSection("DicomSCPServiceConfig"));
|
||||||
|
|
||||||
|
|
||||||
|
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||||
|
//动态webApi 目前存在的唯一小坑是生成api上服务上的动态代理AOP失效 间接掉用不影响
|
||||||
|
builder.Services
|
||||||
|
.AddDynamicWebApi(dynamicWebApiOption =>
|
||||||
|
{
|
||||||
|
//默认是 api
|
||||||
|
dynamicWebApiOption.DefaultApiPrefix = "";
|
||||||
|
//首字母小写
|
||||||
|
dynamicWebApiOption.GetRestFulActionName = (actionName) => char.ToLower(actionName[0]) + actionName.Substring(1);
|
||||||
|
//删除 Service后缀
|
||||||
|
dynamicWebApiOption.RemoveControllerPostfixes.Add("Service");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//AutoMapper
|
||||||
|
builder.Services.AddAutoMapper(automapper =>
|
||||||
|
{
|
||||||
|
|
||||||
|
automapper.AddCollectionMappers();
|
||||||
|
|
||||||
|
|
||||||
|
}, typeof(BaseService).Assembly);
|
||||||
|
|
||||||
|
//EF ORM QueryWithNoLock
|
||||||
|
builder.Services.AddEFSetup(_configuration);
|
||||||
|
|
||||||
|
|
||||||
|
//转发头设置 获取真实IP
|
||||||
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardedHeaders =
|
||||||
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
});
|
||||||
|
|
||||||
|
//Dicom影像渲染图片 跨平台
|
||||||
|
//builder.Services.AddDicomSetup();
|
||||||
|
new DicomSetupBuilder()
|
||||||
|
.RegisterServices(s =>
|
||||||
|
s.AddFellowOakDicom()
|
||||||
|
.AddTranscoderManager<NativeTranscoderManager>()
|
||||||
|
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
||||||
|
.AddImageManager<ImageSharpImageManager>())
|
||||||
|
.SkipValidation()
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Add services to the container.
|
||||||
|
|
||||||
|
builder.Services.AddControllers();
|
||||||
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
|
builder.Services.AddSwaggerGen();
|
||||||
|
|
||||||
|
var app = builder.Build();
|
||||||
|
|
||||||
|
// Configure the HTTP request pipeline.
|
||||||
|
//if (app.Environment.IsDevelopment())
|
||||||
|
//{
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI();
|
||||||
|
//}
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.MapControllers();
|
||||||
|
|
||||||
|
#region 日志
|
||||||
|
|
||||||
|
Log.Logger = new LoggerConfiguration()
|
||||||
|
.MinimumLevel.Information()
|
||||||
|
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
||||||
|
// Filter out ASP.NET Core infrastructre logs that are Information and below 日志太多了 一个请求 记录好几条
|
||||||
|
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
||||||
|
.MinimumLevel.Override("System.Net.Http.HttpClient.HttpReports", LogEventLevel.Warning)
|
||||||
|
.Enrich.WithClientIp()
|
||||||
|
|
||||||
|
.Enrich.FromLogContext()
|
||||||
|
|
||||||
|
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的ip 和用户名 用户类型
|
||||||
|
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Warning,
|
||||||
|
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||||
|
|
||||||
|
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,
|
||||||
|
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine}")
|
||||||
|
.CreateLogger();
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 运行环境 部署平台
|
||||||
|
|
||||||
|
Log.Logger.Warning($"当前环境:{enviromentName}");
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:windows");
|
||||||
|
}
|
||||||
|
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:linux");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services);
|
||||||
|
|
||||||
|
|
||||||
|
app.Run();
|
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:36358",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"http": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"applicationUrl": "http://localhost:5243",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
using AutoMapper;
|
||||||
|
using IRaCIS.Application.Services.BusinessFilter;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
using Panda.DynamicWebApi;
|
||||||
|
using Panda.DynamicWebApi.Attributes;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
|
||||||
|
#pragma warning disable CS8618
|
||||||
|
|
||||||
|
|
||||||
|
#region 非泛型版本
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseService : IBaseService, IDynamicWebApi
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
public IRepository _repository { get; set; }
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseService
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_repository))]
|
||||||
|
public IRepository _repository { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_hostEnvironment))]
|
||||||
|
public IWebHostEnvironment _hostEnvironment { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 泛型版本测试
|
||||||
|
|
||||||
|
|
||||||
|
public interface IBaseServiceTest<T> where T : Entity
|
||||||
|
{
|
||||||
|
[MemberNotNull(nameof(_mapper))]
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_userInfo))]
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_repository))]
|
||||||
|
public IRepository _repository { get; set; }
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(_localizer))]
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Authorize, DynamicWebApi, UnifiedApiResultFilter]
|
||||||
|
public class BaseServiceTest<T> : IBaseServiceTest<T>, IDynamicWebApi where T : Entity
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
public IRepository _repository { get; set; }
|
||||||
|
|
||||||
|
public IStringLocalizer _localizer { get; set; }
|
||||||
|
|
||||||
|
public static IResponseOutput Null404NotFound<TEntity>(TEntity? businessObject) where TEntity : class
|
||||||
|
{
|
||||||
|
return new ResponseOutput<string>()
|
||||||
|
.NotOk($"The query object {typeof(TEntity).Name} does not exist , or was deleted by someone else, or an incorrect parameter query caused", code: ApiResponseCodeEnum.DataNotExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,368 @@
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Serilog;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Data;
|
||||||
|
using FellowOakDicom.Imaging;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using SixLabors.ImageSharp.Formats.Jpeg;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 后台托管服务的方式运行
|
||||||
|
/// </summary>
|
||||||
|
//public class CStoreSCPHostedService : IHostedService
|
||||||
|
//{
|
||||||
|
// private readonly ILogger<CStoreSCPHostedService> _logger;
|
||||||
|
// private readonly IDicomServerFactory _dicomServerFactory;
|
||||||
|
// private IDicomServer? _server;
|
||||||
|
|
||||||
|
// public CStoreSCPHostedService(ILogger<CStoreSCPHostedService> logger, IDicomServerFactory dicomServerFactory)
|
||||||
|
// {
|
||||||
|
// _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
// _dicomServerFactory = dicomServerFactory ?? throw new ArgumentNullException(nameof(dicomServerFactory));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// _logger.LogInformation("Starting DICOM server");
|
||||||
|
// _server = _dicomServerFactory.Create<CStoreSCPService>(104);
|
||||||
|
// _logger.LogInformation("DICOM server is running");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
// {
|
||||||
|
// if (_server != null)
|
||||||
|
// {
|
||||||
|
// _server.Stop();
|
||||||
|
// _server.Dispose();
|
||||||
|
// _server = null;
|
||||||
|
// }
|
||||||
|
// return Task.CompletedTask;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
public class DicomSCPServiceOption
|
||||||
|
{
|
||||||
|
public List<string> CalledAEList { get; set; }
|
||||||
|
|
||||||
|
public string ServerPort { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class CStoreSCPService : DicomService, IDicomServiceProvider, IDicomCStoreProvider, IDicomCEchoProvider
|
||||||
|
{
|
||||||
|
private IServiceProvider _serviceProvider { get; set; }
|
||||||
|
|
||||||
|
private List<Guid> _SCPStudyIdList { get; set; } = new List<Guid>();
|
||||||
|
|
||||||
|
private SCPImageUpload _upload { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
||||||
|
{
|
||||||
|
// Lossless
|
||||||
|
DicomTransferSyntax.JPEGLSLossless,
|
||||||
|
DicomTransferSyntax.JPEG2000Lossless,
|
||||||
|
DicomTransferSyntax.JPEGProcess14SV1,
|
||||||
|
DicomTransferSyntax.JPEGProcess14,
|
||||||
|
DicomTransferSyntax.RLELossless,
|
||||||
|
// Lossy
|
||||||
|
DicomTransferSyntax.JPEGLSNearLossless,
|
||||||
|
DicomTransferSyntax.JPEG2000Lossy,
|
||||||
|
DicomTransferSyntax.JPEGProcess1,
|
||||||
|
DicomTransferSyntax.JPEGProcess2_4,
|
||||||
|
// Uncompressed
|
||||||
|
DicomTransferSyntax.ExplicitVRLittleEndian,
|
||||||
|
DicomTransferSyntax.ExplicitVRBigEndian,
|
||||||
|
DicomTransferSyntax.ImplicitVRLittleEndian
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies)
|
||||||
|
: base(stream, fallbackEncoding, log, dependencies)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
||||||
|
{
|
||||||
|
|
||||||
|
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
||||||
|
|
||||||
|
|
||||||
|
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
||||||
|
|
||||||
|
_serviceProvider = (IServiceProvider)this.UserState;
|
||||||
|
|
||||||
|
var option = _serviceProvider.GetService<IOptionsMonitor<DicomSCPServiceOption>>().CurrentValue;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var calledAEList = option.CalledAEList;
|
||||||
|
|
||||||
|
if (!calledAEList.Contains(association.CalledAE))
|
||||||
|
|
||||||
|
//if (association.CalledAE != "STORESCP")
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"拒绝CalledAE:{association.CalledAE}的连接");
|
||||||
|
|
||||||
|
return SendAssociationRejectAsync(
|
||||||
|
DicomRejectResult.Permanent,
|
||||||
|
DicomRejectSource.ServiceUser,
|
||||||
|
DicomRejectReason.CalledAENotRecognized);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var pc in association.PresentationContexts)
|
||||||
|
{
|
||||||
|
if (pc.AbstractSyntax == DicomUID.Verification)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
||||||
|
}
|
||||||
|
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
||||||
|
{
|
||||||
|
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendAssociationAcceptAsync(association);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task OnReceiveAssociationReleaseRequestAsync()
|
||||||
|
{
|
||||||
|
await DataMaintenanceAsaync();
|
||||||
|
|
||||||
|
//记录监控
|
||||||
|
|
||||||
|
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
||||||
|
|
||||||
|
_upload.EndTime = DateTime.Now;
|
||||||
|
_upload.StudyCount = _SCPStudyIdList.Count;
|
||||||
|
|
||||||
|
await _SCPImageUploadRepository.AddAsync(_upload,true);
|
||||||
|
|
||||||
|
await SendAssociationReleaseResponseAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task DataMaintenanceAsaync()
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality 以及自动创建访视,绑定检查");
|
||||||
|
|
||||||
|
//var patientStudyService = _serviceProvider.GetService<IPatientStudyService>();
|
||||||
|
|
||||||
|
//await patientStudyService.AutoBindingPatientStudyVisitAsync(_SCPStudyIdList);
|
||||||
|
|
||||||
|
//处理检查Modality
|
||||||
|
var _dictionaryRepository = _serviceProvider.GetService<IRepository<Dictionary>>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
|
||||||
|
var dicModalityList = _dictionaryRepository.Where(t => t.Code == "Modality").SelectMany(t => t.ChildList.Select(c => c.Value)).ToList();
|
||||||
|
var seriesModalityList = _seriesRepository.Where(t => _SCPStudyIdList.Contains(t.StudyId)).Select(t => new { SCPStudyId = t.StudyId, t.Modality }).ToList();
|
||||||
|
|
||||||
|
foreach (var g in seriesModalityList.GroupBy(t => t.SCPStudyId))
|
||||||
|
{
|
||||||
|
var modality = string.Join('、', g.Select(t => t.Modality).Distinct().ToList());
|
||||||
|
|
||||||
|
//特殊逻辑
|
||||||
|
var modalityForEdit = dicModalityList.Contains(modality) ? modality : String.Empty;
|
||||||
|
|
||||||
|
if (modality == "MR")
|
||||||
|
{
|
||||||
|
modalityForEdit = "MRI";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modality == "PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET";
|
||||||
|
}
|
||||||
|
if (modality == "PT、CT" || modality == "CT、PT")
|
||||||
|
{
|
||||||
|
modalityForEdit = "PET-CT";
|
||||||
|
}
|
||||||
|
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == g.Key, u => new SCPStudy() { Modalities = modality, ModalityForEdit = modalityForEdit });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}维护数据结束");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnReceiveAbort(DicomAbortSource source, DicomAbortReason reason)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}接收中断,中断原因:{source.ToString() + reason.ToString()}");
|
||||||
|
/* nothing to do here */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async void OnConnectionClosed(Exception exception)
|
||||||
|
{
|
||||||
|
/* nothing to do here */
|
||||||
|
|
||||||
|
//奇怪的bug 上传的时候,用王捷修改的影像,会关闭,重新连接,导致检查id 丢失,然后状态不一致
|
||||||
|
if (exception == null)
|
||||||
|
{
|
||||||
|
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
||||||
|
//将检查设置为传输结束
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
||||||
|
|
||||||
|
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Warning($"连接关闭 {exception?.Message} {exception?.InnerException?.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<DicomCStoreResponse> OnCStoreRequestAsync(DicomCStoreRequest request)
|
||||||
|
{
|
||||||
|
|
||||||
|
string studyInstanceUid = request.Dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = request.Dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = request.Dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
|
|
||||||
|
|
||||||
|
var ossService = _serviceProvider.GetService<IOSSService>();
|
||||||
|
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
||||||
|
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
||||||
|
|
||||||
|
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
||||||
|
|
||||||
|
var storeRelativePath = string.Empty;
|
||||||
|
var ossFolderPath = $"Dicom/{studyInstanceUid}";
|
||||||
|
|
||||||
|
|
||||||
|
long fileSize = 0;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
using (MemoryStream ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
await request.File.SaveAsync(ms);
|
||||||
|
|
||||||
|
//irc 从路径最后一截取Guid
|
||||||
|
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
||||||
|
|
||||||
|
fileSize = ms.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ec)
|
||||||
|
{
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
using (await @lock.AcquireAsync())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, storeRelativePath, Association.CallingAE, Association.CalledAE);
|
||||||
|
|
||||||
|
if (!_SCPStudyIdList.Contains(scpStudyId))
|
||||||
|
{
|
||||||
|
_SCPStudyIdList.Add(scpStudyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var series = await _seriesRepository.FindAsync(seriesId);
|
||||||
|
|
||||||
|
//没有缩略图
|
||||||
|
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
||||||
|
{
|
||||||
|
|
||||||
|
// 生成缩略图
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
DicomImage image = new DicomImage(request.Dataset);
|
||||||
|
|
||||||
|
var sharpimage = image.RenderImage().AsSharpImage();
|
||||||
|
sharpimage.Save(memoryStream, new JpegEncoder());
|
||||||
|
|
||||||
|
// 上传缩略图到 OSS
|
||||||
|
|
||||||
|
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
||||||
|
|
||||||
|
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
||||||
|
|
||||||
|
series.ImageResizePath = seriesPath;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//await _seriesRepository.BatchUpdateNoTrackingAsync(t => t.Id == seriesId, u => new SCPSeries() { ImageResizePath = seriesPath });
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _seriesRepository.SaveChangesAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//监控信息设置
|
||||||
|
_upload.FileCount++;
|
||||||
|
_upload.FileSize= _upload.FileSize+ fileSize;
|
||||||
|
return new DicomCStoreResponse(request, DicomStatus.Success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
||||||
|
{
|
||||||
|
// let library handle logging and error response
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,333 @@
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Medallion.Threading;
|
||||||
|
using FellowOakDicom;
|
||||||
|
using FellowOakDicom.Imaging.Codec;
|
||||||
|
using System.Data;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.SCP.Service;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using MassTransit;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public class DicomArchiveService : BaseService, IDicomArchiveService
|
||||||
|
{
|
||||||
|
private readonly IRepository<SCPPatient> _patientRepository;
|
||||||
|
private readonly IRepository<SCPStudy> _studyRepository;
|
||||||
|
private readonly IRepository<SCPSeries> _seriesRepository;
|
||||||
|
private readonly IRepository<SCPInstance> _instanceRepository;
|
||||||
|
private readonly IRepository<Dictionary> _dictionaryRepository;
|
||||||
|
private readonly IDistributedLockProvider _distributedLockProvider;
|
||||||
|
|
||||||
|
|
||||||
|
private List<Guid> _instanceIdList = new List<Guid>();
|
||||||
|
|
||||||
|
public DicomArchiveService(IRepository<SCPPatient> patientRepository, IRepository<SCPStudy> studyRepository,
|
||||||
|
IRepository<SCPSeries> seriesRepository,
|
||||||
|
IRepository<SCPInstance> instanceRepository,
|
||||||
|
IRepository<Dictionary> dictionaryRepository,
|
||||||
|
IDistributedLockProvider distributedLockProvider)
|
||||||
|
{
|
||||||
|
_distributedLockProvider = distributedLockProvider;
|
||||||
|
_studyRepository = studyRepository;
|
||||||
|
_patientRepository = patientRepository;
|
||||||
|
_seriesRepository = seriesRepository;
|
||||||
|
_instanceRepository = instanceRepository;
|
||||||
|
_dictionaryRepository = dictionaryRepository;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个文件接收 归档
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataset"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
public async Task<Guid> ArchiveDicomFileAsync(DicomDataset dataset, string fileRelativePath, string callingAE, string calledAE)
|
||||||
|
{
|
||||||
|
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
||||||
|
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
||||||
|
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
||||||
|
|
||||||
|
string patientIdStr = dataset.GetString(DicomTag.PatientID);
|
||||||
|
|
||||||
|
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
||||||
|
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid);
|
||||||
|
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid);
|
||||||
|
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid);
|
||||||
|
|
||||||
|
var isStudyNeedAdd = false;
|
||||||
|
var isSeriesNeedAdd = false;
|
||||||
|
var isInstanceNeedAdd = false;
|
||||||
|
var isPatientNeedAdd = false;
|
||||||
|
|
||||||
|
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
||||||
|
|
||||||
|
//using (@lock.Acquire())
|
||||||
|
{
|
||||||
|
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr);
|
||||||
|
var findStudy = await _studyRepository.FindAsync(studyId);
|
||||||
|
var findSerice = await _seriesRepository.FindAsync(seriesId);
|
||||||
|
var findInstance = await _instanceRepository.FindAsync(instanceId);
|
||||||
|
|
||||||
|
DateTime? studyTime = dataset.GetSingleValueOrDefault(DicomTag.StudyDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.StudyDate).Add(dataset.GetSingleValueOrDefault(DicomTag.StudyTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.StudyTime).TimeOfDay);
|
||||||
|
|
||||||
|
//先传输了修改了患者编号的,又传输了没有修改患者编号的,导致后传输的没有修改患者编号的下面的检查为0
|
||||||
|
if (findPatient == null && findStudy==null)
|
||||||
|
{
|
||||||
|
isPatientNeedAdd = true;
|
||||||
|
|
||||||
|
|
||||||
|
findPatient = new SCPPatient()
|
||||||
|
{
|
||||||
|
Id = NewId.NextSequentialGuid(),
|
||||||
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||||
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||||
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||||
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||||
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||||
|
|
||||||
|
EarliestStudyTime = studyTime,
|
||||||
|
LatestStudyTime = studyTime,
|
||||||
|
LatestPushTime = DateTime.Now,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (findPatient.PatientBirthDate.Length == 8)
|
||||||
|
{
|
||||||
|
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
||||||
|
|
||||||
|
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
||||||
|
|
||||||
|
int year = 0;
|
||||||
|
|
||||||
|
var canParse = int.TryParse(yearStr, out year);
|
||||||
|
|
||||||
|
if (canParse && year > 1900)
|
||||||
|
{
|
||||||
|
findPatient.PatientBirthDate = birthDateStr;
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
findPatient.PatientBirthDate = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (studyTime < findPatient.EarliestStudyTime)
|
||||||
|
{
|
||||||
|
findPatient.EarliestStudyTime = studyTime;
|
||||||
|
}
|
||||||
|
if (studyTime > findPatient.LatestStudyTime)
|
||||||
|
{
|
||||||
|
findPatient.LatestStudyTime = studyTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
findPatient.LatestPushTime = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (findStudy == null)
|
||||||
|
{
|
||||||
|
isStudyNeedAdd = true;
|
||||||
|
findStudy = new SCPStudy
|
||||||
|
{
|
||||||
|
CalledAE = calledAE,
|
||||||
|
CallingAE = callingAE,
|
||||||
|
|
||||||
|
PatientId = findPatient.Id,
|
||||||
|
Id = studyId,
|
||||||
|
StudyInstanceUid = studyInstanceUid,
|
||||||
|
StudyTime = studyTime,
|
||||||
|
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||||
|
//ModalityForEdit = modalityForEdit,
|
||||||
|
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
||||||
|
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
||||||
|
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
||||||
|
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
||||||
|
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
||||||
|
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
||||||
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||||
|
|
||||||
|
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
||||||
|
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
||||||
|
|
||||||
|
//需要特殊处理
|
||||||
|
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||||
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||||
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
||||||
|
SeriesCount = 0,
|
||||||
|
InstanceCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
if (findStudy.PatientBirthDate.Length == 8)
|
||||||
|
{
|
||||||
|
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (findSerice == null)
|
||||||
|
{
|
||||||
|
isSeriesNeedAdd = true;
|
||||||
|
|
||||||
|
findSerice = new SCPSeries
|
||||||
|
{
|
||||||
|
Id = seriesId,
|
||||||
|
StudyId = findStudy.Id,
|
||||||
|
|
||||||
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid = seriesInstanceUid,
|
||||||
|
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
||||||
|
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
||||||
|
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.SeriesDate) + dataset.GetSingleValue<string>(DicomTag.SeriesTime), out DateTime dt) ? dt : null,
|
||||||
|
SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.SeriesDate).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.SeriesTime).TimeOfDay),
|
||||||
|
Modality = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
||||||
|
Description = dataset.GetSingleValueOrDefault(DicomTag.SeriesDescription, string.Empty),
|
||||||
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||||
|
|
||||||
|
ImagePositionPatient = dataset.GetSingleValueOrDefault(DicomTag.ImagePositionPatient, string.Empty),
|
||||||
|
ImageOrientationPatient = dataset.GetSingleValueOrDefault(DicomTag.ImageOrientationPatient, string.Empty),
|
||||||
|
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
||||||
|
SequenceName = dataset.GetSingleValueOrDefault(DicomTag.SequenceName, string.Empty),
|
||||||
|
ProtocolName = dataset.GetSingleValueOrDefault(DicomTag.ProtocolName, string.Empty),
|
||||||
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||||
|
|
||||||
|
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
||||||
|
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
||||||
|
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
||||||
|
|
||||||
|
|
||||||
|
InstanceCount = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
++findStudy.SeriesCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (findInstance == null)
|
||||||
|
{
|
||||||
|
isInstanceNeedAdd = true;
|
||||||
|
findInstance = new SCPInstance
|
||||||
|
{
|
||||||
|
Id = instanceId,
|
||||||
|
StudyId = findStudy.Id,
|
||||||
|
SeriesId = findSerice.Id,
|
||||||
|
StudyInstanceUid = findStudy.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid = findSerice.SeriesInstanceUid,
|
||||||
|
|
||||||
|
SopInstanceUid = sopInstanceUid,
|
||||||
|
InstanceNumber = dataset.GetSingleValueOrDefault(DicomTag.InstanceNumber, 1),
|
||||||
|
InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate, string.Empty) == string.Empty ? null : dataset.GetSingleValue<DateTime>(DicomTag.ContentDate).Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, string.Empty) == string.Empty ? TimeSpan.Zero : dataset.GetSingleValue<DateTime>(DicomTag.ContentTime).TimeOfDay),
|
||||||
|
//InstanceTime = DateTime.TryParse(dataset.GetSingleValue<string>(DicomTag.ContentDate) + dataset.GetSingleValue<string>(DicomTag.ContentTime), out DateTime dt) ? dt : null,
|
||||||
|
//InstanceTime = dataset.GetSingleValueOrDefault(DicomTag.ContentDate,(DateTime?)null)?.Add(dataset.GetSingleValueOrDefault(DicomTag.ContentTime, TimeSpan.Zero)),
|
||||||
|
//dataset.GetSingleValueOrDefault(DicomTag.ContentDate,DateTime.Now);//, DicomTag.ContentTime)
|
||||||
|
CPIStatus = false,
|
||||||
|
ImageRows = dataset.GetSingleValueOrDefault(DicomTag.Rows, 0),
|
||||||
|
ImageColumns = dataset.GetSingleValueOrDefault(DicomTag.Columns, 0),
|
||||||
|
SliceLocation = dataset.GetSingleValueOrDefault(DicomTag.SliceLocation, 0),
|
||||||
|
|
||||||
|
SliceThickness = dataset.GetSingleValueOrDefault(DicomTag.SliceThickness, string.Empty),
|
||||||
|
NumberOfFrames = dataset.GetSingleValueOrDefault(DicomTag.NumberOfFrames, 0),
|
||||||
|
PixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.PixelSpacing, string.Empty),
|
||||||
|
ImagerPixelSpacing = dataset.GetSingleValueOrDefault(DicomTag.ImagerPixelSpacing, string.Empty),
|
||||||
|
FrameOfReferenceUID = dataset.GetSingleValueOrDefault(DicomTag.FrameOfReferenceUID, string.Empty),
|
||||||
|
WindowCenter = dataset.GetSingleValueOrDefault(DicomTag.WindowCenter, string.Empty),
|
||||||
|
WindowWidth = dataset.GetSingleValueOrDefault(DicomTag.WindowWidth, string.Empty),
|
||||||
|
|
||||||
|
Path = fileRelativePath
|
||||||
|
};
|
||||||
|
|
||||||
|
++findStudy.InstanceCount;
|
||||||
|
++findSerice.InstanceCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPatientNeedAdd)
|
||||||
|
{
|
||||||
|
var ss = await _patientRepository.AddAsync(findPatient);
|
||||||
|
}
|
||||||
|
if (isStudyNeedAdd)
|
||||||
|
{
|
||||||
|
var dd = await _studyRepository.AddAsync(findStudy);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _studyRepository.BatchUpdateNoTrackingAsync(t => t.Id == findStudy.Id, t => new SCPStudy() { IsUploadFinished = false });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSeriesNeedAdd)
|
||||||
|
{
|
||||||
|
await _seriesRepository.AddAsync(findSerice);
|
||||||
|
}
|
||||||
|
if (isInstanceNeedAdd)
|
||||||
|
{
|
||||||
|
await _instanceRepository.AddAsync(findInstance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _instanceRepository.BatchUpdateNoTrackingAsync(t => t.Id == instanceId, u => new SCPInstance() { Path = fileRelativePath });
|
||||||
|
}
|
||||||
|
|
||||||
|
//await _studyRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
return findStudy.Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 从DICOM文件中获取使用的字符集
|
||||||
|
private string GetEncodingVaulueFromDicomFile(DicomDataset dataset, DicomTag dicomTag)
|
||||||
|
{
|
||||||
|
|
||||||
|
// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||||
|
var charset = dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||||
|
|
||||||
|
var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||||
|
|
||||||
|
|
||||||
|
var dicomStringElement = dataset.GetDicomItem<DicomStringElement>(dicomTag);
|
||||||
|
|
||||||
|
var bytes = dicomStringElement.Buffer.Data;
|
||||||
|
|
||||||
|
|
||||||
|
return dicomEncoding.GetString(bytes);
|
||||||
|
|
||||||
|
|
||||||
|
//// 从DICOM文件中获取使用的字符集
|
||||||
|
//string filePath = "C:\\Users\\hang\\Documents\\WeChat Files\\wxid_r2imdzb7j3q922\\FileStorage\\File\\2024-05\\1.2.840.113619.2.80.169103990.5390.1271401378.4.dcm";
|
||||||
|
//DicomFile dicomFile = DicomFile.Open(filePath);
|
||||||
|
|
||||||
|
//// 获取DICOM文件的特定元素,通常用于指示使用的字符集
|
||||||
|
//var charset = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.SpecificCharacterSet, string.Empty);
|
||||||
|
|
||||||
|
//var dicomEncoding = DicomEncoding.GetEncoding(charset);
|
||||||
|
|
||||||
|
//var value = dicomFile.Dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty);
|
||||||
|
|
||||||
|
//var dicomStringElement = dicomFile.Dataset.GetDicomItem<DicomStringElement>(DicomTag.PatientName);
|
||||||
|
|
||||||
|
//var bytes = dicomStringElement.Buffer.Data;
|
||||||
|
|
||||||
|
//var aa= dicomEncoding.GetString(bytes);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
using FellowOakDicom;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP.Service
|
||||||
|
{
|
||||||
|
public interface IDicomArchiveService
|
||||||
|
{
|
||||||
|
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,string fileRelativePath,string callingAE,string calledAE);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,427 @@
|
||||||
|
using Aliyun.OSS;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Minio.DataModel.Args;
|
||||||
|
using Minio;
|
||||||
|
using SharpCompress.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.AccessControl;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.SCP
|
||||||
|
{
|
||||||
|
public class MinIOOptions : AWSOptions
|
||||||
|
{
|
||||||
|
public int port { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AWSOptions
|
||||||
|
{
|
||||||
|
public string endPoint { get; set; }
|
||||||
|
public bool useSSL { get; set; }
|
||||||
|
public string accessKey { get; set; }
|
||||||
|
public string secretKey { get; set; }
|
||||||
|
public string bucketName { get; set; }
|
||||||
|
public string viewEndpoint { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AliyunOSSOptions
|
||||||
|
{
|
||||||
|
public string regionId { get; set; }
|
||||||
|
public string accessKeyId { get; set; }
|
||||||
|
public string accessKeySecret { get; set; }
|
||||||
|
public string endPoint { get; set; }
|
||||||
|
public string bucketName { get; set; }
|
||||||
|
|
||||||
|
public string roleArn { get; set; }
|
||||||
|
|
||||||
|
public string region { get; set; }
|
||||||
|
|
||||||
|
public string viewEndpoint { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreServiceOptions
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSOptions AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ObjectStoreDTO
|
||||||
|
{
|
||||||
|
public string ObjectStoreUse { get; set; }
|
||||||
|
|
||||||
|
public AliyunOSSOptions AliyunOSS { get; set; }
|
||||||
|
|
||||||
|
public MinIOOptions MinIO { get; set; }
|
||||||
|
|
||||||
|
public AWSOptions AWS { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AliyunOSSTempToken
|
||||||
|
{
|
||||||
|
public string AccessKeyId { get; set; }
|
||||||
|
public string AccessKeySecret { get; set; }
|
||||||
|
public string SecurityToken { get; set; }
|
||||||
|
public string Expiration { get; set; }
|
||||||
|
|
||||||
|
public string Region { get; set; }
|
||||||
|
public string BucketName { get; set; }
|
||||||
|
public string ViewEndpoint { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public enum ObjectStoreUse
|
||||||
|
{
|
||||||
|
AliyunOSS = 0,
|
||||||
|
MinIO = 1,
|
||||||
|
AWS = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IOSSService
|
||||||
|
{
|
||||||
|
public Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName,bool isFileNameAddGuid=true);
|
||||||
|
public Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true);
|
||||||
|
|
||||||
|
public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath);
|
||||||
|
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
public Task<string> GetSignedUrl(string ossRelativePath);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class OSSService : IOSSService
|
||||||
|
{
|
||||||
|
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public OSSService(IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||||
|
{
|
||||||
|
ObjectStoreServiceOptions = options.CurrentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileStream"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <param name="fileRealName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}": $"{oosFolderPath}/{fileRealName}";
|
||||||
|
//var ossRelativePath = oosFolderPath + "/" + fileRealName;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
fileStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
fileStream.CopyTo(memoryStream);
|
||||||
|
|
||||||
|
memoryStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, memoryStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithStreamData(memoryStream)
|
||||||
|
.WithObjectSize(memoryStream.Length);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithStreamData(memoryStream)
|
||||||
|
.WithObjectSize(memoryStream.Length);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); ;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="localFilePath"></param>
|
||||||
|
/// <param name="oosFolderPath"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
|
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
||||||
|
{
|
||||||
|
var localFileName = Path.GetFileName(localFilePath);
|
||||||
|
|
||||||
|
var ossRelativePath = isFileNameAddGuid? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
||||||
|
|
||||||
|
//var ossRelativePath = oosFolderPath + "/" + localFileName;
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.PutObject(aliConfig.bucketName, ossRelativePath, localFilePath);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFileName(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var putObjectArgs = new PutObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFileName(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.PutObjectAsync(putObjectArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
return "/" + ossRelativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
||||||
|
{
|
||||||
|
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||||
|
|
||||||
|
// 上传文件
|
||||||
|
var result = _ossClient.GetObject(aliConfig.bucketName, ossRelativePath);
|
||||||
|
|
||||||
|
// 将下载的文件流保存到本地文件
|
||||||
|
using (var fs = File.OpenWrite(localFilePath))
|
||||||
|
{
|
||||||
|
result.Content.CopyTo(fs);
|
||||||
|
fs.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var getObjectArgs = new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFile(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.GetObjectAsync(getObjectArgs);
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var getObjectArgs = new GetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithFile(localFilePath);
|
||||||
|
|
||||||
|
await minioClient.GetObjectAsync(getObjectArgs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetSignedUrl(string ossRelativePath)
|
||||||
|
{
|
||||||
|
ossRelativePath = ossRelativePath.TrimStart('/');
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
||||||
|
{
|
||||||
|
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
||||||
|
|
||||||
|
var _ossClient = new OssClient(aliConfig.endPoint, aliConfig.accessKeyId, aliConfig.accessKeySecret);
|
||||||
|
|
||||||
|
// 生成签名URL。
|
||||||
|
var req = new GeneratePresignedUriRequest(aliConfig.bucketName, ossRelativePath, SignHttpMethod.Get)
|
||||||
|
{
|
||||||
|
// 设置签名URL过期时间,默认值为3600秒。
|
||||||
|
Expiration = DateTime.Now.AddHours(1),
|
||||||
|
};
|
||||||
|
var uri = _ossClient.GeneratePresignedUri(req);
|
||||||
|
|
||||||
|
return uri.PathAndQuery;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}:{minIOConfig.port}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
//var reqParams = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
// {
|
||||||
|
// { "response-content-type", "application/json" }
|
||||||
|
// };
|
||||||
|
|
||||||
|
var args = new PresignedGetObjectArgs()
|
||||||
|
.WithBucket(minIOConfig.bucketName)
|
||||||
|
.WithObject(ossRelativePath)
|
||||||
|
.WithExpiry(3600)
|
||||||
|
/*.WithHeaders(reqParams)*/;
|
||||||
|
|
||||||
|
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
||||||
|
|
||||||
|
Uri uri = new Uri(presignedUrl);
|
||||||
|
|
||||||
|
string relativePath = uri.PathAndQuery;
|
||||||
|
|
||||||
|
|
||||||
|
return relativePath;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
||||||
|
{
|
||||||
|
var minIOConfig = ObjectStoreServiceOptions.AWS;
|
||||||
|
|
||||||
|
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.endPoint}")
|
||||||
|
.WithCredentials(minIOConfig.accessKey, minIOConfig.secretKey).WithSSL(minIOConfig.useSSL)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
|
||||||
|
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-irc-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endpoint": "http://192.168.3.68",
|
||||||
|
"port": "8001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "IDFkwEpWej0b4DtiuThL",
|
||||||
|
"secretKey": "Lhuu83yMhVwu7c1SnjvGY6lq74jzpYqifK6Qtj4h",
|
||||||
|
"bucketName": "test"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
//"RemoteNew": "Server=47.117.165.18,1434;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
|
//"Hangfire": "Server=47.117.165.18,1434;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
|
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_Study;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_Study_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": true,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": true,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": true,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "study@extimaging.com",
|
||||||
|
"FromName": "研究单位阅片系统",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
"SiteUrl": "https://study.extimaging.com/login"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-irc-test-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "106.14.89.110",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
|
"bucketName": "hir-test",
|
||||||
|
"viewEndpoint": "http://106.14.89.110:9001/hir-test/"
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
|
"bucketName": "ei-irc-test-store",
|
||||||
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "test-study@extimaging.com",
|
||||||
|
"FromName": "Test_Study",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
|
||||||
|
"SiteUrl": "http://study.test.extimaging.com/login"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP",
|
||||||
|
"Value1",
|
||||||
|
"Value2",
|
||||||
|
"Value3"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "MinIO",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-sir-test-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-sir-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "44.218.11.19",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "lH8DkKskLuDqPaiubuSQ",
|
||||||
|
"secretKey": "pdPdicvvLeH7xAC5yFUrI7odMyBfOXxvVWMvKYV4",
|
||||||
|
"bucketName": "hir-us",
|
||||||
|
"viewEndpoint": "http://hir.us.extimaging.com/oss/hir-us"
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
|
"bucketName": "ei-irc-test-store",
|
||||||
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=44.218.11.19,1435;Database=US_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=44.218.11.19,1435;Database=US_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "uat-study@extimaging.com",
|
||||||
|
"FromName": "Uat_Study",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
"SiteUrl": "http://study.uat.extimaging.com/login"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP",
|
||||||
|
"Value1",
|
||||||
|
"Value2",
|
||||||
|
"Value3"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ObjectStoreService": {
|
||||||
|
"ObjectStoreUse": "MinIO",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
|
"bucketName": "zy-irc-uat-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
|
"MinIO": {
|
||||||
|
"endPoint": "47.117.164.182",
|
||||||
|
"port": "9001",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "b9Ul0e98xPzt6PwRXA1Q",
|
||||||
|
"secretKey": "DzMaU2L4OXl90uytwOmDXF2encN0Jf4Nxu2XkYqQ",
|
||||||
|
"bucketName": "hir-uat",
|
||||||
|
"viewEndpoint": "http://hir.uat.extimaging.com/oss/hir-uat"
|
||||||
|
},
|
||||||
|
|
||||||
|
"AWS": {
|
||||||
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
|
"useSSL": false,
|
||||||
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
|
"bucketName": "ei-irc-test-store",
|
||||||
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ConnectionStrings": {
|
||||||
|
"RemoteNew": "Server=47.117.164.182,1434;Database=Uat_HIR;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
|
"Hangfire": "Server=47.117.164.182,1434;Database=Uat_HIR_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
},
|
||||||
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
|
"LoginFailLockMinutes": 30
|
||||||
|
},
|
||||||
|
|
||||||
|
"SystemEmailSendConfig": {
|
||||||
|
"Port": 465,
|
||||||
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
|
"FromEmail": "uat-study@extimaging.com",
|
||||||
|
"FromName": "Uat_Study",
|
||||||
|
"AuthorizationCode": "zhanying123",
|
||||||
|
"SiteUrl": "http://study.uat.extimaging.com/login"
|
||||||
|
},
|
||||||
|
"DicomSCPServiceConfig": {
|
||||||
|
"CalledAEList": [
|
||||||
|
"STORESCP",
|
||||||
|
"Value1",
|
||||||
|
"Value2",
|
||||||
|
"Value3"
|
||||||
|
],
|
||||||
|
"ServerPort": 11112
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
|
@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infra.EFCore",
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRaCIS.Core.Infrastructure", "IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj", "{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -51,6 +53,10 @@ Global
|
||||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
{07EED0F8-08E6-46F3-ACBE-17BC1391BD4C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -64,22 +64,21 @@
|
||||||
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
|
<PackageReference Include="aliyun-net-sdk-sts" Version="3.1.2" />
|
||||||
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
<PackageReference Include="AspNetCoreRateLimit" Version="5.0.0" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
|
||||||
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.2" />
|
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.2" />
|
||||||
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.2">
|
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.2">
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.12" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.14" />
|
||||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.12" />
|
<PackageReference Include="Hangfire.SqlServer" Version="1.8.14" />
|
||||||
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||||
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace IRaCIS.Core.Application.Service
|
||||||
|
|
||||||
var exploreRecommendQueryable =
|
var exploreRecommendQueryable =
|
||||||
|
|
||||||
_exploreRecommendRepository
|
_exploreRecommendRepository.Where().IgnoreQueryFilters()
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title))
|
.WhereIf(string.IsNullOrEmpty(inQuery.Title), t => t.Title.Contains(inQuery.Title))
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName))
|
.WhereIf(string.IsNullOrEmpty(inQuery.FileName), t => t.Title.Contains(inQuery.FileName))
|
||||||
.WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl))
|
.WhereIf(string.IsNullOrEmpty(inQuery.DownloadUrl), t => t.Title.Contains(inQuery.DownloadUrl))
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -9,13 +9,13 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AutoMapper" Version="12.0.1" />
|
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||||
<PackageReference Include="AutoMapper.Collection.EntityFrameworkCore" Version="9.0.0" />
|
<PackageReference Include="AutoMapper.Collection.EntityFrameworkCore" Version="10.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
||||||
<PackageReference Include="SharpCompress" Version="0.35.0" />
|
<PackageReference Include="SharpCompress" Version="0.37.2" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||||
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.3.7" />
|
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue