Compare commits
42 Commits
Test_IRC_N
...
EICS-V1.5.
Author | SHA1 | Date |
---|---|---|
|
e6bd35ae04 | |
|
102e193ae8 | |
|
20d557e795 | |
|
593ab42882 | |
|
b56d256e10 | |
|
614a3bd70e | |
|
2eb3b04365 | |
|
1fa3a5a181 | |
|
cd2007c4fd | |
|
67e710eac0 | |
|
ef451d82ec | |
|
899ebf2a30 | |
|
f233d27f96 | |
|
9e8ab8acca | |
|
2265387f47 | |
|
baf8f39bc6 | |
|
3fa14d92ae | |
|
e6c577e68b | |
|
bb111dbbeb | |
|
0df0b10a72 | |
|
0a7ba9f0f5 | |
|
cd88990c32 | |
|
d53a1300fc | |
|
07332ff3e1 | |
|
8195a1cf3e | |
|
f5e5a47624 | |
|
28608f3ecb | |
|
bb2da7ca81 | |
|
713518a68c | |
|
bc448da297 | |
|
8447878cb2 | |
|
1b5a9673f6 | |
|
27711f4184 | |
|
bb9bac1edf | |
|
41ae2febc7 | |
|
c072c526e2 | |
|
d6bb98b84f | |
|
42587ee59f | |
|
62a964a3ea | |
|
72447eb82f | |
|
2f83c27fde | |
|
b219593f61 |
|
@ -1,4 +0,0 @@
|
||||||
FROM registry.cn-shanghai.aliyuncs.com/extimaging/aspnetcore:v8.2
|
|
||||||
WORKDIR /app
|
|
||||||
COPY publish .
|
|
||||||
ENTRYPOINT ["dotnet", "IRaCIS.Core.API.dll"]
|
|
|
@ -1,37 +0,0 @@
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
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;//标记当前异常已经被处理过了
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.Application.Service.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(I18n.T("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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
using EntityFramework.Exceptions.SqlServer;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using Medallion.Threading.SqlServer;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP
|
|
||||||
{
|
|
||||||
public static class EFSetup
|
|
||||||
{
|
|
||||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddHttpContextAccessor();
|
|
||||||
services.AddScoped<IUserInfo, UserInfo>();
|
|
||||||
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
|
||||||
|
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
|
||||||
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
|
||||||
{
|
|
||||||
// 在控制台
|
|
||||||
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
|
||||||
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
|
||||||
|
|
||||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
|
||||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
|
||||||
|
|
||||||
options.UseLoggerFactory(logFactory);
|
|
||||||
|
|
||||||
options.UseExceptionProcessor();
|
|
||||||
|
|
||||||
options.EnableSensitiveDataLogging();
|
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
|
||||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
|
||||||
|
|
||||||
options.UseProjectables();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
|
||||||
//services.AddScoped<IRaCISDBScopedFactory>();
|
|
||||||
|
|
||||||
//// Finally, arrange for a context to get injected from our Scoped factory:
|
|
||||||
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
|
||||||
|
|
||||||
//注意区分 easy caching 也有 IDistributedLockProvider
|
|
||||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
|
||||||
{
|
|
||||||
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
|
||||||
|
|
||||||
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.11" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
|
||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
|
||||||
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
|
||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
|
||||||
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
|
||||||
<PackageReference Include="Minio" Version="6.0.4" />
|
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infrastructure\IRaCIS.Core.Infrastructure.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Helper\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,186 +0,0 @@
|
||||||
|
|
||||||
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 主机配置
|
|
||||||
|
|
||||||
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<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);
|
|
||||||
|
|
||||||
builder.Services.AddMediator(cfg =>
|
|
||||||
{
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//转发头设置 获取真实IP
|
|
||||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ForwardedHeaders =
|
|
||||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
|
||||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
|
||||||
.AddImageManager<ImageSharpImageManager>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#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.Warning)
|
|
||||||
.WriteTo.Console()
|
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
|
||||||
.CreateLogger();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region 运行环境 部署平台
|
|
||||||
|
|
||||||
Log.Logger.Warning($"当前环境:{enviromentName}");
|
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:windows");
|
|
||||||
}
|
|
||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:linux");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
DicomSetupBuilder.UseServiceProvider(app.Services);
|
|
||||||
|
|
||||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
|
||||||
|
|
||||||
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services, logger: logger);
|
|
||||||
|
|
||||||
|
|
||||||
app.Run();
|
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:11224",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:5127",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
using AutoMapper;
|
|
||||||
using IRaCIS.Core.Application.Service.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 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(_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(_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 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,376 +0,0 @@
|
||||||
using FellowOakDicom.Network;
|
|
||||||
using FellowOakDicom;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Serilog;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Data;
|
|
||||||
using FellowOakDicom.Imaging;
|
|
||||||
using SharpCompress.Common;
|
|
||||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DicomSCPServiceOption
|
|
||||||
{
|
|
||||||
public List<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 Guid _trialId { get; set; }
|
|
||||||
|
|
||||||
private Guid _trialSiteId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
|
||||||
{
|
|
||||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
|
||||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
|
||||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
|
||||||
{
|
|
||||||
// Lossless
|
|
||||||
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
|
||||||
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
|
||||||
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
|
||||||
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
|
||||||
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
|
||||||
// Lossy
|
|
||||||
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
|
||||||
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
|
||||||
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
|
||||||
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
|
||||||
// Uncompressed
|
|
||||||
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
|
||||||
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
|
||||||
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
|
||||||
: base(stream, fallbackEncoding, log, dependencies)
|
|
||||||
{
|
|
||||||
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
|
||||||
{
|
|
||||||
|
|
||||||
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
|
||||||
|
|
||||||
|
|
||||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
|
||||||
|
|
||||||
//_serviceProvider = (IServiceProvider)this.UserState;
|
|
||||||
|
|
||||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
|
||||||
|
|
||||||
|
|
||||||
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
|
||||||
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
|
||||||
|
|
||||||
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
|
||||||
|
|
||||||
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
|
||||||
|
|
||||||
var isCanReceiveIamge = false;
|
|
||||||
|
|
||||||
if (findCalledAE != null)
|
|
||||||
{
|
|
||||||
_trialId = findCalledAE.TrialId;
|
|
||||||
|
|
||||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
|
||||||
|
|
||||||
|
|
||||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
|
||||||
|
|
||||||
if (findTrialSiteAE != null)
|
|
||||||
{
|
|
||||||
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
|
||||||
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (association.CallingAE == "test-callingAE")
|
|
||||||
{
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
|
||||||
{
|
|
||||||
|
|
||||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
|
||||||
|
|
||||||
return SendAssociationRejectAsync(
|
|
||||||
DicomRejectResult.Permanent,
|
|
||||||
DicomRejectSource.ServiceUser,
|
|
||||||
DicomRejectReason.CalledAENotRecognized);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pc in association.PresentationContexts)
|
|
||||||
{
|
|
||||||
if (pc.AbstractSyntax == DicomUID.Verification)
|
|
||||||
{
|
|
||||||
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
|
||||||
}
|
|
||||||
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
|
||||||
{
|
|
||||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return SendAssociationAcceptAsync(association);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task OnReceiveAssociationReleaseRequestAsync()
|
|
||||||
{
|
|
||||||
await DataMaintenanceAsaync();
|
|
||||||
|
|
||||||
//记录监控
|
|
||||||
|
|
||||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
|
||||||
|
|
||||||
_upload.EndTime = DateTime.Now;
|
|
||||||
_upload.StudyCount = _SCPStudyIdList.Count;
|
|
||||||
_upload.TrialId = _trialId;
|
|
||||||
_upload.TrialSiteId = _trialSiteId;
|
|
||||||
|
|
||||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
|
||||||
|
|
||||||
|
|
||||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
|
||||||
//将检查设置为传输结束
|
|
||||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
|
||||||
|
|
||||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
|
||||||
|
|
||||||
await SendAssociationReleaseResponseAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task DataMaintenanceAsaync()
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//处理检查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 studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
|
||||||
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
|
||||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
|
||||||
|
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
|
||||||
|
|
||||||
var storeRelativePath = string.Empty;
|
|
||||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
|
||||||
|
|
||||||
|
|
||||||
long fileSize = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
await request.File.SaveAsync(ms);
|
|
||||||
|
|
||||||
//irc 从路径最后一截取Guid
|
|
||||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
|
||||||
|
|
||||||
fileSize = ms.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ec)
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
||||||
|
|
||||||
using (await @lock.AcquireAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
|
||||||
|
|
||||||
if (!_SCPStudyIdList.Contains(scpStudyId))
|
|
||||||
{
|
|
||||||
_SCPStudyIdList.Add(scpStudyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
||||||
|
|
||||||
//没有缩略图
|
|
||||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
|
||||||
{
|
|
||||||
|
|
||||||
// 生成缩略图
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
DicomImage image = new DicomImage(request.Dataset);
|
|
||||||
|
|
||||||
var sharpimage = image.RenderImage().AsSharpImage();
|
|
||||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
|
||||||
|
|
||||||
// 上传缩略图到 OSS
|
|
||||||
|
|
||||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
|
||||||
|
|
||||||
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
|
||||||
|
|
||||||
series.ImageResizePath = seriesPath;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await _seriesRepository.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//监控信息设置
|
|
||||||
_upload.FileCount++;
|
|
||||||
_upload.FileSize = _upload.FileSize + fileSize;
|
|
||||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
|
||||||
{
|
|
||||||
// let library handle logging and error response
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
|
||||||
{
|
|
||||||
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,356 +0,0 @@
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using FellowOakDicom;
|
|
||||||
using FellowOakDicom.Imaging.Codec;
|
|
||||||
using System.Data;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using FellowOakDicom.Network;
|
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using MassTransit;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
using Serilog.Sinks.File;
|
|
||||||
|
|
||||||
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, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
|
||||||
{
|
|
||||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
|
||||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
|
||||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
|
||||||
|
|
||||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
|
||||||
|
|
||||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
|
||||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
|
||||||
|
|
||||||
var isStudyNeedAdd = false;
|
|
||||||
var isSeriesNeedAdd = false;
|
|
||||||
var isInstanceNeedAdd = false;
|
|
||||||
var isPatientNeedAdd = false;
|
|
||||||
|
|
||||||
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
||||||
|
|
||||||
//using (@lock.Acquire())
|
|
||||||
{
|
|
||||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
|
||||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
|
||||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
||||||
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == 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(),
|
|
||||||
TrialId=trialId,
|
|
||||||
TrialSiteId=trialSiteId,
|
|
||||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
||||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
||||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
||||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
||||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
||||||
|
|
||||||
EarliestStudyTime = studyTime,
|
|
||||||
LatestStudyTime = studyTime,
|
|
||||||
LatestPushTime = DateTime.Now,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (findPatient.PatientBirthDate.Length == 8)
|
|
||||||
{
|
|
||||||
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
|
||||||
|
|
||||||
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
|
||||||
|
|
||||||
int year = 0;
|
|
||||||
|
|
||||||
var canParse = int.TryParse(yearStr, out year);
|
|
||||||
|
|
||||||
if (canParse && year > 1900)
|
|
||||||
{
|
|
||||||
findPatient.PatientBirthDate = birthDateStr;
|
|
||||||
|
|
||||||
DateTime birthDate;
|
|
||||||
|
|
||||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
|
||||||
{
|
|
||||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
|
||||||
// 如果生日还未到,年龄减去一岁
|
|
||||||
if (studyTime.Value < birthDate.AddYears(patientAge))
|
|
||||||
{
|
|
||||||
patientAge--;
|
|
||||||
}
|
|
||||||
|
|
||||||
findPatient.PatientAge = patientAge.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
findPatient.PatientBirthDate = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (studyTime < findPatient.EarliestStudyTime)
|
|
||||||
{
|
|
||||||
findPatient.EarliestStudyTime = studyTime;
|
|
||||||
}
|
|
||||||
if (studyTime > findPatient.LatestStudyTime)
|
|
||||||
{
|
|
||||||
findPatient.LatestStudyTime = studyTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
findPatient.LatestPushTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findStudy == null)
|
|
||||||
{
|
|
||||||
isStudyNeedAdd = true;
|
|
||||||
findStudy = new SCPStudy
|
|
||||||
{
|
|
||||||
CalledAE = calledAE,
|
|
||||||
CallingAE = callingAE,
|
|
||||||
|
|
||||||
PatientId = findPatient.Id,
|
|
||||||
Id = studyId,
|
|
||||||
TrialId = trialId,
|
|
||||||
TrialSiteId = trialSiteId,
|
|
||||||
StudyInstanceUid = studyInstanceUid,
|
|
||||||
StudyTime = studyTime,
|
|
||||||
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
|
||||||
//ModalityForEdit = modalityForEdit,
|
|
||||||
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
|
||||||
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
|
||||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
||||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
||||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
||||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
||||||
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
|
||||||
|
|
||||||
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
|
||||||
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
|
||||||
|
|
||||||
//需要特殊处理
|
|
||||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
||||||
|
|
||||||
|
|
||||||
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
|
||||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
|
||||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
|
||||||
SeriesCount = 0,
|
|
||||||
InstanceCount = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (findStudy.PatientBirthDate.Length == 8)
|
|
||||||
{
|
|
||||||
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (findSerice == null)
|
|
||||||
{
|
|
||||||
isSeriesNeedAdd = true;
|
|
||||||
|
|
||||||
findSerice = new SCPSeries
|
|
||||||
{
|
|
||||||
Id = seriesId,
|
|
||||||
StudyId = findStudy.Id,
|
|
||||||
|
|
||||||
StudyInstanceUid = findStudy.StudyInstanceUid,
|
|
||||||
SeriesInstanceUid = seriesInstanceUid,
|
|
||||||
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
|
||||||
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
|
||||||
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<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,
|
|
||||||
|
|
||||||
FileSize= fileSize,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
++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,FileSize=fileSize });
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using FellowOakDicom;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
|
||||||
{
|
|
||||||
public interface IDicomArchiveService
|
|
||||||
{
|
|
||||||
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,770 +0,0 @@
|
||||||
using AlibabaCloud.SDK.Sts20150401;
|
|
||||||
using Aliyun.OSS;
|
|
||||||
using Amazon;
|
|
||||||
using Amazon.Runtime;
|
|
||||||
using Amazon.S3;
|
|
||||||
using Amazon.S3.Model;
|
|
||||||
using Amazon.SecurityToken;
|
|
||||||
using Amazon.SecurityToken.Model;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
|
||||||
using MassTransit;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Minio;
|
|
||||||
using Minio.DataModel.Args;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP;
|
|
||||||
|
|
||||||
#region 绑定和返回模型
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class MinIOOptions : AWSOptions
|
|
||||||
{
|
|
||||||
public int Port { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class AWSOptions
|
|
||||||
{
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public bool UseSSL { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string RoleArn { get; set; }
|
|
||||||
public string SecretAccessKey { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
public int DurationSeconds { get; set; }
|
|
||||||
public string Region { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AliyunOSSOptions
|
|
||||||
{
|
|
||||||
public string RegionId { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string AccessKeySecret { get; set; }
|
|
||||||
|
|
||||||
public string InternalEndpoint { get; set; }
|
|
||||||
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
|
|
||||||
public string RoleArn { get; set; }
|
|
||||||
|
|
||||||
public string Region { get; set; }
|
|
||||||
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
|
|
||||||
public int DurationSeconds { 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 AliyunOSSTempToken AliyunOSS { get; set; }
|
|
||||||
|
|
||||||
public MinIOOptions MinIO { get; set; }
|
|
||||||
|
|
||||||
public AWSTempToken AWS { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class AliyunOSSTempToken
|
|
||||||
{
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string AccessKeySecret { get; set; }
|
|
||||||
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
|
|
||||||
public string Region { get; set; }
|
|
||||||
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
|
|
||||||
public string SecurityToken { get; set; }
|
|
||||||
public DateTime Expiration { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class AWSTempToken
|
|
||||||
{
|
|
||||||
public string Region { get; set; }
|
|
||||||
public string SessionToken { get; set; }
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string SecretAccessKey { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
public DateTime Expiration { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ObjectStoreUse
|
|
||||||
{
|
|
||||||
AliyunOSS = 0,
|
|
||||||
MinIO = 1,
|
|
||||||
AWS = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
|
|
||||||
|
|
||||||
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 Task DeleteFromPrefix(string prefix);
|
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class OSSService : IOSSService
|
|
||||||
{
|
|
||||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
|
||||||
|
|
||||||
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
|
|
||||||
|
|
||||||
private AWSTempToken AWSTempToken { 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>
|
|
||||||
/// <param name="isFileNameAddGuid"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
fileStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
fileStream.CopyTo(memoryStream);
|
|
||||||
|
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
InputStream = memoryStream,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
/// <param name="isFileNameAddGuid"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
|
||||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
var localFileName = Path.GetFileName(localFilePath);
|
|
||||||
|
|
||||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
FilePath = localFilePath,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
return "/" + ossRelativePath;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 生成签名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.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
var args = new PresignedGetObjectArgs()
|
|
||||||
.WithBucket(minIOConfig.BucketName)
|
|
||||||
.WithObject(ossRelativePath)
|
|
||||||
.WithExpiry(3600)
|
|
||||||
/*.WithHeaders(reqParams)*/;
|
|
||||||
|
|
||||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
|
||||||
|
|
||||||
Uri uri = new Uri(presignedUrl);
|
|
||||||
|
|
||||||
string relativePath = uri.PathAndQuery;
|
|
||||||
|
|
||||||
|
|
||||||
return relativePath;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
Expires = DateTime.UtcNow.AddMinutes(120)
|
|
||||||
});
|
|
||||||
|
|
||||||
Uri uri = new Uri(presignedUrl);
|
|
||||||
|
|
||||||
string relativePath = uri.PathAndQuery;
|
|
||||||
|
|
||||||
|
|
||||||
return relativePath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除某个目录的文件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="prefix"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task DeleteFromPrefix(string prefix)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ObjectListing objectListing = null;
|
|
||||||
string nextMarker = null;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
|
||||||
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
|
||||||
{
|
|
||||||
Prefix = prefix,
|
|
||||||
MaxKeys = 1000,
|
|
||||||
Marker = nextMarker
|
|
||||||
});
|
|
||||||
|
|
||||||
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
|
|
||||||
|
|
||||||
// 删除获取到的文件
|
|
||||||
if (keys.Count > 0)
|
|
||||||
{
|
|
||||||
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置 NextMarker 以获取下一页的数据
|
|
||||||
nextMarker = objectListing.NextMarker;
|
|
||||||
|
|
||||||
} while (objectListing.IsTruncated);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
|
||||||
{
|
|
||||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
|
||||||
|
|
||||||
|
|
||||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
|
||||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 创建一个空列表用于存储对象键
|
|
||||||
var objects = new List<string>();
|
|
||||||
|
|
||||||
// 使用 await foreach 来异步迭代对象列表
|
|
||||||
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
|
|
||||||
{
|
|
||||||
objects.Add(item.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (objects.Count > 0)
|
|
||||||
{
|
|
||||||
var objArgs = new RemoveObjectsArgs()
|
|
||||||
.WithBucket(minIOConfig.BucketName)
|
|
||||||
.WithObjects(objects);
|
|
||||||
|
|
||||||
// 删除对象
|
|
||||||
await minioClient.RemoveObjectsAsync(objArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
|
|
||||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
// 列出指定前缀下的所有对象
|
|
||||||
var listObjectsRequest = new ListObjectsV2Request
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Prefix = prefix
|
|
||||||
};
|
|
||||||
|
|
||||||
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
|
||||||
|
|
||||||
if (listObjectsResponse.S3Objects.Count > 0)
|
|
||||||
{
|
|
||||||
// 准备删除请求
|
|
||||||
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Objects = new List<KeyVersion>()
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var s3Object in listObjectsResponse.S3Objects)
|
|
||||||
{
|
|
||||||
deleteObjectsRequest.Objects.Add(new KeyVersion
|
|
||||||
{
|
|
||||||
Key = s3Object.Key
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量删除对象
|
|
||||||
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken()
|
|
||||||
{
|
|
||||||
|
|
||||||
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
|
||||||
{
|
|
||||||
AccessKeyId = ossOptions.AccessKeyId,
|
|
||||||
AccessKeySecret = ossOptions.AccessKeySecret,
|
|
||||||
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
|
|
||||||
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
|
||||||
|
|
||||||
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
|
||||||
});
|
|
||||||
|
|
||||||
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
|
||||||
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
|
||||||
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
|
||||||
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
|
||||||
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
|
||||||
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
|
||||||
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
|
||||||
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
|
||||||
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
|
||||||
var credentials = response.Body.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AliyunOSSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
AccessKeySecret = credentials.AccessKeySecret,
|
|
||||||
|
|
||||||
//转为服务器时区,最后统一转为客户端时区
|
|
||||||
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
|
||||||
SecurityToken = credentials.SecurityToken,
|
|
||||||
|
|
||||||
|
|
||||||
Region = ossOptions.Region,
|
|
||||||
BucketName = ossOptions.BucketName,
|
|
||||||
EndPoint = ossOptions.EndPoint,
|
|
||||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AliyunOSSTempToken = tempToken;
|
|
||||||
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
|
||||||
{
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
var awsOptions = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
//aws 临时凭证
|
|
||||||
// 创建 STS 客户端
|
|
||||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
|
||||||
|
|
||||||
// 使用 AssumeRole 请求临时凭证
|
|
||||||
var assumeRoleRequest = new AssumeRoleRequest
|
|
||||||
{
|
|
||||||
|
|
||||||
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
|
||||||
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
|
||||||
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
|
||||||
};
|
|
||||||
|
|
||||||
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
|
|
||||||
|
|
||||||
var credentials = assumeRoleResponse.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AWSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
SecretAccessKey = credentials.SecretAccessKey,
|
|
||||||
SessionToken = credentials.SessionToken,
|
|
||||||
Expiration = credentials.Expiration,
|
|
||||||
Region = awsOptions.Region,
|
|
||||||
BucketName = awsOptions.BucketName,
|
|
||||||
EndPoint = awsOptions.EndPoint,
|
|
||||||
ViewEndpoint = awsOptions.ViewEndpoint,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AWSTempToken = tempToken;
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
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.Core.Application.Service;
|
|
||||||
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 + typeof(BaseService).Assembly.GetName().Name+".dll");
|
|
||||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
|
||||||
.PropertiesAutowired().AsImplementedInterfaces();
|
|
||||||
|
|
||||||
|
|
||||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
|
||||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
using EntityFramework.Exceptions.SqlServer;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using Medallion.Threading.SqlServer;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP
|
|
||||||
{
|
|
||||||
public static class EFSetup
|
|
||||||
{
|
|
||||||
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
|
||||||
{
|
|
||||||
services.AddHttpContextAccessor();
|
|
||||||
services.AddScoped<IUserInfo, UserInfo>();
|
|
||||||
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
|
||||||
|
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
|
||||||
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
|
||||||
{
|
|
||||||
// 在控制台
|
|
||||||
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
|
||||||
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
|
||||||
|
|
||||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
|
||||||
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
|
||||||
|
|
||||||
options.UseLoggerFactory(logFactory);
|
|
||||||
|
|
||||||
options.UseExceptionProcessor();
|
|
||||||
|
|
||||||
options.EnableSensitiveDataLogging();
|
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
|
||||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
|
||||||
|
|
||||||
options.UseProjectables();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
|
||||||
//services.AddScoped<IRaCISDBScopedFactory>();
|
|
||||||
|
|
||||||
//// Finally, arrange for a context to get injected from our Scoped factory:
|
|
||||||
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
|
||||||
|
|
||||||
//注意区分 easy caching 也有 IDistributedLockProvider
|
|
||||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
|
||||||
{
|
|
||||||
//var connection = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]!);
|
|
||||||
|
|
||||||
return new SqlDistributedSynchronizationProvider(configuration.GetSection("ConnectionStrings:RemoteNew").Value);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="AlibabaCloud.SDK.Sts20150401" Version="1.1.5" />
|
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
|
|
||||||
<PackageReference Include="Aliyun.OSS.SDK.NetCore" Version="2.14.1" />
|
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.416.8" />
|
|
||||||
<PackageReference Include="AWSSDK.SecurityToken" Version="3.7.401.81" />
|
|
||||||
<PackageReference Include="DistributedLock.Core" Version="1.0.8" />
|
|
||||||
<PackageReference Include="DistributedLock.SqlServer" Version="1.0.6" />
|
|
||||||
<PackageReference Include="fo-dicom" Version="5.2.1" />
|
|
||||||
<PackageReference Include="fo-dicom.Codecs" Version="5.16.1" />
|
|
||||||
<PackageReference Include="fo-dicom.Imaging.ImageSharp" Version="5.2.1" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
|
||||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
|
||||||
<PackageReference Include="Minio" Version="6.0.4" />
|
|
||||||
<PackageReference Include="My.Extensions.Localization.Json" Version="3.3.0">
|
|
||||||
<TreatAsUsed>true</TreatAsUsed>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Panda.DynamicWebApi" Version="1.2.2" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.1.2" />
|
|
||||||
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Domain\IRaCIS.Core.Domain.csproj" />
|
|
||||||
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
|
@ -1,227 +0,0 @@
|
||||||
|
|
||||||
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<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);
|
|
||||||
|
|
||||||
builder.Services.AddMediator(cfg =>
|
|
||||||
{
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
//转发头设置 获取真实IP
|
|
||||||
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ForwardedHeaders =
|
|
||||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
builder.Services.AddFellowOakDicom().AddTranscoderManager<NativeTranscoderManager>()
|
|
||||||
//.AddTranscoderManager<FellowOakDicom.Imaging.NativeCodec.NativeTranscoderManager>()
|
|
||||||
.AddImageManager<ImageSharpImageManager>();
|
|
||||||
|
|
||||||
|
|
||||||
//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();
|
|
||||||
|
|
||||||
Log.Logger = new LoggerConfiguration()
|
|
||||||
//.MinimumLevel.Information()
|
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
|
||||||
.WriteTo.Console()
|
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day)
|
|
||||||
.CreateLogger();
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region 运行环境 部署平台
|
|
||||||
|
|
||||||
Log.Logger.Warning($"当前环境:{enviromentName}");
|
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:windows");
|
|
||||||
}
|
|
||||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:linux");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
DicomSetupBuilder.UseServiceProvider(app.Services);
|
|
||||||
|
|
||||||
var logger = app.Services.GetService<Microsoft.Extensions.Logging.ILogger<Program>>();
|
|
||||||
|
|
||||||
var server = DicomServerFactory.Create<CStoreSCPService>(_configuration.GetSection("DicomSCPServiceConfig").GetValue<int>("ServerPort"), userState: app.Services,logger: logger);
|
|
||||||
|
|
||||||
|
|
||||||
app.Run();
|
|
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"Test_IRC_SCP": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:6200",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_SCP"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Uat_IRC_SCP": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:6200",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Uat_IRC_SCP"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"US_Prod_IRC_SCP": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"applicationUrl": "http://localhost:6200",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_Prod_SCP"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
using AutoMapper;
|
|
||||||
using IRaCIS.Core.Application.Service.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 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(_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(_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 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,376 +0,0 @@
|
||||||
using FellowOakDicom.Network;
|
|
||||||
using FellowOakDicom;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Serilog;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using System.Data;
|
|
||||||
using FellowOakDicom.Imaging;
|
|
||||||
using SharpCompress.Common;
|
|
||||||
using SixLabors.ImageSharp.Formats.Jpeg;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
|
||||||
{
|
|
||||||
|
|
||||||
public class DicomSCPServiceOption
|
|
||||||
{
|
|
||||||
public List<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 Guid _trialId { get; set; }
|
|
||||||
|
|
||||||
private Guid _trialSiteId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly DicomTransferSyntax[] _acceptedTransferSyntaxes = new DicomTransferSyntax[]
|
|
||||||
{
|
|
||||||
DicomTransferSyntax.ExplicitVRLittleEndian,
|
|
||||||
DicomTransferSyntax.ExplicitVRBigEndian,
|
|
||||||
DicomTransferSyntax.ImplicitVRLittleEndian
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly DicomTransferSyntax[] _acceptedImageTransferSyntaxes = new DicomTransferSyntax[]
|
|
||||||
{
|
|
||||||
// Lossless
|
|
||||||
DicomTransferSyntax.JPEGLSLossless, //1.2.840.10008.1.2.4.80
|
|
||||||
DicomTransferSyntax.JPEG2000Lossless, //1.2.840.10008.1.2.4.90
|
|
||||||
DicomTransferSyntax.JPEGProcess14SV1, //1.2.840.10008.1.2.4.70
|
|
||||||
DicomTransferSyntax.JPEGProcess14, //1.2.840.10008.1.2.4.57 JPEG Lossless, Non-Hierarchical (Process 14)
|
|
||||||
DicomTransferSyntax.RLELossless, //1.2.840.10008.1.2.5
|
|
||||||
// Lossy
|
|
||||||
DicomTransferSyntax.JPEGLSNearLossless,//1.2.840.10008.1.2.4.81"
|
|
||||||
DicomTransferSyntax.JPEG2000Lossy, //1.2.840.10008.1.2.4.91
|
|
||||||
DicomTransferSyntax.JPEGProcess1, //1.2.840.10008.1.2.4.50
|
|
||||||
DicomTransferSyntax.JPEGProcess2_4, //1.2.840.10008.1.2.4.51
|
|
||||||
// Uncompressed
|
|
||||||
DicomTransferSyntax.ExplicitVRLittleEndian, //1.2.840.10008.1.2.1
|
|
||||||
DicomTransferSyntax.ExplicitVRBigEndian, //1.2.840.10008.1.2.2
|
|
||||||
DicomTransferSyntax.ImplicitVRLittleEndian //1.2.840.10008.1.2
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public CStoreSCPService(INetworkStream stream, Encoding fallbackEncoding, Microsoft.Extensions.Logging.ILogger log, DicomServiceDependencies dependencies, IServiceProvider injectServiceProvider)
|
|
||||||
: base(stream, fallbackEncoding, log, dependencies)
|
|
||||||
{
|
|
||||||
_serviceProvider = injectServiceProvider.CreateScope().ServiceProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Task OnReceiveAssociationRequestAsync(DicomAssociation association)
|
|
||||||
{
|
|
||||||
|
|
||||||
_upload = new SCPImageUpload() { StartTime = DateTime.Now, CallingAE = association.CallingAE, CalledAE = association.CalledAE, CallingAEIP = association.RemoteHost };
|
|
||||||
|
|
||||||
|
|
||||||
Log.Logger.Warning($"接收到来自{association.CallingAE}的连接");
|
|
||||||
|
|
||||||
//_serviceProvider = (IServiceProvider)this.UserState;
|
|
||||||
|
|
||||||
var _trialDicomAERepository = _serviceProvider.GetService<IRepository<TrialDicomAE>>();
|
|
||||||
|
|
||||||
|
|
||||||
var trialDicomAEList = _trialDicomAERepository.Select(t => new { t.CalledAE, t.TrialId }).ToList();
|
|
||||||
var trialCalledAEList = trialDicomAEList.Select(t => t.CalledAE).ToList();
|
|
||||||
|
|
||||||
Log.Logger.Information("当前系统配置:", string.Join('|', trialDicomAEList));
|
|
||||||
|
|
||||||
var findCalledAE = trialDicomAEList.Where(t => t.CalledAE == association.CalledAE).FirstOrDefault();
|
|
||||||
|
|
||||||
var isCanReceiveIamge = false;
|
|
||||||
|
|
||||||
if (findCalledAE != null)
|
|
||||||
{
|
|
||||||
_trialId = findCalledAE.TrialId;
|
|
||||||
|
|
||||||
var _trialSiteDicomAERepository = _serviceProvider.GetService<IRepository<TrialSiteDicomAE>>();
|
|
||||||
|
|
||||||
|
|
||||||
var findTrialSiteAE = _trialSiteDicomAERepository.Where(t => t.CallingAE == association.CallingAE && t.TrialId==_trialId).FirstOrDefault();
|
|
||||||
|
|
||||||
if (findTrialSiteAE != null)
|
|
||||||
{
|
|
||||||
_trialSiteId = findTrialSiteAE.TrialSiteId;
|
|
||||||
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (association.CallingAE == "test-callingAE")
|
|
||||||
{
|
|
||||||
isCanReceiveIamge = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!trialCalledAEList.Contains(association.CalledAE) || isCanReceiveIamge == false)
|
|
||||||
{
|
|
||||||
|
|
||||||
Log.Logger.Warning($"拒绝CallingAE:{association.CallingAE} CalledAE:{association.CalledAE}的连接");
|
|
||||||
|
|
||||||
return SendAssociationRejectAsync(
|
|
||||||
DicomRejectResult.Permanent,
|
|
||||||
DicomRejectSource.ServiceUser,
|
|
||||||
DicomRejectReason.CalledAENotRecognized);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var pc in association.PresentationContexts)
|
|
||||||
{
|
|
||||||
if (pc.AbstractSyntax == DicomUID.Verification)
|
|
||||||
{
|
|
||||||
pc.AcceptTransferSyntaxes(_acceptedTransferSyntaxes);
|
|
||||||
}
|
|
||||||
else if (pc.AbstractSyntax.StorageCategory != DicomStorageCategory.None)
|
|
||||||
{
|
|
||||||
pc.AcceptTransferSyntaxes(_acceptedImageTransferSyntaxes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return SendAssociationAcceptAsync(association);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task OnReceiveAssociationReleaseRequestAsync()
|
|
||||||
{
|
|
||||||
await DataMaintenanceAsaync();
|
|
||||||
|
|
||||||
//记录监控
|
|
||||||
|
|
||||||
var _SCPImageUploadRepository = _serviceProvider.GetService<IRepository<SCPImageUpload>>();
|
|
||||||
|
|
||||||
_upload.EndTime = DateTime.Now;
|
|
||||||
_upload.StudyCount = _SCPStudyIdList.Count;
|
|
||||||
_upload.TrialId = _trialId;
|
|
||||||
_upload.TrialSiteId = _trialSiteId;
|
|
||||||
|
|
||||||
await _SCPImageUploadRepository.AddAsync(_upload, true);
|
|
||||||
|
|
||||||
|
|
||||||
var _studyRepository = _serviceProvider.GetService<IRepository<SCPStudy>>();
|
|
||||||
//将检查设置为传输结束
|
|
||||||
await _studyRepository.BatchUpdateNoTrackingAsync(t => _SCPStudyIdList.Contains(t.Id), u => new SCPStudy() { IsUploadFinished = true });
|
|
||||||
|
|
||||||
await _studyRepository.SaveChangesAndClearAllTrackingAsync();
|
|
||||||
|
|
||||||
await SendAssociationReleaseResponseAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task DataMaintenanceAsaync()
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE}传输结束:开始维护数据,处理检查Modality");
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//处理检查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 studyId = IdentifierHelper.CreateGuid(studyInstanceUid, trialId.ToString());
|
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, _trialId.ToString());
|
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, _trialId.ToString());
|
|
||||||
|
|
||||||
|
|
||||||
var ossService = _serviceProvider.GetService<IOSSService>();
|
|
||||||
var dicomArchiveService = _serviceProvider.GetService<IDicomArchiveService>();
|
|
||||||
var _seriesRepository = _serviceProvider.GetService<IRepository<SCPSeries>>();
|
|
||||||
|
|
||||||
var _distributedLockProvider = _serviceProvider.GetService<IDistributedLockProvider>();
|
|
||||||
|
|
||||||
var storeRelativePath = string.Empty;
|
|
||||||
var ossFolderPath = $"{_trialId}/Image/PACS/{_trialSiteId}/{studyInstanceUid}";
|
|
||||||
|
|
||||||
|
|
||||||
long fileSize = 0;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
using (MemoryStream ms = new MemoryStream())
|
|
||||||
{
|
|
||||||
await request.File.SaveAsync(ms);
|
|
||||||
|
|
||||||
//irc 从路径最后一截取Guid
|
|
||||||
storeRelativePath = await ossService.UploadToOSSAsync(ms, ossFolderPath, instanceId.ToString(), false);
|
|
||||||
|
|
||||||
fileSize = ms.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Logger.Information($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} {request.SOPInstanceUID} 上传完成 ");
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ec)
|
|
||||||
{
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 上传异常 {ec.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
||||||
|
|
||||||
using (await @lock.AcquireAsync())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var scpStudyId = await dicomArchiveService.ArchiveDicomFileAsync(request.Dataset, _trialId, _trialSiteId, storeRelativePath, Association.CallingAE, Association.CalledAE,fileSize);
|
|
||||||
|
|
||||||
if (!_SCPStudyIdList.Contains(scpStudyId))
|
|
||||||
{
|
|
||||||
_SCPStudyIdList.Add(scpStudyId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var series = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
||||||
|
|
||||||
//没有缩略图
|
|
||||||
if (series != null && string.IsNullOrEmpty(series.ImageResizePath))
|
|
||||||
{
|
|
||||||
|
|
||||||
// 生成缩略图
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
DicomImage image = new DicomImage(request.Dataset);
|
|
||||||
|
|
||||||
var sharpimage = image.RenderImage().AsSharpImage();
|
|
||||||
sharpimage.Save(memoryStream, new JpegEncoder());
|
|
||||||
|
|
||||||
// 上传缩略图到 OSS
|
|
||||||
|
|
||||||
var seriesPath = await ossService.UploadToOSSAsync(memoryStream, ossFolderPath, seriesId.ToString() + ".preview.jpg", false);
|
|
||||||
|
|
||||||
Console.WriteLine(seriesPath + " Id: " + seriesId);
|
|
||||||
|
|
||||||
series.ImageResizePath = seriesPath;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
await _seriesRepository.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
Log.Logger.Warning($"CallingAE:{Association.CallingAE} CalledAE:{Association.CalledAE} 传输处理异常:{ex.ToString()}");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//监控信息设置
|
|
||||||
_upload.FileCount++;
|
|
||||||
_upload.FileSize = _upload.FileSize + fileSize;
|
|
||||||
return new DicomCStoreResponse(request, DicomStatus.Success);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Task OnCStoreRequestExceptionAsync(string tempFileName, Exception e)
|
|
||||||
{
|
|
||||||
// let library handle logging and error response
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Task<DicomCEchoResponse> OnCEchoRequestAsync(DicomCEchoRequest request)
|
|
||||||
{
|
|
||||||
return Task.FromResult(new DicomCEchoResponse(request, DicomStatus.Success));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,356 +0,0 @@
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using Medallion.Threading;
|
|
||||||
using FellowOakDicom;
|
|
||||||
using FellowOakDicom.Imaging.Codec;
|
|
||||||
using System.Data;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using FellowOakDicom.Network;
|
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using MassTransit;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
using Serilog.Sinks.File;
|
|
||||||
|
|
||||||
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, Guid trialId, Guid trialSiteId, string fileRelativePath, string callingAE, string calledAE,long fileSize)
|
|
||||||
{
|
|
||||||
string studyInstanceUid = dataset.GetString(DicomTag.StudyInstanceUID);
|
|
||||||
string seriesInstanceUid = dataset.GetString(DicomTag.SeriesInstanceUID);
|
|
||||||
string sopInstanceUid = dataset.GetString(DicomTag.SOPInstanceUID);
|
|
||||||
|
|
||||||
string patientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID,string.Empty);
|
|
||||||
|
|
||||||
//Guid patientId= IdentifierHelper.CreateGuid(patientIdStr);
|
|
||||||
Guid studyId = IdentifierHelper.CreateGuid(studyInstanceUid,trialId.ToString());
|
|
||||||
Guid seriesId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, trialId.ToString());
|
|
||||||
Guid instanceId = IdentifierHelper.CreateGuid(studyInstanceUid, seriesInstanceUid, sopInstanceUid, trialId.ToString());
|
|
||||||
|
|
||||||
var isStudyNeedAdd = false;
|
|
||||||
var isSeriesNeedAdd = false;
|
|
||||||
var isInstanceNeedAdd = false;
|
|
||||||
var isPatientNeedAdd = false;
|
|
||||||
|
|
||||||
//var @lock = _distributedLockProvider.CreateLock($"{studyInstanceUid}");
|
|
||||||
|
|
||||||
//using (@lock.Acquire())
|
|
||||||
{
|
|
||||||
var findPatient = await _patientRepository.FirstOrDefaultAsync(t => t.PatientIdStr == patientIdStr && t.TrialSiteId==trialSiteId );
|
|
||||||
var findStudy = await _studyRepository.FirstOrDefaultAsync(t=>t.Id== studyId);
|
|
||||||
var findSerice = await _seriesRepository.FirstOrDefaultAsync(t => t.Id == seriesId);
|
|
||||||
var findInstance = await _instanceRepository.FirstOrDefaultAsync(t => t.Id == 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(),
|
|
||||||
TrialId=trialId,
|
|
||||||
TrialSiteId=trialSiteId,
|
|
||||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
||||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
||||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
||||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
||||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
||||||
|
|
||||||
EarliestStudyTime = studyTime,
|
|
||||||
LatestStudyTime = studyTime,
|
|
||||||
LatestPushTime = DateTime.Now,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (findPatient.PatientBirthDate.Length == 8)
|
|
||||||
{
|
|
||||||
var birthDateStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}-{findPatient.PatientBirthDate[4]}{findPatient.PatientBirthDate[5]}-{findPatient.PatientBirthDate[6]}{findPatient.PatientBirthDate[7]}";
|
|
||||||
|
|
||||||
var yearStr = $"{findPatient.PatientBirthDate[0]}{findPatient.PatientBirthDate[1]}{findPatient.PatientBirthDate[2]}{findPatient.PatientBirthDate[3]}";
|
|
||||||
|
|
||||||
int year = 0;
|
|
||||||
|
|
||||||
var canParse = int.TryParse(yearStr, out year);
|
|
||||||
|
|
||||||
if (canParse && year > 1900)
|
|
||||||
{
|
|
||||||
findPatient.PatientBirthDate = birthDateStr;
|
|
||||||
|
|
||||||
DateTime birthDate;
|
|
||||||
|
|
||||||
if (findPatient.PatientAge == string.Empty && studyTime.HasValue && DateTime.TryParse(findPatient.PatientBirthDate,out birthDate))
|
|
||||||
{
|
|
||||||
var patientAge = studyTime.Value.Year - birthDate.Year;
|
|
||||||
// 如果生日还未到,年龄减去一岁
|
|
||||||
if (studyTime.Value < birthDate.AddYears(patientAge))
|
|
||||||
{
|
|
||||||
patientAge--;
|
|
||||||
}
|
|
||||||
|
|
||||||
findPatient.PatientAge = patientAge.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
findPatient.PatientBirthDate = string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (studyTime < findPatient.EarliestStudyTime)
|
|
||||||
{
|
|
||||||
findPatient.EarliestStudyTime = studyTime;
|
|
||||||
}
|
|
||||||
if (studyTime > findPatient.LatestStudyTime)
|
|
||||||
{
|
|
||||||
findPatient.LatestStudyTime = studyTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
findPatient.LatestPushTime = DateTime.Now;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findStudy == null)
|
|
||||||
{
|
|
||||||
isStudyNeedAdd = true;
|
|
||||||
findStudy = new SCPStudy
|
|
||||||
{
|
|
||||||
CalledAE = calledAE,
|
|
||||||
CallingAE = callingAE,
|
|
||||||
|
|
||||||
PatientId = findPatient.Id,
|
|
||||||
Id = studyId,
|
|
||||||
TrialId = trialId,
|
|
||||||
TrialSiteId = trialSiteId,
|
|
||||||
StudyInstanceUid = studyInstanceUid,
|
|
||||||
StudyTime = studyTime,
|
|
||||||
Modalities = dataset.GetSingleValueOrDefault(DicomTag.Modality, string.Empty),
|
|
||||||
//ModalityForEdit = modalityForEdit,
|
|
||||||
Description = dataset.GetSingleValueOrDefault(DicomTag.StudyDescription, string.Empty),
|
|
||||||
InstitutionName = dataset.GetSingleValueOrDefault(DicomTag.InstitutionName, string.Empty),
|
|
||||||
PatientIdStr = dataset.GetSingleValueOrDefault(DicomTag.PatientID, string.Empty),
|
|
||||||
PatientName = dataset.GetSingleValueOrDefault(DicomTag.PatientName, string.Empty),
|
|
||||||
PatientAge = dataset.GetSingleValueOrDefault(DicomTag.PatientAge, string.Empty),
|
|
||||||
PatientSex = dataset.GetSingleValueOrDefault(DicomTag.PatientSex, string.Empty),
|
|
||||||
BodyPartExamined = dataset.GetSingleValueOrDefault(DicomTag.BodyPartExamined, string.Empty),
|
|
||||||
|
|
||||||
StudyId = dataset.GetSingleValueOrDefault(DicomTag.StudyID, string.Empty),
|
|
||||||
AccessionNumber = dataset.GetSingleValueOrDefault(DicomTag.AccessionNumber, string.Empty),
|
|
||||||
|
|
||||||
//需要特殊处理
|
|
||||||
PatientBirthDate = dataset.GetSingleValueOrDefault(DicomTag.PatientBirthDate, string.Empty),
|
|
||||||
|
|
||||||
|
|
||||||
AcquisitionTime = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionTime, string.Empty),
|
|
||||||
AcquisitionNumber = dataset.GetSingleValueOrDefault(DicomTag.AcquisitionNumber, string.Empty),
|
|
||||||
TriggerTime = dataset.GetSingleValueOrDefault(DicomTag.TriggerTime, string.Empty),
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//IsDoubleReview = addtionalInfo.IsDoubleReview,
|
|
||||||
SeriesCount = 0,
|
|
||||||
InstanceCount = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
if (findStudy.PatientBirthDate.Length == 8)
|
|
||||||
{
|
|
||||||
findStudy.PatientBirthDate = $"{findStudy.PatientBirthDate[0]}{findStudy.PatientBirthDate[1]}{findStudy.PatientBirthDate[2]}{findStudy.PatientBirthDate[3]}-{findStudy.PatientBirthDate[4]}{findStudy.PatientBirthDate[5]}-{findStudy.PatientBirthDate[6]}{findStudy.PatientBirthDate[7]}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (findSerice == null)
|
|
||||||
{
|
|
||||||
isSeriesNeedAdd = true;
|
|
||||||
|
|
||||||
findSerice = new SCPSeries
|
|
||||||
{
|
|
||||||
Id = seriesId,
|
|
||||||
StudyId = findStudy.Id,
|
|
||||||
|
|
||||||
StudyInstanceUid = findStudy.StudyInstanceUid,
|
|
||||||
SeriesInstanceUid = seriesInstanceUid,
|
|
||||||
SeriesNumber = dataset.GetSingleValueOrDefault(DicomTag.SeriesNumber, 1),
|
|
||||||
//SeriesTime = dataset.GetSingleValueOrDefault(DicomTag.SeriesDate, DateTime.Now).Add(dataset.GetSingleValueOrDefault(DicomTag.SeriesTime, DateTime.Now).TimeOfDay),
|
|
||||||
//SeriesTime = DateTime.TryParse(dataset.GetSingleValue<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,
|
|
||||||
|
|
||||||
FileSize= fileSize,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
++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,FileSize=fileSize });
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
using FellowOakDicom;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion.Internal;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP.Service
|
|
||||||
{
|
|
||||||
public interface IDicomArchiveService
|
|
||||||
{
|
|
||||||
Task<Guid> ArchiveDicomFileAsync(DicomDataset dicomDataset,Guid trialId,Guid trialSiteId, string fileRelativePath,string callingAE,string calledAE,long fileSize);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,770 +0,0 @@
|
||||||
using AlibabaCloud.SDK.Sts20150401;
|
|
||||||
using Aliyun.OSS;
|
|
||||||
using Amazon;
|
|
||||||
using Amazon.Runtime;
|
|
||||||
using Amazon.S3;
|
|
||||||
using Amazon.S3.Model;
|
|
||||||
using Amazon.SecurityToken;
|
|
||||||
using Amazon.SecurityToken.Model;
|
|
||||||
using IRaCIS.Core.Infrastructure;
|
|
||||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
|
||||||
using MassTransit;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Minio;
|
|
||||||
using Minio.DataModel.Args;
|
|
||||||
using System.Reactive.Linq;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP;
|
|
||||||
|
|
||||||
#region 绑定和返回模型
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class MinIOOptions : AWSOptions
|
|
||||||
{
|
|
||||||
public int Port { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class AWSOptions
|
|
||||||
{
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public bool UseSSL { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string RoleArn { get; set; }
|
|
||||||
public string SecretAccessKey { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
public int DurationSeconds { get; set; }
|
|
||||||
public string Region { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AliyunOSSOptions
|
|
||||||
{
|
|
||||||
public string RegionId { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string AccessKeySecret { get; set; }
|
|
||||||
|
|
||||||
public string InternalEndpoint { get; set; }
|
|
||||||
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
|
|
||||||
public string RoleArn { get; set; }
|
|
||||||
|
|
||||||
public string Region { get; set; }
|
|
||||||
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
|
|
||||||
public int DurationSeconds { 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 AliyunOSSTempToken AliyunOSS { get; set; }
|
|
||||||
|
|
||||||
public MinIOOptions MinIO { get; set; }
|
|
||||||
|
|
||||||
public AWSTempToken AWS { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class AliyunOSSTempToken
|
|
||||||
{
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string AccessKeySecret { get; set; }
|
|
||||||
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
|
|
||||||
public string Region { get; set; }
|
|
||||||
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
|
|
||||||
public string SecurityToken { get; set; }
|
|
||||||
public DateTime Expiration { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[LowerCamelCaseJson]
|
|
||||||
public class AWSTempToken
|
|
||||||
{
|
|
||||||
public string Region { get; set; }
|
|
||||||
public string SessionToken { get; set; }
|
|
||||||
public string EndPoint { get; set; }
|
|
||||||
public string AccessKeyId { get; set; }
|
|
||||||
public string SecretAccessKey { get; set; }
|
|
||||||
public string BucketName { get; set; }
|
|
||||||
public string ViewEndpoint { get; set; }
|
|
||||||
public DateTime Expiration { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ObjectStoreUse
|
|
||||||
{
|
|
||||||
AliyunOSS = 0,
|
|
||||||
MinIO = 1,
|
|
||||||
AWS = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// aws 参考链接 https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3/S3/S3_Basics
|
|
||||||
|
|
||||||
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 Task DeleteFromPrefix(string prefix);
|
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class OSSService : IOSSService
|
|
||||||
{
|
|
||||||
public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; }
|
|
||||||
|
|
||||||
private AliyunOSSTempToken AliyunOSSTempToken { get; set; }
|
|
||||||
|
|
||||||
private AWSTempToken AWSTempToken { 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>
|
|
||||||
/// <param name="isFileNameAddGuid"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<string> UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}";
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var memoryStream = new MemoryStream())
|
|
||||||
{
|
|
||||||
fileStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
fileStream.CopyTo(memoryStream);
|
|
||||||
|
|
||||||
memoryStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
InputStream = memoryStream,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
/// <param name="isFileNameAddGuid"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
|
||||||
public async Task<string> UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
var localFileName = Path.GetFileName(localFilePath);
|
|
||||||
|
|
||||||
var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}";
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var putObjectRequest = new Amazon.S3.Model.PutObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
FilePath = localFilePath,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
await amazonS3Client.PutObjectAsync(putObjectRequest);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
return "/" + ossRelativePath;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 上传文件
|
|
||||||
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.AccessKeyId, minIOConfig.SecretAccessKey).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 awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var getObjectArgs = new Amazon.S3.Model.GetObjectRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
throw new BusinessValidationFailedException("oss下载失败!" + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> GetSignedUrl(string ossRelativePath)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
ossRelativePath = ossRelativePath.TrimStart('/');
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
// 生成签名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.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
var args = new PresignedGetObjectArgs()
|
|
||||||
.WithBucket(minIOConfig.BucketName)
|
|
||||||
.WithObject(ossRelativePath)
|
|
||||||
.WithExpiry(3600)
|
|
||||||
/*.WithHeaders(reqParams)*/;
|
|
||||||
|
|
||||||
var presignedUrl = await minioClient.PresignedGetObjectAsync(args);
|
|
||||||
|
|
||||||
Uri uri = new Uri(presignedUrl);
|
|
||||||
|
|
||||||
string relativePath = uri.PathAndQuery;
|
|
||||||
|
|
||||||
|
|
||||||
return relativePath;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest()
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Key = ossRelativePath,
|
|
||||||
Expires = DateTime.UtcNow.AddMinutes(120)
|
|
||||||
});
|
|
||||||
|
|
||||||
Uri uri = new Uri(presignedUrl);
|
|
||||||
|
|
||||||
string relativePath = uri.PathAndQuery;
|
|
||||||
|
|
||||||
|
|
||||||
return relativePath;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除某个目录的文件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="prefix"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task DeleteFromPrefix(string prefix)
|
|
||||||
{
|
|
||||||
GetObjectStoreTempToken();
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var aliConfig = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken);
|
|
||||||
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ObjectListing objectListing = null;
|
|
||||||
string nextMarker = null;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
// 使用 prefix 模拟目录结构,设置 MaxKeys 和 NextMarker
|
|
||||||
objectListing = _ossClient.ListObjects(new Aliyun.OSS.ListObjectsRequest(aliConfig.BucketName)
|
|
||||||
{
|
|
||||||
Prefix = prefix,
|
|
||||||
MaxKeys = 1000,
|
|
||||||
Marker = nextMarker
|
|
||||||
});
|
|
||||||
|
|
||||||
List<string> keys = objectListing.ObjectSummaries.Select(t => t.Key).ToList();
|
|
||||||
|
|
||||||
// 删除获取到的文件
|
|
||||||
if (keys.Count > 0)
|
|
||||||
{
|
|
||||||
_ossClient.DeleteObjects(new Aliyun.OSS.DeleteObjectsRequest(aliConfig.BucketName, keys, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置 NextMarker 以获取下一页的数据
|
|
||||||
nextMarker = objectListing.NextMarker;
|
|
||||||
|
|
||||||
} while (objectListing.IsTruncated);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"Error: {ex.Message}");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
|
||||||
{
|
|
||||||
var minIOConfig = ObjectStoreServiceOptions.MinIO;
|
|
||||||
|
|
||||||
|
|
||||||
var minioClient = new MinioClient().WithEndpoint($"{minIOConfig.EndPoint}:{minIOConfig.Port}")
|
|
||||||
.WithCredentials(minIOConfig.AccessKeyId, minIOConfig.SecretAccessKey).WithSSL(minIOConfig.UseSSL)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
var listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 创建一个空列表用于存储对象键
|
|
||||||
var objects = new List<string>();
|
|
||||||
|
|
||||||
// 使用 await foreach 来异步迭代对象列表
|
|
||||||
await foreach (var item in minioClient.ListObjectsEnumAsync(listArgs))
|
|
||||||
{
|
|
||||||
objects.Add(item.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (objects.Count > 0)
|
|
||||||
{
|
|
||||||
var objArgs = new RemoveObjectsArgs()
|
|
||||||
.WithBucket(minIOConfig.BucketName)
|
|
||||||
.WithObjects(objects);
|
|
||||||
|
|
||||||
// 删除对象
|
|
||||||
await minioClient.RemoveObjectsAsync(objArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
|
|
||||||
var awsConfig = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
|
|
||||||
// 提供awsAccessKeyId和awsSecretAccessKey构造凭证
|
|
||||||
var credentials = new SessionAWSCredentials(AWSTempToken.AccessKeyId, AWSTempToken.SecretAccessKey, AWSTempToken.SessionToken);
|
|
||||||
|
|
||||||
//提供awsEndPoint(域名)进行访问配置
|
|
||||||
var clientConfig = new AmazonS3Config
|
|
||||||
{
|
|
||||||
RegionEndpoint = RegionEndpoint.USEast1,
|
|
||||||
UseHttp = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
var amazonS3Client = new AmazonS3Client(credentials, clientConfig);
|
|
||||||
|
|
||||||
// 列出指定前缀下的所有对象
|
|
||||||
var listObjectsRequest = new ListObjectsV2Request
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Prefix = prefix
|
|
||||||
};
|
|
||||||
|
|
||||||
var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest);
|
|
||||||
|
|
||||||
if (listObjectsResponse.S3Objects.Count > 0)
|
|
||||||
{
|
|
||||||
// 准备删除请求
|
|
||||||
var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest
|
|
||||||
{
|
|
||||||
BucketName = awsConfig.BucketName,
|
|
||||||
Objects = new List<KeyVersion>()
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var s3Object in listObjectsResponse.S3Objects)
|
|
||||||
{
|
|
||||||
deleteObjectsRequest.Objects.Add(new KeyVersion
|
|
||||||
{
|
|
||||||
Key = s3Object.Key
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量删除对象
|
|
||||||
var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public ObjectStoreDTO GetObjectStoreTempToken()
|
|
||||||
{
|
|
||||||
|
|
||||||
var ossOptions = ObjectStoreServiceOptions.AliyunOSS;
|
|
||||||
|
|
||||||
if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS")
|
|
||||||
{
|
|
||||||
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
|
||||||
{
|
|
||||||
AccessKeyId = ossOptions.AccessKeyId,
|
|
||||||
AccessKeySecret = ossOptions.AccessKeySecret,
|
|
||||||
//AccessKeyId = "LTAI5tJV76pYX5yPg1N9QVE8",
|
|
||||||
//AccessKeySecret = "roRNLa9YG1of4pYruJGCNKBXEWTAWa",
|
|
||||||
|
|
||||||
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
|
||||||
});
|
|
||||||
|
|
||||||
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
|
||||||
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
|
||||||
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
|
||||||
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
|
||||||
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
|
||||||
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
|
||||||
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
|
||||||
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
|
||||||
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
|
||||||
var credentials = response.Body.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AliyunOSSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
AccessKeySecret = credentials.AccessKeySecret,
|
|
||||||
|
|
||||||
//转为服务器时区,最后统一转为客户端时区
|
|
||||||
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
|
||||||
SecurityToken = credentials.SecurityToken,
|
|
||||||
|
|
||||||
|
|
||||||
Region = ossOptions.Region,
|
|
||||||
BucketName = ossOptions.BucketName,
|
|
||||||
EndPoint = ossOptions.EndPoint,
|
|
||||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AliyunOSSTempToken = tempToken;
|
|
||||||
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AliyunOSS = tempToken };
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO")
|
|
||||||
{
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, MinIO = ObjectStoreServiceOptions.MinIO };
|
|
||||||
}
|
|
||||||
else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS")
|
|
||||||
{
|
|
||||||
var awsOptions = ObjectStoreServiceOptions.AWS;
|
|
||||||
|
|
||||||
//aws 临时凭证
|
|
||||||
// 创建 STS 客户端
|
|
||||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
|
||||||
|
|
||||||
// 使用 AssumeRole 请求临时凭证
|
|
||||||
var assumeRoleRequest = new AssumeRoleRequest
|
|
||||||
{
|
|
||||||
|
|
||||||
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
|
||||||
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
|
||||||
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
|
||||||
};
|
|
||||||
|
|
||||||
var assumeRoleResponse = stsClient.AssumeRoleAsync(assumeRoleRequest).Result;
|
|
||||||
|
|
||||||
var credentials = assumeRoleResponse.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AWSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
SecretAccessKey = credentials.SecretAccessKey,
|
|
||||||
SessionToken = credentials.SessionToken,
|
|
||||||
Expiration = credentials.Expiration,
|
|
||||||
Region = awsOptions.Region,
|
|
||||||
BucketName = awsOptions.BucketName,
|
|
||||||
EndPoint = awsOptions.EndPoint,
|
|
||||||
ViewEndpoint = awsOptions.ViewEndpoint,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AWSTempToken = tempToken;
|
|
||||||
return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new BusinessValidationFailedException("未定义的存储介质类型");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ObjectStoreService": {
|
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
|
||||||
"AliyunOSS": {
|
|
||||||
"RegionId": "cn-shanghai",
|
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
|
||||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
|
||||||
"BucketName": "zy-irc-store",
|
|
||||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
|
||||||
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
|
|
||||||
"DicomSCPServiceConfig": {
|
|
||||||
"CalledAEList": [
|
|
||||||
"STORESCP"
|
|
||||||
],
|
|
||||||
"ServerPort": 11112
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ObjectStoreService": {
|
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
|
||||||
"AliyunOSS": {
|
|
||||||
"RegionId": "cn-shanghai",
|
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
|
||||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
|
||||||
"BucketName": "zy-irc-test-store",
|
|
||||||
"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
},
|
|
||||||
|
|
||||||
"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/"
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
|
||||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
|
|
||||||
"DicomSCPServiceConfig": {
|
|
||||||
"CalledAEList": [
|
|
||||||
"STORESCP"
|
|
||||||
],
|
|
||||||
"ServerPort": 11112
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ObjectStoreService": {
|
|
||||||
"ObjectStoreUse": "AWS",
|
|
||||||
"AWS": {
|
|
||||||
"Region": "us-east-1",
|
|
||||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
|
||||||
"UseSSL": true,
|
|
||||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
|
||||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
|
||||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
|
||||||
"BucketName": "ei-med-s3-lili-store",
|
|
||||||
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"RemoteNew": "Server=us-mssql-prod,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
|
||||||
"Hangfire": "Server=us-mssql-prod,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
"DicomSCPServiceConfig": {
|
|
||||||
"CalledAEList": [
|
|
||||||
"STORESCP"
|
|
||||||
],
|
|
||||||
"ServerPort": 11112
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ObjectStoreService": {
|
|
||||||
"ObjectStoreUse": "AWS",
|
|
||||||
"AWS": {
|
|
||||||
"Region": "us-east-1",
|
|
||||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
|
||||||
"UseSSL": true,
|
|
||||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
|
||||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
|
||||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
|
||||||
"BucketName": "ei-med-s3-lili-uat-store",
|
|
||||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com/",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"RemoteNew": "Server=us-mssql-service,1433;Database=US_Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
|
||||||
"Hangfire": "Server=us-mssql-service,1433;Database=US_Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
"DicomSCPServiceConfig": {
|
|
||||||
"CalledAEList": [
|
|
||||||
"STORESCP"
|
|
||||||
],
|
|
||||||
"ServerPort": 11112
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ObjectStoreService": {
|
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
|
||||||
"AliyunOSS": {
|
|
||||||
"RegionId": "cn-shanghai",
|
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"AccessKeyId": "LTAI5tFUCCmz5TwghZHsj45Y",
|
|
||||||
"AccessKeySecret": "8evrBy1fVfzJG25i67Jm0xqn9Xcw2T",
|
|
||||||
"RoleArn": "acs:ram::1078130221702011:role/uat-oss-access",
|
|
||||||
"BucketName": "tl-med-irc-uat-store",
|
|
||||||
"ViewEndpoint": "https://tl-med-irc-uat-store.oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"RemoteNew": "Server=101.132.253.119,1435;Database=Uat_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
|
||||||
"Hangfire": "Server101.132.253.119,1435;Database=Uat_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
"DicomSCPServiceConfig": {
|
|
||||||
"CalledAEList": [
|
|
||||||
"STORESCP"
|
|
||||||
],
|
|
||||||
"ServerPort": 11112
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft.AspNetCore": "Warning"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"AllowedHosts": "*"
|
|
||||||
}
|
|
|
@ -17,10 +17,6 @@ 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("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IRC.Core.SCP", "IRC.Core.SCP\IRC.Core.SCP.csproj", "{ECD08F47-DC1A-484E-BB91-6CDDC8823CC5}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IRC.Core.Dicom", "IRC.Core.Dicom\IRC.Core.Dicom.csproj", "{0545F0A5-D97B-4A47-92A6-A8A02A181322}"
|
|
||||||
EndProject
|
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -55,14 +51,6 @@ 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
|
|
||||||
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{0545F0A5-D97B-4A47-92A6-A8A02A181322}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
namespace EFSaving.Concurrency
|
||||||
|
{
|
||||||
|
public class Sample
|
||||||
|
{
|
||||||
|
public static void Run()
|
||||||
|
{
|
||||||
|
// Ensure database is created and has a person in it
|
||||||
|
using (var setupContext = new PersonContext())
|
||||||
|
{
|
||||||
|
setupContext.Database.EnsureDeleted();
|
||||||
|
setupContext.Database.EnsureCreated();
|
||||||
|
|
||||||
|
setupContext.People.Add(new Person { FirstName = "John", LastName = "Doe" });
|
||||||
|
setupContext.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region ConcurrencyHandlingCode
|
||||||
|
using var context = new PersonContext();
|
||||||
|
// Fetch a person from database and change phone number
|
||||||
|
var person = context.People.Single(p => p.PersonId == 1);
|
||||||
|
person.PhoneNumber = "555-555-5555";
|
||||||
|
|
||||||
|
// Change the person's name in the database to simulate a concurrency conflict
|
||||||
|
context.Database.ExecuteSqlRaw(
|
||||||
|
"UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");
|
||||||
|
|
||||||
|
var saved = false;
|
||||||
|
while (!saved)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Attempt to save changes to the database
|
||||||
|
context.SaveChanges();
|
||||||
|
saved = true;
|
||||||
|
}
|
||||||
|
catch (DbUpdateConcurrencyException ex)
|
||||||
|
{
|
||||||
|
foreach (var entry in ex.Entries)
|
||||||
|
{
|
||||||
|
if (entry.Entity is Person)
|
||||||
|
{
|
||||||
|
var proposedValues = entry.CurrentValues;
|
||||||
|
var databaseValues = entry.GetDatabaseValues();
|
||||||
|
|
||||||
|
foreach (var property in proposedValues.Properties)
|
||||||
|
{
|
||||||
|
var proposedValue = proposedValues[property];
|
||||||
|
var databaseValue = databaseValues[property];
|
||||||
|
|
||||||
|
// TODO: decide which value should be written to database
|
||||||
|
// proposedValues[property] = <value to be saved>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh original values to bypass next concurrency check
|
||||||
|
entry.OriginalValues.SetValues(databaseValues);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException(
|
||||||
|
"Don't know how to handle concurrency conflicts for "
|
||||||
|
+ entry.Metadata.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public class PersonContext : DbContext
|
||||||
|
{
|
||||||
|
public DbSet<Person> People { get; set; }
|
||||||
|
|
||||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
|
{
|
||||||
|
// Requires NuGet package Microsoft.EntityFrameworkCore.SqlServer
|
||||||
|
optionsBuilder.UseSqlServer(
|
||||||
|
@"Server=(localdb)\mssqllocaldb;Database=EFSaving.Concurrency;Trusted_Connection=True");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Person
|
||||||
|
{
|
||||||
|
public int PersonId { get; set; }
|
||||||
|
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
public string PhoneNumber { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,42 +1,37 @@
|
||||||
using AlibabaCloud.SDK.Sts20150401;
|
using System;
|
||||||
using Amazon.Auth.AccessControlPolicy;
|
|
||||||
using Amazon.SecurityToken;
|
|
||||||
using AutoMapper;
|
|
||||||
using Azure.Core;
|
|
||||||
using IdentityModel.Client;
|
|
||||||
using IdentityModel.OidcClient;
|
|
||||||
using IRaCIS.Application.Contracts;
|
|
||||||
using IRaCIS.Application.Interfaces;
|
|
||||||
using IRaCIS.Core.Application.Auth;
|
|
||||||
using IRaCIS.Core.Application.Contracts;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
using MassTransit;
|
|
||||||
using MassTransit.Futures.Contracts;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Org.BouncyCastle.Tls;
|
|
||||||
using RestSharp;
|
|
||||||
using RestSharp.Authenticators;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using EasyCaching.Core;
|
||||||
|
using IRaCIS.Application.Interfaces;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Auth;
|
||||||
|
using IRaCIS.Core.Application.Filter;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using IRaCIS.Core.Application.Interfaces;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using ZiggyCreatures.Caching.Fusion;
|
using IRaCIS.Application.Services;
|
||||||
using AssumeRoleRequest = Amazon.SecurityToken.Model.AssumeRoleRequest;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using MassTransit;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Aliyun.Acs.Core.Profile;
|
||||||
|
using Aliyun.Acs.Core.Auth.Sts;
|
||||||
|
using Aliyun.Acs.Core;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
using LoginReturnDTO = IRaCIS.Application.Contracts.LoginReturnDTO;
|
||||||
|
using DocumentFormat.OpenXml.Spreadsheet;
|
||||||
|
using AutoMapper.QueryableExtensions;
|
||||||
|
|
||||||
namespace IRaCIS.Api.Controllers
|
namespace IRaCIS.Api.Controllers
|
||||||
{
|
{
|
||||||
|
@ -44,10 +39,7 @@ namespace IRaCIS.Api.Controllers
|
||||||
/// 医生基本信息 、工作信息 专业信息、审核状态
|
/// 医生基本信息 、工作信息 专业信息、审核状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||||
public class ExtraController([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
public class ExtraController : ControllerBase
|
||||||
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
|
||||||
|
|
||||||
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService) : ControllerBase
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,40 +54,34 @@ namespace IRaCIS.Api.Controllers
|
||||||
/// <param name="_vacationService"></param>
|
/// <param name="_vacationService"></param>
|
||||||
/// <param name="doctorId"></param>
|
/// <param name="doctorId"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("doctor/getDetail")]
|
[HttpGet, Route("doctor/getDetail/{doctorId:guid}")]
|
||||||
|
|
||||||
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail(GetDoctorDetailInDto inDto)
|
public async Task<IResponseOutput<DoctorDetailDTO>> GetDoctorDetail([FromServices] IAttachmentService attachmentService, [FromServices] IDoctorService _doctorService,
|
||||||
|
[FromServices] IEducationService _educationService, [FromServices] ITrialExperienceService _trialExperienceService,
|
||||||
|
|
||||||
|
[FromServices] IResearchPublicationService _researchPublicationService, [FromServices] IVacationService _vacationService, Guid doctorId)
|
||||||
{
|
{
|
||||||
var education = await _educationService.GetEducation(inDto.doctorId);
|
var education = await _educationService.GetEducation(doctorId);
|
||||||
|
|
||||||
var sowList = _doctorService.GetDoctorSowList(inDto.doctorId);
|
var sowList = _doctorService.GetDoctorSowList(doctorId);
|
||||||
var ackSowList = _doctorService.GetDoctorAckSowList(inDto.doctorId);
|
var ackSowList = _doctorService.GetDoctorAckSowList(doctorId);
|
||||||
|
|
||||||
var doctorDetail = new DoctorDetailDTO
|
var doctorDetail = new DoctorDetailDTO
|
||||||
{
|
{
|
||||||
AuditView = await _doctorService.GetAuditState(inDto.doctorId),
|
AuditView = await _doctorService.GetAuditState(doctorId),
|
||||||
BasicInfoView = await _doctorService.GetBasicInfo(inDto.doctorId),
|
BasicInfoView = await _doctorService.GetBasicInfo(doctorId),
|
||||||
EmploymentView = await _doctorService.GetEmploymentInfo(inDto.doctorId),
|
EmploymentView = await _doctorService.GetEmploymentInfo(doctorId),
|
||||||
AttachmentList = await attachmentService.GetAttachments(inDto.doctorId),
|
AttachmentList = await attachmentService.GetAttachments(doctorId),
|
||||||
SummarizeInfo = await _doctorService.GetSummarizeInfo(new GetSummarizeInfoInDto()
|
|
||||||
{
|
|
||||||
DoctorId = inDto.doctorId,
|
|
||||||
TrialId = inDto.TrialId
|
|
||||||
}),
|
|
||||||
PaymentModeInfo = await _doctorService.GetPaymentMode(inDto.doctorId),
|
|
||||||
EducationList = education.EducationList,
|
EducationList = education.EducationList,
|
||||||
PostgraduateList = education.PostgraduateList,
|
PostgraduateList = education.PostgraduateList,
|
||||||
|
|
||||||
TrialExperienceView = await _trialExperienceService.GetTrialExperience(new TrialExperienceModelIndto()
|
TrialExperienceView = await _trialExperienceService.GetTrialExperience(doctorId),
|
||||||
{
|
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(doctorId),
|
||||||
DoctorId = inDto.doctorId,
|
|
||||||
TrialId = inDto.TrialId
|
|
||||||
}),
|
|
||||||
ResearchPublicationView = await _researchPublicationService.GetResearchPublication(inDto.doctorId),
|
|
||||||
|
|
||||||
SpecialtyView = await _doctorService.GetSpecialtyInfo(inDto.doctorId),
|
SpecialtyView = await _doctorService.GetSpecialtyInfo(doctorId),
|
||||||
InHoliday = (await _vacationService.OnVacation(inDto.doctorId)).IsSuccess,
|
InHoliday = (await _vacationService.OnVacation(doctorId)).IsSuccess,
|
||||||
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(inDto.doctorId),
|
IntoGroupInfo = _doctorService.GetDoctorIntoGroupInfo(doctorId),
|
||||||
SowList = sowList,
|
SowList = sowList,
|
||||||
AckSowList = ackSowList
|
AckSowList = ackSowList
|
||||||
};
|
};
|
||||||
|
@ -109,136 +95,312 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> 系统用户登录接口[New] </summary>
|
||||||
|
[HttpPost, Route("user/login")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpGet, Route("user/getPublicKey")]
|
public async Task<IResponseOutput> Login(UserLoginDTO loginUser,
|
||||||
public IResponseOutput GetPublicKey([FromServices] IOptionsMonitor<IRCEncreptOption> _IRCEncreptOption)
|
[FromServices] IEasyCachingProvider provider,
|
||||||
|
[FromServices] IUserService _userService,
|
||||||
|
[FromServices] ITokenService _tokenService,
|
||||||
|
[FromServices] IReadingImageTaskService readingImageTaskService,
|
||||||
|
IOptionsMonitor<ServiceVerifyConfigOption> _verifyConfig,
|
||||||
|
IMailVerificationService _mailVerificationService)
|
||||||
{
|
{
|
||||||
//var pemPublicKey = Encoding.UTF8.GetString(Convert.FromBase64String(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey));
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(_IRCEncreptOption.CurrentValue.Base64RSAPublicKey);
|
//MFA 邮箱验证 前端传递用户Id 和MFACode
|
||||||
|
if (loginUser.UserId != null && _verifyConfig.CurrentValue.OpenLoginMFA)
|
||||||
|
{
|
||||||
|
Guid userId = (Guid)loginUser.UserId;
|
||||||
|
|
||||||
|
//验证MFA 编码是否有问题 ,前端要拆开,自己调用验证的逻辑
|
||||||
|
//await _userService.VerifyMFACodeAsync(userId, loginUser.MFACode);
|
||||||
|
|
||||||
|
//var loginUser = await _userRepository.Where(u => u.UserName.Equals(userName) && u.Password == password).ProjectTo<UserBasicInfo>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
var basicInfo = await _userService.GetUserBasicInfo(userId, loginUser.Password);
|
||||||
|
|
||||||
|
var loginReturn = new LoginReturnDTO() { BasicInfo = basicInfo };
|
||||||
|
|
||||||
|
loginReturn.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(loginReturn.BasicInfo));
|
||||||
|
|
||||||
|
|
||||||
|
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||||
|
var option = new CookieOptions
|
||||||
|
{
|
||||||
|
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
|
||||||
|
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
|
||||||
|
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
|
||||||
|
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpContext.Response.Cookies.Append("access_token", loginReturn.JWTStr, option);
|
||||||
|
|
||||||
|
// 验证阅片休息时间
|
||||||
|
await readingImageTaskService.ResetReadingRestTime(userId);
|
||||||
|
|
||||||
|
await provider.SetAsync(userId.ToString(), loginReturn.JWTStr, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(loginReturn);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var returnModel = await _userService.Login(loginUser.UserName, loginUser.Password);
|
||||||
|
|
||||||
|
if (returnModel.IsSuccess)
|
||||||
|
{
|
||||||
|
#region GRPC 调用鉴权中心,因为服务器IIS问题 http/2 故而没法使用
|
||||||
|
|
||||||
|
////重试策略
|
||||||
|
//var defaultMethodConfig = new MethodConfig
|
||||||
|
//{
|
||||||
|
// Names = { MethodName.Default },
|
||||||
|
// RetryPolicy = new RetryPolicy
|
||||||
|
// {
|
||||||
|
// MaxAttempts = 3,
|
||||||
|
// InitialBackoff = TimeSpan.FromSeconds(1),
|
||||||
|
// MaxBackoff = TimeSpan.FromSeconds(5),
|
||||||
|
// BackoffMultiplier = 1.5,
|
||||||
|
// RetryableStatusCodes = { Grpc.Core.StatusCode.Unavailable }
|
||||||
|
// }
|
||||||
|
//};
|
||||||
|
|
||||||
|
//#region unable to trust the certificate then the gRPC client can be configured to ignore the invalid certificate
|
||||||
|
|
||||||
|
//var httpHandler = new HttpClientHandler();
|
||||||
|
//// Return `true` to allow certificates that are untrusted/invalid
|
||||||
|
//httpHandler.ServerCertificateCustomValidationCallback =
|
||||||
|
// HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
|
||||||
|
|
||||||
|
|
||||||
|
//////这一句是让grpc支持本地 http 如果本地访问部署在服务器上,那么是访问不成功的
|
||||||
|
//AppContext.SetSwitch(
|
||||||
|
// "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//var grpcAdress = configuration.GetValue<string>("GrpcAddress");
|
||||||
|
////var grpcAdress = "http://localhost:7200";
|
||||||
|
|
||||||
|
//var channel = GrpcChannel.ForAddress(grpcAdress, new GrpcChannelOptions
|
||||||
|
//{
|
||||||
|
// HttpHandler = httpHandler,
|
||||||
|
// ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }
|
||||||
|
|
||||||
|
//});
|
||||||
|
////var channel = GrpcChannel.ForAddress(grpcAdress);
|
||||||
|
//var grpcClient = new TokenGrpcService.TokenGrpcServiceClient(channel);
|
||||||
|
|
||||||
|
//var userInfo = returnModel.Data.BasicInfo;
|
||||||
|
|
||||||
|
//var tokenResponse = grpcClient.GetUserToken(new GetTokenReuqest()
|
||||||
|
//{
|
||||||
|
// Id = userInfo.Id.ToString(),
|
||||||
|
// ReviewerCode = userInfo.ReviewerCode,
|
||||||
|
// IsAdmin = userInfo.IsAdmin,
|
||||||
|
// RealName = userInfo.RealName,
|
||||||
|
// UserTypeEnumInt = (int)userInfo.UserTypeEnum,
|
||||||
|
// UserTypeShortName = userInfo.UserTypeShortName,
|
||||||
|
// UserName = userInfo.UserName
|
||||||
|
//});
|
||||||
|
|
||||||
|
//returnModel.Data.JWTStr = tokenResponse.Token;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var userId = returnModel.Data.BasicInfo.Id;
|
||||||
|
|
||||||
|
if (_verifyConfig.CurrentValue.OpenLoginMFA)
|
||||||
|
{
|
||||||
|
//发版屏蔽
|
||||||
|
|
||||||
|
//returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||||
|
|
||||||
|
//MFA 发送邮件
|
||||||
|
|
||||||
|
returnModel.Data.IsMFA = true;
|
||||||
|
|
||||||
|
var email = returnModel.Data.BasicInfo.EMail;
|
||||||
|
|
||||||
|
#region 隐藏Email
|
||||||
|
// 找到 "@" 符号的位置
|
||||||
|
int atIndex = email.IndexOf('@');
|
||||||
|
|
||||||
|
// 替换 "@" 符号前的中间两位为星号
|
||||||
|
string visiblePart = email.Substring(0, atIndex);
|
||||||
|
|
||||||
|
int startIndex = (visiblePart.Length - 2) / 2;
|
||||||
|
|
||||||
|
// 替换中间两位字符为星号
|
||||||
|
string hiddenPartBeforeAt = visiblePart.Substring(0, startIndex) + "**" + visiblePart.Substring(startIndex + 2);
|
||||||
|
|
||||||
|
string afterAt = email.Substring(atIndex + 1);
|
||||||
|
|
||||||
|
// 组合隐藏和可见部分
|
||||||
|
string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt;
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
returnModel.Data.BasicInfo.EMail = hiddenEmail;
|
||||||
|
|
||||||
|
await _userService.SendMFAEmail(userId);
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnModel.Data.JWTStr = _tokenService.GetToken(IRaCISClaims.Create(returnModel.Data.BasicInfo));
|
||||||
|
|
||||||
|
// 创建一个 CookieOptions 对象,用于设置 Cookie 的属性
|
||||||
|
var option = new CookieOptions
|
||||||
|
{
|
||||||
|
Expires = DateTime.Now.AddMonths(1), // 设置过期时间为 30 分钟之后
|
||||||
|
HttpOnly = false, // 确保 cookie 只能通过 HTTP 访问
|
||||||
|
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, // 设置 SameSite 属性
|
||||||
|
Secure = false // 确保 cookie 只能通过 HTTPS 访问
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpContext.Response.Cookies.Append("access_token", returnModel.Data.JWTStr, option);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 验证阅片休息时间
|
||||||
|
await readingImageTaskService.ResetReadingRestTime(returnModel.Data.BasicInfo.Id);
|
||||||
|
|
||||||
|
await provider.SetAsync(userId.ToString(), returnModel.Data.JWTStr, TimeSpan.FromDays(7));
|
||||||
|
|
||||||
|
await provider.SetAsync($"{userId.ToString()}_Online", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), TimeSpan.FromMinutes(_verifyConfig.CurrentValue.AutoLoginOutMinutes));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return returnModel;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet, Route("imageShare/ShareImage")]
|
[HttpGet, Route("imageShare/ShareImage")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
public IResponseOutput ShareImage([FromServices] ITokenService _tokenService)
|
||||||
{
|
{
|
||||||
var token = _tokenService.GetToken(new UserTokenInfo()
|
var token = _tokenService.GetToken(IRaCISClaims.Create(new UserBasicInfo()
|
||||||
{
|
{
|
||||||
IdentityUserId = Guid.NewGuid(),
|
Id = Guid.Empty,
|
||||||
|
IsReviewer = false,
|
||||||
|
IsAdmin = false,
|
||||||
|
RealName = "Share001",
|
||||||
UserName = "Share001",
|
UserName = "Share001",
|
||||||
|
Sex = 0,
|
||||||
|
//UserType = "ShareType",
|
||||||
UserTypeEnum = UserTypeEnum.ShareImage,
|
UserTypeEnum = UserTypeEnum.ShareImage,
|
||||||
|
Code = "ShareCode001",
|
||||||
});
|
}));
|
||||||
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
return ResponseOutput.Ok("/showdicom?studyId=f7b67793-8155-0223-2f15-118f2642efb8&type=Share&token=" + token);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("user/GetObjectStoreToken")]
|
[HttpGet("user/GetObjectStoreToken")]
|
||||||
public async Task<IResponseOutput> GetObjectStoreTokenAsync([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options, [FromServices] IOSSService _oSSService)
|
public IResponseOutput GetObjectStoreToken([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
||||||
|
{
|
||||||
|
var serviceOption = options.CurrentValue;
|
||||||
|
|
||||||
|
if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedEnum) && parsedEnum == ObjectStoreUse.AliyunOSS)
|
||||||
{
|
{
|
||||||
|
|
||||||
var result = _oSSService.GetObjectStoreTempToken();
|
var ossOptions = serviceOption.AliyunOSS;
|
||||||
|
|
||||||
//result.AWS = await GetAWSTemToken(options.CurrentValue);
|
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AliyunOSS = serviceOption.AliyunOSS, AWS = serviceOption.AWS });
|
||||||
|
|
||||||
return ResponseOutput.Ok(result);
|
#region 临时token 屏蔽
|
||||||
|
//IClientProfile profile = DefaultProfile.GetProfile(ossOptions.RegionId, ossOptions.AccessKeyId, ossOptions.AccessKeySecret);
|
||||||
|
//DefaultAcsClient client = new DefaultAcsClient(profile);
|
||||||
|
|
||||||
|
|
||||||
|
//// 创建一个STS请求
|
||||||
|
//AssumeRoleRequest request = new AssumeRoleRequest
|
||||||
|
//{
|
||||||
|
// RoleArn = ossOptions.RoleArn, // 角色ARN,需要替换为你的角色ARN
|
||||||
|
// RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
||||||
|
// DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
||||||
|
//};
|
||||||
|
|
||||||
|
|
||||||
|
//AssumeRoleResponse response = client.GetAcsResponse(request);
|
||||||
|
|
||||||
|
//// 返回STS令牌信息给前端
|
||||||
|
//var stsToken = new ObjectStoreDTO()
|
||||||
|
//{
|
||||||
|
// ObjectStoreUse = serviceOption.ObjectStoreUse,
|
||||||
|
// AliyunOSS = new AliyunOSSTempToken()
|
||||||
|
// {
|
||||||
|
// AccessKeyId = response.Credentials.AccessKeyId,
|
||||||
|
// AccessKeySecret = response.Credentials.AccessKeySecret,
|
||||||
|
// SecurityToken = response.Credentials.SecurityToken,
|
||||||
|
// Expiration = response.Credentials.Expiration,
|
||||||
|
|
||||||
|
// Region = ossOptions.Region,
|
||||||
|
// BucketName = ossOptions.BucketName,
|
||||||
|
// ViewEndpoint = ossOptions.ViewEndpoint,
|
||||||
|
|
||||||
|
// },
|
||||||
|
// MinIO = serviceOption.MinIO
|
||||||
|
//};
|
||||||
|
//return ResponseOutput.Ok(stsToken);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else if (Enum.TryParse<ObjectStoreUse>(serviceOption.ObjectStoreUse, out var parsedValue) && parsedValue == ObjectStoreUse.MinIO)
|
||||||
|
|
||||||
private async Task<AWSTempToken> GetAWSTemToken(ObjectStoreServiceOptions serviceOption)
|
|
||||||
{
|
{
|
||||||
var awsOptions = serviceOption.AWS;
|
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = serviceOption.AWS });
|
||||||
|
}
|
||||||
//aws 临时凭证
|
else
|
||||||
// 创建 STS 客户端
|
|
||||||
var stsClient = new AmazonSecurityTokenServiceClient(awsOptions.AccessKeyId, awsOptions.SecretAccessKey);
|
|
||||||
|
|
||||||
// 使用 AssumeRole 请求临时凭证
|
|
||||||
var assumeRoleRequest = new AssumeRoleRequest
|
|
||||||
{
|
{
|
||||||
|
return ResponseOutput.Ok(new ObjectStoreDTO() { ObjectStoreUse = serviceOption.ObjectStoreUse, MinIO = serviceOption.MinIO, AWS = serviceOption.AWS });
|
||||||
RoleArn = awsOptions.RoleArn, // 角色 ARN
|
|
||||||
RoleSessionName = $"session-name-{NewId.NextGuid()}",
|
|
||||||
DurationSeconds = awsOptions.DurationSeconds // 临时凭证有效期
|
|
||||||
};
|
|
||||||
|
|
||||||
var assumeRoleResponse = await stsClient.AssumeRoleAsync(assumeRoleRequest);
|
|
||||||
|
|
||||||
var credentials = assumeRoleResponse.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AWSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
SecretAccessKey = credentials.SecretAccessKey,
|
|
||||||
SessionToken = credentials.SessionToken,
|
|
||||||
Expiration = credentials.Expiration,
|
|
||||||
Region = awsOptions.Region,
|
|
||||||
BucketName = awsOptions.BucketName,
|
|
||||||
EndPoint = awsOptions.EndPoint,
|
|
||||||
ViewEndpoint = awsOptions.ViewEndpoint,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
return tempToken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
#region 老项目依赖
|
|
||||||
|
|
||||||
[HttpGet("user/GenerateSTS")]
|
[HttpGet("user/GenerateSTS")]
|
||||||
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<ObjectStoreServiceOptions> options)
|
public IResponseOutput GenerateSTS([FromServices] IOptionsMonitor<AliyunOSSOptions> options)
|
||||||
{
|
{
|
||||||
|
var ossOptions = options.CurrentValue;
|
||||||
|
|
||||||
var ossOptions = options.CurrentValue.AliyunOSS;
|
|
||||||
|
|
||||||
var client = new Client(new AlibabaCloud.OpenApiClient.Models.Config()
|
IClientProfile profile = DefaultProfile.GetProfile(ossOptions.regionId, ossOptions.accessKeyId, ossOptions.accessKeySecret);
|
||||||
|
DefaultAcsClient client = new DefaultAcsClient(profile);
|
||||||
|
|
||||||
|
|
||||||
|
// 创建一个STS请求
|
||||||
|
AssumeRoleRequest request = new AssumeRoleRequest
|
||||||
{
|
{
|
||||||
AccessKeyId = ossOptions.AccessKeyId,
|
RoleArn = ossOptions.roleArn, // 角色ARN,需要替换为你的角色ARN
|
||||||
AccessKeySecret = ossOptions.AccessKeySecret,
|
RoleSessionName = $"session-name-{NewId.NextGuid()}", // 角色会话名称,可自定义
|
||||||
Endpoint = "sts.cn-hangzhou.aliyuncs.com"
|
DurationSeconds = 900, // 令牌有效期(单位:秒),这里设置为1小时
|
||||||
});
|
|
||||||
|
|
||||||
var assumeRoleRequest = new AlibabaCloud.SDK.Sts20150401.Models.AssumeRoleRequest();
|
|
||||||
// 将<YOUR_ROLE_SESSION_NAME>设置为自定义的会话名称,例如oss-role-session。
|
|
||||||
assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}";
|
|
||||||
// 将<YOUR_ROLE_ARN>替换为拥有上传文件到指定OSS Bucket权限的RAM角色的ARN。
|
|
||||||
assumeRoleRequest.RoleArn = ossOptions.RoleArn;
|
|
||||||
//assumeRoleRequest.RoleArn = "acs:ram::1899121822495495:role/webdirect";
|
|
||||||
assumeRoleRequest.DurationSeconds = ossOptions.DurationSeconds;
|
|
||||||
var runtime = new AlibabaCloud.TeaUtil.Models.RuntimeOptions();
|
|
||||||
var response = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
|
|
||||||
var credentials = response.Body.Credentials;
|
|
||||||
|
|
||||||
var tempToken = new AliyunOSSTempToken()
|
|
||||||
{
|
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
|
||||||
AccessKeySecret = credentials.AccessKeySecret,
|
|
||||||
|
|
||||||
//转为服务器时区,最后统一转为客户端时区
|
|
||||||
Expiration = TimeZoneInfo.ConvertTimeFromUtc(DateTime.Parse(credentials.Expiration), TimeZoneInfo.Local),
|
|
||||||
SecurityToken = credentials.SecurityToken,
|
|
||||||
|
|
||||||
|
|
||||||
Region = ossOptions.Region,
|
|
||||||
BucketName = ossOptions.BucketName,
|
|
||||||
EndPoint = ossOptions.EndPoint,
|
|
||||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
|
||||||
PreviewEndpoint = ossOptions.PreviewEndpoint
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
AssumeRoleResponse response = client.GetAcsResponse(request);
|
||||||
|
|
||||||
// 返回STS令牌信息给前端
|
// 返回STS令牌信息给前端
|
||||||
var stsToken = new
|
var stsToken = new
|
||||||
{
|
{
|
||||||
AccessKeyId = credentials.AccessKeyId,
|
AccessKeyId = response.Credentials.AccessKeyId,
|
||||||
AccessKeySecret = credentials.AccessKeySecret,
|
AccessKeySecret = response.Credentials.AccessKeySecret,
|
||||||
SecurityToken = credentials.SecurityToken,
|
SecurityToken = response.Credentials.SecurityToken,
|
||||||
Expiration = credentials.Expiration,
|
Expiration = response.Credentials.Expiration,
|
||||||
|
|
||||||
Region = ossOptions.Region,
|
Region = ossOptions.region,
|
||||||
BucketName = ossOptions.BucketName,
|
BucketName = ossOptions.bucketName,
|
||||||
ViewEndpoint = ossOptions.ViewEndpoint,
|
ViewEndpoint = ossOptions.viewEndpoint,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -246,12 +408,11 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
[HttpGet("User/UserRedirect")]
|
[HttpGet("User/UserRedirect")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public async Task<IActionResult> UserRedirect([FromServices] IRepository<IdentityUser> _useRepository, string url, [FromServices] ILogger<ExtraController> _logger, [FromServices] ITokenService _tokenService)
|
public async Task<IActionResult> UserRedirect([FromServices] IRepository<User> _userRepository, string url, [FromServices] ILogger<ExtraController> _logger)
|
||||||
{
|
{
|
||||||
|
|
||||||
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
var decodeUrl = System.Web.HttpUtility.UrlDecode(url);
|
||||||
|
@ -266,22 +427,10 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
var errorUrl = domainStrList[0] + "//" + domainStrList[2] + "/error";
|
||||||
|
|
||||||
if (lang == "zh")
|
|
||||||
{
|
|
||||||
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
|
|
||||||
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.zh_CN);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CultureInfo.CurrentCulture = new CultureInfo(StaticData.CultureInfo.en_US);
|
|
||||||
CultureInfo.CurrentUICulture = new CultureInfo(StaticData.CultureInfo.en_US);
|
|
||||||
}
|
|
||||||
|
|
||||||
var isExpire = _tokenService.IsTokenExpired(token);
|
if (!await _userRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd))
|
||||||
|
|
||||||
if (!await _useRepository.AnyAsync(t => t.Id == Guid.Parse(userId) && t.EmailToken == token && t.IsFirstAdd) || isExpire)
|
|
||||||
{
|
{
|
||||||
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(I18n.T("UserRedirect_InitializationLinkExpire"))} ";
|
decodeUrl = errorUrl + $"?lang={lang}&ErrorMessage={System.Web.HttpUtility.UrlEncode(lang == "zh" ? "您的初始化链接已过期" : "Error!The initialization link has expired. Return")} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
return Redirect(decodeUrl);
|
return Redirect(decodeUrl);
|
||||||
|
@ -290,50 +439,10 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 项目支持Oauth 对接修改
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 回调到前端,前端调用后端的接口
|
|
||||||
/// 参考链接:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
|
|
||||||
/// 后端通过这个code ,带上客户端信息,和授权类型 可以向单点登录提供商,获取厂商token
|
|
||||||
///
|
|
||||||
/// 但是单点登录提供商提供的token 和我们系统的token 是有区别的,我们的token里面有我们业务系统的UserId,涉及到很多业务操作,所以在此出现了两种方案
|
|
||||||
/// 1、前端使用厂商的Token。 后端通过code 获取厂商的Token 返回前端的同时返回我们系统的UserId,前段在http 请求头加上一个自定义参数,带上UserId 后端取用户Id的地方变动下,
|
|
||||||
/// 但是除了UserId外,后端还有其他信息也是从Token取的,所以在请求头也需要带上,此外后端认证Token的方式也需要变化,改造成本稍大(如果是微服务,做这种处理还是可以的)。
|
|
||||||
/// 2、前端还是使用我们后台自己的Token。后端通过code 获取厂商Token的同时,后端做一个隐藏登录,返回厂商的Token的同时,也返回我们系统的Token。
|
|
||||||
/// (像我们单体,这种方式最简单,我们用单点登录,无非就是不想记多个系统的密码,自动登录而已,其他不支持的项目改造成本也是最低的)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">回调的厂商类型 比如github, google, 我们用的logto ,不同的厂商回调到前端的地址可以不同的,但是请求后端的接口可以是同一个 </param>
|
|
||||||
/// <param name="code">在第三方平台登录成功后,回调前端的时候会返回一个code </param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpGet("User/OAuthCallBack")]
|
|
||||||
public async Task<IResponseOutput> OAuthCallBack(string type, string code)
|
|
||||||
{
|
|
||||||
#region 获取AccessTo
|
|
||||||
|
|
||||||
//var headerDic = new Dictionary<string, string>();
|
|
||||||
//headerDic.Add("code", code);
|
|
||||||
//headerDic.Add("grant_type", "authorization_code");
|
|
||||||
//headerDic.Add("redirect_uri", "http://localhost:6100");
|
|
||||||
//headerDic.Add("scope", "all");
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 测试获取用户 ip
|
|
||||||
[HttpGet, Route("ip")]
|
[HttpGet, Route("ip")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput Get([FromServices] IHttpContextAccessor _context)
|
public IResponseOutput Get([FromServices] IHttpContextAccessor _context/*, [FromServices] IUserService _userService*/)
|
||||||
{
|
{
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -354,7 +463,7 @@ namespace IRaCIS.Api.Controllers
|
||||||
|
|
||||||
[HttpGet, Route("ip2")]
|
[HttpGet, Route("ip2")]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context)
|
public IResponseOutput Get2([FromServices] IHttpContextAccessor _context, [FromServices] IRepository _userService)
|
||||||
{
|
{
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
@ -371,9 +480,6 @@ namespace IRaCIS.Api.Controllers
|
||||||
}
|
}
|
||||||
return ResponseOutput.Ok(sb.ToString());
|
return ResponseOutput.Ok(sb.ToString());
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,36 @@
|
||||||
using IRaCIS.Application.Contracts;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using IRaCIS.Application.Interfaces;
|
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
|
||||||
using IRaCIS.Core.Application.Filter;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using IRaCIS.Application.Interfaces;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
|
using IRaCIS.Core.Application.Filter;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using IRaCIS.Application.Services;
|
||||||
|
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Application.Auth;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers.Special
|
namespace IRaCIS.Core.API.Controllers.Special
|
||||||
{
|
{
|
||||||
//谨慎修改 涉及到财务模块
|
//谨慎修改 涉及到财务模块
|
||||||
|
|
||||||
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
[ApiController, Authorize, ApiExplorerSettings(GroupName = "Financial")]
|
||||||
public class FinancialChangeController(
|
public class FinancialChangeController : ControllerBase
|
||||||
ITrialService _trialService,
|
|
||||||
ICalculateService _calculateService,
|
|
||||||
IStringLocalizer _localizer) : ControllerBase
|
|
||||||
{
|
{
|
||||||
|
private readonly ITrialService _trialService;
|
||||||
|
private readonly ICalculateService _calculateService;
|
||||||
|
private IStringLocalizer _localizer { get; set; }
|
||||||
|
public FinancialChangeController(ITrialService trialService, ICalculateService calculateService, IStringLocalizer localizer
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_localizer = localizer;
|
||||||
|
_trialService = trialService;
|
||||||
|
_calculateService = calculateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
//[TrialAudit(AuditType.TrialAudit, AuditOptType.AddOrUpdateTrial)]
|
||||||
|
@ -48,33 +54,33 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||||
[HttpPost, Route("trial/addOrUpdateTrial")]
|
[HttpPost, Route("trial/addOrUpdateTrial")]
|
||||||
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
//[Authorize(Policy = IRaCISPolicy.PM_APM)]
|
||||||
|
|
||||||
[TrialGlobalLimit( "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AddOrUpdateTrial", "BeforeOngoingCantOpt", "AfterStopCannNotOpt" })]
|
||||||
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
public async Task<IResponseOutput<Trial>> AddOrUpdateTrial(TrialCommand param)
|
||||||
{
|
{
|
||||||
//var userId = Guid.Parse(User.FindFirst("id").Value);
|
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||||
var result = await _trialService.AddOrUpdateTrial(param);
|
var result = await _trialService.AddOrUpdateTrial(param);
|
||||||
|
|
||||||
//if (_trialService.TrialExpeditedChange)
|
if (_trialService.TrialExpeditedChange)
|
||||||
//{
|
{
|
||||||
// var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
var needCalReviewerIds = await _trialService.GetTrialEnrollmentReviewerIds(param.Id.Value);
|
||||||
// var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
var calcList = await _calculateService.GetNeedCalculateReviewerList(Guid.Empty, string.Empty);
|
||||||
|
|
||||||
// calcList.ForEach(t =>
|
calcList.ForEach(t =>
|
||||||
// {
|
{
|
||||||
// if (needCalReviewerIds.Contains(t.DoctorId))
|
if (needCalReviewerIds.Contains(t.DoctorId))
|
||||||
// {
|
{
|
||||||
// _calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
_calculateService.CalculateMonthlyPayment(new CalculateDoctorAndMonthDTO()
|
||||||
// {
|
{
|
||||||
// NeedCalculateReviewers = new List<Guid>()
|
NeedCalculateReviewers = new List<Guid>()
|
||||||
// {
|
{
|
||||||
// t.DoctorId
|
t.DoctorId
|
||||||
// },
|
},
|
||||||
// CalculateMonth = DateTime.Parse(t.YearMonth)
|
CalculateMonth = DateTime.Parse(t.YearMonth)
|
||||||
// }, User.FindFirst("id").Value);
|
}, User.FindFirst("id").Value);
|
||||||
|
|
||||||
// }
|
}
|
||||||
// });
|
});
|
||||||
//}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +94,7 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|
||||||
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
[HttpPost, Route("doctorWorkload/workLoadAddOrUpdate")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
public async Task<IResponseOutput> WorkLoadAddOrUpdate([FromServices] IDoctorWorkloadService _trialWorkloadService, WorkloadCommand workLoadAddOrUpdateModel)
|
||||||
{
|
{
|
||||||
var userId = Guid.Parse(User.FindFirst("id").Value);
|
var userId = Guid.Parse(User.FindFirst("id").Value);
|
||||||
|
@ -110,7 +116,7 @@ namespace IRaCIS.Core.API.Controllers.Special
|
||||||
|
|
||||||
|
|
||||||
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
[HttpDelete, Route("doctorWorkload/deleteWorkLoad/{id:guid}/{trialId:guid}")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
public async Task<IResponseOutput> DeleteWorkLoad([FromServices] IDoctorWorkloadService _trialWorkloadService, Guid id)
|
||||||
{
|
{
|
||||||
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
//先判断该工作量的费用是否被锁定,如果被锁定,则不能删除
|
||||||
|
|
|
@ -1,19 +1,24 @@
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using AutoMapper;
|
||||||
|
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.Image.QA;
|
using IRaCIS.Core.Application.Image.QA;
|
||||||
|
using IRaCIS.Core.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
using IRaCIS.Core.Application.Service.Inspection.DTO;
|
||||||
using IRaCIS.Core.Application.Service.Inspection.Interface;
|
using IRaCIS.Core.Application.Service.Inspection.Interface;
|
||||||
using IRaCIS.Core.Application.Service.Reading.Dto;
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
using IRaCIS.Core.Application.Service.Reading.Interface;
|
using IRaCIS.Core.Application.Service.Reading.Interface;
|
||||||
using IRaCIS.Core.Application.ViewModel;
|
using IRaCIS.Core.Application.ViewModel;
|
||||||
|
using IRaCIS.Core.Domain.Models;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.Controllers
|
namespace IRaCIS.Core.API.Controllers
|
||||||
|
@ -21,18 +26,77 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
[ApiController, ApiExplorerSettings(GroupName = "Reviewer")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public class InspectionController(
|
public class InspectionController : ControllerBase
|
||||||
ITrialDocumentService _trialDocumentService,
|
|
||||||
IReadingImageTaskService _iReadingImageTaskService,
|
|
||||||
ITrialConfigService _trialConfigService,
|
|
||||||
IClinicalAnswerService _clinicalAnswerService,
|
|
||||||
IReadingClinicalDataService _readingClinicalDataService,
|
|
||||||
IQCOperationService _qCOperationService,
|
|
||||||
IInspectionService _inspectionService,
|
|
||||||
IReadingMedicalReviewService _readingMedicalReviewService,
|
|
||||||
IReadingMedicineQuestionService _readingMedicineQuestionService
|
|
||||||
) : ControllerBase
|
|
||||||
{
|
{
|
||||||
|
private readonly IRepository _repository;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
private readonly IUserInfo _userInfo;
|
||||||
|
private readonly ITrialDocumentService _trialDocumentService;
|
||||||
|
private readonly IQCListService _qCListService;
|
||||||
|
private readonly IReadingImageTaskService _iReadingImageTaskService;
|
||||||
|
private readonly IHttpContextAccessor _httpContext;
|
||||||
|
private readonly ITrialConfigService _trialConfigService;
|
||||||
|
private readonly INoneDicomStudyService _noneDicomStudyService;
|
||||||
|
private readonly IClinicalAnswerService _clinicalAnswerService;
|
||||||
|
private readonly ISubjectService _subjectService;
|
||||||
|
private readonly IReadingClinicalDataService _readingClinicalDataService;
|
||||||
|
private readonly ISubjectVisitService _subjectVisitService;
|
||||||
|
private readonly IQCOperationService _qCOperationService;
|
||||||
|
private readonly IClinicalDataService _clinicalDataService;
|
||||||
|
private readonly IVisitPlanService _visitPlanService;
|
||||||
|
|
||||||
|
private readonly IInspectionService _inspectionService;
|
||||||
|
private readonly IReadingMedicalReviewService _readingMedicalReviewService;
|
||||||
|
private readonly IReadingMedicineQuestionService _readingMedicineQuestionService;
|
||||||
|
private readonly IRepository<DataInspection> _dataInspectionRepository;
|
||||||
|
private delegate Task<IResponseOutput> executionFun(dynamic data);
|
||||||
|
|
||||||
|
public InspectionController(IRepository repository,
|
||||||
|
IRepository<DataInspection> _repositoryDataInspection,
|
||||||
|
IMapper mapper, IUserInfo userInfo,
|
||||||
|
ITrialDocumentService trialDocumentService,
|
||||||
|
IRepository<DataInspection> dataInspectionRepository,
|
||||||
|
IQCListService _qCListService,
|
||||||
|
IReadingImageTaskService _iReadingImageTaskService,
|
||||||
|
IHttpContextAccessor httpContext,
|
||||||
|
IInspectionService sinspectionService,
|
||||||
|
IReadingMedicalReviewService readingMedicalReviewService,
|
||||||
|
IReadingMedicineQuestionService readingMedicineQuestionService,
|
||||||
|
ITrialConfigService _trialConfigService,
|
||||||
|
INoneDicomStudyService noneDicomStudyService,
|
||||||
|
IClinicalAnswerService clinicalAnswerService,
|
||||||
|
ISubjectService _subjectService,
|
||||||
|
IReadingClinicalDataService _readingClinicalDataService,
|
||||||
|
ISubjectVisitService subjectVisitService,
|
||||||
|
IQCOperationService qCOperationService,
|
||||||
|
IClinicalDataService clinicalDataService,
|
||||||
|
IVisitPlanService visitPlanService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this._repository = repository;
|
||||||
|
this._mapper = mapper;
|
||||||
|
this._userInfo = userInfo;
|
||||||
|
this._inspectionService = sinspectionService;
|
||||||
|
this._readingMedicalReviewService = readingMedicalReviewService;
|
||||||
|
this._readingMedicineQuestionService = readingMedicineQuestionService;
|
||||||
|
this._trialDocumentService = trialDocumentService;
|
||||||
|
this._qCListService = _qCListService;
|
||||||
|
this._iReadingImageTaskService = _iReadingImageTaskService;
|
||||||
|
this._httpContext = httpContext;
|
||||||
|
this._trialConfigService = _trialConfigService;
|
||||||
|
this._noneDicomStudyService = noneDicomStudyService;
|
||||||
|
this._clinicalAnswerService = clinicalAnswerService;
|
||||||
|
this._subjectService = _subjectService;
|
||||||
|
this._readingClinicalDataService = _readingClinicalDataService;
|
||||||
|
this._subjectVisitService = subjectVisitService;
|
||||||
|
this._qCOperationService = qCOperationService;
|
||||||
|
this._clinicalDataService = clinicalDataService;
|
||||||
|
this._visitPlanService = visitPlanService;
|
||||||
|
this._dataInspectionRepository = dataInspectionRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 获取稽查数据
|
#region 获取稽查数据
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -52,7 +116,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitOncologyReadingInfo")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SetOncologyReadingInfo(DataInspectionDto<SubmitOncologyReadingInfoInDto> opt)
|
public async Task<IResponseOutput> SetOncologyReadingInfo(DataInspectionDto<SubmitOncologyReadingInfoInDto> opt)
|
||||||
|
@ -69,7 +133,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitDicomVisitTask")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitDicomVisitTask(DataInspectionDto<SubmitDicomVisitTaskInDto> opt)
|
public async Task<IResponseOutput> SubmitDicomVisitTask(DataInspectionDto<SubmitDicomVisitTaskInDto> opt)
|
||||||
|
@ -88,7 +152,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitGlobalReadingInfo")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitGlobalReadingInfo(DataInspectionDto<SubmitGlobalReadingInfoInDto> opt)
|
public async Task<IResponseOutput> SubmitGlobalReadingInfo(DataInspectionDto<SubmitGlobalReadingInfoInDto> opt)
|
||||||
|
@ -107,7 +171,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialReadingInfoSign")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> TrialReadingInfoSign(DataInspectionDto<TrialReadingInfoSignInDto> opt)
|
public async Task<IResponseOutput> TrialReadingInfoSign(DataInspectionDto<TrialReadingInfoSignInDto> opt)
|
||||||
|
@ -126,7 +190,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")]
|
[HttpPost, Route("Inspection/ReadingMedicalReview/FinishMedicalReview")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
|
public async Task<IResponseOutput> FinishMedicalReview(DataInspectionDto<FinishMedicalReviewInDto> opt)
|
||||||
|
@ -143,7 +207,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")]
|
[HttpPost, Route("Inspection/ReadingMedicineQuestion/ConfirmReadingMedicineQuestion")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> ConfirmReadingMedicineQuestion(DataInspectionDto<ConfirmReadingMedicineQuestionInDto> opt)
|
public async Task<IResponseOutput> ConfirmReadingMedicineQuestion(DataInspectionDto<ConfirmReadingMedicineQuestionInDto> opt)
|
||||||
|
@ -161,7 +225,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitVisitTaskQuestions")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitVisitTaskQuestions(DataInspectionDto<SubmitVisitTaskQuestionsInDto> opt)
|
public async Task<IResponseOutput> SubmitVisitTaskQuestions(DataInspectionDto<SubmitVisitTaskQuestionsInDto> opt)
|
||||||
|
@ -179,7 +243,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCSignClinicalData")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCSignClinicalData")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCSignClinicalData(DataInspectionDto<CRCSignClinicalDataInDto> opt)
|
public async Task<IResponseOutput> CRCSignClinicalData(DataInspectionDto<CRCSignClinicalDataInDto> opt)
|
||||||
|
@ -197,7 +261,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCConfirmClinical")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> CRCConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
||||||
|
@ -214,7 +278,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/CRCCancelConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/CRCCancelConfirmClinical")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCCancelConfirmClinical(DataInspectionDto<CRCCancelConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> CRCCancelConfirmClinical(DataInspectionDto<CRCCancelConfirmClinicalInDto> opt)
|
||||||
|
@ -232,7 +296,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/PMConfirmClinical")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/PMConfirmClinical")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> PMConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
public async Task<IResponseOutput> PMConfirmClinical(DataInspectionDto<CRCConfirmClinicalInDto> opt)
|
||||||
|
@ -244,30 +308,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// PM签名一致性分析临床数据
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="opt"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("Inspection/ReadingClinicalData/SignConsistencyAnalysisReadingClinicalData")]
|
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
|
||||||
|
|
||||||
[UnitOfWork]
|
|
||||||
public async Task<IResponseOutput> SignConsistencyAnalysisReadingClinicalData(DataInspectionDto<SignConsistencyAnalysisReadingClinicalDataInDto> opt)
|
|
||||||
{
|
|
||||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
|
||||||
var result = await _readingClinicalDataService.SignConsistencyAnalysisReadingClinicalData(opt.Data);
|
|
||||||
await _inspectionService.CompletedSign(singid, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 提交结构化录入并签名
|
/// 提交结构化录入并签名
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
[HttpPost, Route("Inspection/ClinicalAnswer/SubmitClinicalFormAndSign")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitClinicalFormAndSign(DataInspectionDto<SubmitClinicalFormInDto> opt)
|
public async Task<IResponseOutput> SubmitClinicalFormAndSign(DataInspectionDto<SubmitClinicalFormInDto> opt)
|
||||||
|
@ -284,7 +331,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")]
|
[HttpPost, Route("Inspection/ReadingImageTask/SubmitJudgeVisitTaskResult")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SubmitJudgeVisitTaskResult(DataInspectionDto<SaveJudgeVisitTaskResult> opt)
|
public async Task<IResponseOutput> SubmitJudgeVisitTaskResult(DataInspectionDto<SaveJudgeVisitTaskResult> opt)
|
||||||
|
@ -303,7 +350,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialBasicInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||||
public async Task<IResponseOutput> ConfigTrialBasicInfoConfirm(DataInspectionDto<BasicTrialConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialBasicInfoConfirm(DataInspectionDto<BasicTrialConfig> opt)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -323,7 +370,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialProcessInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialProcessInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
//[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||||
public async Task<IResponseOutput> ConfigTrialProcessInfoConfirm(DataInspectionDto<TrialProcessConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialProcessInfoConfirm(DataInspectionDto<TrialProcessConfig> opt)
|
||||||
{
|
{
|
||||||
opt.Data.IsTrialProcessConfirmed = true;
|
opt.Data.IsTrialProcessConfirmed = true;
|
||||||
|
@ -345,7 +392,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialUrgentInfoConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||||
public async Task<IResponseOutput> ConfigTrialUrgentInfoConfirm(DataInspectionDto<TrialUrgentConfig> opt)
|
public async Task<IResponseOutput> ConfigTrialUrgentInfoConfirm(DataInspectionDto<TrialUrgentConfig> opt)
|
||||||
{
|
{
|
||||||
opt.Data.IsTrialUrgentConfirmed = true;
|
opt.Data.IsTrialUrgentConfirmed = true;
|
||||||
|
@ -355,26 +402,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/ConfigTrialPACSInfoConfirm")]
|
|
||||||
[UnitOfWork]
|
|
||||||
[TrialGlobalLimit( "BeforeOngoingCantOpt" )]
|
|
||||||
public async Task<IResponseOutput> ConfigTrialPACSInfoConfirm(DataInspectionDto<TrialPACSConfig> opt)
|
|
||||||
{
|
|
||||||
opt.Data.IsTrialPACSConfirmed = true;
|
|
||||||
var singid = await _inspectionService.RecordSing(opt.SignInfo);
|
|
||||||
var result = await _trialConfigService.ConfigTrialPACSInfo(opt.Data);
|
|
||||||
await _inspectionService.CompletedSign(singid, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 签名确认
|
/// 签名确认
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")]
|
[HttpPost, Route("Inspection/configTrialBasicInfo/TrialConfigSignatureConfirm")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter),Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> TrialConfigSignatureConfirm(DataInspectionDto<SignConfirmDTO> opt)
|
public async Task<IResponseOutput> TrialConfigSignatureConfirm(DataInspectionDto<SignConfirmDTO> opt)
|
||||||
{
|
{
|
||||||
|
@ -391,7 +425,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
[HttpPost, Route("Inspection/ReadingCriterion/ResetAndAsyncCriterion")]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
public async Task<IResponseOutput> ResetAndAsyncCriterion(DataInspectionDto<ResetAndAsyncCriterionInDto> opt)
|
||||||
{
|
{
|
||||||
|
@ -409,7 +443,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
[HttpPost, Route("Inspection/QCOperation/CRCRequestToQC")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
public async Task<IResponseOutput> CRCRequestToQC(DataInspectionDto<CRCRequestToQCCommand> opt)
|
||||||
{
|
{
|
||||||
|
@ -424,7 +458,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// 设置QC 通过或者不通过 7:QC failed 8:QC passed
|
/// 设置QC 通过或者不通过 7:QC failed 8:QC passed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")]
|
[HttpPost, Route("Inspection/QCOperation/QCPassedOrFailed")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> QCPassedOrFailed(DataInspectionDto<QCPassedOrFailedDto> opt)
|
public async Task<IResponseOutput> QCPassedOrFailed(DataInspectionDto<QCPassedOrFailedDto> opt)
|
||||||
{
|
{
|
||||||
|
@ -438,7 +472,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// 一致性核查 回退 对话记录不清除 只允许PM回退
|
/// 一致性核查 回退 对话记录不清除 只允许PM回退
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/CheckBack")]
|
[HttpPost, Route("Inspection/QCOperation/CheckBack")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> CheckBack(DataInspectionDto<IDDto> opt)
|
public async Task<IResponseOutput> CheckBack(DataInspectionDto<IDDto> opt)
|
||||||
{
|
{
|
||||||
|
@ -455,7 +489,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")]
|
[HttpPost, Route("Inspection/ReadClinicalData/ReadClinicalDataSign")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> ReadClinicalDataSign(DataInspectionDto<ReadingClinicalDataSignIndto> opt)
|
public async Task<IResponseOutput> ReadClinicalDataSign(DataInspectionDto<ReadingClinicalDataSignIndto> opt)
|
||||||
{
|
{
|
||||||
|
@ -470,7 +504,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// CRC 设置已经重传完成
|
/// CRC 设置已经重传完成
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")]
|
[HttpPost, Route("Inspection/QCOperation/SetReuploadFinished")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> SetReuploadFinished(DataInspectionDto<CRCReuploadFinishedCommand> opt)
|
public async Task<IResponseOutput> SetReuploadFinished(DataInspectionDto<CRCReuploadFinishedCommand> opt)
|
||||||
{
|
{
|
||||||
|
@ -486,7 +520,8 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// <param name="opt"></param>
|
/// <param name="opt"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/TrialConfig/updateTrialState")]
|
[HttpPost, Route("Inspection/TrialConfig/updateTrialState")]
|
||||||
[TrialGlobalLimit( "BeforeOngoingCantOpt")]
|
//[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> UpdateTrialState(DataInspectionDto<UpdateTrialStateDto> opt)
|
public async Task<IResponseOutput> UpdateTrialState(DataInspectionDto<UpdateTrialStateDto> opt)
|
||||||
{
|
{
|
||||||
|
@ -502,7 +537,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/TrialDocument/userConfirm")]
|
[HttpPost, Route("Inspection/TrialDocument/userConfirm")]
|
||||||
[TrialGlobalLimit( "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "BeforeOngoingCantOpt", "SignSystemDocNoTrialId", "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
public async Task<IResponseOutput> UserConfirm(DataInspectionDto<UserConfirmCommand> opt)
|
public async Task<IResponseOutput> UserConfirm(DataInspectionDto<UserConfirmCommand> opt)
|
||||||
{
|
{
|
||||||
|
@ -519,13 +554,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost, Route("Inspection/VisitTask/ConfirmReReading")]
|
[HttpPost, Route("Inspection/VisitTask/ConfirmReReading")]
|
||||||
[TrialGlobalLimit( "AfterStopCannNotOpt" )]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
[UnitOfWork]
|
[UnitOfWork]
|
||||||
|
|
||||||
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt, [FromServices] IVisitTaskService _visitTaskService)
|
public async Task<IResponseOutput> ConfirmReReading(DataInspectionDto<ConfirmReReadingCommand> opt , [FromServices] IVisitTaskHelpeService _visitTaskCommonService,[FromServices] IVisitTaskService _visitTaskService)
|
||||||
{
|
{
|
||||||
var singId = await _inspectionService.RecordSing(opt.SignInfo);
|
var singId = await _inspectionService.RecordSing(opt.SignInfo);
|
||||||
var result = await _visitTaskService.ConfirmReReading(opt.Data);
|
var result = await _visitTaskService.ConfirmReReading(opt.Data, _visitTaskCommonService);
|
||||||
await _inspectionService.CompletedSign(singId, result);
|
await _inspectionService.CompletedSign(singId, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,32 @@
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
|
using DocumentFormat.OpenXml.Drawing;
|
||||||
|
using EasyCaching.Core;
|
||||||
using ExcelDataReader;
|
using ExcelDataReader;
|
||||||
|
using IRaCIS.Application.Contracts;
|
||||||
using IRaCIS.Application.Interfaces;
|
using IRaCIS.Application.Interfaces;
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using IRaCIS.Core.Application.Auth;
|
||||||
using IRaCIS.Core.Application.Contracts;
|
using IRaCIS.Core.Application.Contracts;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom;
|
using IRaCIS.Core.Application.Contracts.Dicom;
|
||||||
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
using IRaCIS.Core.Application.Contracts.Dicom.DTO;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using IRaCIS.Core.Application.Filter;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Application.MassTransit.Command;
|
using IRaCIS.Core.Application.MediatR.CommandAndQueries;
|
||||||
|
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
|
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||||
|
using IRaCIS.Core.Application.Service.Reading.Dto;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infrastructure;
|
using IRaCIS.Core.Infrastructure;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
using MassTransit.Mediator;
|
using MassTransit;
|
||||||
|
using MediatR;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Cors.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
|
@ -33,8 +40,8 @@ using Newtonsoft.Json;
|
||||||
using SharpCompress.Archives;
|
using SharpCompress.Archives;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -45,36 +52,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
{
|
{
|
||||||
|
|
||||||
#region 上传基类封装
|
#region 上传基类封装
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
public abstract class UploadBaseController : ControllerBase
|
public abstract class UploadBaseController : ControllerBase
|
||||||
{
|
{
|
||||||
/// <summary> 流式上传 直接返回</summary>
|
/// <summary> 流式上传 直接返回</summary>
|
||||||
[Route("SingleFileUpload")]
|
[Route("base")]
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
|
|
||||||
public virtual async Task<IResponseOutput> SingleFileUploadAsync(Func<string, (string, string)> filePathFunc)
|
public virtual async Task<IResponseOutput> SingleFileUploadAsync(Func<string, (string, string)> filePathFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
|
@ -109,9 +91,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
|
|
||||||
/// <summary> 流式上传 通用封装 不返回任何数据,后续还有事情处理 </summary>
|
/// <summary> 流式上传 通用封装 不返回任何数据,后续还有事情处理 </summary>
|
||||||
[Route("FileUpload")]
|
[Route("base")]
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
|
|
||||||
public virtual async Task FileUploadAsync(Func<string, Task<string>> filePathFunc)
|
public virtual async Task FileUploadAsync(Func<string, Task<string>> filePathFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
|
@ -154,9 +134,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Route("FileUploadToOSS")]
|
[Route("base")]
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
|
|
||||||
public virtual async Task FileUploadToOSSAsync(Func<string, Stream, Task<string>> toMemoryStreamFunc)
|
public virtual async Task FileUploadToOSSAsync(Func<string, Stream, Task<string>> toMemoryStreamFunc)
|
||||||
{
|
{
|
||||||
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(Request.ContentType).Boundary).Value;
|
||||||
|
@ -182,10 +160,10 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> 流式上传 Dicom上传 </summary>
|
|
||||||
[Route("DicomFileUpload")]
|
|
||||||
[ApiExplorerSettings(IgnoreApi = true)]
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> 流式上传 Dicom上传 </summary>
|
||||||
|
[Route("base")]
|
||||||
public virtual async Task DicomFileUploadAsync(Func<string, Stream, int, Task> filePathFunc, string boundary)
|
public virtual async Task DicomFileUploadAsync(Func<string, Stream, int, Task> filePathFunc, string boundary)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -247,59 +225,82 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Dicom 影像上传 临床数据 非diocm
|
#region Dicom 影像上传 临床数据 非diocm
|
||||||
public class UploadNoneDicomFileCommand
|
|
||||||
|
[ApiExplorerSettings(GroupName = "Image")]
|
||||||
|
[ApiController]
|
||||||
|
public class StudyController : UploadBaseController
|
||||||
{
|
{
|
||||||
[NotDefault]
|
public IMapper _mapper { get; set; }
|
||||||
public Guid SubjectVisitId { get; set; }
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
private readonly IMediator _mediator;
|
||||||
|
|
||||||
|
|
||||||
[NotDefault]
|
public IStringLocalizer _localizer { get; set; }
|
||||||
public Guid StudyMonitorId { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public Guid? NoneDicomStudyId { get; set; }
|
private readonly IWebHostEnvironment _hostEnvironment;
|
||||||
|
|
||||||
//IR 上传的时候跟任务绑定
|
private readonly IRepository _repository;
|
||||||
public Guid? VisitTaskId { get; set; }
|
|
||||||
|
|
||||||
public string RecordPath { get; set; }
|
private readonly IEasyCachingProvider _provider;
|
||||||
|
private readonly QCCommon _qCCommon;
|
||||||
public int FailedFileCount { get; set; }
|
public StudyController(IMapper mapper, IStringLocalizer localizer, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IMediator mediator, IEasyCachingProvider provider,
|
||||||
|
QCCommon qCCommon,
|
||||||
public long FileSize { get; set; }
|
IRepository repository)
|
||||||
|
|
||||||
|
|
||||||
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
|
||||||
|
|
||||||
|
|
||||||
public class OSSFileDTO
|
|
||||||
{
|
{
|
||||||
public string FilePath { get; set; }
|
_qCCommon = qCCommon;
|
||||||
public string FileName { get; set; }
|
_provider = provider;
|
||||||
public int FileFize { get; set; }
|
_hostEnvironment = hostEnvironment;
|
||||||
|
_mediator = mediator;
|
||||||
public string FileType { get; set; }
|
_localizer = localizer;
|
||||||
}
|
_mapper = mapper;
|
||||||
|
_userInfo = userInfo;
|
||||||
|
_repository = repository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Image")]
|
|
||||||
public class StudyController(
|
[HttpPost, Route("Study/PreArchiveStudy")]
|
||||||
IMediator _mediator,
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
QCCommon _qCCommon,
|
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
||||||
IUserInfo _userInfo,
|
[FromServices] IStudyService _studyService,
|
||||||
IRepository<SubjectVisit> _subjectVisitRepository,
|
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
IStringLocalizer _localizer) : UploadBaseController
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (_provider.Get<List<SystemAnonymization>>(StaticData.Anonymize.Anonymize_AddFixedFiled).Value == null)
|
||||||
|
{
|
||||||
|
await _mediator.Send(new AnonymizeCacheRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
|
||||||
|
|
||||||
|
var studyMonitor = new StudyMonitor()
|
||||||
|
{
|
||||||
|
TrialId = savedInfo.TrialId,
|
||||||
|
SubjectId = savedInfo.SubjectId,
|
||||||
|
SubjectVisitId = savedInfo.SubjectVisitId,
|
||||||
|
|
||||||
|
IsSuccess = false,
|
||||||
|
UploadStartTime = DateTime.Now,
|
||||||
|
IsDicom = preArchiveStudyCommand.IsDicom,
|
||||||
|
IP = _userInfo.IP
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(addEntity.Id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>Dicom 归档</summary>
|
/// <summary>Dicom 归档</summary>
|
||||||
[HttpPost, Route("Study/ArchiveStudy")]
|
[HttpPost, Route("Study/ArchiveStudy")]
|
||||||
[DisableFormValueModelBinding]
|
[DisableFormValueModelBinding]
|
||||||
[DisableRequestSizeLimit]
|
[DisableRequestSizeLimit]
|
||||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
public async Task<IResponseOutput> ArchiveStudyNew(Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
public async Task<IResponseOutput> ArchiveStudyNew(/*[FromForm] ArchiveStudyCommand archiveStudyCommand,*/ Guid trialId, Guid subjectVisitId, string studyInstanceUid, Guid? abandonStudyId, Guid studyMonitorId,
|
||||||
[FromServices] ILogger<UploadDownLoadController> _logger,
|
[FromServices] ILogger<UploadDownLoadController> _logger,
|
||||||
|
[FromServices] IEasyCachingProvider _provider,
|
||||||
[FromServices] IStudyService _studyService,
|
[FromServices] IStudyService _studyService,
|
||||||
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
|
[FromServices] IHubContext<UploadHub, IUploadClient> _uploadHub,
|
||||||
[FromServices] IDicomArchiveService _dicomArchiveService,
|
[FromServices] IDicomArchiveService _dicomArchiveService,
|
||||||
|
@ -324,6 +325,22 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
var startTime = DateTime.Now;
|
var startTime = DateTime.Now;
|
||||||
|
|
||||||
|
|
||||||
|
if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}") && _provider.Get<Guid>($"StudyUid_{trialId}_{studyInstanceUid}").Value == _userInfo.Id)
|
||||||
|
{
|
||||||
|
_provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//if (_provider.Exists($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}"))
|
||||||
|
//{
|
||||||
|
// //---当前已有人正在上传和归档该检查!
|
||||||
|
// return ResponseOutput.NotOk(StaticData.International("UploadDownLoad_ArchiveInProgress"));
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// _provider.Set($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}", _userInfo.Id, TimeSpan.FromMinutes(30));
|
||||||
|
//}
|
||||||
|
|
||||||
//到了接口,代表上传结束了
|
//到了接口,代表上传结束了
|
||||||
|
|
||||||
|
@ -370,7 +387,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
//await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
//await _uploadHub.Clients.All.ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
||||||
|
|
||||||
|
|
||||||
await _uploadHub.Clients.User(_userInfo.UserRoleId.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
await _uploadHub.Clients.User(_userInfo.Id.ToString()).ReceivProgressAsync(archiveStudyCommand.StudyInstanceUid, receivedCount);
|
||||||
|
|
||||||
archiveResult.ReceivedFileCount = receivedCount;
|
archiveResult.ReceivedFileCount = receivedCount;
|
||||||
|
|
||||||
|
@ -389,12 +406,13 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
|
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
||||||
|
|
||||||
//---请求异常,请重试!
|
//---请求异常,请重试!
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_RequestError"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
studyMonitor.FileSize = (long)HttpContext.Request.ContentLength;
|
studyMonitor.FileSize = (decimal)HttpContext.Request.ContentLength;
|
||||||
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
|
studyMonitor.FileCount = archiveResult.ReceivedFileCount;
|
||||||
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
|
studyMonitor.FailedFileCount = archiveResult.ErrorFiles.Count;
|
||||||
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
|
studyMonitor.IsDicomReUpload = archiveStudyCommand.AbandonStudyId != null;
|
||||||
|
@ -427,6 +445,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
_provider.Remove($"StudyUid_{trialId}_{archiveStudyCommand.StudyInstanceUid}");
|
||||||
|
|
||||||
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
|
studyMonitor.StudyId = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.Id ?? Guid.Empty;
|
||||||
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
|
studyMonitor.StudyCode = archiveResult.ArchivedDicomStudies.FirstOrDefault()?.StudyCode;
|
||||||
|
@ -444,55 +463,39 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
public class UploadNoneDicomFileCommand
|
||||||
/// 非dicom 上传预上传接口
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="preArchiveStudyCommand"></param>
|
|
||||||
/// <param name="_studyService"></param>
|
|
||||||
/// <param name="_studyMonitorRepository"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
[HttpPost, Route("Study/PreArchiveStudy")]
|
|
||||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
|
||||||
public async Task<IResponseOutput> PreArchiveStudy(PreArchiveStudyCommand preArchiveStudyCommand,
|
|
||||||
[FromServices] IStudyService _studyService,
|
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
|
||||||
{
|
{
|
||||||
|
public Guid TrialId { get; set; }
|
||||||
|
public Guid SubjectVisitId { get; set; }
|
||||||
|
public Guid NoneDicomStudyId { get; set; }
|
||||||
|
public Guid StudyMonitorId { get; set; }
|
||||||
|
|
||||||
var savedInfo = _studyService.GetSaveToDicomInfo(preArchiveStudyCommand.SubjectVisitId);
|
|
||||||
|
|
||||||
var studyMonitor = new StudyMonitor()
|
public List<OSSFileDTO> UploadedFileList { get; set; } = new List<OSSFileDTO>();
|
||||||
|
|
||||||
|
|
||||||
|
public class OSSFileDTO
|
||||||
{
|
{
|
||||||
TrialId = savedInfo.TrialId,
|
public string FilePath { get; set; }
|
||||||
SubjectId = savedInfo.SubjectId,
|
public string FileName { get; set; }
|
||||||
SubjectVisitId = savedInfo.SubjectVisitId,
|
public int FileFize { get; set; }
|
||||||
|
|
||||||
IsSuccess = false,
|
|
||||||
UploadStartTime = DateTime.Now,
|
|
||||||
FileCount = preArchiveStudyCommand.FileCount,
|
|
||||||
IsDicom = preArchiveStudyCommand.IsDicom,
|
|
||||||
IP = _userInfo.IP
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var addEntity = await _studyMonitorRepository.AddAsync(studyMonitor, true);
|
|
||||||
|
|
||||||
return ResponseOutput.Ok(addEntity.Id);
|
|
||||||
|
|
||||||
|
public string FileType { get; set; }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
/// 上传非Dicom 文件 支持压缩包 多文件上传
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="incommand"></param>
|
/// <param name="incommand"></param>
|
||||||
/// <param name="_noneDicomStudyRepository"></param>
|
/// <param name="_noneDicomStudyRepository"></param>
|
||||||
/// <param name="_studyMonitorRepository"></param>
|
/// <param name="_studyMonitorRepository"></param>
|
||||||
/// <param name="_noneDicomStudyFileRepository"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
[HttpPost("NoneDicomStudy/UploadNoneDicomFile")]
|
||||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
public async Task<IResponseOutput> UploadNoneDicomFile(UploadNoneDicomFileCommand incommand,
|
||||||
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository,
|
[FromServices] IRepository<NoneDicomStudy> _noneDicomStudyRepository, [FromServices] IRepository<StudyMonitor> _studyMonitorRepository)
|
||||||
[FromServices] IRepository<StudyMonitor> _studyMonitorRepository,
|
|
||||||
[FromServices] IRepository<NoneDicomStudyFile> _noneDicomStudyFileRepository)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var subjectVisitId = incommand.SubjectVisitId;
|
var subjectVisitId = incommand.SubjectVisitId;
|
||||||
|
@ -500,9 +503,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
var noneDicomStudyId = incommand.NoneDicomStudyId;
|
var noneDicomStudyId = incommand.NoneDicomStudyId;
|
||||||
|
|
||||||
|
|
||||||
await _qCCommon.VerifyIsCRCSubmmitAsync(_subjectVisitRepository, _userInfo, subjectVisitId);
|
await _qCCommon.VerifyIsCRCSubmmitAsync(_repository, _userInfo, subjectVisitId);
|
||||||
|
|
||||||
var sv = (await _subjectVisitRepository.Where(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
var sv = (await _repository.Where<SubjectVisit>(t => t.Id == subjectVisitId).Select(t => new { t.TrialId, t.TrialSiteId, t.SubjectId }).FirstOrDefaultAsync()).IfNullThrowConvertException();
|
||||||
|
|
||||||
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
var studyMonitor = await _studyMonitorRepository.FirstOrDefaultAsync(t => t.Id == studyMonitorId);
|
||||||
|
|
||||||
|
@ -510,39 +513,25 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
foreach (var item in incommand.UploadedFileList)
|
foreach (var item in incommand.UploadedFileList)
|
||||||
{
|
{
|
||||||
//如果是跟任务绑,那么NoneDicomStudyId 设置为空,不影响之前的检查,同时设置 OriginNoneDicomStudyId 保证关系
|
await _repository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId,FileType=item.FileType });
|
||||||
|
|
||||||
if (incommand.VisitTaskId != null && incommand.VisitTaskId != Guid.Empty)
|
|
||||||
{
|
|
||||||
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, OriginNoneDicomStudyId = noneDicomStudyId.Value, VisitTaskId = incommand.VisitTaskId, FileType = item.FileType, FileSize = item.FileFize });
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _noneDicomStudyFileRepository.AddAsync(new NoneDicomStudyFile() { FileName = item.FileName, Path = item.FilePath, NoneDicomStudyId = noneDicomStudyId.Value, FileType = item.FileType, FileSize = item.FileFize });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
var uploadFinishedTime = DateTime.Now;
|
var uploadFinishedTime = DateTime.Now;
|
||||||
|
|
||||||
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync(t => t.Id == noneDicomStudyId,true);
|
var noneDicomStudy = await _noneDicomStudyRepository.FirstOrDefaultAsync((t => t.Id == noneDicomStudyId));
|
||||||
|
|
||||||
noneDicomStudy.FileCount = noneDicomStudy.FileCount + (incommand.VisitTaskId != null ? 0 : incommand.UploadedFileList.Count);
|
noneDicomStudy.FileCount = noneDicomStudy.FileCount + incommand.UploadedFileList.Count;
|
||||||
|
|
||||||
studyMonitor.RecordPath = incommand.RecordPath;
|
studyMonitor.FileCount = incommand.UploadedFileList.Count;
|
||||||
studyMonitor.FailedFileCount = incommand.FailedFileCount;
|
|
||||||
studyMonitor.IsSuccess = incommand.FailedFileCount == 0;
|
|
||||||
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
|
studyMonitor.FileSize = incommand.UploadedFileList.Sum(t => t.FileFize);
|
||||||
studyMonitor.IsDicom = false;
|
studyMonitor.IsDicom = false;
|
||||||
studyMonitor.IsDicomReUpload = false;
|
studyMonitor.IsDicomReUpload = false;
|
||||||
studyMonitor.StudyId = noneDicomStudyId.Value;
|
studyMonitor.StudyId = noneDicomStudyId;
|
||||||
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
studyMonitor.StudyCode = noneDicomStudy.StudyCode;
|
||||||
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
studyMonitor.ArchiveFinishedTime = DateTime.Now;
|
||||||
studyMonitor.IP = _userInfo.IP;
|
studyMonitor.IP = _userInfo.IP;
|
||||||
|
|
||||||
await _noneDicomStudyRepository.SaveChangesAsync();
|
await _repository.SaveChangesAsync();
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
}
|
}
|
||||||
|
@ -556,13 +545,12 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="trialId"></param>
|
/// <param name="trialId"></param>
|
||||||
/// <param name="oSSService"></param>
|
/// <param name="oSSService"></param>
|
||||||
/// <param name="_inspectionFileRepository"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="BusinessValidationFailedException"></exception>
|
/// <exception cref="BusinessValidationFailedException"></exception>
|
||||||
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
[HttpPost("QCOperation/UploadVisitCheckExcel/{trialId:guid}")]
|
||||||
[TrialGlobalLimit("AfterStopCannNotOpt")]
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
|
||||||
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService, [FromServices] IRepository<InspectionFile> _inspectionFileRepository)
|
public async Task<IResponseOutput> UploadVisitCheckExcel(Guid trialId, [FromServices] IOSSService oSSService)
|
||||||
{
|
{
|
||||||
|
|
||||||
var fileName = string.Empty;
|
var fileName = string.Empty;
|
||||||
|
@ -572,9 +560,9 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
{
|
{
|
||||||
fileName = realFileName;
|
fileName = realFileName;
|
||||||
|
|
||||||
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".xls"))
|
if (!fileName.EndsWith(".xlsx") && !fileName.EndsWith(".csv") && !fileName.EndsWith(".xls"))
|
||||||
{
|
{
|
||||||
//---支持.xlsx、.xls格式的文件上传。
|
//---支持.xlsx、.xls、.csv格式的文件上传。
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_SupportedFormats"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +572,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
var ossRelativePath = await oSSService.UploadToOSSAsync(fileStream, "InspectionUpload/Check", realFileName);
|
||||||
|
|
||||||
await _inspectionFileRepository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
await _repository.AddAsync(new InspectionFile() { FileName = realFileName, RelativePath = ossRelativePath, TrialId = trialId });
|
||||||
|
|
||||||
return ossRelativePath;
|
return ossRelativePath;
|
||||||
|
|
||||||
|
@ -754,16 +742,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
//---请保证上传数据符合模板文件中的样式,且存在有效数据。
|
||||||
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
return ResponseOutput.NotOk(_localizer["UploadDownLoad_InvalidData"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await _mediator.Send(new ConsistencyVerificationRequest() { ETCList = etcCheckList, TrialId = trialId });
|
||||||
// 适合获取结果的
|
|
||||||
//var client = _mediator.CreateRequestClient<ConsistenCheckCommand>();
|
|
||||||
//await client.GetResponse<ConsistenCheckResult>(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
|
||||||
|
|
||||||
//不获取结果,不用定义返回类型
|
|
||||||
await _mediator.Send(new ConsistenCheckCommand() { ETCList = etcCheckList, TrialId = trialId });
|
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
@ -773,13 +756,229 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 医生文件上传下载
|
||||||
|
|
||||||
|
|
||||||
|
#region DTO
|
||||||
|
public class DoctorDownloadInfo
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string ReviewerCode { get; set; }
|
||||||
|
|
||||||
|
public List<DownloadFileInfo> FileList { get; set; }
|
||||||
|
}
|
||||||
|
public class DownloadFileInfo
|
||||||
|
{
|
||||||
|
public string FileName { get; set; }
|
||||||
|
|
||||||
|
public string Path { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetDoctorPathCommand
|
||||||
|
{
|
||||||
|
public int Language { get; set; }
|
||||||
|
|
||||||
|
public List<Guid> DoctorIdList { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GetDoctoreAttachPathCommand
|
||||||
|
{
|
||||||
|
public Guid DoctorId { get; set; }
|
||||||
|
public List<Guid> AttachmentIdList { get; set; }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>医生文件上传下载</summary>
|
||||||
|
[ApiExplorerSettings(GroupName = "Common")]
|
||||||
|
[ApiController]
|
||||||
|
public class FileController : UploadBaseController
|
||||||
|
{
|
||||||
|
public IMapper _mapper { get; set; }
|
||||||
|
public IUserInfo _userInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private readonly IWebHostEnvironment _hostEnvironment;
|
||||||
|
|
||||||
|
private readonly IFileService _fileService;
|
||||||
|
|
||||||
|
|
||||||
|
public FileController(IMapper mapper, IUserInfo userInfo, IWebHostEnvironment hostEnvironment, IFileService fileService)
|
||||||
|
{
|
||||||
|
_fileService = fileService;
|
||||||
|
_hostEnvironment = hostEnvironment;
|
||||||
|
_mapper = mapper;
|
||||||
|
_userInfo = userInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("file/GetOfficialResume")]
|
||||||
|
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetOfficialResume(GetDoctorPathCommand command,
|
||||||
|
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||||
|
{
|
||||||
|
|
||||||
|
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||||
|
.Select(g => new DoctorDownloadInfo()
|
||||||
|
{
|
||||||
|
Id = g.Key.DoctorId,
|
||||||
|
Name = g.Key.Name,
|
||||||
|
ReviewerCode = g.Key.ReviewerCode,
|
||||||
|
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||||
|
}).ToListAsync();
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// New 项目入组 勾选获取简历路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <param name="_doctorService"></param>
|
||||||
|
/// <param name="_attachmentrepository"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("file/GetTrialDoctorOfficialResume")]
|
||||||
|
public async Task<IResponseOutput<List<DoctorDownloadInfo>>> GetTrialDoctorOfficialResume(GetDoctorPathCommand command,
|
||||||
|
[FromServices] IDoctorService _doctorService,
|
||||||
|
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||||
|
{
|
||||||
|
|
||||||
|
var list = await _attachmentrepository.Where(t => command.DoctorIdList.Contains(t.DoctorId) && command.Language == t.Language && t.IsOfficial && t.Type.Equals("Resume")).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||||
|
.Select(g => new DoctorDownloadInfo()
|
||||||
|
{
|
||||||
|
Id = g.Key.DoctorId,
|
||||||
|
Name = g.Key.Name,
|
||||||
|
ReviewerCode = g.Key.ReviewerCode,
|
||||||
|
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||||
|
}).ToListAsync();
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(list);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// new 医生详情 勾选或者下载文件路径
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="command"></param>
|
||||||
|
/// <param name="_doctorService"></param>
|
||||||
|
/// <param name="_attachmentrepository"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("file/GetDoctorAttachment")]
|
||||||
|
public async Task<IResponseOutput<DoctorDownloadInfo>> GetDoctorAttachment(GetDoctoreAttachPathCommand command,
|
||||||
|
[FromServices] IDoctorService _doctorService,
|
||||||
|
[FromServices] IRepository<Attachment> _attachmentrepository)
|
||||||
|
{
|
||||||
|
|
||||||
|
var find = await _attachmentrepository.Where(t => command.DoctorId == t.DoctorId && command.AttachmentIdList.Contains(t.Id)).GroupBy(t => new { Name = t.Doctor.FirstName + "_" + t.Doctor.LastName, ReviewerCode = t.Doctor.ReviewerCode, t.DoctorId })
|
||||||
|
.Select(g => new DoctorDownloadInfo()
|
||||||
|
{
|
||||||
|
Id = g.Key.DoctorId,
|
||||||
|
Name = g.Key.Name,
|
||||||
|
ReviewerCode = g.Key.ReviewerCode,
|
||||||
|
FileList = g.Select(t => new DownloadFileInfo() { FileName = t.FileName, Path = t.Path }).ToList()
|
||||||
|
}).FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(find);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 废弃
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载多个医生的所有附件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="doctorIds"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[Obsolete]
|
||||||
|
[HttpPost, Route("file/downloadDoctorAttachments")]
|
||||||
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachment(Guid[] doctorIds)
|
||||||
|
{
|
||||||
|
|
||||||
|
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||||
|
{
|
||||||
|
FilePath = path,
|
||||||
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载指定医生的指定附件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="doctorId">医生Id</param>
|
||||||
|
/// <param name="attachmentIds">要下载的附件Id</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("file/downloadByAttachmentId/{doctorId}")]
|
||||||
|
[Obsolete]
|
||||||
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadAttachmentById(Guid doctorId, Guid[] attachmentIds)
|
||||||
|
{
|
||||||
|
var path = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds);
|
||||||
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||||
|
{
|
||||||
|
FilePath = await _fileService.CreateZipPackageByAttachment(doctorId, attachmentIds),
|
||||||
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下载医生官方简历 首页 区分 中文和英文
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language"></param>
|
||||||
|
/// <param name="doctorIds"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("file/downloadOfficialCV/{language}")]
|
||||||
|
[Obsolete]
|
||||||
|
public async Task<IResponseOutput<UploadFileInfoDTO>> DownloadOfficialResume(int language, Guid[] doctorIds)
|
||||||
|
{
|
||||||
|
|
||||||
|
var path = await _fileService.CreateDoctorsAllAttachmentZip(doctorIds);
|
||||||
|
return ResponseOutput.Ok(new UploadFileInfoDTO
|
||||||
|
{
|
||||||
|
FilePath = await _fileService.CreateOfficialResumeZip(language, doctorIds),
|
||||||
|
FullFilePath = path + "?access_token=" + HttpContext.Request.Headers["Authorization"].ToString().Substring(7)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 入组 项目下载简历
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language"></param>
|
||||||
|
/// <param name="trialId"></param>
|
||||||
|
/// <param name="doctorIdArray"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost, Route("enroll/downloadResume/{trialId:guid}/{language}")]
|
||||||
|
[TypeFilter(typeof(TrialResourceFilter), Arguments = new object[] { "AfterStopCannNotOpt" })]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[Obsolete]
|
||||||
|
public async Task<IResponseOutput<string>> DownloadResume(int language, Guid trialId, Guid[] doctorIdArray)
|
||||||
|
{
|
||||||
|
var zipPath = await _fileService.CreateOfficialResumeZip(language, doctorIdArray);
|
||||||
|
|
||||||
|
return ResponseOutput.Ok(zipPath);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 项目 系统 基本文件 上传 下载 预览
|
#region 项目 系统 基本文件 上传 下载 预览
|
||||||
|
|
||||||
[ApiController, ApiExplorerSettings(GroupName = "Common")]
|
[ApiExplorerSettings(GroupName = "Common")]
|
||||||
|
[ApiController]
|
||||||
public class UploadDownLoadController : UploadBaseController
|
public class UploadDownLoadController : UploadBaseController
|
||||||
{
|
{
|
||||||
public IMapper _mapper { get; set; }
|
public IMapper _mapper { get; set; }
|
||||||
|
@ -850,19 +1049,12 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidCenters"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var item in excelList.GroupBy(t => t.Email.Trim()))
|
if (excelList.GroupBy(t => new { t.TrialSiteCode, t.UserTypeStr, t.Email }).Any(g => g.Count() > 1))
|
||||||
{
|
{
|
||||||
var itemList = item.ToList();
|
// 同一邮箱,同一用户类型,只能生成一个账户,请核查Excel数据
|
||||||
|
|
||||||
var first = item.First();
|
|
||||||
|
|
||||||
if (itemList.Any(t => t.Email.Trim() != first.Email.Trim() || t.Phone.Trim() != first.Phone.Trim() || t.OrganizationName.Trim() != first.OrganizationName.Trim() || t.FirstName.Trim() != first.FirstName.Trim() || t.LastName.Trim() != first.LastName.Trim()))
|
|
||||||
{
|
|
||||||
//同一邮箱,用户信息应该保持一致!
|
|
||||||
|
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_CheckDuplicateAccounts"]);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (excelList.Any(t => !t.Email.Contains("@")))
|
if (excelList.Any(t => !t.Email.Contains("@")))
|
||||||
{
|
{
|
||||||
|
@ -871,11 +1063,11 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
}
|
}
|
||||||
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
var generateUserTypeList = new List<string>() { "CRC", "CRA" };
|
||||||
|
|
||||||
//if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
if (excelList.Any(t => !generateUserTypeList.Contains(t.UserTypeStr.ToUpper())))
|
||||||
//{
|
{
|
||||||
// //用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
//用户类型仅能为 CRC,SR,CRA 请核查Excel数据
|
||||||
// throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_InvalidUserType"]);
|
||||||
//}
|
}
|
||||||
if (excelList.Count == 0)
|
if (excelList.Count == 0)
|
||||||
{
|
{
|
||||||
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
throw new BusinessValidationFailedException(_localizer["UploadDownLoad_NoValiddata"]);
|
||||||
|
@ -886,7 +1078,7 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
foreach (var item in excelList)
|
foreach (var item in excelList)
|
||||||
{
|
{
|
||||||
switch (item.UserTypeStr.Trim().ToUpper())
|
switch (item.UserTypeStr.ToUpper())
|
||||||
{
|
{
|
||||||
case "CRC":
|
case "CRC":
|
||||||
|
|
||||||
|
@ -903,12 +1095,12 @@ namespace IRaCIS.Core.API.Controllers
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.Trim().ToUpper() == item.TrialSiteCode.Trim().ToUpper()).TrialSiteId;
|
item.TrialSiteId = siteList.FirstOrDefault(t => t.TrialSiteCode.ToUpper() == item.TrialSiteCode.ToUpper()).TrialSiteId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = excelList.Where(t => t.UserTypeEnum == UserTypeEnum.ClinicalResearchCoordinator || t.UserTypeEnum == UserTypeEnum.CRA).ToList();
|
|
||||||
|
|
||||||
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, list);
|
|
||||||
|
await _trialSiteSurveyService.ImportGenerateAccountAndJoinTrialAsync(trialId, baseUrl, routeUrl, excelList.ToList());
|
||||||
|
|
||||||
return ResponseOutput.Ok();
|
return ResponseOutput.Ok();
|
||||||
|
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System.Threading;
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MassTransit;
|
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Hangfire;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using MassTransit.Scheduling;
|
|
||||||
using Hangfire.Storage;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using MassTransit.Mediator;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.HostService;
|
|
||||||
|
|
||||||
public class HangfireHostService(IRecurringMessageScheduler _recurringMessageScheduler,
|
|
||||||
IRepository<TrialEmailNoticeConfig> _trialEmailNoticeConfigRepository,
|
|
||||||
|
|
||||||
IRepository<EmailNoticeConfig> _emailNoticeConfigrepository,
|
|
||||||
IMediator _mediator,
|
|
||||||
ILogger<HangfireHostService> _logger) : IHostedService
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public async Task StartAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行开始~");
|
|
||||||
|
|
||||||
|
|
||||||
//创建邮件定时任务
|
|
||||||
//项目定时任务都在default 队列
|
|
||||||
var dbJobIdList = JobStorage.Current.GetConnection().GetRecurringJobs().Where(t => t.Queue == "default").Select(t => t.Id).ToList();
|
|
||||||
|
|
||||||
foreach (var jobId in dbJobIdList)
|
|
||||||
{
|
|
||||||
HangfireJobHelper.RemoveCronJob(jobId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var taskInfoList = await _trialEmailNoticeConfigRepository.Where(t => t.Trial.TrialStatusStr == StaticData.TrialState.TrialOngoing && t.EmailCron != string.Empty && t.IsAutoSend)
|
|
||||||
.Select(t => new { t.Id, t.Code, TrialCode = t.Trial.TrialCode, t.EmailCron, t.BusinessScenarioEnum, t.TrialId })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
foreach (var task in taskInfoList)
|
|
||||||
{
|
|
||||||
//利用主键作为任务Id
|
|
||||||
var jobId = $"{task.TrialId}({task.TrialCode})_({task.BusinessScenarioEnum})";
|
|
||||||
|
|
||||||
var trialId = task.TrialId;
|
|
||||||
|
|
||||||
HangfireJobHelper.AddOrUpdateTrialCronJob(jobId, trialId, task.BusinessScenarioEnum, task.EmailCron);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 系统邮件定时任务
|
|
||||||
var systemTaskInfoList = await _emailNoticeConfigrepository.Where(t => t.EmailCron != string.Empty && t.IsAutoSend)
|
|
||||||
.Select(t => new { t.Id, t.Code, t.EmailCron, t.BusinessScenarioEnum, })
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
foreach (var task in systemTaskInfoList)
|
|
||||||
{
|
|
||||||
//利用主键作为任务Id
|
|
||||||
var jobId = $"{task.Id}_({task.BusinessScenarioEnum})";
|
|
||||||
|
|
||||||
HangfireJobHelper.AddOrUpdateTimingCronJob(jobId, task.BusinessScenarioEnum, task.EmailCron);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//await _recurringMessageScheduler.ScheduleRecurringPublish(new QCImageQuestionSchedule() { CronExpression = "0/3 * * * * ? " }, new MasstransiTestCommand { value = "message at " + DateTime.Now.ToString() });
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_logger.LogInformation("项目启动 hangfire 任务初始化 执行结束");
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
|
||||||
using MassTransit;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API.HostService
|
|
||||||
{
|
|
||||||
public class RecurringJobConfigurationService :
|
|
||||||
BackgroundService
|
|
||||||
{
|
|
||||||
readonly IServiceScopeFactory _scopeFactory;
|
|
||||||
|
|
||||||
public RecurringJobConfigurationService(IServiceScopeFactory scopeFactory)
|
|
||||||
{
|
|
||||||
_scopeFactory = scopeFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
await using var scope = _scopeFactory.CreateAsyncScope();
|
|
||||||
|
|
||||||
var endpoint = scope.ServiceProvider.GetRequiredService<IPublishEndpoint>();
|
|
||||||
|
|
||||||
await endpoint.AddOrUpdateRecurringJob(nameof(MasstransitTestConsumer), new MasstransiTestCommand(), x => x.Every(minutes: 1),
|
|
||||||
stoppingToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<SignAssembly>false</SignAssembly>
|
||||||
|
<UserSecretsId>354572d4-9e15-4099-807c-63a2d29ff9f2</UserSecretsId>
|
||||||
|
<LangVersion>default</LangVersion>
|
||||||
|
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<DocumentationFile>.\IRaCIS.Core.API.xml</DocumentationFile>
|
||||||
|
<NoWarn>1701;1702;1591;</NoWarn>
|
||||||
|
<OutputPath>..\bin\</OutputPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
|
<DocumentationFile>bin\Release\IRaCIS.Core.API.xml</DocumentationFile>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<NoWarn>1701;1702;1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Controllers\ReviewerApi\**" />
|
||||||
|
<Compile Remove="UploadFile\**" />
|
||||||
|
<Content Remove="Controllers\ReviewerApi\**" />
|
||||||
|
<Content Remove="UploadFile\**" />
|
||||||
|
<EmbeddedResource Remove="Controllers\ReviewerApi\**" />
|
||||||
|
<EmbeddedResource Remove="UploadFile\**" />
|
||||||
|
<None Remove="Controllers\ReviewerApi\**" />
|
||||||
|
<None Remove="UploadFile\**" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Remove="web.config" />
|
||||||
|
<Content Remove="wwwroot\swagger\ui\abp.js" />
|
||||||
|
<Content Remove="wwwroot\swagger\ui\abp.swagger.js" />
|
||||||
|
<Content Remove="wwwroot\swagger\ui\Index.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Remove=".preview.jpg" />
|
||||||
|
<None Remove="GrpcToken.proto" />
|
||||||
|
<None Remove="IRaCIS.Core.API.xml" />
|
||||||
|
<None Remove="Protos\GrpcToken.proto" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js" />
|
||||||
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.swagger.js" />
|
||||||
|
<EmbeddedResource Include="wwwroot\swagger\ui\Index.html" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Protobuf Include="Protos\GrpcToken.proto">
|
||||||
|
<GrpcServices>Client</GrpcServices>
|
||||||
|
</Protobuf>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="AspNetCoreRateLimit" Version="4.0.1" />
|
||||||
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="7.2.0" />
|
||||||
|
<PackageReference Include="EasyCaching.InMemory" Version="1.4.1" />
|
||||||
|
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.4.1" />
|
||||||
|
<PackageReference Include="Google.Protobuf" Version="3.19.1" />
|
||||||
|
<PackageReference Include="Grpc.Net.Client" Version="2.41.0" />
|
||||||
|
<PackageReference Include="Grpc.Tools" Version="2.42.0">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Hangfire.Tags.SqlServer" Version="1.8.0" />
|
||||||
|
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||||
|
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||||
|
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="6.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.1" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||||
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="1.1.4" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Email" Version="2.4.0" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Application\IRaCIS.Core.Application.csproj" />
|
||||||
|
<ProjectReference Include="..\IRaCIS.Core.Infra.EFCore\IRaCIS.Core.Infra.EFCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Properties\PublishProfiles\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="NLog.config">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ProjectExtensions><VisualStudio><UserProperties anonymizetagsetting_1json__JsonSchema="http://json.schemastore.org/jovo-language-model" /></VisualStudio></ProjectExtensions>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
|
@ -38,18 +38,11 @@
|
||||||
<None Remove="GrpcToken.proto" />
|
<None Remove="GrpcToken.proto" />
|
||||||
<None Remove="IRaCIS.Core.API.xml" />
|
<None Remove="IRaCIS.Core.API.xml" />
|
||||||
<None Remove="Protos\GrpcToken.proto" />
|
<None Remove="Protos\GrpcToken.proto" />
|
||||||
<None Remove="Resources\ip2region.xdb" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ApplicationDefinition Include="GrpcToken.proto" />
|
<ApplicationDefinition Include="GrpcToken.proto" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="Resources\ip2region.xdb">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
<EmbeddedResource Include="wwwroot\swagger\ui\abp.js">
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
@ -68,24 +61,26 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<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="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.14" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.15">
|
<PackageReference Include="EasyCaching.Interceptor.Castle" Version="1.9.2" />
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PackageReference Include="EasyCaching.Serialization.MessagePack" Version="1.9.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<TreatAsUsed>true</TreatAsUsed>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="ConfigMapFileProvider" Version="2.0.1" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.12" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.18" />
|
|
||||||
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
|
||||||
<PackageReference Include="Hangfire.InMemory" Version="1.0.0" />
|
<PackageReference Include="Hangfire.SqlServer" Version="1.8.12" />
|
||||||
<PackageReference Include="Hangfire.SqlServer" Version="1.8.18" />
|
<PackageReference Include="Invio.Extensions.Authentication.JwtBearer" Version="2.0.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.10" />
|
<PackageReference Include="LogDashboard" Version="1.4.8" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="8.0.5" />
|
||||||
<PackageReference Include="Serilog.Formatting.Compact" Version="3.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Email" Version="4.0.0" />
|
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.1" />
|
<PackageReference Include="Serilog.Enrichers.ClientInfo" Version="2.0.3" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.6.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -97,33 +92,68 @@
|
||||||
<None Include="..\.editorconfig" Link=".editorconfig" />
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\AdminAddUser_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\AdminResetUser_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\AdminResetUser.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialSiteSurveyReject_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialDoctorExistJoin.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialUserExistJoin.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\AdminAddUser.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialDoctorFirstJoin_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\SubjectEnrollConfirmOrPDProgress.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\UserOptCommon_US.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\UserOptCommon.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="wwwroot\EmailTemplate\TrialUserFirstJoin.html">
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Properties\PublishProfiles\" />
|
<Folder Include="Properties\PublishProfiles\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ProjectExtensions><VisualStudio><UserProperties properties_4launchsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>
|
||||||
<Content Update="appsettings.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="Resources\en-US.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
<Content Update="Resources\zh-CN.json">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Update="Resources\GeoLite2-City.mmdb">
|
|
||||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
|
||||||
</None>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ProjectExtensions>
|
|
||||||
<VisualStudio>
|
|
||||||
<UserProperties properties_4launchsettings_1json__JsonSchema="" />
|
|
||||||
</VisualStudio>
|
|
||||||
</ProjectExtensions>
|
|
||||||
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -16,12 +16,7 @@
|
||||||
医生基本信息 、工作信息 专业信息、审核状态
|
医生基本信息 、工作信息 专业信息、审核状态
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.#ctor(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Interfaces.IAttachmentService,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Application.Interfaces.IEducationService,IRaCIS.Application.Interfaces.ITrialExperienceService,IRaCIS.Application.Interfaces.IResearchPublicationService,IRaCIS.Application.Interfaces.IVacationService,System.Guid)">
|
||||||
<summary>
|
|
||||||
医生基本信息 、工作信息 专业信息、审核状态
|
|
||||||
</summary>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.GetDoctorDetail(IRaCIS.Application.Contracts.GetDoctorDetailInDto)">
|
|
||||||
<summary>
|
<summary>
|
||||||
获取医生详情
|
获取医生详情
|
||||||
</summary>
|
</summary>
|
||||||
|
@ -34,21 +29,8 @@
|
||||||
<param name="doctorId"></param>
|
<param name="doctorId"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Api.Controllers.ExtraController.OAuthCallBack(System.String,System.String)">
|
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,EasyCaching.Core.IEasyCachingProvider,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},IRaCIS.Application.Services.IMailVerificationService)">
|
||||||
<summary>
|
<summary> 系统用户登录接口[New] </summary>
|
||||||
回调到前端,前端调用后端的接口
|
|
||||||
参考链接:https://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html
|
|
||||||
后端通过这个code ,带上客户端信息,和授权类型 可以向单点登录提供商,获取厂商token
|
|
||||||
|
|
||||||
但是单点登录提供商提供的token 和我们系统的token 是有区别的,我们的token里面有我们业务系统的UserId,涉及到很多业务操作,所以在此出现了两种方案
|
|
||||||
1、前端使用厂商的Token。 后端通过code 获取厂商的Token 返回前端的同时返回我们系统的UserId,前段在http 请求头加上一个自定义参数,带上UserId 后端取用户Id的地方变动下,
|
|
||||||
但是除了UserId外,后端还有其他信息也是从Token取的,所以在请求头也需要带上,此外后端认证Token的方式也需要变化,改造成本稍大(如果是微服务,做这种处理还是可以的)。
|
|
||||||
2、前端还是使用我们后台自己的Token。后端通过code 获取厂商Token的同时,后端做一个隐藏登录,返回厂商的Token的同时,也返回我们系统的Token。
|
|
||||||
(像我们单体,这种方式最简单,我们用单点登录,无非就是不想记多个系统的密码,自动登录而已,其他不支持的项目改造成本也是最低的)
|
|
||||||
</summary>
|
|
||||||
<param name="type">回调的厂商类型 比如github, google, 我们用的logto ,不同的厂商回调到前端的地址可以不同的,但是请求后端的接口可以是同一个 </param>
|
|
||||||
<param name="code">在第三方平台登录成功后,回调前端的时候会返回一个code </param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.AddOrUpdateTrialInspection(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Application.Contracts.TrialCommand})">
|
||||||
<summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
<summary> 添加实验项目-返回新增Id[AUTH]</summary>
|
||||||
|
@ -59,7 +41,7 @@
|
||||||
<param name="param"></param>
|
<param name="param"></param>
|
||||||
<returns>新记录Id</returns>
|
<returns>新记录Id</returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Core.Application.Service.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
<member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Application.Services.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
|
||||||
<summary>
|
<summary>
|
||||||
添加或更新工作量[AUTH]
|
添加或更新工作量[AUTH]
|
||||||
</summary>
|
</summary>
|
||||||
|
@ -185,13 +167,6 @@
|
||||||
<param name="opt"></param>
|
<param name="opt"></param>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SignConsistencyAnalysisReadingClinicalData(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SignConsistencyAnalysisReadingClinicalDataInDto})">
|
|
||||||
<summary>
|
|
||||||
PM签名一致性分析临床数据
|
|
||||||
</summary>
|
|
||||||
<param name="opt"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
|
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.SubmitClinicalFormAndSign(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.Service.Reading.Dto.SubmitClinicalFormInDto})">
|
||||||
<summary>
|
<summary>
|
||||||
提交结构化录入并签名
|
提交结构化录入并签名
|
||||||
|
@ -281,7 +256,7 @@
|
||||||
</summary>
|
</summary>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ConfirmReReading(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.ViewModel.ConfirmReReadingCommand},IRaCIS.Core.Application.Service.IVisitTaskService)">
|
<member name="M:IRaCIS.Core.API.Controllers.InspectionController.ConfirmReReading(IRaCIS.Core.Application.Service.Inspection.DTO.DataInspectionDto{IRaCIS.Core.Application.ViewModel.ConfirmReReadingCommand},IRaCIS.Core.Application.Service.IVisitTaskHelpeService,IRaCIS.Core.Application.Service.IVisitTaskService)">
|
||||||
<summary>
|
<summary>
|
||||||
重阅同意
|
重阅同意
|
||||||
</summary>
|
</summary>
|
||||||
|
@ -296,38 +271,86 @@
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.UploadBaseController.DicomFileUploadAsync(System.Func{System.String,System.IO.Stream,System.Int32,System.Threading.Tasks.Task},System.String)">
|
<member name="M:IRaCIS.Core.API.Controllers.UploadBaseController.DicomFileUploadAsync(System.Func{System.String,System.IO.Stream,System.Int32,System.Threading.Tasks.Task},System.String)">
|
||||||
<summary> 流式上传 Dicom上传 </summary>
|
<summary> 流式上传 Dicom上传 </summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.ArchiveStudyNew(System.Guid,System.Guid,System.String,System.Nullable{System.Guid},System.Guid,Microsoft.Extensions.Logging.ILogger{IRaCIS.Core.API.Controllers.UploadDownLoadController},EasyCaching.Core.IEasyCachingProvider,IRaCIS.Core.Application.Contracts.IStudyService,Microsoft.AspNetCore.SignalR.IHubContext{IRaCIS.Core.API.UploadHub,IRaCIS.Core.API.IUploadClient},IRaCIS.Core.Application.Contracts.Dicom.IDicomArchiveService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||||
<summary>Dicom 归档</summary>
|
<summary>Dicom 归档</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.PreArchiveStudy(IRaCIS.Core.Application.Contracts.PreArchiveStudyCommand,IRaCIS.Core.Application.Contracts.IStudyService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor})">
|
||||||
<summary>
|
|
||||||
非dicom 上传预上传接口
|
|
||||||
</summary>
|
|
||||||
<param name="preArchiveStudyCommand"></param>
|
|
||||||
<param name="_studyService"></param>
|
|
||||||
<param name="_studyMonitorRepository"></param>
|
|
||||||
<returns></returns>
|
|
||||||
</member>
|
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadNoneDicomFile(IRaCIS.Core.API.Controllers.UploadNoneDicomFileCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudy},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.StudyMonitor},IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.NoneDicomStudyFile})">
|
|
||||||
<summary>
|
<summary>
|
||||||
上传非Dicom 文件 支持压缩包 多文件上传
|
上传非Dicom 文件 支持压缩包 多文件上传
|
||||||
</summary>
|
</summary>
|
||||||
<param name="incommand"></param>
|
<param name="incommand"></param>
|
||||||
<param name="_noneDicomStudyRepository"></param>
|
<param name="_noneDicomStudyRepository"></param>
|
||||||
<param name="_studyMonitorRepository"></param>
|
<param name="_studyMonitorRepository"></param>
|
||||||
<param name="_noneDicomStudyFileRepository"></param>
|
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.InspectionFile})">
|
<member name="M:IRaCIS.Core.API.Controllers.StudyController.UploadVisitCheckExcel(System.Guid,IRaCIS.Core.Application.Helper.IOSSService)">
|
||||||
<summary>
|
<summary>
|
||||||
一致性核查 excel上传 支持三种格式
|
一致性核查 excel上传 支持三种格式
|
||||||
</summary>
|
</summary>
|
||||||
<param name="trialId"></param>
|
<param name="trialId"></param>
|
||||||
<param name="oSSService"></param>
|
<param name="oSSService"></param>
|
||||||
<param name="_inspectionFileRepository"></param>
|
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
<exception cref="T:IRaCIS.Core.Infrastructure.BusinessValidationFailedException"></exception>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:IRaCIS.Core.API.Controllers.FileController">
|
||||||
|
<summary>医生文件上传下载</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||||
|
<summary>
|
||||||
|
New 医生首页 多选 获取多个医生信息+文件路径列表 医生压缩包名称 doctorCode + "_" + doctorName _(时间戳或者随机的Guid)
|
||||||
|
</summary>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetTrialDoctorOfficialResume(IRaCIS.Core.API.Controllers.GetDoctorPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||||
|
<summary>
|
||||||
|
New 项目入组 勾选获取简历路径
|
||||||
|
</summary>
|
||||||
|
<param name="command"></param>
|
||||||
|
<param name="_doctorService"></param>
|
||||||
|
<param name="_attachmentrepository"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.GetDoctorAttachment(IRaCIS.Core.API.Controllers.GetDoctoreAttachPathCommand,IRaCIS.Application.Interfaces.IDoctorService,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.Attachment})">
|
||||||
|
<summary>
|
||||||
|
new 医生详情 勾选或者下载文件路径
|
||||||
|
</summary>
|
||||||
|
<param name="command"></param>
|
||||||
|
<param name="_doctorService"></param>
|
||||||
|
<param name="_attachmentrepository"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadAttachment(System.Guid[])">
|
||||||
|
<summary>
|
||||||
|
下载多个医生的所有附件
|
||||||
|
</summary>
|
||||||
|
<param name="doctorIds"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadAttachmentById(System.Guid,System.Guid[])">
|
||||||
|
<summary>
|
||||||
|
下载指定医生的指定附件
|
||||||
|
</summary>
|
||||||
|
<param name="doctorId">医生Id</param>
|
||||||
|
<param name="attachmentIds">要下载的附件Id</param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadOfficialResume(System.Int32,System.Guid[])">
|
||||||
|
<summary>
|
||||||
|
下载医生官方简历 首页 区分 中文和英文
|
||||||
|
</summary>
|
||||||
|
<param name="language"></param>
|
||||||
|
<param name="doctorIds"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.API.Controllers.FileController.DownloadResume(System.Int32,System.Guid,System.Guid[])">
|
||||||
|
<summary>
|
||||||
|
入组 项目下载简历
|
||||||
|
</summary>
|
||||||
|
<param name="language"></param>
|
||||||
|
<param name="trialId"></param>
|
||||||
|
<param name="doctorIdArray"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
<member name="M:IRaCIS.Core.API.Controllers.UploadDownLoadController.DownloadCommonFile(System.String,IRaCIS.Core.Infra.EFCore.IRepository{IRaCIS.Core.Domain.Models.CommonDocument})">
|
||||||
<summary> 通用文件下载 </summary>
|
<summary> 通用文件下载 </summary>
|
||||||
</member>
|
</member>
|
||||||
|
@ -353,14 +376,24 @@
|
||||||
序列化,反序列化的时候,处理时间 时区转换
|
序列化,反序列化的时候,处理时间 时区转换
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="M:IRaCIS.Core.API.JSONTimeZoneConverter.#ctor(Microsoft.AspNetCore.Http.IHttpContextAccessor)">
|
<member name="M:IRaCIS.Core.API.NullToEmptyStringResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
|
||||||
<summary>
|
<summary>
|
||||||
序列化,反序列化的时候,处理时间 时区转换
|
创建属性
|
||||||
</summary>
|
</summary>
|
||||||
|
<param name="type">类型</param>
|
||||||
|
<param name="memberSerialization">序列化成员</param>
|
||||||
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:IRaCIS.Core.API.NullToEmptyStringResolver">
|
<member name="M:IRaCIS.WX.CoreApi.Auth.AuthMiddleware.Invoke(Microsoft.AspNetCore.Http.HttpContext)">
|
||||||
<summary>
|
<summary>
|
||||||
LowerCamelCaseJsonAttribute 可以设置类小写返回给前端
|
为了前端 一段时间无操作,需要重新登陆
|
||||||
|
</summary>
|
||||||
|
<param name="httpContext"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="T:TimeZoneAdjustmentMiddleware">
|
||||||
|
<summary>
|
||||||
|
废弃,没用
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
<member name="T:ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt.CustomHSJWTService">
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 废弃,没用
|
||||||
|
/// </summary>
|
||||||
|
public class TimeZoneAdjustmentMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public TimeZoneAdjustmentMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(context.Request.ContentType))
|
||||||
|
{
|
||||||
|
// 请求没有内容体,可能是一个没有请求体的请求,比如 GET 请求
|
||||||
|
await _next(context);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var timeZoneId = "Asia/Shanghai"; // 客户端默认时区
|
||||||
|
|
||||||
|
var timeZoneIdHeaderValue = context.Request.Headers["TimeZoneId"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(timeZoneIdHeaderValue))
|
||||||
|
{
|
||||||
|
timeZoneId = timeZoneIdHeaderValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 JSON 请求体中的时间字段
|
||||||
|
if (context.Request.ContentType.StartsWith("application/json"))
|
||||||
|
{
|
||||||
|
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
|
||||||
|
|
||||||
|
// 使用 JSON.NET 或 System.Text.Json 解析 JSON 请求体
|
||||||
|
// 假设请求体中有一个名为 "dateTime" 的时间字段
|
||||||
|
dynamic jsonData = JsonConvert.DeserializeObject(requestBody);
|
||||||
|
|
||||||
|
if (jsonData.dateTime != null)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse((string)jsonData.dateTime, out DateTime dateTime))
|
||||||
|
{
|
||||||
|
// 将 JSON 请求体中的时间字段转换为服务器时区的时间
|
||||||
|
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||||
|
jsonData.dateTime = serverTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将修改后的 JSON 请求体重新写入请求流中
|
||||||
|
var jsonBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonData));
|
||||||
|
context.Request.Body = new MemoryStream(jsonBytes);
|
||||||
|
context.Request.ContentLength = jsonBytes.Length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 URL 表单参数
|
||||||
|
var modifiedQuery = new Dictionary<string, StringValues>();
|
||||||
|
|
||||||
|
foreach (var key in context.Request.Query.Keys)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(context.Request.Query[key], out DateTime dateTime))
|
||||||
|
{
|
||||||
|
// 将 URL 表单参数中的时间转换为服务器时区的时间
|
||||||
|
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||||
|
modifiedQuery[key] = new StringValues(serverTime.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modifiedQuery[key] = context.Request.Query[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.Request.Query = new QueryCollection(modifiedQuery);
|
||||||
|
|
||||||
|
// 处理Form请求体中的参数
|
||||||
|
if (context.Request.HasFormContentType)
|
||||||
|
{
|
||||||
|
var modifiedForm = new Dictionary<string, StringValues>();
|
||||||
|
|
||||||
|
foreach (var key in context.Request.Form.Keys)
|
||||||
|
{
|
||||||
|
if (DateTime.TryParse(context.Request.Form[key], out DateTime dateTime))
|
||||||
|
{
|
||||||
|
// 将请求体中的时间转换为服务器时区的时间
|
||||||
|
var serverTime = TimeZoneInfo.ConvertTime(dateTime, timeZone);
|
||||||
|
modifiedForm[key] = new StringValues(serverTime.ToString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modifiedForm[key] = context.Request.Form[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newFormCollection = new FormCollection(modifiedForm);
|
||||||
|
|
||||||
|
// 将新的表单集合设置回请求对象
|
||||||
|
context.Request.Form = newFormCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,32 +1,34 @@
|
||||||
using IRaCIS.Core.API;
|
using System;
|
||||||
using IRaCIS.Core.API.HostService;
|
using Autofac.Extensions.DependencyInjection;
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using IRaCIS.Core.Application.Filter;
|
using Microsoft.Extensions.Hosting;
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
using Microsoft.Extensions.Configuration;
|
||||||
using IRaCIS.Core.Application.Service;
|
using Serilog;
|
||||||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
using MediatR;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Application.MediatR.Handlers;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using System.Threading.Tasks;
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using MassTransit.NewIdProviders;
|
using MassTransit.NewIdProviders;
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Serilog;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using IRaCIS.Core.API;
|
||||||
|
using Autofac;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using IRaCIS.Core.Application.Filter;
|
||||||
|
using Microsoft.AspNetCore.HttpOverrides;
|
||||||
|
using IRaCIS.Application.Services.BackGroundJob;
|
||||||
|
using LogDashboard;
|
||||||
|
using FellowOakDicom.Network;
|
||||||
|
using IRaCIS.Core.Application.Service.ImageAndDoc;
|
||||||
|
using IP2Region.Net.Abstractions;
|
||||||
|
using IP2Region.Net.XDB;
|
||||||
|
|
||||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
|
||||||
AppContext.SetSwitch("Npgsql.DisableDateTimeInfinityConversions", true);
|
|
||||||
|
|
||||||
#region 获取环境变量
|
#region 获取环境变量
|
||||||
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
//以配置文件为准,否则 从url中取环境值(服务以命令行传递参数启动,配置文件配置了就不需要传递环境参数)
|
||||||
|
@ -36,11 +38,6 @@ var config = new ConfigurationBuilder()
|
||||||
|
|
||||||
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
var enviromentName = config["ASPNETCORE_ENVIRONMENT"];
|
||||||
|
|
||||||
var openSwaggerStr = config["ASPNETCORE_OpenSwagger"];
|
|
||||||
|
|
||||||
var isOpenSwagger= openSwaggerStr == null|| openSwaggerStr?.ToLower()=="true";
|
|
||||||
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(enviromentName))
|
if (string.IsNullOrWhiteSpace(enviromentName))
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -51,107 +48,136 @@ if (string.IsNullOrWhiteSpace(enviromentName))
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
// Serilog
|
|
||||||
SerilogExtension.AddSerilogSetup(enviromentName);
|
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
|
||||||
{
|
{
|
||||||
|
|
||||||
EnvironmentName = enviromentName
|
EnvironmentName = enviromentName
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#region 兼容windows 服务命令行的方式
|
||||||
|
|
||||||
|
//foreach (var arg in args)
|
||||||
|
//{
|
||||||
|
// Console.WriteLine(arg);
|
||||||
|
//}
|
||||||
|
|
||||||
|
int urlsIndex = Array.FindIndex(args, arg => arg != null && arg.StartsWith("--urls"));
|
||||||
|
|
||||||
|
if (urlsIndex > -1)
|
||||||
|
{
|
||||||
|
var url = args[urlsIndex].Substring("--urls=".Length);
|
||||||
|
Console.WriteLine(url);
|
||||||
|
builder.WebHost.UseUrls(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region 主机配置
|
#region 主机配置
|
||||||
|
|
||||||
|
|
||||||
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
NewId.SetProcessIdProvider(new CurrentProcessIdProvider());
|
||||||
|
|
||||||
builder.Configuration.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), "appsettings.json", false, true)
|
builder.Configuration.AddJsonFile("appsettings.json", false, true)
|
||||||
.AddJsonFile(ConfigMapFileProvider.FromRelativePath(""), $"appsettings.{enviromentName}.json", false, true);
|
.AddJsonFile($"appsettings.{enviromentName}.json", false, true);
|
||||||
|
|
||||||
builder.Host.UseSerilog();
|
|
||||||
|
|
||||||
|
|
||||||
|
builder.Host
|
||||||
|
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
|
||||||
|
.ConfigureContainer<ContainerBuilder>(containerBuilder =>
|
||||||
|
{
|
||||||
|
containerBuilder.RegisterModule<AutofacModuleSetup>();
|
||||||
|
})
|
||||||
|
.UseWindowsService().UseSerilog();
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 配置服务
|
#region 配置服务
|
||||||
var _configuration = builder.Configuration;
|
var _configuration = builder.Configuration;
|
||||||
|
|
||||||
//手动注册服务
|
|
||||||
builder.Services.ConfigureServices(_configuration);
|
|
||||||
|
|
||||||
builder.Services.AddHostedService<HangfireHostService>();
|
|
||||||
|
|
||||||
//minimal api 异常处理
|
|
||||||
builder.Services.AddExceptionHandler<GlobalExceptionHandler>();
|
|
||||||
//builder.Services.AddProblemDetails();
|
|
||||||
|
|
||||||
//健康检查
|
//健康检查
|
||||||
builder.Services.AddHealthChecks();
|
builder.Services.AddHealthChecks();
|
||||||
builder.Services.AddSerilog();
|
|
||||||
//本地化
|
//本地化
|
||||||
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
builder.Services.AddJsonLocalization(options => options.ResourcesPath = "Resources");
|
||||||
|
|
||||||
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
// 异常、参数统一验证过滤器、Json序列化配置、字符串参数绑型统一Trim()
|
||||||
builder.Services.AddControllers(options =>
|
builder.Services.AddControllers(options =>
|
||||||
{
|
{
|
||||||
|
//options.Filters.Add<LogActionFilter>();
|
||||||
options.Filters.Add<ModelActionFilter>();
|
options.Filters.Add<ModelActionFilter>();
|
||||||
options.Filters.Add<ProjectExceptionFilter>();
|
options.Filters.Add<ProjectExceptionFilter>();
|
||||||
options.Filters.Add<UnitOfWorkFilter>();
|
options.Filters.Add<UnitOfWorkFilter>();
|
||||||
|
|
||||||
|
if (_configuration.GetSection("BasicSystemConfig").GetValue<bool>("OpenLoginLimit"))
|
||||||
|
{
|
||||||
options.Filters.Add<LimitUserRequestAuthorization>();
|
options.Filters.Add<LimitUserRequestAuthorization>();
|
||||||
options.Filters.Add<TrialGlobalLimitActionFilter>();
|
}
|
||||||
|
|
||||||
})
|
})
|
||||||
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
.AddNewtonsoftJsonSetup(builder.Services); // NewtonsoftJson 序列化 处理
|
||||||
|
|
||||||
// Panda动态WebApi + UnifiedApiResultFilter + 省掉控制器代码
|
builder.Services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
||||||
builder.Services.AddDynamicWebApiSetup();
|
builder.Services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
||||||
//MinimalAPI
|
builder.Services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
||||||
builder.Services.AddMasaMinimalAPiSetUp();
|
builder.Services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
||||||
|
|
||||||
|
|
||||||
|
//动态WebApi + UnifiedApiResultFilter 省掉控制器代码
|
||||||
|
builder.Services.AddDynamicWebApiSetup();
|
||||||
//AutoMapper
|
//AutoMapper
|
||||||
builder.Services.AddAutoMapperSetup();
|
builder.Services.AddAutoMapperSetup();
|
||||||
//EF ORM QueryWithNoLock
|
//EF ORM QueryWithNoLock
|
||||||
builder.Services.AddEFSetup(_configuration, enviromentName);
|
builder.Services.AddEFSetup(_configuration);
|
||||||
//Http 响应压缩
|
//Http 响应压缩
|
||||||
builder.Services.AddResponseCompressionSetup();
|
builder.Services.AddResponseCompressionSetup();
|
||||||
|
|
||||||
if (isOpenSwagger)
|
|
||||||
{
|
|
||||||
//Swagger Api 文档
|
//Swagger Api 文档
|
||||||
builder.Services.AddSwaggerSetup();
|
builder.Services.AddSwaggerSetup();
|
||||||
}
|
|
||||||
|
|
||||||
//JWT Token 验证
|
//JWT Token 验证
|
||||||
builder.Services.AddJWTAuthSetup(_configuration);
|
builder.Services.AddJWTAuthSetup(_configuration);
|
||||||
|
|
||||||
//MassTransit
|
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
||||||
builder.Services.AddMassTransitSetup();
|
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
||||||
|
// EasyCaching 缓存
|
||||||
// FusionCache
|
builder.Services.AddEasyCachingSetup(_configuration);
|
||||||
builder.Services.AddFusionCache();
|
|
||||||
|
|
||||||
// hangfire 定时任务框架 有界面,更友好~
|
// hangfire 定时任务框架 有界面,更友好~
|
||||||
builder.Services.AddhangfireSetup(_configuration);
|
builder.Services.AddhangfireSetup(_configuration);
|
||||||
|
|
||||||
//Serilog 日志可视化 LogDashboard日志
|
|
||||||
//builder.Services.AddLogDashboardSetup();
|
|
||||||
|
|
||||||
|
//Serilog 日志可视化 LogDashboard日志
|
||||||
|
builder.Services.AddLogDashboardSetup();
|
||||||
|
|
||||||
|
|
||||||
|
builder.Services.AddJsonConfigSetup(_configuration);
|
||||||
|
//转发头设置 获取真实IP
|
||||||
|
builder.Services.Configure<ForwardedHeadersOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ForwardedHeaders =
|
||||||
|
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
||||||
|
});
|
||||||
//Dicom影像渲染图片 跨平台
|
//Dicom影像渲染图片 跨平台
|
||||||
builder.Services.AddDicomSetup();
|
builder.Services.AddDicomSetup();
|
||||||
|
|
||||||
// 实时应用
|
// 实时应用
|
||||||
builder.Services.AddSignalR();
|
builder.Services.AddSignalR();
|
||||||
|
|
||||||
|
builder.Services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
||||||
|
|
||||||
//// 添加反伪造服务
|
|
||||||
//builder.Services.AddAntiforgery(options =>
|
builder.Services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
||||||
|
|
||||||
|
|
||||||
|
#region 历史废弃配置
|
||||||
|
//builder.Services.AddMemoryCache();
|
||||||
|
////上传限制 配置
|
||||||
|
//builder.Services.Configure<FormOptions>(options =>
|
||||||
//{
|
//{
|
||||||
// // 可选:设置自定义的头部名称以支持 AJAX 请求等
|
// options.MultipartBodyLengthLimit = int.MaxValue;
|
||||||
// options.HeaderName = "X-XSRF-TOKEN";
|
// options.ValueCountLimit = int.MaxValue;
|
||||||
|
// options.ValueLengthLimit = int.MaxValue;
|
||||||
//});
|
//});
|
||||||
|
//IP 限流 可设置白名单 或者黑名单
|
||||||
//builder.Services.AddAntiforgery();
|
//services.AddIpPolicyRateLimitSetup(_configuration);
|
||||||
|
// 用户类型 策略授权
|
||||||
|
//services.AddAuthorizationPolicySetup(_configuration);
|
||||||
|
#endregion
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -161,105 +187,80 @@ var env = app.Environment;
|
||||||
#region 配置中间件
|
#region 配置中间件
|
||||||
|
|
||||||
|
|
||||||
app.UseMiddleware<EncryptionRequestMiddleware>();
|
// Configure the HTTP request pipeline.
|
||||||
|
|
||||||
#region 异常处理 全局业务异常已统一处理了,非业务错误会来到这里 400 -500状态码
|
|
||||||
|
|
||||||
|
|
||||||
//app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
|
||||||
|
|
||||||
app.UseStatusCodePages(async context =>
|
|
||||||
{
|
|
||||||
var code = context.HttpContext.Response.StatusCode;
|
|
||||||
context.HttpContext.Response.ContentType = "application/json";
|
|
||||||
if (code < 500)
|
|
||||||
{
|
|
||||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject(ResponseOutput.NotOk($"Client error, actual request error status code({code})")));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//ResultFilter 里面的异常并不会到这里
|
|
||||||
await context.HttpContext.Response.WriteAsync(JsonConvert.SerializeObject((ResponseOutput.NotOk($"Server error , actual request error status code({code})"))));
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
//app.UseExceptionHandler();
|
|
||||||
app.UseExceptionHandler(o => { });
|
|
||||||
|
|
||||||
#region 暂时废弃
|
|
||||||
|
|
||||||
//app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
|
||||||
////限流 中间件
|
|
||||||
//app.UseIpRateLimiting();
|
|
||||||
//if (env.IsDevelopment())
|
|
||||||
//{
|
|
||||||
// app.UseDeveloperExceptionPage();
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// //app.UseHsts();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//app.UseIRacisHostStaticFileStore(env);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
app.UseIRacisHostStaticFileStore(env);
|
|
||||||
|
|
||||||
//本地化
|
//本地化
|
||||||
await app.UseLocalization(app.Services);
|
app.UseLocalization();
|
||||||
|
|
||||||
app.UseForwardedHeaders();
|
app.UseForwardedHeaders();
|
||||||
|
|
||||||
//响应压缩
|
//响应压缩
|
||||||
app.UseResponseCompression();
|
app.UseResponseCompression();
|
||||||
|
|
||||||
|
//app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||||
|
|
||||||
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
//不需要 token 访问的静态文件 wwwroot css, JavaScript, and images don't require authentication.
|
||||||
app.UseStaticFiles();
|
app.UseStaticFiles();
|
||||||
|
|
||||||
|
app.UseMiddleware<MultiDiskStaticFilesMiddleware>();
|
||||||
|
|
||||||
//LogDashboard
|
//LogDashboard
|
||||||
//app.UseLogDashboard("/LogDashboard");
|
app.UseLogDashboard("/LogDashboard");
|
||||||
|
|
||||||
//hangfire
|
//hangfire
|
||||||
app.UseHangfireConfig(env);
|
app.UseHangfireConfig(env);
|
||||||
|
|
||||||
// Swagger
|
|
||||||
|
|
||||||
if (isOpenSwagger)
|
////限流 中间件
|
||||||
|
//app.UseIpRateLimiting();
|
||||||
|
|
||||||
|
|
||||||
|
if (env.IsDevelopment())
|
||||||
{
|
{
|
||||||
SwaggerSetup.Configure(app, env);
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//app.UseHsts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 特殊异常处理 比如 404
|
||||||
|
app.UseStatusCodePagesWithReExecute("/Error/{0}");
|
||||||
|
|
||||||
//serilog 记录请求的用户信息
|
SwaggerSetup.Configure(app, env);
|
||||||
|
|
||||||
|
|
||||||
|
////serilog 记录请求的用户信息
|
||||||
app.UseSerilogConfig(env);
|
app.UseSerilogConfig(env);
|
||||||
|
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
|
|
||||||
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
app.UseCors(t => t.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||||
|
|
||||||
|
//app.UseIRacisHostStaticFileStore(env);
|
||||||
|
|
||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
|
||||||
//Map MinimalAPI routes
|
|
||||||
app.MapMasaMinimalAPIs();
|
|
||||||
|
|
||||||
//// 这里添加反伪造中间件
|
|
||||||
//app.UseAntiforgery();
|
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
|
|
||||||
app.MapHub<UploadHub>("/UploadHub");
|
app.MapHub<UploadHub>("/UploadHub");
|
||||||
app.MapHealthChecks("/health");
|
app.MapHealthChecks("/health");
|
||||||
|
|
||||||
|
|
||||||
|
// Serilog
|
||||||
|
SerilogExtension.AddSerilogSetup(enviromentName, app.Services);
|
||||||
|
|
||||||
|
|
||||||
|
var hangfireJobService = app.Services.GetRequiredService<IIRaCISHangfireJob>();
|
||||||
|
|
||||||
|
await hangfireJobService.InitHangfireJobTaskAsync();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
#region 运行环境 部署平台
|
#region 运行环境 部署平台
|
||||||
|
@ -279,6 +280,9 @@ try
|
||||||
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
Log.Logger.Warning($"当前部署平台环境:OSX or FreeBSD");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}");
|
Log.Logger.Warning($"ContentRootPath:{env.ContentRootPath}");
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,11 +293,14 @@ try
|
||||||
|
|
||||||
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
//Log.Logger.Warning($"ContentRootPath——GetParent:{Directory.GetParent(env.ContentRootPath).Parent.FullName}");
|
||||||
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
//Log.Logger.Warning($"ContentRootPath——xx:{Path.GetDirectoryName(Path.GetDirectoryName(env.ContentRootPath))}");
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var server = DicomServerFactory.Create<CStoreSCPService>(11112,userState: app.Services);
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,16 +20,7 @@
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"launchBrowser": true,
|
"launchBrowser": true,
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC",
|
"ASPNETCORE_ENVIRONMENT": "Test_IRC"
|
||||||
"ASPNETCORE_OpenSwagger": "true"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
},
|
|
||||||
"IRaCIS.Test_IRC_PGSQL": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Test_IRC_PGSQL"
|
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
},
|
||||||
|
@ -70,14 +61,6 @@
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
"ASPNETCORE_ENVIRONMENT": "US_Uat_IRC"
|
||||||
},
|
},
|
||||||
"applicationUrl": "http://localhost:6100"
|
"applicationUrl": "http://localhost:6100"
|
||||||
},
|
|
||||||
"IRaCIS.US_Prod_IRC": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "US_Prod_IRC"
|
|
||||||
},
|
|
||||||
"applicationUrl": "http://localhost:6100"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 73 MiB |
|
@ -1,2 +0,0 @@
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
using IRaCIS.Core.Domain.Share;
|
using EasyCaching.Core;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Cors;
|
using Microsoft.AspNetCore.Cors;
|
||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
@ -17,7 +18,7 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public virtual string GetUserId(HubConnectionContext connection)
|
public virtual string GetUserId(HubConnectionContext connection)
|
||||||
{
|
{
|
||||||
return connection.User?.FindFirst(JwtIRaCISClaimType.IdentityUserId)?.Value!;
|
return connection.User?.FindFirst(JwtIRaCISClaimType.Id)?.Value!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.Filter
|
||||||
|
{
|
||||||
|
public class EnableBufferingAttribute : Attribute, IResourceFilter
|
||||||
|
{
|
||||||
|
public void OnResourceExecuting(ResourceExecutingContext context)
|
||||||
|
{
|
||||||
|
context.HttpContext.Request.EnableBuffering();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnResourceExecuted(ResourceExecutedContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,270 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.Utility
|
||||||
|
{
|
||||||
|
public static class FileHelpers
|
||||||
|
{
|
||||||
|
private static readonly byte[] _allowedChars = { };
|
||||||
|
// For more file signatures, see the File Signatures Database (https://www.filesignatures.net/)
|
||||||
|
// and the official specifications for the file types you wish to add.
|
||||||
|
private static readonly Dictionary<string, List<byte[]>> _fileSignature = new Dictionary<string, List<byte[]>>
|
||||||
|
{
|
||||||
|
{ ".gif", new List<byte[]> { new byte[] { 0x47, 0x49, 0x46, 0x38 } } },
|
||||||
|
{ ".png", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
|
||||||
|
{ ".jpeg", new List<byte[]>
|
||||||
|
{
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ ".jpg", new List<byte[]>
|
||||||
|
{
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
|
||||||
|
new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ ".zip", new List<byte[]>
|
||||||
|
{
|
||||||
|
new byte[] { 0x50, 0x4B, 0x03, 0x04 },
|
||||||
|
new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x45 },
|
||||||
|
new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 },
|
||||||
|
new byte[] { 0x50, 0x4B, 0x05, 0x06 },
|
||||||
|
new byte[] { 0x50, 0x4B, 0x07, 0x08 },
|
||||||
|
new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 },
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// **WARNING!**
|
||||||
|
// In the following file processing methods, the file's content isn't scanned.
|
||||||
|
// In most production scenarios, an anti-virus/anti-malware scanner API is
|
||||||
|
// used on the file before making the file available to users or other
|
||||||
|
// systems. For more information, see the topic that accompanies this sample
|
||||||
|
// app.
|
||||||
|
|
||||||
|
public static async Task<byte[]> ProcessFormFile<T>(IFormFile formFile,
|
||||||
|
ModelStateDictionary modelState, string[] permittedExtensions,
|
||||||
|
long sizeLimit)
|
||||||
|
{
|
||||||
|
var fieldDisplayName = string.Empty;
|
||||||
|
|
||||||
|
// Use reflection to obtain the display name for the model
|
||||||
|
// property associated with this IFormFile. If a display
|
||||||
|
// name isn't found, error messages simply won't show
|
||||||
|
// a display name.
|
||||||
|
MemberInfo property =
|
||||||
|
typeof(T).GetProperty(
|
||||||
|
formFile.Name.Substring(formFile.Name.IndexOf(".",
|
||||||
|
StringComparison.Ordinal) + 1));
|
||||||
|
|
||||||
|
if (property != null)
|
||||||
|
{
|
||||||
|
if (property.GetCustomAttribute(typeof(DisplayAttribute)) is
|
||||||
|
DisplayAttribute displayAttribute)
|
||||||
|
{
|
||||||
|
fieldDisplayName = $"{displayAttribute.Name} ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't trust the file name sent by the client. To display
|
||||||
|
// the file name, HTML-encode the value.
|
||||||
|
var trustedFileNameForDisplay = WebUtility.HtmlEncode(
|
||||||
|
formFile.FileName);
|
||||||
|
|
||||||
|
// Check the file length. This check doesn't catch files that only have
|
||||||
|
// a BOM as their content.
|
||||||
|
if (formFile.Length == 0)
|
||||||
|
{
|
||||||
|
modelState.AddModelError(formFile.Name,
|
||||||
|
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
|
||||||
|
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formFile.Length > sizeLimit)
|
||||||
|
{
|
||||||
|
var megabyteSizeLimit = sizeLimit / 1048576;
|
||||||
|
modelState.AddModelError(formFile.Name,
|
||||||
|
$"{fieldDisplayName}({trustedFileNameForDisplay}) exceeds " +
|
||||||
|
$"{megabyteSizeLimit:N1} MB.");
|
||||||
|
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
await formFile.CopyToAsync(memoryStream);
|
||||||
|
|
||||||
|
// Check the content length in case the file's only
|
||||||
|
// content was a BOM and the content is actually
|
||||||
|
// empty after removing the BOM.
|
||||||
|
if (memoryStream.Length == 0)
|
||||||
|
{
|
||||||
|
modelState.AddModelError(formFile.Name,
|
||||||
|
$"{fieldDisplayName}({trustedFileNameForDisplay}) is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidFileExtensionAndSignature(
|
||||||
|
formFile.FileName, memoryStream, permittedExtensions))
|
||||||
|
{
|
||||||
|
modelState.AddModelError(formFile.Name,
|
||||||
|
$"{fieldDisplayName}({trustedFileNameForDisplay}) file " +
|
||||||
|
"type isn't permitted or the file's signature " +
|
||||||
|
"doesn't match the file's extension.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
modelState.AddModelError(formFile.Name,
|
||||||
|
$"{fieldDisplayName}({trustedFileNameForDisplay}) upload failed. " +
|
||||||
|
$"Please contact the Help Desk for support. Error: {ex.HResult}");
|
||||||
|
}
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task<byte[]> ProcessStreamedFile(
|
||||||
|
MultipartSection section, ContentDispositionHeaderValue contentDisposition,
|
||||||
|
ModelStateDictionary modelState, string[] permittedExtensions, long sizeLimit)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var memoryStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
await section.Body.CopyToAsync(memoryStream);
|
||||||
|
|
||||||
|
// Check if the file is empty or exceeds the size limit.
|
||||||
|
if (memoryStream.Length == 0)
|
||||||
|
{
|
||||||
|
modelState.AddModelError("File", "The file is empty.");
|
||||||
|
}
|
||||||
|
else if (memoryStream.Length > sizeLimit)
|
||||||
|
{
|
||||||
|
var megabyteSizeLimit = sizeLimit / 1048576;
|
||||||
|
modelState.AddModelError("File",
|
||||||
|
$"The file exceeds {megabyteSizeLimit:N1} MB.");
|
||||||
|
}
|
||||||
|
else if (!IsValidFileExtensionAndSignature(
|
||||||
|
contentDisposition.FileName.Value, memoryStream,
|
||||||
|
permittedExtensions))
|
||||||
|
{
|
||||||
|
modelState.AddModelError("File",
|
||||||
|
"The file type isn't permitted or the file's " +
|
||||||
|
"signature doesn't match the file's extension.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return memoryStream.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
modelState.AddModelError("File",
|
||||||
|
"The upload failed. Please contact the Help Desk " +
|
||||||
|
$" for support. Error: {ex.HResult}");
|
||||||
|
// Log the exception
|
||||||
|
}
|
||||||
|
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsValidFileExtensionAndSignature(string fileName, Stream data, string[] permittedExtensions)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(fileName) || data == null || data.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ext = Path.GetExtension(fileName).ToLowerInvariant();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(ext) || !permittedExtensions.Contains(ext))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data.Position = 0;
|
||||||
|
|
||||||
|
using (var reader = new BinaryReader(data))
|
||||||
|
{
|
||||||
|
if (ext.Equals(".txt") || ext.Equals(".csv") || ext.Equals(".prn"))
|
||||||
|
{
|
||||||
|
if (_allowedChars.Length == 0)
|
||||||
|
{
|
||||||
|
// Limits characters to ASCII encoding.
|
||||||
|
for (var i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
if (reader.ReadByte() > sbyte.MaxValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Limits characters to ASCII encoding and
|
||||||
|
// values of the _allowedChars array.
|
||||||
|
for (var i = 0; i < data.Length; i++)
|
||||||
|
{
|
||||||
|
var b = reader.ReadByte();
|
||||||
|
if (b > sbyte.MaxValue ||
|
||||||
|
!_allowedChars.Contains(b))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uncomment the following code block if you must permit
|
||||||
|
// files whose signature isn't provided in the _fileSignature
|
||||||
|
// dictionary. We recommend that you add file signatures
|
||||||
|
// for files (when possible) for all file types you intend
|
||||||
|
// to allow on the system and perform the file signature
|
||||||
|
// check.
|
||||||
|
|
||||||
|
//if (!_fileSignature.ContainsKey(ext))
|
||||||
|
//{
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// File signature check
|
||||||
|
// --------------------
|
||||||
|
// With the file signatures provided in the _fileSignature
|
||||||
|
// dictionary, the following code tests the input content's
|
||||||
|
// file signature.
|
||||||
|
|
||||||
|
//var signatures = _fileSignature[ext];
|
||||||
|
//var headerBytes = reader.ReadBytes(signatures.Max(m => m.Length));
|
||||||
|
|
||||||
|
//return signatures.Any(signature =>
|
||||||
|
// headerBytes.Take(signature.Length).SequenceEqual(signature));
|
||||||
|
|
||||||
|
//test
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,12 @@
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
public interface ICustomJWTService
|
public interface ICustomJWTService
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
public class JWTTokenOptions
|
public class JWTTokenOptions
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
namespace ZhaoXi._001.NET5Demo.Practice.WebApi.Utility.Jwt
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API.Utility
|
||||||
|
{
|
||||||
|
public static class MultipartRequestHelper
|
||||||
|
{
|
||||||
|
// Content-Type: multipart/form-data; boundary="----WebKitFormBoundarymx2fSWqWSd0OxQqq"
|
||||||
|
// The spec at https://tools.ietf.org/html/rfc2046#section-5.1 states that 70 characters is a reasonable limit.
|
||||||
|
public static string GetBoundary(MediaTypeHeaderValue contentType, int lengthLimit)
|
||||||
|
{
|
||||||
|
var boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary).Value;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(boundary))
|
||||||
|
{
|
||||||
|
throw new InvalidDataException(
|
||||||
|
"Missing content-type boundary.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundary.Length > lengthLimit)
|
||||||
|
{
|
||||||
|
throw new InvalidDataException(
|
||||||
|
$"Multipart boundary length limit {lengthLimit} exceeded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return boundary;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsMultipartContentType(string contentType)
|
||||||
|
{
|
||||||
|
return !string.IsNullOrEmpty(contentType)
|
||||||
|
&& contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasFormDataContentDisposition(ContentDispositionHeaderValue contentDisposition)
|
||||||
|
{
|
||||||
|
// Content-Disposition: form-data; name="key";
|
||||||
|
return contentDisposition != null
|
||||||
|
&& contentDisposition.DispositionType.Equals("form-data")
|
||||||
|
&& string.IsNullOrEmpty(contentDisposition.FileName.Value)
|
||||||
|
&& string.IsNullOrEmpty(contentDisposition.FileNameStar.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool HasFileContentDisposition(ContentDispositionHeaderValue contentDisposition)
|
||||||
|
{
|
||||||
|
// Content-Disposition: form-data; name="myfile1"; filename="Misc 002.jpg"
|
||||||
|
return contentDisposition != null
|
||||||
|
&& contentDisposition.DispositionType.Equals("form-data")
|
||||||
|
&& (!string.IsNullOrEmpty(contentDisposition.FileName.Value)
|
||||||
|
|| !string.IsNullOrEmpty(contentDisposition.FileNameStar.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using EasyCaching.Core;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
|
namespace IRaCIS.WX.CoreApi.Auth
|
||||||
|
{
|
||||||
|
public class AuthMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IEasyCachingProvider _provider;
|
||||||
|
public AuthMiddleware(RequestDelegate next, IEasyCachingProvider provider)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_provider = provider;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///为了前端 一段时间无操作,需要重新登陆
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContext"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task Invoke(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
|
||||||
|
var isLogin = httpContext.Request.Path.ToString().ToLower().Contains("login");
|
||||||
|
|
||||||
|
var result = await httpContext.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
|
||||||
|
|
||||||
|
if (!isLogin)
|
||||||
|
{
|
||||||
|
if (!result.Succeeded)
|
||||||
|
{
|
||||||
|
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
|
await httpContext.Response.WriteAsync("Unauthorized");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var toekn = result.Properties.Items[".Token.access_token"];
|
||||||
|
var jwtHandler = new JwtSecurityTokenHandler();
|
||||||
|
JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(toekn);
|
||||||
|
object userId;
|
||||||
|
jwtToken.Payload.TryGetValue("id", out userId);
|
||||||
|
|
||||||
|
var cacheValueExist = await _provider.ExistsAsync(userId.ToString()); //Get<string>(userId.ToString()).ToString();
|
||||||
|
if (!cacheValueExist)
|
||||||
|
{
|
||||||
|
httpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
|
||||||
|
await httpContext.Response.WriteAsync("Unauthorized");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _provider.SetAsync(userId.ToString(), userId.ToString(), TimeSpan.FromMinutes(15));
|
||||||
|
httpContext.User = result.Principal;
|
||||||
|
await _next.Invoke(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else await _next.Invoke(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,9 @@
|
||||||
using Hangfire;
|
using Hangfire;
|
||||||
using Hangfire.Dashboard;
|
using Hangfire.Dashboard;
|
||||||
using Hangfire.Dashboard.BasicAuthorization;
|
using Hangfire.Dashboard.BasicAuthorization;
|
||||||
|
using IRaCIS.Application.Services.BackGroundJob;
|
||||||
|
using IRaCIS.Core.API.Filter;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,21 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
using Azure;
|
||||||
|
using IRaCIS.Core.Application.Helper;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.Extensions.FileProviders;
|
using Microsoft.Extensions.FileProviders;
|
||||||
using Microsoft.Extensions.FileProviders.Physical;
|
using Microsoft.Extensions.FileProviders.Physical;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Hosting.Internal;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.VisualBasic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using SharpCompress.Common;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
|
@ -1,30 +1,19 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using Microsoft.AspNetCore.Localization;
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using IRaCIS.Core.Infrastructure.Extention;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class LocalizationConfig
|
public static class LocalizationConfig
|
||||||
{
|
{
|
||||||
|
|
||||||
public static async Task UseLocalization(this IApplicationBuilder app,IServiceProvider serviceProvider)
|
public static void UseLocalization(this IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
var supportedCultures = new List<CultureInfo>
|
var supportedCultures = new List<CultureInfo>
|
||||||
{
|
{
|
||||||
|
new CultureInfo("en-US"),
|
||||||
new CultureInfo(StaticData.CultureInfo.en_US),
|
new CultureInfo("zh-CN")
|
||||||
new CultureInfo(StaticData.CultureInfo.zh_CN)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var options = new RequestLocalizationOptions
|
var options = new RequestLocalizationOptions
|
||||||
|
@ -40,28 +29,6 @@ namespace IRaCIS.Core.API
|
||||||
//options.RequestCultureProviders.RemoveAt(1);
|
//options.RequestCultureProviders.RemoveAt(1);
|
||||||
|
|
||||||
app.UseRequestLocalization(options);
|
app.UseRequestLocalization(options);
|
||||||
|
|
||||||
//设置国际化I18n
|
|
||||||
var localizer = serviceProvider.GetRequiredService<IStringLocalizer>();
|
|
||||||
I18n.SetLocalizer(localizer);
|
|
||||||
|
|
||||||
//初始化国际化
|
|
||||||
|
|
||||||
var _internationalizationRepository = serviceProvider.GetRequiredService<IRepository<Internationalization>>();
|
|
||||||
|
|
||||||
//查询数据库的数据
|
|
||||||
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new IRCGlobalInfoDTO()
|
|
||||||
{
|
|
||||||
Code = t.Code,
|
|
||||||
Value = t.Value,
|
|
||||||
ValueCN = t.ValueCN,
|
|
||||||
Description = t.Description
|
|
||||||
}).ToListAsync();
|
|
||||||
|
|
||||||
|
|
||||||
await InternationalizationHelper.BatchAddJsonKeyValueAsync(toJsonList);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
//using LogDashboard;
|
using LogDashboard;
|
||||||
//using LogDashboard.Authorization;
|
using LogDashboard.Authorization;
|
||||||
|
|
||||||
//namespace IRaCIS.Core.API.Filter
|
namespace IRaCIS.Core.API.Filter
|
||||||
//{
|
{
|
||||||
|
|
||||||
// public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
public class LogDashBoardAuthFilter : ILogDashboardAuthorizationFilter
|
||||||
// {
|
{
|
||||||
// //在此可以利用 本系统的UerTypeEnum 判断
|
//在此可以利用 本系统的UerTypeEnum 判断
|
||||||
// public bool Authorization(LogDashboardContext context)
|
public bool Authorization(LogDashboardContext context)
|
||||||
// {
|
{
|
||||||
// return context.HttpContext.User.Identity.IsAuthenticated;
|
return context.HttpContext.User.Identity.IsAuthenticated;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//}
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Serilog;
|
||||||
using Serilog.Context;
|
using Serilog.Context;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
using Hangfire;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using Hangfire.Dashboard;
|
||||||
|
using IRaCIS.Core.API._PipelineExtensions.Serilog;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
@ -20,66 +19,8 @@ namespace IRaCIS.Core.API
|
||||||
app.UseSerilogRequestLogging(opts
|
app.UseSerilogRequestLogging(opts
|
||||||
=>
|
=>
|
||||||
{
|
{
|
||||||
|
opts.MessageTemplate = "{TokenUserRealName} {TokenUserTypeShortName} {ClientIp} {LocalIP} {Host} {Protocol} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
||||||
opts.MessageTemplate = "{FullName} {UserType} {UserIp} {Host} {RequestMethod} {RequestPath} {RequestBody} responded {StatusCode} in {Elapsed:0.0000} ms";
|
opts.EnrichDiagnosticContext = SerilogHelper.EnrichFromRequest;
|
||||||
|
|
||||||
opts.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
|
|
||||||
{
|
|
||||||
|
|
||||||
var request = httpContext.Request;
|
|
||||||
|
|
||||||
// Set all the common properties available for every request
|
|
||||||
diagnosticContext.Set("Host", request.Host.Value);
|
|
||||||
|
|
||||||
|
|
||||||
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
|
||||||
if (request.QueryString.HasValue)
|
|
||||||
{
|
|
||||||
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnosticContext.Set("FullName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.FullName)?.Value);
|
|
||||||
|
|
||||||
diagnosticContext.Set("UserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
|
||||||
|
|
||||||
var clientIp = httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault() ??
|
|
||||||
httpContext.Connection.RemoteIpAddress?.ToString();
|
|
||||||
|
|
||||||
if (clientIp.StartsWith("::ffff:"))
|
|
||||||
{
|
|
||||||
clientIp = clientIp.Substring(7); // 移除前缀
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnosticContext.Set("UserIp", clientIp);
|
|
||||||
|
|
||||||
#region 非必要不记录
|
|
||||||
//diagnosticContext.Set("Protocol", request.Protocol);
|
|
||||||
//diagnosticContext.Set("Scheme", request.Scheme);
|
|
||||||
//// Retrieve the IEndpointFeature selected for the request
|
|
||||||
//var endpoint = httpContext.GetEndpoint();
|
|
||||||
//if (endpoint is object) // endpoint != null
|
|
||||||
//{
|
|
||||||
// diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
|
||||||
//}
|
|
||||||
// Set the content-type of the Response at this point
|
|
||||||
//diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region old 未用
|
|
||||||
//这种获取的Ip不准 配置服务才行
|
|
||||||
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
|
||||||
|
|
||||||
//这种方式可以,但是serilog提供了 就不用了
|
|
||||||
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
|
||||||
|
|
||||||
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
|
||||||
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
|
||||||
//diagnosticContext.Set("RequestBody", RequestPayload);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Serilog;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public class SerilogHelper
|
||||||
|
{
|
||||||
|
//public static string RequestPayload = "";
|
||||||
|
|
||||||
|
public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext)
|
||||||
|
{
|
||||||
|
var request = httpContext.Request;
|
||||||
|
|
||||||
|
// Set all the common properties available for every request
|
||||||
|
diagnosticContext.Set("Host", request.Host);
|
||||||
|
|
||||||
|
//这种获取的Ip不准 配置服务才行
|
||||||
|
//diagnosticContext.Set("RequestIP", httpContext.Connection.RemoteIpAddress.ToString());
|
||||||
|
|
||||||
|
//这种方式可以,但是serilog提供了 就不用了
|
||||||
|
//diagnosticContext.Set("TestIP", httpContext.GetUserIp());
|
||||||
|
|
||||||
|
diagnosticContext.Set("Protocol", request.Protocol);
|
||||||
|
diagnosticContext.Set("Scheme", request.Scheme);
|
||||||
|
|
||||||
|
//这种方式不行 读取的body为空字符串 必须在中间件中读取
|
||||||
|
//diagnosticContext.Set("RequestBody", await ReadRequestBody(httpContext.Request));
|
||||||
|
//diagnosticContext.Set("RequestBody", RequestPayload);
|
||||||
|
|
||||||
|
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
||||||
|
if (request.QueryString.HasValue)
|
||||||
|
{
|
||||||
|
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the content-type of the Response at this point
|
||||||
|
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
|
||||||
|
|
||||||
|
diagnosticContext.Set("TokenUserRealName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.RealName)?.Value);
|
||||||
|
|
||||||
|
diagnosticContext.Set("TokenUserTypeShortName", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value);
|
||||||
|
|
||||||
|
// Retrieve the IEndpointFeature selected for the request
|
||||||
|
var endpoint = httpContext.GetEndpoint();
|
||||||
|
if (endpoint is object) // endpoint != null
|
||||||
|
{
|
||||||
|
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
using AutoMapper.EquivalencyExpression;
|
using AutoMapper.EquivalencyExpression;
|
||||||
using IRaCIS.Core.Application.Service;
|
using IRaCIS.Core.Application.Service;
|
||||||
using IRaCIS.Core.Domain.Models;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
|
@ -15,11 +14,6 @@ namespace IRaCIS.Core.API
|
||||||
//AutoMapper.Collection.EntityFrameworkCore
|
//AutoMapper.Collection.EntityFrameworkCore
|
||||||
automapper.AddCollectionMappers();
|
automapper.AddCollectionMappers();
|
||||||
|
|
||||||
|
|
||||||
// 全局忽略 DomainEvents 属性
|
|
||||||
automapper.AddGlobalIgnore(nameof(Entity.DomainEvents));
|
|
||||||
automapper.AddGlobalIgnore(nameof(Entity.DomainCommands));
|
|
||||||
|
|
||||||
#region 会使 IncludeMembers 失效 不能全局使用
|
#region 会使 IncludeMembers 失效 不能全局使用
|
||||||
//mapping an EntityFramework Core DbContext-object.
|
//mapping an EntityFramework Core DbContext-object.
|
||||||
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
//automapper.UseEntityFrameworkCoreModel<IRaCISDBContext>(services);
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
using Autofac;
|
using Autofac;
|
||||||
|
using Autofac.Extras.DynamicProxy;
|
||||||
|
using IRaCIS.Core.Application;
|
||||||
|
using IRaCIS.Core.Application.BackGroundJob;
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Panda.DynamicWebApi;
|
using Panda.DynamicWebApi;
|
||||||
|
@ -7,11 +10,12 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using IRaCIS.Core.Domain.Models;
|
using IRaCIS.Core.Domain.Models;
|
||||||
using IRaCIS.Core.Domain.Share;
|
using IRaCIS.Core.Domain.Share;
|
||||||
using IRaCIS.Core.Application.Service;
|
using MediatR;
|
||||||
|
using IRaCIS.Application.Services;
|
||||||
|
using IRaCIS.Application.Interfaces;
|
||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using IRaCIS.Core.SCP.Service;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
// ReSharper disable once IdentifierTypo
|
// ReSharper disable once IdentifierTypo
|
||||||
public class AutofacModuleSetup : Autofac.Module
|
public class AutofacModuleSetup : Autofac.Module
|
||||||
|
@ -36,18 +40,27 @@ namespace IRaCIS.Core.SCP
|
||||||
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
.Where(type => typeof(IDynamicWebApi).IsAssignableFrom(type))
|
||||||
.PropertiesAutowired();
|
.PropertiesAutowired();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + typeof(BaseService).Assembly.GetName().Name+".dll");
|
|
||||||
|
|
||||||
|
|
||||||
|
Assembly application = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "IRaCIS.Core.Application.dll");
|
||||||
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
||||||
.PropertiesAutowired().AsImplementedInterfaces();
|
.PropertiesAutowired().AsImplementedInterfaces().EnableClassInterceptors();
|
||||||
|
|
||||||
|
|
||||||
//containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
|
||||||
//containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
|
containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
||||||
|
containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
//注册hangfire任务 依赖注入
|
||||||
|
containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
using IRaCIS.Core.Application.BusinessFilter;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using IRaCIS.Core.Application.Service.BusinessFilter;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Panda.DynamicWebApi;
|
using Panda.DynamicWebApi;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class WebApiSetup
|
public static class DynamicWebApiSetup
|
||||||
{
|
{
|
||||||
//20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况
|
//20210910 避免冗余的控制器层代码编写,仅仅包了一层前后台定义的格式 这里采用动态webAPi+IResultFilter 替代大部分情况
|
||||||
public static void AddDynamicWebApiSetup(this IServiceCollection services)
|
public static void AddDynamicWebApiSetup(this IServiceCollection services)
|
||||||
|
@ -25,38 +20,5 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddMasaMinimalAPiSetUp(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddMasaMinimalAPIs(options =>
|
|
||||||
{
|
|
||||||
options.Prefix = "";//自定义前缀 默认是api
|
|
||||||
options.Version = ""; //默认是V1
|
|
||||||
options.AutoAppendId = false; //路由是否自动附加参数Id 默认是true
|
|
||||||
options.PluralizeServiceName = false; //服务名称是否启用复数
|
|
||||||
|
|
||||||
//options.Assemblies = new List<Assembly>() { typeof(UserSiteSurveySubmitedEventConsumer).Assembly };
|
|
||||||
|
|
||||||
options.GetPrefixes = new List<string> { "Get", "Select", "Find" };
|
|
||||||
options.PostPrefixes = new List<string> { "Post", "Add", "Create", "List" };
|
|
||||||
options.PutPrefixes = new List<string> { "Put", "Update" };
|
|
||||||
options.DeletePrefixes = new List<string> { "Delete", "Remove" };
|
|
||||||
|
|
||||||
options.RouteHandlerBuilder = t => {
|
|
||||||
t.RequireAuthorization()
|
|
||||||
.AddEndpointFilter<LimitUserRequestAuthorizationEndpointFilter>()
|
|
||||||
.AddEndpointFilter<TrialGlobalLimitEndpointFilter>()
|
|
||||||
//.AddEndpointFilter<ModelValidationEndpointFilter>()
|
|
||||||
.AddEndpointFilter<UnifiedApiResultEndpointFilter>()
|
|
||||||
.WithGroupName("Institution").DisableAntiforgery();
|
|
||||||
};
|
|
||||||
options.MapHttpMethodsForUnmatched = new string[] { "Post" };
|
|
||||||
options.DisableTrimMethodPrefix = true; //禁用去除方法前缀
|
|
||||||
options.DisableAutoMapRoute = false;//可通过配置true禁用全局自动路由映射或者删除此配置以启用全局自动路由映射
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,102 +1,67 @@
|
||||||
using EntityFramework.Exceptions.SqlServer;
|
using Hangfire.SqlServer;
|
||||||
using IRaCIS.Core.Application.Triggers;
|
using IRaCIS.Core.Application.Triggers;
|
||||||
using IRaCIS.Core.Application.Triggers.AfterSaveTrigger;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
using IRaCIS.Core.Infra.EFCore;
|
||||||
using IRaCIS.Core.Infra.EFCore.Interceptor;
|
|
||||||
using Medallion.Threading;
|
using Medallion.Threading;
|
||||||
using Medallion.Threading.SqlServer;
|
using Medallion.Threading.SqlServer;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using StackExchange.Redis;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static class EFSetup
|
public static class EFSetup
|
||||||
{
|
{
|
||||||
public static void AddEFSetup(this IServiceCollection services, IConfiguration configuration, string envName)
|
public static void AddEFSetup( this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
|
//services.AddScoped<DbContext, IRaCISDBContext>();
|
||||||
services.AddHttpContextAccessor();
|
|
||||||
services.AddScoped<IUserInfo, UserInfo>();
|
|
||||||
services.AddScoped<ISaveChangesInterceptor, AuditEntityInterceptor>();
|
|
||||||
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
|
|
||||||
|
|
||||||
|
|
||||||
// First, register a pooling context factory as a Singleton service, as usual:
|
|
||||||
|
|
||||||
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
//这个注入没有成功--注入是没问题的,构造函数也只是支持参数就好,错在注入的地方不能写DbContext
|
||||||
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
//Web程序中通过重用池中DbContext实例可提高高并发场景下的吞吐量, 这在概念上类似于ADO.NET Provider原生的连接池操作方式,具有节省DbContext实例化成本的优点
|
||||||
services.AddDbContext<IRaCISDBContext>((sp, options) =>
|
services.AddDbContext<IRaCISDBContext>(options =>
|
||||||
{
|
{
|
||||||
|
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value,
|
||||||
// 在控制台
|
contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
||||||
//public static readonly ILoggerFactory MyLoggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); });
|
|
||||||
var logFactory = LoggerFactory.Create(builder => { builder.AddDebug(); });
|
|
||||||
|
|
||||||
var dbType = configuration.GetSection("ConnectionStrings:Db_Type").Value;
|
|
||||||
if (!string.IsNullOrWhiteSpace(dbType) && dbType == "pgsql")
|
|
||||||
{
|
|
||||||
options.UseNpgsql(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
options.UseSqlServer(configuration.GetSection("ConnectionStrings:RemoteNew").Value, contextOptionsBuilder => contextOptionsBuilder.EnableRetryOnFailure());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//迁移的时候,不生成外键
|
|
||||||
options.ReplaceService<IMigrationsSqlGenerator, NoForeignKeyMigrationsSqlGenerator>();
|
|
||||||
|
|
||||||
options.UseLoggerFactory(logFactory);
|
|
||||||
|
|
||||||
options.UseExceptionProcessor();
|
|
||||||
|
|
||||||
options.EnableSensitiveDataLogging();
|
options.EnableSensitiveDataLogging();
|
||||||
|
|
||||||
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
options.AddInterceptors(new QueryWithNoLockDbCommandInterceptor());
|
||||||
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
|
|
||||||
|
|
||||||
options.UseProjectables();
|
options.UseProjectables();
|
||||||
|
|
||||||
|
//options.AddInterceptors(new AuditingInterceptor(configuration.GetSection("ConnectionStrings:RemoteNew").Value));
|
||||||
|
|
||||||
|
//options.UseTriggers(triggerOptions => triggerOptions.AddTrigger<SubjectVisitImageDateTrigger>());
|
||||||
|
|
||||||
//options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers(typeof(SubjectVisitTrigger).Assembly));
|
//options.UseTriggers(triggerOptions => triggerOptions.AddAssemblyTriggers(typeof(SubjectVisitTrigger).Assembly));
|
||||||
|
|
||||||
|
|
||||||
options.UseTriggers(triggerOptions =>
|
options.UseTriggers(triggerOptions =>
|
||||||
{
|
{
|
||||||
triggerOptions.AddTrigger<SubjectTrigger>();
|
triggerOptions.AddTrigger<AddSubjectTrigger>();
|
||||||
triggerOptions.AddTrigger<ChallengeStateTrigger>();
|
triggerOptions.AddTrigger<ChallengeStateTrigger>();
|
||||||
|
triggerOptions.AddTrigger<SubjectStateTrigger>();
|
||||||
|
triggerOptions.AddTrigger<AddCRCCliniaclDataTrigger>();
|
||||||
|
triggerOptions.AddTrigger<SubjectVisitCheckPassedTrigger>();
|
||||||
|
triggerOptions.AddTrigger<SubjectVisitFinalVisitTrigger>();
|
||||||
triggerOptions.AddTrigger<SubjectVisitTrigger>();
|
triggerOptions.AddTrigger<SubjectVisitTrigger>();
|
||||||
triggerOptions.AddTrigger<SubjectVisitScanDateTrigger>();
|
|
||||||
triggerOptions.AddTrigger<TrialCriterionSignTrigger>();
|
triggerOptions.AddTrigger<TrialCriterionSignTrigger>();
|
||||||
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
|
triggerOptions.AddTrigger<TableQuestionRowTrigger>();
|
||||||
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
//triggerOptions.AddTrigger<AddlTrialUserTrigger>();
|
||||||
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
triggerOptions.AddTrigger<VisitTaskIsFrontTaskNeedSignButNotSignTrigger>();
|
||||||
triggerOptions.AddTrigger<JudgeVisitTaskTrigger>();
|
triggerOptions.AddTrigger<VisitTaskIbeforeTrigger>();
|
||||||
|
|
||||||
triggerOptions.AddTrigger<UserLogTrigger>();
|
triggerOptions.AddTrigger<UserLogTrigger>();
|
||||||
|
|
||||||
triggerOptions.AddTrigger<UserAddTrigger>();
|
|
||||||
|
|
||||||
triggerOptions.AddTrigger<UserLogAfterTrigger>();
|
|
||||||
|
|
||||||
triggerOptions.AddTrigger<IdenttiyUserRoleInfoTrigger>();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register an additional context factory as a Scoped service, which gets a pooled context from the Singleton factory we registered above,
|
|
||||||
//services.AddScoped<IRaCISDBScopedFactory>();
|
|
||||||
|
|
||||||
//// Finally, arrange for a context to get injected from our Scoped factory:
|
|
||||||
//services.AddScoped(sp => sp.GetRequiredService<IRaCISDBScopedFactory>().CreateDbContext());
|
|
||||||
|
|
||||||
//注意区分 easy caching 也有 IDistributedLockProvider
|
//注意区分 easy caching 也有 IDistributedLockProvider
|
||||||
services.AddSingleton<IDistributedLockProvider>(sp =>
|
services.AddSingleton<IDistributedLockProvider>(sp =>
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
using EasyCaching.Core;
|
||||||
|
using EasyCaching.Core.Configurations;
|
||||||
|
using EasyCaching.Interceptor.Castle;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class EasyCachingSetup
|
||||||
|
{
|
||||||
|
public static void AddEasyCachingSetup(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
services.AddEasyCaching(options =>
|
||||||
|
{
|
||||||
|
options.UseInMemory();
|
||||||
|
|
||||||
|
//options.UseRedis(configuration, EasyCachingConstValue.DefaultRedisName).WithMessagePack(EasyCachingConstValue.DefaultRedisName);
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
//services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultRedisName);
|
||||||
|
|
||||||
|
services.ConfigureCastleInterceptor(options => options.CacheProviderName = EasyCachingConstValue.DefaultInMemoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
using AspNetCoreRateLimit;
|
using AspNetCoreRateLimit;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class JsonConfigSetup
|
||||||
|
{
|
||||||
|
public static void AddJsonConfigSetup(this IServiceCollection services, IConfiguration configuration)
|
||||||
|
{
|
||||||
|
|
||||||
|
services.Configure<ServiceVerifyConfigOption>(configuration.GetSection("BasicSystemConfig"));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,25 +1,26 @@
|
||||||
|
|
||||||
//using LogDashboard;
|
using LogDashboard;
|
||||||
//using Microsoft.Extensions.DependencyInjection;
|
using LogDashboard.Authorization.Filters;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
//namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
//{
|
{
|
||||||
// public static class LogDashboardSetup
|
public static class LogDashboardSetup
|
||||||
// {
|
{
|
||||||
// public static void AddLogDashboardSetup(this IServiceCollection services)
|
public static void AddLogDashboardSetup(this IServiceCollection services)
|
||||||
// {
|
{
|
||||||
// //IIS 配置虚拟路径部署,会出现IIS静态文件404
|
//IIS 配置虚拟路径部署,会出现IIS静态文件404
|
||||||
// services.AddLogDashboard(opt =>
|
services.AddLogDashboard(opt =>
|
||||||
// {
|
{
|
||||||
// //opt.PathMatch = "/api/LogDashboard";
|
//opt.PathMatch = "/api/LogDashboard";
|
||||||
// opt.PathMatch = "/LogDashboard";
|
opt.PathMatch = "/LogDashboard";
|
||||||
|
|
||||||
// //opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
//opt.AddAuthorizationFilter(new LogDashboardBasicAuthFilter("admin", "zhizhun2018"));
|
||||||
|
|
||||||
// //opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
//opt.AddAuthorizationFilter(new LogDashBoardAuthFilter());
|
||||||
|
|
||||||
// });
|
});
|
||||||
|
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
|
|
|
@ -1,120 +0,0 @@
|
||||||
using IRaCIS.Core.API.HostService;
|
|
||||||
using IRaCIS.Core.Application.MassTransit.Consumer;
|
|
||||||
using IRaCIS.Core.Domain.BaseModel;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using MassTransit;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
|
||||||
{
|
|
||||||
public static class MassTransitSetup
|
|
||||||
{
|
|
||||||
public static void AddMassTransitSetup(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
|
|
||||||
#region MassTransit
|
|
||||||
//masstransit组件 也支持MediatR 中介者模式,但是支持分布式,考虑后续,所以在次替代MediatR
|
|
||||||
//参考链接:https://masstransit.io/documentation/concepts/mediator#scoped-mediator
|
|
||||||
services.AddMediator(cfg =>
|
|
||||||
{
|
|
||||||
cfg.AddConsumers(typeof(UserSiteSurveySubmitedEventConsumer).Assembly);
|
|
||||||
|
|
||||||
//cfg.AddConsumer<ConsistencyCheckConsumer>();
|
|
||||||
//cfg.AddConsumer<AddSubjectTriggerConsumer>();
|
|
||||||
//cfg.AddConsumer<AddSubjectTriggerConsumer2>();
|
|
||||||
//cfg.ConfigureMediator((context, cfg) => cfg.UseHttpContextScopeFilter(context));
|
|
||||||
});
|
|
||||||
|
|
||||||
//添加 MassTransit 和 InMemory 传输
|
|
||||||
services.AddMassTransit(cfg =>
|
|
||||||
{
|
|
||||||
cfg.AddConsumers(typeof(UserSiteSurveySubmitedEventConsumer).Assembly);
|
|
||||||
|
|
||||||
cfg.AddPublishMessageScheduler();
|
|
||||||
cfg.AddHangfireConsumers();
|
|
||||||
|
|
||||||
// 使用 InMemory 作为消息传递机制
|
|
||||||
cfg.UsingInMemory((context, cfg) =>
|
|
||||||
{
|
|
||||||
cfg.UsePublishMessageScheduler();
|
|
||||||
|
|
||||||
|
|
||||||
cfg.UseConsumeFilter(typeof(ConsumeExceptionFilter<>), context,
|
|
||||||
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
|
||||||
|
|
||||||
cfg.UseConsumeFilter(typeof(CultureInfoFilter<>), context,
|
|
||||||
x => x.Include(type => type.IsAssignableTo(typeof(DomainEvent))));
|
|
||||||
|
|
||||||
cfg.ConfigureEndpoints(context); // 自动配置所有消费者的端点
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
#region rabitmq obsolute
|
|
||||||
|
|
||||||
//cfg.UsingRabbitMq((context, cfg) =>
|
|
||||||
//{
|
|
||||||
// cfg.UsePublishMessageScheduler();
|
|
||||||
|
|
||||||
// cfg.Host(
|
|
||||||
// host: "106.14.89.110",
|
|
||||||
// port: 5672,
|
|
||||||
// virtualHost: "/",
|
|
||||||
// configure: hostConfig =>
|
|
||||||
// {
|
|
||||||
// hostConfig.Username("rabbitmq");
|
|
||||||
// hostConfig.Password("rabbitmq");
|
|
||||||
// });
|
|
||||||
|
|
||||||
// cfg.ConfigureEndpoints(context);
|
|
||||||
//});
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Outbox obsolute
|
|
||||||
|
|
||||||
//cfg.AddConfigureEndpointsCallback((context, name, cfg) =>
|
|
||||||
//{
|
|
||||||
// cfg.UseEntityFrameworkOutbox<IRaCISDBContext>(context);
|
|
||||||
|
|
||||||
// //cfg.UseDelayedRedelivery(r => r.Intervals(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(15), TimeSpan.FromMinutes(30)));
|
|
||||||
|
|
||||||
// //// 全局重试策略:重试 3 次,每次延迟 5 秒
|
|
||||||
// //cfg.UseMessageRetry(retryConfig =>
|
|
||||||
// //{
|
|
||||||
// // retryConfig.Interval(3, TimeSpan.FromSeconds(10));
|
|
||||||
// //});
|
|
||||||
//});
|
|
||||||
|
|
||||||
//cfg.AddEntityFrameworkOutbox<IRaCISDBContext>(o =>
|
|
||||||
//{
|
|
||||||
// o.UseSqlServer();
|
|
||||||
// o.UseBusOutbox();
|
|
||||||
//});
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//services.AddOptions<MassTransitHostOptions>()
|
|
||||||
// .Configure(options =>
|
|
||||||
// {
|
|
||||||
// options.WaitUntilStarted = true;
|
|
||||||
// options.StartTimeout = TimeSpan.FromMinutes(1);
|
|
||||||
// options.StopTimeout = TimeSpan.FromMinutes(1);
|
|
||||||
// });
|
|
||||||
|
|
||||||
//services.AddOptions<HostOptions>()
|
|
||||||
// .Configure(options => options.ShutdownTimeout = TimeSpan.FromMinutes(1));
|
|
||||||
|
|
||||||
//services.AddHostedService<RecurringJobConfigurationService>();
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,6 +2,7 @@
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
|
using StackExchange.Redis;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
|
@ -10,41 +11,19 @@ namespace IRaCIS.Core.API
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 序列化,反序列化的时候,处理时间 时区转换
|
/// 序列化,反序列化的时候,处理时间 时区转换
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class JSONTimeZoneConverter(IHttpContextAccessor _httpContextAccessor) : DateTimeConverterBase
|
public class JSONTimeZoneConverter : DateTimeConverterBase
|
||||||
{
|
{
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
private TimeZoneInfo _clientTimeZone;
|
private readonly TimeZoneInfo _clientTimeZone;
|
||||||
|
public JSONTimeZoneConverter(IHttpContextAccessor httpContextAccessor)
|
||||||
private string _dateFormat;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public override bool CanConvert(Type objectType)
|
|
||||||
{
|
{
|
||||||
#region 设置语言格式化方式,放在构造函数里面做不到动态切换
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
|
||||||
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
if (!isEn_US)
|
|
||||||
{
|
|
||||||
// Chinese date format
|
|
||||||
_dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default or English date format
|
|
||||||
//_dateFormat = "MM/dd/yyyy HH:mm:ss";
|
|
||||||
|
|
||||||
_dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 获取当前请求的客户端时区
|
|
||||||
|
|
||||||
|
//默认是UTC
|
||||||
//var timeZoneId = "Etc/UTC";
|
//var timeZoneId = "Etc/UTC";
|
||||||
var timeZoneId = "Asia/Shanghai";
|
var timeZoneId = "Asia/Shanghai";
|
||||||
|
|
||||||
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
var timeZoneIdHeader = _httpContextAccessor?.HttpContext?.Request?.Headers["TimeZoneId"];
|
||||||
|
|
||||||
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
if (timeZoneIdHeader is not null && !string.IsNullOrEmpty(timeZoneIdHeader.Value))
|
||||||
|
@ -54,12 +33,12 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
_clientTimeZone = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId);
|
||||||
|
|
||||||
#endregion
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
// 仅支持 DateTime 类型的转换
|
// 仅支持 DateTime 类型的转换
|
||||||
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
return objectType == typeof(DateTime)|| objectType == typeof(DateTime?);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
@ -112,10 +91,7 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
//第一个参数默认使用系统本地时区 也就是应用服务器的时区
|
||||||
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
DateTime clientZoneTime = TimeZoneInfo.ConvertTime(nullableDateTime.Value, _clientTimeZone);
|
||||||
|
writer.WriteValue(clientZoneTime);
|
||||||
//writer.WriteValue(clientZoneTime);
|
|
||||||
|
|
||||||
writer.WriteValue(clientZoneTime.ToString(_dateFormat));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -124,75 +100,4 @@ namespace IRaCIS.Core.API
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 废弃
|
|
||||||
|
|
||||||
public class MyDateTimeConverter : JsonConverter<DateTime>
|
|
||||||
{
|
|
||||||
public override DateTime ReadJson(JsonReader reader, Type objectType, DateTime existingValue, bool hasExistingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
return reader.ReadAsDateTime().Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, DateTime value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
string dateFormat;
|
|
||||||
if (!isEn_US)
|
|
||||||
{
|
|
||||||
// Chinese date format
|
|
||||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default or English date format
|
|
||||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteValue(value.ToString(dateFormat));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MyNullableDateTimeConverter : JsonConverter<DateTime?>
|
|
||||||
{
|
|
||||||
public override DateTime? ReadJson(JsonReader reader, Type objectType, DateTime? existingValue, bool hasExistingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var val = reader.ReadAsDateTime();
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, DateTime? value, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
|
|
||||||
|
|
||||||
string dateFormat;
|
|
||||||
if (!isEn_US)
|
|
||||||
{
|
|
||||||
// Chinese date format
|
|
||||||
dateFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Default or English date format
|
|
||||||
dateFormat = "MM/dd/yyyy HH:mm:ss";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.HasValue)
|
|
||||||
{
|
|
||||||
writer.WriteValue(value.Value.ToString(dateFormat));
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writer.WriteValue(default(DateTime?));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ using IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson;
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
|
@ -11,44 +12,34 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
public static void AddNewtonsoftJsonSetup(this IMvcBuilder builder, IServiceCollection services)
|
||||||
{
|
{
|
||||||
|
|
||||||
services.AddHttpContextAccessor();
|
services.AddHttpContextAccessor();
|
||||||
services.AddScoped<JSONTimeZoneConverter>();
|
services.AddScoped<JSONTimeZoneConverter>();
|
||||||
services.AddScoped<ObjectStorePathConvert>();
|
services.AddScoped<CustomStringConverter>();
|
||||||
services.AddScoped<IOSSService,OSSService>();
|
services.AddScoped<IOSSService,OSSService>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
builder.AddNewtonsoftJson(options =>
|
builder.AddNewtonsoftJson(options =>
|
||||||
{
|
{
|
||||||
|
//options.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.Objects;
|
||||||
// 忽略循环引用
|
// 忽略循环引用
|
||||||
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
|
||||||
|
//options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
|
||||||
|
|
||||||
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
//处理返回给前端 可空类型 给出默认值 比如in? 为null 设置 默认值0
|
||||||
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver();
|
options.SerializerSettings.ContractResolver = new NullToEmptyStringResolver(); //new DefaultContractResolver();// new NullToEmptyStringResolver();
|
||||||
|
// 设置时间格式
|
||||||
// 设置时间格式 isEn_US? "MM/dd/yyyy HH:mm:ss" :
|
options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
||||||
//options.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
|
|
||||||
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind;
|
||||||
|
|
||||||
#region 废弃
|
//options.SerializerSettings.Converters.Add(new JSONCustomDateConverter()) ;
|
||||||
//大驼峰
|
|
||||||
//options.SerializerSettings.ContractResolver = new DefaultContractResolver();
|
|
||||||
//小驼峰
|
|
||||||
//options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//二者只能取其一
|
|
||||||
//options.SerializerSettings.Converters.Add(new MyDateTimeConverter());
|
|
||||||
//options.SerializerSettings.Converters.Add(new MyNullableDateTimeConverter());
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
//必须放在后面
|
|
||||||
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<JSONTimeZoneConverter>());
|
||||||
|
|
||||||
|
//options.SerializerSettings.Converters.Add(services.BuildServiceProvider().GetService<CustomStringConverter>());
|
||||||
|
|
||||||
|
|
||||||
|
//IsoDateTimeConverter
|
||||||
|
//options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore;
|
||||||
|
|
||||||
})
|
})
|
||||||
.AddControllersAsServices()//动态webApi属性注入需要
|
.AddControllersAsServices()//动态webApi属性注入需要
|
||||||
|
@ -56,7 +47,6 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
o.SuppressModelStateInvalidFilter = true; //自己写验证
|
||||||
|
|
||||||
#region 废弃验证
|
|
||||||
////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter
|
////这里是自定义验证结果和返回状态码 因为这里是在[ApiController]控制器层校验,动态webApi的不会校验 所以需要单独写一个Filter
|
||||||
//o.InvalidModelStateResponseFactory = (context) =>
|
//o.InvalidModelStateResponseFactory = (context) =>
|
||||||
//{
|
//{
|
||||||
|
@ -67,7 +57,7 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
//return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error)));
|
//return new JsonResult(ResponseOutput.NotOk("The inputs supplied to the API are invalid. " + JsonConvert.SerializeObject( error)));
|
||||||
//};
|
//};
|
||||||
#endregion
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,35 +1,37 @@
|
||||||
using IRaCIS.Core.Infrastructure.NewtonsoftJson;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// LowerCamelCaseJsonAttribute 可以设置类小写返回给前端
|
|
||||||
/// </summary>
|
|
||||||
public class NullToEmptyStringResolver : DefaultContractResolver
|
public class NullToEmptyStringResolver : DefaultContractResolver
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 创建属性
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">类型</param>
|
||||||
|
/// <param name="memberSerialization">序列化成员</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
//protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
|
//{
|
||||||
|
// IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
|
||||||
|
// foreach (var jsonProperty in properties)
|
||||||
|
// {
|
||||||
|
// jsonProperty.DefaultValue = new NullToEmptyStringValueProvider(jsonProperty);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return properties;
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
|
||||||
{
|
{
|
||||||
// 检查类是否有 LowerCamelCaseJsonAttribute 标记 有的话,属性名小写
|
|
||||||
if (type.GetCustomAttribute<LowerCamelCaseJsonAttribute>() != null)
|
|
||||||
{
|
|
||||||
base.NamingStrategy = new LowerCamelCaseNamingStrategy();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
base.NamingStrategy = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
|
||||||
|
|
||||||
|
|
||||||
var list= type.GetProperties()
|
var list= type.GetProperties()
|
||||||
.Select(p =>
|
.Select(p =>
|
||||||
{
|
{
|
||||||
|
@ -49,37 +51,4 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace IRaCIS.Core.SCP
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
|
||||||
public class NullToEmptyStringValueProvider : IValueProvider
|
public class NullToEmptyStringValueProvider : IValueProvider
|
||||||
|
@ -28,7 +28,7 @@ namespace IRaCIS.Core.SCP
|
||||||
if(_MemberInfo.PropertyType == typeof(string))
|
if(_MemberInfo.PropertyType == typeof(string))
|
||||||
{
|
{
|
||||||
//去掉前后空格
|
//去掉前后空格
|
||||||
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value.ToString().Trim());
|
_MemberInfo.SetValue(target, value==null?string.Empty: value.ToString()==string.Empty? value:value/*.ToString().Trim()*/);
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
|
@ -1,18 +1,23 @@
|
||||||
using IRaCIS.Core.Application.Helper;
|
using IRaCIS.Core.Application.Helper;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Converters;
|
||||||
|
using Newtonsoft.Json.Serialization;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
namespace IRaCIS.Core.API._ServiceExtensions.NewtonsoftJson
|
||||||
{
|
{
|
||||||
public class ObjectStorePathConvert : JsonConverter<string>
|
public class CustomStringConverter : JsonConverter<string>
|
||||||
{
|
{
|
||||||
|
//private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IOSSService _oSSService;
|
private readonly IOSSService _oSSService;
|
||||||
|
|
||||||
// 构造函数
|
// 构造函数
|
||||||
public ObjectStorePathConvert(IOSSService oSSService)
|
public CustomStringConverter(/*IHttpContextAccessor httpContextAccessor*/ IOSSService oSSService)
|
||||||
{
|
{
|
||||||
|
//_httpContextAccessor = httpContextAccessor;
|
||||||
_oSSService = oSSService;
|
_oSSService = oSSService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.ResponseCompression;
|
using Microsoft.AspNetCore.ResponseCompression;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.IO.Compression;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
@ -11,20 +10,9 @@ namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
services.AddResponseCompression(options =>
|
services.AddResponseCompression(options =>
|
||||||
{
|
{
|
||||||
options.EnableForHttps = true;
|
|
||||||
options.Providers.Add<BrotliCompressionProvider>();
|
options.Providers.Add<BrotliCompressionProvider>();
|
||||||
options.Providers.Add<GzipCompressionProvider>();
|
options.Providers.Add<GzipCompressionProvider>();
|
||||||
});
|
});
|
||||||
|
|
||||||
services.Configure<BrotliCompressionProviderOptions>(options =>
|
|
||||||
{
|
|
||||||
options.Level = CompressionLevel.Optimal;
|
|
||||||
});
|
|
||||||
|
|
||||||
services.Configure<GzipCompressionProviderOptions>(options =>
|
|
||||||
{
|
|
||||||
options.Level = CompressionLevel.Optimal;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Configuration;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class EnricherExtensions
|
||||||
|
{
|
||||||
|
public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
if (enrich == null)
|
||||||
|
throw new ArgumentNullException(nameof(enrich));
|
||||||
|
|
||||||
|
return enrich.With(new HttpContextEnricher(serviceProvider));
|
||||||
|
}
|
||||||
|
public static LoggerConfiguration WithHttpContextInfo(this LoggerEnrichmentConfiguration enrich, IServiceProvider serviceProvider, Action<LogEvent, ILogEventPropertyFactory, HttpContext> enrichAction)
|
||||||
|
{
|
||||||
|
if (enrich == null)
|
||||||
|
throw new ArgumentNullException(nameof(enrich));
|
||||||
|
|
||||||
|
return enrich.With(new HttpContextEnricher(serviceProvider, enrichAction));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
using IRaCIS.Core.Infrastructure;
|
||||||
|
using IRaCIS.Core.Infrastructure.Extention;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using IRaCIS.Core.Domain.Share;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public class HttpContextEnricher : ILogEventEnricher
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly Action<LogEvent, ILogEventPropertyFactory, HttpContext> _enrichAction;
|
||||||
|
|
||||||
|
public HttpContextEnricher(IServiceProvider serviceProvider) : this(serviceProvider, null)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public HttpContextEnricher(IServiceProvider serviceProvider, Action<LogEvent, ILogEventPropertyFactory, HttpContext> enrichAction)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
if (enrichAction == null)
|
||||||
|
{
|
||||||
|
_enrichAction = (logEvent, propertyFactory, httpContext) =>
|
||||||
|
{
|
||||||
|
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", httpContext.Connection.RemoteIpAddress.ToString()));
|
||||||
|
|
||||||
|
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("LocalIP", httpContext.Connection.LocalIpAddress.MapToIPv4().ToString()));
|
||||||
|
|
||||||
|
|
||||||
|
//这样读取没用
|
||||||
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestBody", await ReadRequestBody(httpContext.Request)));
|
||||||
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("RequestIP", IPHelper.GetIP(httpContext.Request) ));
|
||||||
|
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserRealName", httpContext?.User?.FindFirst(ClaimAttributes.RealName)?.Value));
|
||||||
|
logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("TokenUserType", httpContext?.User?.FindFirst(JwtIRaCISClaimType.UserTypeShortName)?.Value));
|
||||||
|
|
||||||
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("Referer", httpContext.Request.Headers["Referer"].ToString()));
|
||||||
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_path", httpContext.Request.Path));
|
||||||
|
//logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("request_method", httpContext.Request.Method));
|
||||||
|
//if (httpContext.Response.HasStarted)
|
||||||
|
//{
|
||||||
|
// logEvent.AddPropertyIfAbsent(propertyFactory.CreateProperty("response_status", httpContext.Response.StatusCode));
|
||||||
|
//}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_enrichAction = enrichAction;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||||
|
{
|
||||||
|
var httpContext = _serviceProvider.GetService<IHttpContextAccessor>()?.HttpContext;
|
||||||
|
if (null != httpContext)
|
||||||
|
{
|
||||||
|
_enrichAction.Invoke(logEvent, propertyFactory, httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ReadRequestBody(HttpRequest request)
|
||||||
|
{
|
||||||
|
// Ensure the request's body can be read multiple times (for the next middlewares in the pipeline).
|
||||||
|
request.EnableBuffering();
|
||||||
|
|
||||||
|
using var streamReader = new StreamReader(request.Body, leaveOpen: true);
|
||||||
|
var requestBody = await streamReader.ReadToEndAsync();
|
||||||
|
|
||||||
|
// Reset the request's body stream position for next middleware in the pipeline.
|
||||||
|
request.Body.Position = 0;
|
||||||
|
return requestBody==null?String.Empty: requestBody.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ReadResponseBody(HttpResponse response)
|
||||||
|
{
|
||||||
|
response.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
string responseBody = await new StreamReader(response.Body).ReadToEndAsync();
|
||||||
|
response.Body.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
return $"{responseBody}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Serilog;
|
||||||
|
using Serilog.Events;
|
||||||
|
//using Serilog.Sinks.Email;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public class SerilogExtension
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void AddSerilogSetup(string environment, IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
|
||||||
|
var config = 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()
|
||||||
|
.Filter.ByExcluding(logEvent =>logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
||||||
|
|
||||||
|
//控制台 方便调试 问题 我们显示记录日志 时 获取上下文的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}");
|
||||||
|
|
||||||
|
//.WriteTo.MSSqlServer("Data Source=DESKTOP-4TU9A6M;Initial Catalog=CoreFrame;User ID=sa;Password=123456", "logs", autoCreateSqlTable: true, restrictedToMinimumLevel: LogEventLevel.Information)//从左至右四个参数分别是数据库连接字符串、表名、如果表不存在是否创建、最低等级。Serilog会默认创建一些列。
|
||||||
|
|
||||||
|
//if (environment == "Production")
|
||||||
|
//{
|
||||||
|
// config.WriteTo.Email(new EmailConnectionInfo()
|
||||||
|
// {
|
||||||
|
// EmailSubject = "系统警告,请速速查看!",//邮件标题
|
||||||
|
// FromEmail = "test@extimaging.com",//发件人邮箱
|
||||||
|
// MailServer = "smtp.qiye.aliyun.com",//smtp服务器地址
|
||||||
|
// NetworkCredentials = new NetworkCredential("test@extimaging.com", "SHzyyl2021"),//两个参数分别是发件人邮箱与客户端授权码
|
||||||
|
// Port = 465,//端口号
|
||||||
|
// ToEmail = "872297557@qq.com"//收件人
|
||||||
|
// }, restrictedToMinimumLevel: LogEventLevel.Error,
|
||||||
|
// outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [ {Level} {ClientIp} {ClientAgent} {TokenUserRealName} {TokenUserType} ] || [path: {RequestPath} arguments: {RequestBody}] {SourceContext:l} || {Message} || {Exception} ||end {NewLine})");
|
||||||
|
//}
|
||||||
|
|
||||||
|
//扩展方法 获取上下文的ip 用户名 用户类型
|
||||||
|
Log.Logger = config.Enrich.WithHttpContextInfo(serviceProvider).CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,76 +0,0 @@
|
||||||
using Amazon.SecurityToken.Model;
|
|
||||||
using DocumentFormat.OpenXml.Bibliography;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using Microsoft.Extensions.Configuration.Json;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
using Serilog.Formatting.Compact;
|
|
||||||
using Serilog.Formatting.Display;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
|
||||||
{
|
|
||||||
public class SerilogExtension
|
|
||||||
{
|
|
||||||
|
|
||||||
public static void AddSerilogSetup(string environment)
|
|
||||||
{
|
|
||||||
|
|
||||||
var config = 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("Microsoft.AspNetCore.Hosting", LogEventLevel.Warning)
|
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Mvc", LogEventLevel.Warning)
|
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore.Routing", LogEventLevel.Warning)
|
|
||||||
.MinimumLevel.Override("Hangfire", LogEventLevel.Warning)
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.Filter.ByExcluding(logEvent => logEvent.Properties.ContainsKey("RequestPath") && logEvent.Properties["RequestPath"].ToString().Contains("/health"))
|
|
||||||
.WriteTo.Console()
|
|
||||||
.WriteTo.File($"{AppContext.BaseDirectory}Serilogs/.log", rollingInterval: RollingInterval.Day,retainedFileCountLimit:60);
|
|
||||||
|
|
||||||
#region 根据环境配置是否打开错误发送邮件通知
|
|
||||||
|
|
||||||
//读取配置文件
|
|
||||||
var configuration = new ConfigurationBuilder().Add(new JsonConfigurationSource { Path = $"appsettings.{environment}.json", ReloadOnChange = true }).Build();
|
|
||||||
|
|
||||||
// 手动绑定配置
|
|
||||||
var emailConfig = new SystemEmailSendConfig();
|
|
||||||
configuration.GetSection("SystemEmailSendConfig").Bind(emailConfig);
|
|
||||||
|
|
||||||
if (emailConfig.IsOpenErrorNoticeEmail)
|
|
||||||
{
|
|
||||||
config.WriteTo.Email(options: new Serilog.Sinks.Email.EmailSinkOptions()
|
|
||||||
{
|
|
||||||
From = emailConfig.FromEmail,
|
|
||||||
To = emailConfig.ErrorNoticeEmailList,
|
|
||||||
Host = emailConfig.Host,
|
|
||||||
Port = emailConfig.Port,
|
|
||||||
Subject = new MessageTemplateTextFormatter("Log Alert - 系统发生了异常,请核查"),
|
|
||||||
Credentials = new NetworkCredential(emailConfig.FromEmail, emailConfig.AuthorizationCode)
|
|
||||||
|
|
||||||
}, restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Error);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
Log.Logger = config.CreateLogger();
|
|
||||||
|
|
||||||
#region 废弃-输出Json格式的日志
|
|
||||||
//如果有反向代理并不会获取到用户的真实IP
|
|
||||||
//.Enrich.WithClientIp()
|
|
||||||
//.Enrich.WithRequestHeader("User-Agent")
|
|
||||||
//https://github.com/serilog/serilog-formatting-compact
|
|
||||||
//// 控制台输出 JSON 格式
|
|
||||||
//.WriteTo.Console(formatter: new CompactJsonFormatter(), LogEventLevel.Warning),
|
|
||||||
//// 文件输出 JSON 格式
|
|
||||||
//.WriteTo.File(new CompactJsonFormatter(), $"{AppContext.BaseDirectory}Serilogs/.json", rollingInterval: RollingInterval.Day);
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
using IP2Region.Net.Abstractions;
|
|
||||||
using IP2Region.Net.XDB;
|
|
||||||
using IRaCIS.Core.Application.BackGroundJob;
|
|
||||||
using IRaCIS.Core.Application.Helper;
|
|
||||||
using IRaCIS.Core.Application.Service;
|
|
||||||
using IRaCIS.Core.Domain.Share;
|
|
||||||
using IRaCIS.Core.Infra.EFCore;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.HttpOverrides;
|
|
||||||
using Microsoft.AspNetCore.SignalR;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Panda.DynamicWebApi;
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API;
|
|
||||||
|
|
||||||
|
|
||||||
public static class ServiceCollectionSetup
|
|
||||||
{
|
|
||||||
public static void ConfigureServices(this IServiceCollection services, IConfiguration _configuration)
|
|
||||||
{
|
|
||||||
services.AddOptions().Configure<SystemEmailSendConfig>(_configuration.GetSection("SystemEmailSendConfig"));
|
|
||||||
services.AddOptions().Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
|
||||||
services.AddOptions().Configure<AliyunOSSOptions>(_configuration.GetSection("AliyunOSS"));
|
|
||||||
services.AddOptions().Configure<ObjectStoreServiceOptions>(_configuration.GetSection("ObjectStoreService"));
|
|
||||||
services.AddOptions().Configure<IRCEncreptOption>(_configuration.GetSection("EncrypteResponseConfig"));
|
|
||||||
services.AddOptions().Configure<SystemPacsConfig>(_configuration.GetSection("SystemPacsConfig"));
|
|
||||||
services.Configure<IRaCISBasicConfigOption>(_configuration.GetSection("IRaCISBasicConfig"));
|
|
||||||
|
|
||||||
services.Configure<ServiceVerifyConfigOption>(_configuration.GetSection("BasicSystemConfig"));
|
|
||||||
|
|
||||||
//转发头设置 获取真实IP
|
|
||||||
services.Configure<ForwardedHeadersOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ForwardedHeaders =
|
|
||||||
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
|
|
||||||
});
|
|
||||||
|
|
||||||
services.AddSingleton<IUserIdProvider, IRaCISUserIdProvider>();
|
|
||||||
services.AddSingleton<ISearcher>(new Searcher(CachePolicy.Content, Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources, "ip2region.xdb")));
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
||||||
services.AddScoped<IRepository, Repository>();
|
|
||||||
|
|
||||||
services.AddScoped<IObtainTaskAutoCancelJob, ObtainTaskAutoCancelJob>();
|
|
||||||
|
|
||||||
// 注册以Service 结尾的服务
|
|
||||||
services.Scan(scan => scan
|
|
||||||
.FromAssemblies(typeof(BaseService).Assembly)
|
|
||||||
.AddClasses(classes => classes.Where(t => t.Name.Contains("Service")))
|
|
||||||
.AsImplementedInterfaces()
|
|
||||||
.WithScopedLifetime());
|
|
||||||
|
|
||||||
#region Register Controllers
|
|
||||||
|
|
||||||
// Register all controllers in the assembly that implement IDynamicWebApi
|
|
||||||
services.Scan(scan => scan
|
|
||||||
.FromAssemblies(typeof(BaseService).Assembly)
|
|
||||||
.AddClasses(classes => classes.AssignableTo<IDynamicWebApi>())
|
|
||||||
.AsSelf()
|
|
||||||
.WithScopedLifetime());
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
// MediatR 进程内消息 事件解耦 从程序集中 注册命令和handler对应关系
|
|
||||||
//builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<ConsistencyVerificationHandler>());
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 历史废弃配置
|
|
||||||
//builder.Services.AddMemoryCache();
|
|
||||||
////上传限制 配置
|
|
||||||
//builder.Services.Configure<FormOptions>(options =>
|
|
||||||
//{
|
|
||||||
// options.MultipartBodyLengthLimit = int.MaxValue;
|
|
||||||
// options.ValueCountLimit = int.MaxValue;
|
|
||||||
// options.ValueLengthLimit = int.MaxValue;
|
|
||||||
//});
|
|
||||||
//IP 限流 可设置白名单 或者黑名单
|
|
||||||
//services.AddIpPolicyRateLimitSetup(_configuration);
|
|
||||||
// 用户类型 策略授权
|
|
||||||
//services.AddAuthorizationPolicySetup(_configuration);
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Autofac 废弃
|
|
||||||
//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.Application.dll");
|
|
||||||
// containerBuilder.RegisterAssemblyTypes(application).Where(t => t.FullName.Contains("Service"))
|
|
||||||
// .PropertiesAutowired().AsImplementedInterfaces();
|
|
||||||
|
|
||||||
// //containerBuilder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
|
|
||||||
// //containerBuilder.RegisterType<UserInfo>().As<IUserInfo>().InstancePerLifetimeScope();
|
|
||||||
|
|
||||||
|
|
||||||
// //注册hangfire任务 依赖注入
|
|
||||||
// containerBuilder.RegisterType<ObtainTaskAutoCancelJob>().As<IObtainTaskAutoCancelJob>().InstancePerDependency();
|
|
||||||
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
#endregion
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class StaticFileAuthorizationSetup
|
||||||
|
{
|
||||||
|
public static void AddStaticFileAuthorizationSetup(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddAuthorization(options =>
|
||||||
|
{
|
||||||
|
options.FallbackPolicy = new AuthorizationPolicyBuilder()
|
||||||
|
.RequireAuthenticatedUser()
|
||||||
|
.Build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
using IRaCIS.Core.Application.Contracts;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.Filters;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.API
|
||||||
|
{
|
||||||
|
public static class SwaggerSetup
|
||||||
|
{
|
||||||
|
public static void AddSwaggerSetup(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
|
||||||
|
services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
//此处的Name 是控制器上分组的名称 Title是界面的大标题
|
||||||
|
//分组
|
||||||
|
|
||||||
|
options.SwaggerDoc("Reviewer", new OpenApiInfo {Title = "医生模块",Version = "Reviewer", });
|
||||||
|
options.SwaggerDoc("Trial", new OpenApiInfo { Title = "项目模块", Version = "Trial" });
|
||||||
|
options.SwaggerDoc("Enroll", new OpenApiInfo { Title = "入组模块", Version = "Enroll" });
|
||||||
|
options.SwaggerDoc("Workload", new OpenApiInfo { Title = "工作量模块", Version = "Workload" });
|
||||||
|
options.SwaggerDoc("Common", new OpenApiInfo { Title = "通用信息获取", Version = "Common" });
|
||||||
|
options.SwaggerDoc("Institution", new OpenApiInfo { Title = "机构信息模块", Version = "Institution" });
|
||||||
|
options.SwaggerDoc("Dashboard&Statistics", new OpenApiInfo { Title = "统计模块", Version = "Dashboard&Statistics" });
|
||||||
|
|
||||||
|
options.SwaggerDoc("Financial", new OpenApiInfo { Title = "财务模块", Version = "Financial" });
|
||||||
|
options.SwaggerDoc("Management", new OpenApiInfo { Title = "管理模块", Version = "Management" });
|
||||||
|
options.SwaggerDoc("Image", new OpenApiInfo { Title = "影像模块", Version = "Image" });
|
||||||
|
options.SwaggerDoc("Reading", new OpenApiInfo { Title = "读片模块", Version = "Reading" });
|
||||||
|
|
||||||
|
|
||||||
|
// 接口排序
|
||||||
|
options.OrderActionsBy(o => o.GroupName);
|
||||||
|
|
||||||
|
options.DocInclusionPredicate((docName, apiDes) =>
|
||||||
|
{
|
||||||
|
if (!apiDes.TryGetMethodInfo(out MethodInfo methodInfo)) return false;
|
||||||
|
var versions = methodInfo.DeclaringType.GetCustomAttributes(true)
|
||||||
|
.OfType<ApiExplorerSettingsAttribute>()
|
||||||
|
.Select(attr => attr.GroupName);
|
||||||
|
|
||||||
|
return versions.Any(v => v.ToString() == docName);
|
||||||
|
});
|
||||||
|
|
||||||
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.API.xml");//这个就是刚刚配置的xml文件名
|
||||||
|
options.IncludeXmlComments(xmlPath, true);
|
||||||
|
|
||||||
|
var xmlPath2 = Path.Combine(AppContext.BaseDirectory, "IRaCIS.Core.Application.xml");//这个就是刚刚配置的xml文件名
|
||||||
|
options.IncludeXmlComments(xmlPath2, true);
|
||||||
|
//默认的第二个参数是false,这个是controller的注释,记得修改
|
||||||
|
|
||||||
|
|
||||||
|
// 在header中添加token,传递到后台
|
||||||
|
options.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||||
|
|
||||||
|
|
||||||
|
// 添加登录按钮
|
||||||
|
options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
|
||||||
|
{
|
||||||
|
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
|
||||||
|
Name = "Authorization",
|
||||||
|
|
||||||
|
//In = "header",
|
||||||
|
//Type = "apiKey"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//// Bearer
|
||||||
|
//options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||||
|
//{
|
||||||
|
// Description = "JWT Authorization header using the Bearer scheme.",
|
||||||
|
// Name = "Authorization",
|
||||||
|
// In = ParameterLocation.Header,
|
||||||
|
// Scheme = "bearer",
|
||||||
|
// Type = SecuritySchemeType.Http,
|
||||||
|
// BearerFormat = "JWT"
|
||||||
|
//});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
app.UseSwagger();
|
||||||
|
app.UseSwaggerUI(options =>
|
||||||
|
{
|
||||||
|
//此处的Name 是页面 选择文档下拉框 显示的名称
|
||||||
|
options.SwaggerEndpoint($"swagger/Reviewer/swagger.json", "医生模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Trial/swagger.json", "项目模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Enroll/swagger.json", "入组模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Workload/swagger.json", "工作量模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Dashboard&Statistics/swagger.json", "统计模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Common/swagger.json", "通用模块");
|
||||||
|
|
||||||
|
options.SwaggerEndpoint($"swagger/Financial/swagger.json", "财务模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Institution/swagger.json", "机构信息模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Management/swagger.json", "管理模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Image/swagger.json", "影像模块");
|
||||||
|
options.SwaggerEndpoint($"swagger/Reading/swagger.json", "读片模块");
|
||||||
|
|
||||||
|
|
||||||
|
//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,
|
||||||
|
//注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.Route = "doc";
|
||||||
|
//options.RoutePrefix = string.Empty;
|
||||||
|
|
||||||
|
var data = Assembly.GetExecutingAssembly().Location;
|
||||||
|
options.IndexStream = () => Assembly.GetExecutingAssembly()
|
||||||
|
.GetManifestResourceStream("IRaCIS.Core.API.wwwroot.swagger.ui.Index.html");
|
||||||
|
|
||||||
|
options.RoutePrefix = string.Empty;
|
||||||
|
|
||||||
|
//DocExpansion设置为none可折叠所有方法
|
||||||
|
options.DocExpansion(DocExpansion.None);
|
||||||
|
//DefaultModelsExpandDepth设置为 - 1 可不显示models
|
||||||
|
options.DefaultModelsExpandDepth(-1);
|
||||||
|
|
||||||
|
|
||||||
|
// 引入静态文件添加登录功能
|
||||||
|
// 清除静态文件缓存
|
||||||
|
// options.IndexStream = () => null;
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,130 +0,0 @@
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.OpenApi.Models;
|
|
||||||
using Swashbuckle.AspNetCore.Filters;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
||||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
|
||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API;
|
|
||||||
|
|
||||||
public enum SwaggerVersion
|
|
||||||
{
|
|
||||||
[Description("文件记录(FileRecord)")]
|
|
||||||
FileRecord = -1,
|
|
||||||
|
|
||||||
[Description("医生模块(Reviewer)")]
|
|
||||||
Reviewer = 1,
|
|
||||||
[Description("项目模块(Trial)")]
|
|
||||||
Trial = 2,
|
|
||||||
[Description("入组模块(Enroll)")]
|
|
||||||
Enroll = 3,
|
|
||||||
[Description("工作量模块(Workload)")]
|
|
||||||
Workload = 4,
|
|
||||||
[Description("通用信息获取(Common)")]
|
|
||||||
Common = 5,
|
|
||||||
[Description("机构信息模块(Institution)")]
|
|
||||||
Institution = 6,
|
|
||||||
[Description("统计模块(DashboardStatistics)")]
|
|
||||||
DashboardStatistics = 7,
|
|
||||||
[Description("财务模块(Financial)")]
|
|
||||||
Financial = 8,
|
|
||||||
[Description("管理模块(Management)")]
|
|
||||||
Management =9,
|
|
||||||
[Description("影像模块(Image)")]
|
|
||||||
Image =10,
|
|
||||||
[Description("读片模块(Reading)")]
|
|
||||||
Reading =11
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public static class SwaggerSetup
|
|
||||||
{
|
|
||||||
public static void AddSwaggerSetup(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddEndpointsApiExplorer();
|
|
||||||
services.AddSwaggerGen(options =>
|
|
||||||
{
|
|
||||||
|
|
||||||
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static).ToList()
|
|
||||||
.ForEach(field =>
|
|
||||||
{
|
|
||||||
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
|
||||||
options.SwaggerDoc(field.Name, new Microsoft.OpenApi.Models.OpenApiInfo
|
|
||||||
{
|
|
||||||
Version = field.Name,
|
|
||||||
Description = $"{field.Name} API",
|
|
||||||
Title = description // 使用Description作为Title
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// 接口排序
|
|
||||||
options.OrderActionsBy(o => o.GroupName);
|
|
||||||
|
|
||||||
//添加注释
|
|
||||||
var basePath = AppContext.BaseDirectory;
|
|
||||||
var xmlPath1 = Path.Combine(basePath, "IRaCIS.Core.Application.xml");
|
|
||||||
var xmlPath2 = Path.Combine(basePath, "IRaCIS.Core.API.xml");
|
|
||||||
options.IncludeXmlComments(xmlPath1, true);
|
|
||||||
options.IncludeXmlComments(xmlPath2, true);
|
|
||||||
|
|
||||||
// 在header中添加token,传递到后台
|
|
||||||
options.OperationFilter<SecurityRequirementsOperationFilter>();
|
|
||||||
|
|
||||||
|
|
||||||
// 添加登录按钮
|
|
||||||
options.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme()
|
|
||||||
{
|
|
||||||
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
|
|
||||||
Name = "Authorization",
|
|
||||||
//In = "header",
|
|
||||||
//Type = "apiKey"
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
|
||||||
{
|
|
||||||
app.UseSwagger();
|
|
||||||
app.UseSwaggerUI(options =>
|
|
||||||
{
|
|
||||||
typeof(SwaggerVersion).GetFields(BindingFlags.Public | BindingFlags.Static).ToList()
|
|
||||||
.ForEach(field =>
|
|
||||||
{
|
|
||||||
var description = field.GetCustomAttribute<DescriptionAttribute>()?.Description ?? field.Name;
|
|
||||||
options.SwaggerEndpoint($"swagger/{field.Name}/swagger.json", $"{description}");
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
var data = Assembly.GetExecutingAssembly().Location;
|
|
||||||
options.IndexStream = () => Assembly.GetExecutingAssembly()
|
|
||||||
.GetManifestResourceStream("IRaCIS.Core.API.wwwroot.swagger.ui.Index.html");
|
|
||||||
|
|
||||||
//路径配置,设置为空,表示直接在根域名(localhost:8001)访问该文件,
|
|
||||||
//注意localhost:8001/swagger是访问不到的,去launchSettings.json把launchUrl去掉,如果你想换一个路径,直接写名字即可,比如直接写c.Route = "doc";
|
|
||||||
options.RoutePrefix = string.Empty;
|
|
||||||
|
|
||||||
//DocExpansion设置为none可折叠所有方法
|
|
||||||
options.DocExpansion(DocExpansion.None);
|
|
||||||
|
|
||||||
//DefaultModelsExpandDepth设置为 - 1 可不显示models
|
|
||||||
options.DefaultModelsExpandDepth(-1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -3,7 +3,6 @@ using Hangfire.SqlServer;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace IRaCIS.Core.API
|
namespace IRaCIS.Core.API
|
||||||
{
|
{
|
||||||
|
@ -15,21 +14,19 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
services.AddHangfire(hangFireConfig =>
|
services.AddHangfire(hangFireConfig =>
|
||||||
{
|
{
|
||||||
//本地window 调试 使用内存,服务器部署使用数据库,防止服务器任务调度到本地
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
hangFireConfig.UseInMemoryStorage();
|
|
||||||
|
|
||||||
}
|
//hangFireConfig.UseInMemoryStorage();
|
||||||
else
|
|
||||||
{
|
|
||||||
//指定存储介质
|
//指定存储介质
|
||||||
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
hangFireConfig.UseSqlServerStorage(hangFireConnStr, new SqlServerStorageOptions()
|
||||||
{
|
{
|
||||||
SchemaName = "dbo",
|
SchemaName = "dbo",
|
||||||
}).UseRecommendedSerializerSettings().UseSimpleAssemblyNameTypeSerializer();
|
CommandBatchMaxTimeout = TimeSpan.FromMinutes(5),
|
||||||
}
|
SlidingInvisibilityTimeout = TimeSpan.FromMinutes(5),
|
||||||
|
QueuePollInterval = TimeSpan.Zero,
|
||||||
|
UseRecommendedIsolationLevel = true,
|
||||||
|
DisableGlobalLocks = true
|
||||||
|
});
|
||||||
|
|
||||||
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
//hangFireConfig.UseTagsWithSql(); //nuget引入Hangfire.Tags.SqlServer
|
||||||
//.UseHangfireHttpJob();
|
//.UseHangfireHttpJob();
|
||||||
|
@ -38,8 +35,6 @@ namespace IRaCIS.Core.API
|
||||||
|
|
||||||
services.AddHangfireServer(option =>
|
services.AddHangfireServer(option =>
|
||||||
{
|
{
|
||||||
//默认15s检查一次
|
|
||||||
option.SchedulePollingInterval = TimeSpan.FromSeconds(5);
|
|
||||||
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
|
option.Queues = new[] { "immediately_once", "default", "sys_init", "not_immediately_once" };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7,22 +7,20 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=101.132.253.119,1435;Database=irc_Prpd_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=47.117.164.182,1434;Database=Event_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=101.132.253.119,1435;Database=irc_Hangfire_bak;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"Hangfire": "Server=47.117.164.182,1434;Database=Event_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"RegionId": "cn-shanghai",
|
"regionId": "cn-shanghai",
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
"bucketName": "zy-irc-store",
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
"BucketName": "zy-irc-store",
|
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
"region": "oss-cn-shanghai"
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endpoint": "http://192.168.3.68",
|
"endpoint": "http://192.168.3.68",
|
||||||
|
@ -48,14 +46,7 @@
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
"ReadingRestTimeMin": 10
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
|
|
||||||
"ChangePassWordDays": 90,
|
|
||||||
|
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
|
||||||
"TemplateType": 2
|
|
||||||
},
|
},
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
|
@ -67,8 +58,7 @@
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗"
|
||||||
"IsEnv_US": false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,24 +7,22 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
//"RemoteNew": "Server=101.132.193.237,1434;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
//"Hangfire": "Server=101.132.193.237,1434;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
//"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
"RemoteNew": "Server=prod_mssql_standard,1433;Database=Prod_IRC;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true",
|
||||||
//"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
"Hangfire": "Server=prod_mssql_standard,1433;Database=Prod_IRC_Hangfire;User ID=sa;Password=zhanying@2021;TrustServerCertificate=true"
|
||||||
},
|
},
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"RegionId": "cn-shanghai",
|
"regionId": "cn-shanghai",
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
"endpoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
"AccessKeyId": "LTAI5tNRTsqL6aWmHkDmTwoH",
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
"AccessKeySecret": "7mtGz3qrYWI6JMMBZiLeC119VWicZH",
|
"bucketName": "zy-irc-store",
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/irc-oss-access",
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
"BucketName": "zy-irc-store",
|
"viewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
||||||
"ViewEndpoint": "https://zy-irc-cache.oss-cn-shanghai.aliyuncs.com",
|
"region": "oss-cn-shanghai"
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endpoint": "http://192.168.3.68",
|
"endpoint": "http://192.168.3.68",
|
||||||
|
@ -43,40 +41,29 @@
|
||||||
"OpenLoginLimit": true,
|
"OpenLoginLimit": true,
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
"LoginFailLockMinutes": 30,
|
"LoginFailLockMinutes": 30,
|
||||||
"AutoLoginOutMinutes": 120,
|
|
||||||
|
"AutoLoginOutMinutes": 360,
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
"ReadingRestTimeMin": 10,
|
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"ReadingRestTimeMin": 10
|
||||||
"ChangePassWordDays": 90,
|
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
|
||||||
"TemplateType": 2
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
"Host": "smtp.qiye.aliyun.com",
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
"FromEmail": "irc@extimaging.com",
|
"FromEmail": "IRC@extimaging.com",
|
||||||
"FromName": "IRC Imaging System",
|
"FromName": "IRC",
|
||||||
"AuthorizationCode": "ExtImg@2022",
|
"AuthorizationCode": "ExtImg@2022",
|
||||||
"SiteUrl": "http://irc.extimaging.com/login",
|
"SiteUrl": "http://irc.extimaging.com/login",
|
||||||
"SystemShortName": "IRC",
|
|
||||||
"OrganizationName": "Extlmaging",
|
"OrganizationName": "Extlmaging",
|
||||||
"OrganizationNameCN": "Extlmaging",
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗"
|
||||||
"IsEnv_US": false,
|
|
||||||
"IsOpenErrorNoticeEmail": false,
|
|
||||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
|
||||||
},
|
|
||||||
"SystemPacsConfig": {
|
|
||||||
"Port": "11113",
|
|
||||||
"IP": "101.132.193.237"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=106.14.89.110,1435;Database=Test_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
|
@ -16,89 +17,73 @@
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
"ObjectStoreUse": "AliyunOSS",
|
||||||
|
|
||||||
"AliyunOSS": {
|
"AliyunOSS": {
|
||||||
"RegionId": "cn-shanghai",
|
"regionId": "cn-shanghai",
|
||||||
"InternalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
"EndPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
||||||
"AccessKeyId": "LTAI5tRRZehUp2V9pyTPtAJm",
|
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
||||||
"AccessKeySecret": "FLizxkHsMm4CGYHtkV8E3PNJJZU7oV",
|
"bucketName": "zy-irc-test-store",
|
||||||
"RoleArn": "acs:ram::1899121822495495:role/dev-oss-access",
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
"BucketName": "zy-irc-test-store",
|
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
//"ViewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
"region": "oss-cn-shanghai"
|
||||||
"ViewEndpoint": "https://zy-irc-test-dev-cache.oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"Region": "oss-cn-shanghai",
|
|
||||||
"DurationSeconds": 7200,
|
|
||||||
"PreviewEndpoint": "https://test-oss.test.extimaging.com"
|
|
||||||
},
|
},
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"EndPoint": "hir-oss.test.extimaging.com",
|
"endPoint": "hir-oss.test.extimaging.com",
|
||||||
"Port": "443",
|
"port": "443",
|
||||||
"UseSSL": true,
|
"useSSL": true,
|
||||||
"AccessKey": "fbStsVYCIPKHQneeqMwD",
|
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
||||||
"SecretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
||||||
"BucketName": "irc-test",
|
"bucketName": "irc-test",
|
||||||
"ViewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
||||||
},
|
},
|
||||||
|
|
||||||
"AWS": {
|
"AWS": {
|
||||||
"Region": "us-east-1",
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
"useSSL": true,
|
||||||
"UseSSL": true,
|
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
||||||
"RoleArn": "arn:aws:iam::471112624751:role/uat_s3_access",
|
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
||||||
"AccessKeyId": "AKIAW3MEAFJX7IPXISP4",
|
"bucketName": "ei-irc-test-store",
|
||||||
"SecretAccessKey": "Pgrg3le5jPxZQ7MR1yYNS30J0XRyJeKVyIIjElXc",
|
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
||||||
"BucketName": "ei-med-s3-lili-uat-store",
|
|
||||||
"ViewEndpoint": "https://ei-med-s3-lili-uat-store.s3.amazonaws.com",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": false,
|
"OpenUserComplexPassword": false,
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": false,
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
"OpenLoginLimit": false,
|
"OpenLoginLimit": false,
|
||||||
|
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
|
|
||||||
"LoginFailLockMinutes": 1,
|
"LoginFailLockMinutes": 1,
|
||||||
"AutoLoginOutMinutes": 10,
|
|
||||||
|
"AutoLoginOutMinutes": 1,
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
"OpenLoginMFA": false,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
"ReadingRestTimeMin": 10,
|
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
"ReadingRestTimeMin": 10
|
||||||
"ChangePassWordDays": 90,
|
|
||||||
|
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
|
||||||
"TemplateType": 2,
|
|
||||||
|
|
||||||
"OpenTrialRelationDelete": true,
|
|
||||||
|
|
||||||
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 465,
|
"Port": 465,
|
||||||
"Host": "smtp.qiye.aliyun.com",
|
"Host": "smtp.qiye.aliyun.com",
|
||||||
"FromEmail": "test@extimaging.com",
|
"FromEmail": "test@extimaging.com",
|
||||||
"FromName": "Test IRC Imaging System",
|
"FromName": "Test_IRC",
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
"AuthorizationCode": "SHzyyl2021",
|
||||||
|
|
||||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||||
|
|
||||||
"SystemShortName": "IRC",
|
|
||||||
"OrganizationName": "Extlmaging",
|
"OrganizationName": "Extlmaging",
|
||||||
"OrganizationNameCN": "Extlmaging",
|
"OrganizationNameCN": "Extlmaging",
|
||||||
"CompanyName": "Extensive Imaging",
|
"CompanyName": "Extensive Imaging",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Extensive Imaging",
|
"CompanyShortName": "Extensive Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗"
|
||||||
"IsEnv_US": false,
|
}
|
||||||
"IsOpenErrorNoticeEmail": false,
|
|
||||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
|
||||||
},
|
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
|
||||||
"Port": "11113",
|
|
||||||
"IP": "106.14.89.110"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,95 +0,0 @@
|
||||||
{
|
|
||||||
"Logging": {
|
|
||||||
"LogLevel": {
|
|
||||||
"Default": "Information",
|
|
||||||
"Microsoft": "Warning",
|
|
||||||
"Microsoft.Hosting.Lifetime": "Information"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ConnectionStrings": {
|
|
||||||
"Db_Type": "pgsql",
|
|
||||||
"RemoteNew": "Host=106.14.89.110;Port=5432;Username=sa;Password=pgsql_pwd;Database=Test2_PG",
|
|
||||||
"Hangfire": "Server=106.14.89.110,1435;Database=Test_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
|
||||||
},
|
|
||||||
|
|
||||||
"ObjectStoreService": {
|
|
||||||
|
|
||||||
"ObjectStoreUse": "AliyunOSS",
|
|
||||||
|
|
||||||
"AliyunOSS": {
|
|
||||||
"regionId": "cn-shanghai",
|
|
||||||
"internalEndpoint": "https://oss-cn-shanghai-internal.aliyuncs.com",
|
|
||||||
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
|
||||||
"accessKeyId": "LTAI5tKvzs7ed3UfSpNk3xwQ",
|
|
||||||
"accessKeySecret": "zTIceGEShlZDGnLrCFfIGFE7TXVRio",
|
|
||||||
"bucketName": "zy-irc-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": "hir-oss.test.extimaging.com",
|
|
||||||
"port": "443",
|
|
||||||
"useSSL": true,
|
|
||||||
"accessKey": "fbStsVYCIPKHQneeqMwD",
|
|
||||||
"secretKey": "TzgvyA3zGXMUnpilJNUlyMYHfosl1hBMl6lxPmjy",
|
|
||||||
"bucketName": "irc-test",
|
|
||||||
"viewEndpoint": "https://hir-oss.test.extimaging.com/irc-test"
|
|
||||||
},
|
|
||||||
"AWS": {
|
|
||||||
"endPoint": "s3.us-east-1.amazonaws.com",
|
|
||||||
"useSSL": true,
|
|
||||||
"accessKey": "AKIAZQ3DRSOHFPJJ6FEU",
|
|
||||||
"secretKey": "l+yjtvV7Z4jiwm/7xCYv30UeUj/SvuqqYzAwjJHf",
|
|
||||||
"bucketName": "ei-irc-test-store",
|
|
||||||
"viewEndpoint": "https://ei-irc-test-store.s3.amazonaws.com/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"BasicSystemConfig": {
|
|
||||||
|
|
||||||
"OpenUserComplexPassword": false,
|
|
||||||
|
|
||||||
"OpenSignDocumentBeforeWork": false,
|
|
||||||
|
|
||||||
"OpenTrialRelationDelete": true,
|
|
||||||
|
|
||||||
"OpenLoginLimit": false,
|
|
||||||
|
|
||||||
"LoginMaxFailCount": 5,
|
|
||||||
|
|
||||||
"LoginFailLockMinutes": 1,
|
|
||||||
|
|
||||||
"AutoLoginOutMinutes": 1,
|
|
||||||
|
|
||||||
"OpenLoginMFA": false,
|
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
|
||||||
|
|
||||||
"ReadingRestTimeMin": 10,
|
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
|
|
||||||
"ChangePassWordDays": 90
|
|
||||||
},
|
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
|
||||||
"Port": 465,
|
|
||||||
"Host": "smtp.qiye.aliyun.com",
|
|
||||||
"FromEmail": "test@extimaging.com",
|
|
||||||
"FromName": "Test_IRC",
|
|
||||||
"AuthorizationCode": "SHzyyl2021",
|
|
||||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
|
||||||
|
|
||||||
"OrganizationName": "Extlmaging",
|
|
||||||
"OrganizationNameCN": "Extlmaging",
|
|
||||||
"CompanyName": "Extensive Imaging",
|
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
|
||||||
"CompanyShortName": "Extensive Imaging",
|
|
||||||
"CompanyShortNameCN": "展影医疗"
|
|
||||||
},
|
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
|
||||||
"Port": "11113",
|
|
||||||
"IP": "106.14.89.110"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -7,14 +7,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConnectionStrings": {
|
"ConnectionStrings": {
|
||||||
"RemoteNew": "Server=us-mssql-prod,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
"RemoteNew": "Server=us-prod-mssql-service,1433;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
"Hangfire": "Server=us-mssql-prod,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
"Hangfire": "Server=us-prod-mssql-service,1433;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true"
|
||||||
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
//"RemoteNew": "Server=44.210.231.169,1435;Database=US_IRC;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
//"Hangfire": "Server=44.210.231.169,1435;Database=US_IRC_Hangfire;User ID=sa;Password=xc@123456;TrustServerCertificate=true",
|
||||||
},
|
},
|
||||||
|
|
||||||
"ObjectStoreService": {
|
"ObjectStoreService": {
|
||||||
"ObjectStoreUse": "AWS",
|
"ObjectStoreUse": "AWS",
|
||||||
|
"AliyunOSS": {
|
||||||
|
"regionId": "cn-shanghai",
|
||||||
|
"endPoint": "https://oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"accessKeyId": "",
|
||||||
|
"accessKeySecret": "",
|
||||||
|
"bucketName": "zy-irc-test-store",
|
||||||
|
"roleArn": "acs:ram::1899121822495495:role/oss-upload",
|
||||||
|
"viewEndpoint": "https://zy-irc-test-store.oss-cn-shanghai.aliyuncs.com",
|
||||||
|
"region": "oss-cn-shanghai"
|
||||||
|
},
|
||||||
|
|
||||||
"MinIO": {
|
"MinIO": {
|
||||||
"endPoint": "44.210.231.169",
|
"endPoint": "44.210.231.169",
|
||||||
"port": "9001",
|
"port": "9001",
|
||||||
|
@ -26,64 +37,48 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"AWS": {
|
"AWS": {
|
||||||
"Region": "us-east-1",
|
"endPoint": "s3.us-east-1.amazonaws.com",
|
||||||
"EndPoint": "s3.us-east-1.amazonaws.com",
|
"useSSL": true,
|
||||||
"UseSSL": true,
|
"accessKey": "AKIAW3MEAFJX5P32P6NA",
|
||||||
"RoleArn": "arn:aws:iam::471112624751:role/lili_s3_access",
|
"secretKey": "soKfYlzZE11Zi4RyTjXp0myXN0U3U+ka8rT49+B/",
|
||||||
"AccessKeyId": "AKIAW3MEAFJXZ2TZK7GM",
|
"bucketName": "ei-med-s3-lili-store",
|
||||||
"SecretAccessKey": "9MLQCQ1HifEVW1gf068zBRAOb4wNnfrOkvBVByth",
|
"viewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com/"
|
||||||
"BucketName": "ei-med-s3-lili-store",
|
|
||||||
"ViewEndpoint": "https://ei-med-s3-lili-store.s3.amazonaws.com",
|
|
||||||
"DurationSeconds": 7200
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"BasicSystemConfig": {
|
"BasicSystemConfig": {
|
||||||
|
|
||||||
"OpenUserComplexPassword": true,
|
"OpenUserComplexPassword": false,
|
||||||
"OpenSignDocumentBeforeWork": true,
|
|
||||||
|
"OpenSignDocumentBeforeWork": false,
|
||||||
|
|
||||||
|
"OpenTrialRelationDelete": true,
|
||||||
|
|
||||||
|
"OpenLoginLimit": false,
|
||||||
|
|
||||||
"OpenLoginLimit": true,
|
|
||||||
"LoginMaxFailCount": 5,
|
"LoginMaxFailCount": 5,
|
||||||
"LoginFailLockMinutes": 30,
|
|
||||||
"AutoLoginOutMinutes": 120,
|
|
||||||
|
|
||||||
"OpenLoginMFA": true,
|
"LoginFailLockMinutes": 30,
|
||||||
|
"AutoLoginOutMinutes": 60,
|
||||||
|
|
||||||
"ContinuousReadingTimeMin": 120,
|
"ContinuousReadingTimeMin": 120,
|
||||||
"ReadingRestTimeMin": 10,
|
|
||||||
|
|
||||||
"IsNeedChangePassWord": true,
|
|
||||||
"ChangePassWordDays": 90,
|
|
||||||
// 模板类型 1 Elevate 2 Extensive
|
|
||||||
"TemplateType": 1,
|
|
||||||
|
|
||||||
"OpenTrialRelationDelete": false
|
|
||||||
|
|
||||||
|
"ReadingRestTimeMin": 10
|
||||||
},
|
},
|
||||||
|
|
||||||
"SystemEmailSendConfig": {
|
"SystemEmailSendConfig": {
|
||||||
"Port": 587,
|
"Port": 587,
|
||||||
"Host": "smtp-mail.outlook.com",
|
"Host": "smtp-mail.outlook.com",
|
||||||
"FromEmail": "donotreply@elevateimaging.ai",
|
"FromEmail": "donotreply@elevateimaging.ai",
|
||||||
"FromName": "LiLi System",
|
"FromName": "LiLi",
|
||||||
"AuthorizationCode": "Q#669869497420ul",
|
"AuthorizationCode": "Q#669869497420ul",
|
||||||
|
|
||||||
"SystemShortName": "LiLi",
|
|
||||||
"OrganizationName": "Elevate Imaging",
|
"OrganizationName": "Elevate Imaging",
|
||||||
"OrganizationNameCN": "Elevate Imaging",
|
"OrganizationNameCN": "Elevate Imaging",
|
||||||
"CompanyName": "Elevate Imaging Inc.",
|
"CompanyName": "Elevate Imaging Inc.",
|
||||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||||
"CompanyShortName": "Elevate Imaging",
|
"CompanyShortName": "Elevate Imaging",
|
||||||
"CompanyShortNameCN": "展影医疗",
|
"CompanyShortNameCN": "展影医疗",
|
||||||
"SiteUrl": "https://lili.elevateimaging.ai/login",
|
"SiteUrl": "https://lili.elevateimaging.ai/login"
|
||||||
"IsEnv_US": true,
|
|
||||||
"IsOpenErrorNoticeEmail": false,
|
|
||||||
"ErrorNoticeEmailList": [ "872297557@qq.com" ]
|
|
||||||
},
|
|
||||||
|
|
||||||
"SystemPacsConfig": {
|
|
||||||
"Port": "104",
|
|
||||||
"IP": "44.210.231.169"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue