整理命名空间
continuous-integration/drone/push Build is passing Details

IRC_NewDev
hang 2024-09-20 10:35:35 +08:00
parent fc30cb39ee
commit 103d4b42a3
16 changed files with 1410 additions and 1427 deletions

View File

@ -29,7 +29,7 @@
<param name="doctorId"></param> <param name="doctorId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Application.Services.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Application.Services.IMailVerificationService)"> <member name="M:IRaCIS.Api.Controllers.ExtraController.Login(IRaCIS.Application.Contracts.UserLoginDTO,ZiggyCreatures.Caching.Fusion.IFusionCache,IRaCIS.Core.Application.Service.IUserService,IRaCIS.Core.Application.Auth.ITokenService,IRaCIS.Core.Application.Contracts.IReadingImageTaskService,Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.ServiceVerifyConfigOption},Microsoft.Extensions.Options.IOptionsMonitor{IRaCIS.Core.Domain.Share.SystemEmailSendConfig},IRaCIS.Core.Application.Service.IMailVerificationService)">
<summary> 系统用户登录接口[New] </summary> <summary> 系统用户登录接口[New] </summary>
</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})">
@ -41,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.Application.Services.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)"> <member name="M:IRaCIS.Core.API.Controllers.Special.FinancialChangeController.WorkLoadAddOrUpdate(IRaCIS.Core.Application.Service.IDoctorWorkloadService,IRaCIS.Application.Contracts.WorkloadCommand)">
<summary> <summary>
添加或更新工作量[AUTH] 添加或更新工作量[AUTH]
</summary> </summary>

View File

@ -10,7 +10,7 @@ using Panda.DynamicWebApi.Attributes;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using ZiggyCreatures.Caching.Fusion; using ZiggyCreatures.Caching.Fusion;
namespace IRaCIS.Core.Application namespace IRaCIS.Core.Application.Service
{ {
#pragma warning disable CS8618 #pragma warning disable CS8618

View File

@ -3,42 +3,41 @@ using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.Application.BusinessFilter namespace IRaCIS.Core.Application.BusinessFilter;
/// <summary>
/// 不生效,不知道为啥
/// </summary>
public class GlobalExceptionHandler : IExceptionHandler
{ {
private readonly ILogger<GlobalExceptionHandler> _logger;
/// <summary> public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
/// 不生效,不知道为啥 {
/// </summary> this._logger = logger;
public class GlobalExceptionHandler : IExceptionHandler }
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{ {
private readonly ILogger<GlobalExceptionHandler> _logger;
public GlobalExceptionHandler(ILogger<GlobalExceptionHandler> logger)
{
this._logger = logger;
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
httpContext.Response.ContentType = "application/json"; httpContext.Response.ContentType = "application/json";
var ex = exception; var ex = exception;
var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : ""); var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : "");
httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk($"{ex?.Message}")); httpContext.Response.WriteAsJsonAsync(ResponseOutput.NotOk($"{ex?.Message}"));
_logger.LogError(errorInfo); _logger.LogError(errorInfo);
// return true to signal that this exception is handled // return true to signal that this exception is handled
return ValueTask.FromResult(false); return ValueTask.FromResult(false);
}
} }
} }

View File

@ -4,33 +4,31 @@ using Microsoft.Extensions.Localization;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace IRaCIS.Core.Application.Filter namespace IRaCIS.Core.Application.Filter;
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
{ {
public IStringLocalizer _localizer;
public ModelActionFilter(IStringLocalizer localizer)
public class ModelActionFilter : ActionFilterAttribute, IActionFilter
{ {
public IStringLocalizer _localizer; _localizer = 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)));
}
}
} }
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)));
}
}
} }

View File

@ -2,26 +2,25 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace IRaCIS.Core.Application.Filter namespace IRaCIS.Core.Application.Filter;
#region snippet_DisableFormValueModelBindingAttribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{ {
#region snippet_DisableFormValueModelBindingAttribute public void OnResourceExecuting(ResourceExecutingContext context)
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{ {
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories; var factories = context.ValueProviderFactories;
//factories.RemoveType<FormValueProviderFactory>(); //factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>(); factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>(); factories.RemoveType<JQueryFormValueProviderFactory>();
context.HttpContext.Request.EnableBuffering(); context.HttpContext.Request.EnableBuffering();
} }
public void OnResourceExecuted(ResourceExecutedContext context) public void OnResourceExecuted(ResourceExecutedContext context)
{ {
}
} }
#endregion
} }
#endregion

View File

@ -5,72 +5,71 @@ using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Localization; using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.Application.Filter namespace IRaCIS.Core.Application.Filter;
public class ProjectExceptionFilter : Attribute, IExceptionFilter
{ {
public class ProjectExceptionFilter : Attribute, IExceptionFilter private readonly ILogger<ProjectExceptionFilter> _logger;
public IStringLocalizer _localizer;
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
{ {
private readonly ILogger<ProjectExceptionFilter> _logger; _logger = logger;
_localizer = localizer;
}
public void OnException(ExceptionContext context)
{
//context.ExceptionHandled;//记录当前这个异常是否已经被处理过了
public IStringLocalizer _localizer; if (!context.ExceptionHandled)
public ProjectExceptionFilter(IStringLocalizer localizer, ILogger<ProjectExceptionFilter> logger)
{ {
_logger = logger; if (context.Exception.GetType().Name == "DbUpdateConcurrencyException")
_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;
var info = string.Empty;
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey))
{ {
//---并发更新,当前不允许该操作 info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}";
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message));
} }
if (context.Exception.GetType() == typeof(BusinessValidationFailedException))
{
var error = context.Exception as BusinessValidationFailedException;
var info = string.Empty; context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info));
if (!string.IsNullOrWhiteSpace(error!.LocalizedKey) && StaticData.Log_Locoalize_Dic.ContainsKey(error!.LocalizedKey))
{
info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}";
}
context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info)); //warning 级别记录
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}");
}
//warning 级别记录 else if (context.Exception.GetType() == typeof(QueryBusinessObjectNotExistException))
//_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"); {
} context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, ApiResponseCodeEnum.DataNotExist));
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));
}
context.ExceptionHandled = true;//标记当前异常已经被处理过了
//throw new Exception("test-result-exceptioin");
} }
else 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));
} }
context.ExceptionHandled = true;//标记当前异常已经被处理过了
//throw new Exception("test-result-exceptioin");
}
else
{
//继续
} }
} }
} }

View File

@ -8,80 +8,124 @@ using System.Text.RegularExpressions;
using ZiggyCreatures.Caching.Fusion; using ZiggyCreatures.Caching.Fusion;
using static IRaCIS.Core.Domain.Share.StaticData; using static IRaCIS.Core.Domain.Share.StaticData;
namespace IRaCIS.Core.Application.Filter namespace IRaCIS.Core.Application.Filter;
/// <summary>
/// 主要为了 处理项目结束 锁库,不允许操作
/// </summary>
public class TrialResourceFilter : Attribute, IAsyncResourceFilter
{ {
/// <summary> private readonly IUserInfo _userInfo;
/// 主要为了 处理项目结束 锁库,不允许操作 private readonly IFusionCache _fusionCache;
/// </summary> public IStringLocalizer _localizer;
public class TrialResourceFilter : Attribute, IAsyncResourceFilter private readonly IRepository<Trial> _trialRepository;
private readonly List<string> _trialOptList = new List<string>();
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null)
{ {
private readonly IUserInfo _userInfo; _fusionCache = fusionCache;
private readonly IFusionCache _fusionCache; _userInfo = userInfo;
public IStringLocalizer _localizer; _localizer = localizer;
private readonly IRepository<Trial> _trialRepository; _trialRepository = trialRepository;
private readonly List<string> _trialOptList = new List<string>();
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
public TrialResourceFilter(IFusionCache fusionCache, IRepository<Trial> trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null) }
//优先选择异步的方法
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault();
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList();
#region 处理新的用户类型,不能操作项目相关接口
// 后期列举出具体的类型,其他任何用户类型,都不允许操作
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
{ {
_fusionCache = fusionCache; //---对不起,您的账户没有操作权限。
_userInfo = userInfo; context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"]));
_localizer = localizer;
_trialRepository = trialRepository;
if (!string.IsNullOrWhiteSpace(trialOpt)) _trialOptList.Add(trialOpt.Trim());
if (!string.IsNullOrWhiteSpace(trialOpt2)) _trialOptList.Add(trialOpt2.Trim());
if (!string.IsNullOrWhiteSpace(trialOpt3)) _trialOptList.Add(trialOpt3.Trim());
return;
} }
//优先选择异步的方法 #endregion
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
//TrialId 传递的途径多种可能在path 可能在body 可能在数组中也可能在对象中可能就在url
var trialIdStr = string.Empty;
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
{ {
// var typeFilter = context.ActionDescriptor.EndpointMetadata.Where(t => t.GetType() == typeof(TypeFilterAttribute)).Select(t => (TypeFilterAttribute)t).ToList().FirstOrDefault(); trialIdStr = context.HttpContext.Request.Query["trialId"];
//var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList(); }
#region 处理新的用户类型,不能操作项目相关接口 //先尝试从path中取TrialId
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
{
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
}
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
{
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
// 后期列举出具体的类型,其他任何用户类型,都不允许操作 var trialIdIndex = headerStr.IndexOf("trialId");
if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower())
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
if (matchResult.Success)
{ {
//---对不起,您的账户没有操作权限。 trialIdStr = matchResult.Value;
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"])); }
else
return; {
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
} }
#endregion }
else
{
#region body 中取数据
//设置可以多次读
context.HttpContext.Request.EnableBuffering();
var reader = new StreamReader(context.HttpContext.Request.Body);
var contentFromBody = await reader.ReadToEndAsync();
//读取后,流的位置还原
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
//context.HttpContext.Request.Body.Position = 0;
//找到参数位置在字符串中的索引
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
//TrialId 传递的途径多种可能在path 可能在body 可能在数组中也可能在对象中可能就在url if (trialIdIndex > -1)
var trialIdStr = string.Empty;
if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"]))
{ {
trialIdStr = context.HttpContext.Request.Query["trialId"]; // (?<="trialId" *: *").*?(?=",)
}
//先尝试从path中取TrialId //使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
else if (context.RouteData.Values.Keys.Any(t => t.Contains("trialId")))
{
var index = context.RouteData.Values.Keys.ToList().IndexOf("trialId");
trialIdStr = context.RouteData.Values.Values.ToList()[index] as string;
}
else if (context.HttpContext.Request.Headers["Referer"].ToString().Contains("trialId"))
{
var headerStr = context.HttpContext.Request.Headers["Referer"].ToString();
var trialIdIndex = headerStr.IndexOf("trialId"); var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
if (matchResult.Success) if (matchResult.Success)
{ {
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
trialIdStr = matchResult.Value; trialIdStr = matchResult.Value;
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
if (string.IsNullOrWhiteSpace(trialStatusStr))
{
//数据库 检查该项目Id不对
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
}
} }
else else
{ {
@ -89,93 +133,48 @@ namespace IRaCIS.Core.Application.Filter
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"])); context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
} }
} //使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
else //trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
{
#region body 中取数据
//设置可以多次读
context.HttpContext.Request.EnableBuffering();
var reader = new StreamReader(context.HttpContext.Request.Body);
var contentFromBody = await reader.ReadToEndAsync();
//读取后,流的位置还原
context.HttpContext.Request.Body.Seek(0, SeekOrigin.Begin);
//context.HttpContext.Request.Body.Position = 0;
//找到参数位置在字符串中的索引
var trialIdIndex = contentFromBody.IndexOf("\"TrialId\"", StringComparison.OrdinalIgnoreCase);
if (trialIdIndex > -1)
{
// (?<="trialId" *: *").*?(?=",)
//使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}
var matchResult = Regex.Match(contentFromBody.Substring(trialIdIndex), @"[0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12}");
if (matchResult.Success)
{
//有可能匹配错误 "trialId":"","documentId":"b8180000-3e2c-0016-9fe0-08da33f96236" 从缓存里面验证下
trialIdStr = matchResult.Value;
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
if (string.IsNullOrWhiteSpace(trialStatusStr))
{
//数据库 检查该项目Id不对
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
}
}
else
{
//---正则取请求Refer 中trialId 失败,请联系开发人员核查
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"]));
}
//使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因
//trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3
}
#endregion
} }
//通过path 或者body 找到trialId 了 #endregion
if (!string.IsNullOrWhiteSpace(trialIdStr)) }
//通过path 或者body 找到trialId 了
if (!string.IsNullOrWhiteSpace(trialIdStr))
{
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方比如项目配置有的在多种状态初始化ongoing都可以操作有的仅仅在Initializing还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
{ {
var trialStatusStr = await _fusionCache.GetOrSetAsync(CacheKeys.Trial(trialIdStr), _ => CacheHelper.GetTrialStatusAsync(Guid.Parse(trialIdStr), _trialRepository), TimeSpan.FromDays(7));
// 这里是统一拦截 项目有关的操作允许情况(特殊的地方比如项目配置有的在多种状态初始化ongoing都可以操作有的仅仅在Initializing还有 项目添加和更新,不走这里,特殊处理,不然在这里显得很乱,判断是哪个接口)
if (trialStatusStr == StaticData.TrialState.TrialOngoing || _trialOptList.Any(t => t == TrialOpt.BeforeOngoingCantOpt))
{
await next.Invoke();
}
// 项目停止、或者完成 不允许操作
else
{
//---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
}
}
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
{
await next.Invoke(); await next.Invoke();
}
}
// 项目停止、或者完成 不允许操作
else else
{ {
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改 //---本次请求被配置规则拦截:项目状态处于进行中时,才允许操作,若此处逻辑有误,请联系开发人员修改
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_InterceptedProjectStatusRule"]));
//---该接口参数中,没有传递项目编号,请核对。
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
} }
} }
//添加项目 签名系统文档的时候 不做拦截 但是更新项目 签名项目文档的时候需要拦截
else if (_trialOptList.Any(t => t == TrialOpt.AddOrUpdateTrial || t == TrialOpt.SignSystemDocNoTrialId))
{
await next.Invoke();
}
else
{
//如果项目相关接口没有传递trialId 会来到这里,提醒,以便修改
//---该接口参数中,没有传递项目编号,请核对。
context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_MissingProjectNumber"]));
}
} }
} }

View File

@ -3,121 +3,120 @@ using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace IRaCIS.Core.Application.Service.BusinessFilter namespace IRaCIS.Core.Application.Service.BusinessFilter;
/// <summary>
/// 统一返回前端数据包装之前在控制器包装现在修改为动态Api 在ResultFilter这里包装减少重复冗余代码
/// by zhouhang 2021.09.12 周末
/// </summary>
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
{ {
/// <summary> private readonly ILogger _logger;
/// 统一返回前端数据包装之前在控制器包装现在修改为动态Api 在ResultFilter这里包装减少重复冗余代码
/// by zhouhang 2021.09.12 周末 public UnifiedApiResultFilter(ILogger<UnifiedApiResultFilter> logger)
/// </summary>
public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter
{ {
private readonly ILogger _logger; _logger = logger;
}
public UnifiedApiResultFilter(ILogger<UnifiedApiResultFilter> logger) /// <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)
{ {
_logger = logger; var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
}
/// <summary> //是200 并且没有包装 那么包装结果
/// 异步版本 if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
/// </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 并且没有包装 那么包装结果 var type = objectResult.Value?.GetType();
if (statusCode == 200 && !(objectResult.Value is IResponseOutput))
if (type != null && type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(ValueTuple<,>) || type.GetGenericTypeDefinition() == typeof(Tuple<,>)))
{ {
var type = objectResult.Value?.GetType(); //报错
//var tuple = (object, object))objectResult.Value;
//var (val1, val2) = ((dynamic, dynamic))objectResult.Value;
if (type != null && type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(ValueTuple<,>) || type.GetGenericTypeDefinition() == typeof(Tuple<,>))) //var apiResponse = ResponseOutput.Ok(val1, val2);
{
//OK
//报错 var tuple = (dynamic)objectResult.Value;
//var tuple = (object, object))objectResult.Value; var apiResponse = ResponseOutput.Ok(tuple!.Item1, tuple!.Item2);
//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();
}
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
} }
//如果是200 是IResponseOutput 记录下日志 else
else if (statusCode == 200 && (objectResult.Value is IResponseOutput))
{ {
var result = objectResult.Value as IResponseOutput; var apiResponse = ResponseOutput.Ok(objectResult.Value);
if (!string.IsNullOrWhiteSpace(result.LocalizedInfo))
{
//统一在这里记录国际化的日志信息
_logger.LogWarning($"{result.LocalizedInfo}");
}
}
else if (statusCode != 200 && !(objectResult.Value is IResponseOutput))
{
//---程序错误,请联系开发人员。
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
objectResult.Value = apiResponse; objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType(); objectResult.DeclaredType = apiResponse.GetType();
} }
} }
//如果是200 是IResponseOutput 记录下日志
await next.Invoke(); else if (statusCode == 200 && (objectResult.Value is IResponseOutput))
}
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 result = objectResult.Value as IResponseOutput;
if (!string.IsNullOrWhiteSpace(result.LocalizedInfo))
{ {
var genType = type.GetGenericTypeDefinition(); //统一在这里记录国际化的日志信息
if (genType == typeof(Tuple<>) _logger.LogWarning($"{result.LocalizedInfo}");
|| genType == typeof(Tuple<,>)
|| genType == typeof(Tuple<,>))
return true;
} }
if (!checkBaseTypes)
break;
type = type.BaseType;
} }
return false; else if (statusCode != 200 && !(objectResult.Value is IResponseOutput))
{
//---程序错误,请联系开发人员。
var apiResponse = ResponseOutput.NotOk(StaticData.International("UnifiedAPI_ProgramError"));
objectResult.Value = apiResponse;
objectResult.DeclaredType = apiResponse.GetType();
}
} }
await next.Invoke();
}
public static bool IsTupleType(Type type, bool checkBaseTypes = false)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
if (type == typeof(Tuple))
return true;
while (type != null)
{
if (type.IsGenericType)
{
var genType = type.GetGenericTypeDefinition();
if (genType == typeof(Tuple<>)
|| genType == typeof(Tuple<,>)
|| genType == typeof(Tuple<,>))
return true;
}
if (!checkBaseTypes)
break;
type = type.BaseType;
}
return false;
} }
} }

View File

@ -1,5 +1,4 @@
global using AutoMapper.QueryableExtensions; global using AutoMapper.QueryableExtensions;
global using IRaCIS.Core.Application;
global using IRaCIS.Core.Application.Service; global using IRaCIS.Core.Application.Service;
global using IRaCIS.Core.Domain.Models; global using IRaCIS.Core.Domain.Models;
global using IRaCIS.Core.Infra.EFCore; global using IRaCIS.Core.Infra.EFCore;

View File

@ -1,70 +1,69 @@
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
public static class CacheKeys
{ {
//项目缓存
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
public static class CacheKeys //检查编号递增锁
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
public static string TrialStudyUidUploading(Guid trialId, string studyUid) => $"TrialStudyUid:{trialId}_{studyUid}";
//CRC上传影像提交锁key
public static string TrialStudyUidDBLock(Guid trialId, string studyUid) => $"TrialStudyUidDBLock:{trialId}_{studyUid}";
public static string TrialTaskStudyUidUploading(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialStudyUid:{trialId}_{visiTaskId}_{studyUid}";
//影像后处理上传提交锁key
public static string TrialTaskStudyUidDBLock(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialTaskStudyUidDBLock:{trialId}_{visiTaskId}_{studyUid}";
//系统匿名化
public static string SystemAnonymization => $"SystemAnonymization";
//前端国际化
public static string FrontInternational => $"FrontInternationalList";
//登录挤账号
public static string UserToken(Guid userId) => $"UserToken:{userId}";
//超时没请求接口自动退出
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
/// <summary>
/// 跳过阅片
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache";
/// <summary>
/// 开始阅片时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime";
/// <summary>
/// 开始休息时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
}
public static class CacheHelper
{
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository)
{ {
//项目缓存 var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
public static string Trial(string trialIdStr) => $"TrialId:{trialIdStr}";
//检查编号递增锁
public static string TrialStudyMaxCode(Guid trialId) => $"TrialStudyMaxCode:{trialId}";
public static string TrialStudyUidUploading(Guid trialId, string studyUid) => $"TrialStudyUid:{trialId}_{studyUid}";
//CRC上传影像提交锁key
public static string TrialStudyUidDBLock(Guid trialId, string studyUid) => $"TrialStudyUidDBLock:{trialId}_{studyUid}";
public static string TrialTaskStudyUidUploading(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialStudyUid:{trialId}_{visiTaskId}_{studyUid}";
//影像后处理上传提交锁key
public static string TrialTaskStudyUidDBLock(Guid trialId, Guid visiTaskId, string studyUid) => $"TrialTaskStudyUidDBLock:{trialId}_{visiTaskId}_{studyUid}";
//系统匿名化
public static string SystemAnonymization => $"SystemAnonymization";
//前端国际化
public static string FrontInternational => $"FrontInternationalList";
//登录挤账号
public static string UserToken(Guid userId) => $"UserToken:{userId}";
//超时没请求接口自动退出
public static string UserAutoLoginOut(Guid userId) => $"UserAutoLoginOut:{userId}";
/// <summary>
/// 跳过阅片
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache";
/// <summary>
/// 开始阅片时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime";
/// <summary>
/// 开始休息时间
/// </summary>
/// <param name="userId"></param>
/// <returns></returns>
public static string StartRestTime(Guid userId) => $"{userId}StartRestTime";
return statusStr;
} }
public static class CacheHelper public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
{ {
public static async Task<string?> GetTrialStatusAsync(Guid trialId, IRepository<Trial> _trialRepository) var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
{
var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync();
return statusStr; return list;
}
public static async Task<List<SystemAnonymization>> GetSystemAnonymizationListAsync(IRepository<SystemAnonymization> _systemAnonymizationRepository)
{
var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync();
return list;
}
} }
} }

View File

@ -1,34 +1,33 @@
using System.Diagnostics; using System.Diagnostics;
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
public class FileConvertHelper
{ {
public class FileConvertHelper
static public void ConvertWordToPdf(string inputWordFilePath, string outputPdfDir)
{ {
// 设置 libreoffice 命令行参数
string arguments = $"--headless --invisible --convert-to pdf \"{inputWordFilePath}\" --outdir \"{outputPdfDir}\"";
// 启动 libreoffice 进程
static public void ConvertWordToPdf(string inputWordFilePath, string outputPdfDir) using (Process process = new Process())
{ {
// 设置 libreoffice 命令行参数 process.StartInfo.FileName = "libreoffice";
string arguments = $"--headless --invisible --convert-to pdf \"{inputWordFilePath}\" --outdir \"{outputPdfDir}\""; process.StartInfo.Arguments = arguments;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
// 启动 libreoffice 进程 process.Start();
using (Process process = new Process())
{
process.StartInfo.FileName = "libreoffice";
process.StartInfo.Arguments = arguments;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start(); // 等待进程结束
process.WaitForExit();
// 等待进程结束
process.WaitForExit();
}
} }
} }
} }

View File

@ -1,106 +1,105 @@
using RestSharp; using RestSharp;
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
#region 对接模型
public class HolidayCalendar
{ {
public string Name { get; set; }
#region 对接模型 public string Version { get; set; }
public class HolidayCalendar public string Generated { get; set; }
{ public string Timezone { get; set; }
public string Name { get; set; } public string Author { get; set; }
public string Version { get; set; } public string URL { get; set; }
public string Generated { get; set; } public Dictionary<string, List<HolidayYear>> Years { get; set; }
public string Timezone { get; set; } }
public string Author { get; set; }
public string URL { get; set; } public class HolidayYear
public Dictionary<string, List<HolidayYear>> Years { get; set; } {
} public string Name { get; set; }
public DateTime StartDate { get; set; }
public class HolidayYear public DateTime EndDate { get; set; }
{ public int Duration { get; set; }
public string Name { get; set; } public List<DateTime> CompDays { get; set; }
public DateTime StartDate { get; set; } public string URL { get; set; }
public DateTime EndDate { get; set; } public string Memo { get; set; }
public int Duration { get; set; } }
public List<DateTime> CompDays { get; set; } #endregion
public string URL { get; set; }
public string Memo { get; set; }
}
#endregion
public class HolidayHelper
{
/// <summary>
/// github 链接https://github.com/lanceliao/china-holiday-calender?tab=readme-ov-file 
public class HolidayHelper /// 接口请求参考文档https://www.koudingke.cn/docs/zh-Hans/net-lib-docs/latest/RestSharp/Usage/Usage
{ /// </summary>
/// <summary> private static RestClient _client => new RestClient("https://www.shuyz.com/githubfiles/china-holiday-calender/master/holidayAPI.json");
/// github 链接https://github.com/lanceliao/china-holiday-calender?tab=readme-ov-file 
/// 接口请求参考文档https://www.koudingke.cn/docs/zh-Hans/net-lib-docs/latest/RestSharp/Usage/Usage private static HolidayCalendar _holidayCalendar { get; set; }
/// </summary>
private static RestClient _client => new RestClient("https://www.shuyz.com/githubfiles/china-holiday-calender/master/holidayAPI.json");
private static List<DateTime> _holidays = new List<DateTime>();
private static HolidayCalendar _holidayCalendar { get; set; } private static List<DateTime> _compensatedWorkdays = new List<DateTime>();
static HolidayHelper()
private static List<DateTime> _holidays = new List<DateTime>(); {
private static List<DateTime> _compensatedWorkdays = new List<DateTime>(); _holidayCalendar = _client.Get<HolidayCalendar>(new RestRequest()) ?? new HolidayCalendar();
static HolidayHelper() foreach (var year in _holidayCalendar.Years.Values.SelectMany(x => x))
{ {
_holidayCalendar = _client.Get<HolidayCalendar>(new RestRequest()) ?? new HolidayCalendar(); // 添加节假日到假日集合
for (DateTime date = year.StartDate; date <= year.EndDate; date = date.AddDays(1))
foreach (var year in _holidayCalendar.Years.Values.SelectMany(x => x)) {
{ _holidays.Add(date);
// 添加节假日到假日集合 }
for (DateTime date = year.StartDate; date <= year.EndDate; date = date.AddDays(1))
{ // 添加补班日到补班日集合
_holidays.Add(date); foreach (var compDay in year.CompDays)
} {
_compensatedWorkdays.Add(compDay);
// 添加补班日到补班日集合 }
foreach (var compDay in year.CompDays) }
{
_compensatedWorkdays.Add(compDay); }
}
} public static TimeSpan GetChinaWorkTimeSpan(DateTime startDate, DateTime endDate)
{
} if (startDate > endDate)
{
public static TimeSpan GetChinaWorkTimeSpan(DateTime startDate, DateTime endDate) throw new ArgumentException("结束日期必须大于或等于开始日期");
{ }
if (startDate > endDate)
{ var diffTimeSpan = endDate - startDate;
throw new ArgumentException("结束日期必须大于或等于开始日期");
} // 初始化工作日数为0
int workdays = 0;
var diffTimeSpan = endDate - startDate;
// 找出在给定日期范围内的假期和补班日
// 初始化工作日数为0 var filteredHolidays = _holidays.Where(d => d >= startDate && d <= endDate).ToList();
int workdays = 0; var filteredCompensatedWorkdays = _compensatedWorkdays.Where(d => d >= startDate && d <= endDate).ToList();
// 找出在给定日期范围内的假期和补班日 // 遍历日期范围,计算工作日 注意 这里是小于
var filteredHolidays = _holidays.Where(d => d >= startDate && d <= endDate).ToList(); for (DateTime date = startDate.Date; date < endDate.Date; date = date.AddDays(1))
var filteredCompensatedWorkdays = _compensatedWorkdays.Where(d => d >= startDate && d <= endDate).ToList(); {
// 判断是否为工作日
// 遍历日期范围,计算工作日 注意 这里是小于 bool isWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday;
for (DateTime date = startDate.Date; date < endDate.Date; date = date.AddDays(1)) bool isHoliday = filteredHolidays.Contains(date);
{ bool isCompensatedWorkday = filteredCompensatedWorkdays.Contains(date);
// 判断是否为工作日
bool isWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; // 如果是补班日,或是平日(非假期,非周末),则算作工作日
bool isHoliday = filteredHolidays.Contains(date); if (isCompensatedWorkday || (!isWeekend && !isHoliday))
bool isCompensatedWorkday = filteredCompensatedWorkdays.Contains(date); {
workdays++;
// 如果是补班日,或是平日(非假期,非周末),则算作工作日 }
if (isCompensatedWorkday || (!isWeekend && !isHoliday)) }
{
workdays++; return new TimeSpan(workdays, diffTimeSpan.Hours, diffTimeSpan.Minutes, diffTimeSpan.Seconds);
}
}
}
return new TimeSpan(workdays, diffTimeSpan.Hours, diffTimeSpan.Minutes, diffTimeSpan.Seconds);
}
}
} }

View File

@ -1,78 +1,77 @@
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
public static class IRCEmailPasswordHelper
{ {
public static class IRCEmailPasswordHelper private static readonly Random Random = new Random();
//显示位数3分之2的位数向上取整
//取哪几个个值:最后一位和前面几位
//其他3个***。
//比如hlj23@126.com
//为hlj***3@126.com
//he@126.com
//为h*** e@126.com
public static string MaskEmail(string email)
{ {
private static readonly Random Random = new Random();
//显示位数3分之2的位数向上取整 // 找到 "@" 符号的位置
//取哪几个个值:最后一位和前面几位 int atIndex = email.IndexOf('@');
//其他3个***。
//比如hlj23@126.com
//为hlj***3@126.com
//he@126.com string visiblePartBefore = email.Substring(0, atIndex);
//为h*** e@126.com
public static string MaskEmail(string email) string afterAt = email.Substring(atIndex + 1);
int visibleLength = (int)Math.Ceiling((double)visiblePartBefore.Length * 2 / 3);
// 替换中间两位字符为星号
string hiddenPartBeforeAt = visiblePartBefore.Substring(0, visibleLength - 1) + "***" + visiblePartBefore.Last();
// 组合隐藏和可见部分
string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt;
return hiddenEmail;
}
/// <summary>
/// 密码必须包含18 32 个字符2至少1个数字3) 至少1个大写字母4至少1个小写字母5至少1个特殊字符 (~!-@#$%^&*_+?)
/// </summary>
/// <returns></returns>
public static string GenerateRandomPassword(int length)
{
// 必须包含的字符组
const string numbers = "0123456789";
const string upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz";
const string specialCharacters = "~!-@#$%^&*_+?";
// 随机选择至少一个字符
char[] requiredCharacters =
{ {
numbers[Random.Next(numbers.Length)],
upperCaseLetters[Random.Next(upperCaseLetters.Length)],
lowerCaseLetters[Random.Next(lowerCaseLetters.Length)],
specialCharacters[Random.Next(specialCharacters.Length)]
};
// 找到 "@" 符号的位置 // 构建剩余的字符集,用于填充密码的其余部分
int atIndex = email.IndexOf('@'); string allCharacters = numbers + upperCaseLetters + lowerCaseLetters + specialCharacters;
string visiblePartBefore = email.Substring(0, atIndex); // 确保密码长度满足用户要求
char[] password = new char[length];
string afterAt = email.Substring(atIndex + 1); // 将必须包含的字符放入密码中
requiredCharacters.CopyTo(password, 0);
int visibleLength = (int)Math.Ceiling((double)visiblePartBefore.Length * 2 / 3); // 填充剩余的字符
for (int i = requiredCharacters.Length; i < length; i++)
// 替换中间两位字符为星号 {
string hiddenPartBeforeAt = visiblePartBefore.Substring(0, visibleLength - 1) + "***" + visiblePartBefore.Last(); password[i] = allCharacters[Random.Next(allCharacters.Length)];
// 组合隐藏和可见部分
string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt;
return hiddenEmail;
} }
/// <summary> // 随机打乱密码字符顺序
/// 密码必须包含18 32 个字符2至少1个数字3) 至少1个大写字母4至少1个小写字母5至少1个特殊字符 (~!-@#$%^&*_+?) return new string(password.OrderBy(_ => Random.Next()).ToArray());
/// </summary>
/// <returns></returns>
public static string GenerateRandomPassword(int length)
{
// 必须包含的字符组
const string numbers = "0123456789";
const string upperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
const string lowerCaseLetters = "abcdefghijklmnopqrstuvwxyz";
const string specialCharacters = "~!-@#$%^&*_+?";
// 随机选择至少一个字符
char[] requiredCharacters =
{
numbers[Random.Next(numbers.Length)],
upperCaseLetters[Random.Next(upperCaseLetters.Length)],
lowerCaseLetters[Random.Next(lowerCaseLetters.Length)],
specialCharacters[Random.Next(specialCharacters.Length)]
};
// 构建剩余的字符集,用于填充密码的其余部分
string allCharacters = numbers + upperCaseLetters + lowerCaseLetters + specialCharacters;
// 确保密码长度满足用户要求
char[] password = new char[length];
// 将必须包含的字符放入密码中
requiredCharacters.CopyTo(password, 0);
// 填充剩余的字符
for (int i = requiredCharacters.Length; i < length; i++)
{
password[i] = allCharacters[Random.Next(allCharacters.Length)];
}
// 随机打乱密码字符顺序
return new string(password.OrderBy(_ => Random.Next()).ToArray());
}
} }
} }

View File

@ -5,204 +5,203 @@ using Microsoft.Extensions.Configuration;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
public static class InternationalizationHelper
{ {
public static class InternationalizationHelper public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources);
public static FileSystemWatcher FileSystemWatcher_US { get; set; }
public static FileSystemWatcher FileSystemWatcher_CN { get; set; }
private static void VerifyFolder()
{ {
public static string JsonFileFolder = Path.Combine(AppContext.BaseDirectory, StaticData.Folder.Resources); if (!Directory.Exists(JsonFileFolder) ||
!Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase)))
public static FileSystemWatcher FileSystemWatcher_US { get; set; }
public static FileSystemWatcher FileSystemWatcher_CN { get; set; }
private static void VerifyFolder()
{ {
if (!Directory.Exists(JsonFileFolder) || throw new BusinessValidationFailedException("国际化Json文件目录有误");
!Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase)))
{
throw new BusinessValidationFailedException("国际化Json文件目录有误");
}
} }
}
public static async Task BatchAddJsonKeyValueAsync(List<BatchAddInternationalizationDto> batchAddDtos) public static async Task BatchAddJsonKeyValueAsync(List<BatchAddInternationalizationDto> batchAddDtos)
{
VerifyFolder();
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
//更新json 文件 同时更新内存缓存的数据
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
{ {
VerifyFolder(); var json = await File.ReadAllTextAsync(filePath);
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json); JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
// 添加或更新指定的键值对
//更新json 文件 同时更新内存缓存的数据 if (filePath.Contains(StaticData.En_US_Json))
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
{ {
var json = await File.ReadAllTextAsync(filePath); foreach (var item in batchAddDtos)
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
// 添加或更新指定的键值对
if (filePath.Contains(StaticData.En_US_Json))
{ {
foreach (var item in batchAddDtos) jsonObject[item.Code] = item.Value;
{
jsonObject[item.Code] = item.Value;
StaticData.En_US_Dic[item.Code] = item.Value; StaticData.En_US_Dic[item.Code] = item.Value;
//日志记录该信息方便自己人看, 返回给客户的是配置的
StaticData.Log_Locoalize_Dic[item.Code] = item.Description;
}
}
else
{
foreach (var item in batchAddDtos)
{
jsonObject[item.Code] = item.Value;
StaticData.Zh_CN_Dic[item.Code] = item.Value;
}
}
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
}
}
public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN, string description)
{
VerifyFolder();
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
//更新json 文件 同时更新内存缓存的数据
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
{
var json = await File.ReadAllTextAsync(filePath);
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
// 添加或更新指定的键值对
if (filePath.Contains(StaticData.En_US_Json))
{
jsonObject[key] = value;
StaticData.En_US_Dic[key] = value;
//日志记录该信息方便自己人看, 返回给客户的是配置的 //日志记录该信息方便自己人看, 返回给客户的是配置的
StaticData.Log_Locoalize_Dic[key] = description; StaticData.Log_Locoalize_Dic[item.Code] = item.Description;
} }
else }
else
{
foreach (var item in batchAddDtos)
{ {
jsonObject[key] = valueCN; jsonObject[item.Code] = item.Value;
StaticData.Zh_CN_Dic[key] = valueCN; StaticData.Zh_CN_Dic[item.Code] = item.Value;
} }
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
} }
await File.WriteAllTextAsync(filePath, jsonObject.ToString());
} }
}
public static async Task AddOrUpdateJsonKeyValueAsync(string key, string value, string valueCN, string description)
{
VerifyFolder();
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository<Internationalization> _internationalizationRepository) //更新json 文件 同时更新内存缓存的数据
foreach (var filePath in new string[] { usJsonPath, cnJsonPath })
{ {
//查询数据库的数据 var json = await File.ReadAllTextAsync(filePath);
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new
JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load });
// 添加或更新指定的键值对
if (filePath.Contains(StaticData.En_US_Json))
{ {
t.Code, jsonObject[key] = value;
t.Value,
t.ValueCN,
t.Description
}).ToListAsync();
//组织成json 文件 StaticData.En_US_Dic[key] = value;
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
//本地静态文件国际化需要
foreach (var tojsonItem in toJsonList)
{
StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value;
StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN;
//日志记录该信息方便自己人看, 返回给客户的是配置的 //日志记录该信息方便自己人看, 返回给客户的是配置的
StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description; StaticData.Log_Locoalize_Dic[key] = description;
} }
else
File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic));
File.WriteAllText(cnJsonPath, JsonConvert.SerializeObject(StaticData.Zh_CN_Dic));
//监测Json文件变更 实时刷新数据
if (!File.Exists(usJsonPath) || !File.Exists(cnJsonPath))
{ {
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound")); jsonObject[key] = valueCN;
StaticData.Zh_CN_Dic[key] = valueCN;
} }
// //监测Json文件变更 实时刷新数据 await File.WriteAllTextAsync(filePath, jsonObject.ToString());
FileSystemWatcher_US = new FileSystemWatcher
{
Path = Path.GetDirectoryName(usJsonPath)!,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
Filter = Path.GetFileName(usJsonPath),
EnableRaisingEvents = true,
};
// 添加文件更改事件的处理程序
FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json);
FileSystemWatcher_CN = new FileSystemWatcher
{
Path = Path.GetDirectoryName(cnJsonPath)!,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
Filter = Path.GetFileName(cnJsonPath),
EnableRaisingEvents = true,
};
FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json);
} }
private static void LoadJsonFile(string filePath)
{
Console.WriteLine("刷新json内存数据");
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false);
IConfigurationRoot enConfiguration = builder.Build();
foreach (IConfigurationSection section in enConfiguration.GetChildren())
{
if (filePath.Contains(StaticData.En_US_Json))
{
StaticData.En_US_Dic[section.Key] = section.Value;
}
else
{
StaticData.Zh_CN_Dic[section.Key] = section.Value;
}
}
}
} }
public static async Task InitInternationlizationDataAndWatchJsonFileAsync(IRepository<Internationalization> _internationalizationRepository)
{
//查询数据库的数据
var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new
{
t.Code,
t.Value,
t.ValueCN,
t.Description
}).ToListAsync();
//组织成json 文件
var usJsonPath = Path.Combine(JsonFileFolder, StaticData.En_US_Json);
var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json);
//本地静态文件国际化需要
foreach (var tojsonItem in toJsonList)
{
StaticData.En_US_Dic[tojsonItem.Code] = tojsonItem.Value;
StaticData.Zh_CN_Dic[tojsonItem.Code] = tojsonItem.ValueCN;
//日志记录该信息方便自己人看, 返回给客户的是配置的
StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description;
}
File.WriteAllText(usJsonPath, JsonConvert.SerializeObject(StaticData.En_US_Dic));
File.WriteAllText(cnJsonPath, JsonConvert.SerializeObject(StaticData.Zh_CN_Dic));
//监测Json文件变更 实时刷新数据
if (!File.Exists(usJsonPath) || !File.Exists(cnJsonPath))
{
throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound"));
}
// //监测Json文件变更 实时刷新数据
FileSystemWatcher_US = new FileSystemWatcher
{
Path = Path.GetDirectoryName(usJsonPath)!,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
Filter = Path.GetFileName(usJsonPath),
EnableRaisingEvents = true,
};
// 添加文件更改事件的处理程序
FileSystemWatcher_US.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.En_US_Json);
FileSystemWatcher_CN = new FileSystemWatcher
{
Path = Path.GetDirectoryName(cnJsonPath)!,
NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size,
Filter = Path.GetFileName(cnJsonPath),
EnableRaisingEvents = true,
};
FileSystemWatcher_CN.Changed += (sender, e) => LoadJsonFile(StaticData.Folder.Resources + "\\" + StaticData.Zh_CN_Json);
}
private static void LoadJsonFile(string filePath)
{
Console.WriteLine("刷新json内存数据");
IConfigurationBuilder builder = new ConfigurationBuilder().AddJsonFile(filePath, false, false);
IConfigurationRoot enConfiguration = builder.Build();
foreach (IConfigurationSection section in enConfiguration.GetChildren())
{
if (filePath.Contains(StaticData.En_US_Json))
{
StaticData.En_US_Dic[section.Key] = section.Value;
}
else
{
StaticData.Zh_CN_Dic[section.Key] = section.Value;
}
}
}
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,93 +4,92 @@ using System.Globalization;
using Xceed.Document.NET; using Xceed.Document.NET;
using Xceed.Words.NET; using Xceed.Words.NET;
namespace IRaCIS.Core.Application.Helper namespace IRaCIS.Core.Application.Helper;
/// <summary>
/// 利用DocX 库 处理word国际化模板
/// </summary>
public static class WordTempleteHelper
{ {
/// <summary> public static void DocX_GetInternationalTempleteStream(string filePath, Stream memoryStream)
/// 利用DocX 库 处理word国际化模板
/// </summary>
public static class WordTempleteHelper
{ {
public static void DocX_GetInternationalTempleteStream(string filePath, Stream memoryStream)
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
using (DocX document = DocX.Load(filePath))
{ {
// 查找书签
var bookmarkEn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == StaticData.CultureInfo.en_US_bookMark);
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US; if (bookmarkEn_Start != null)
using (DocX document = DocX.Load(filePath))
{ {
// 查找书签 // 获取书签的起始位置
var bookmarkEn_Start = document.Bookmarks.FirstOrDefault(b => b.Name == StaticData.CultureInfo.en_US_bookMark); //int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex;
if (bookmarkEn_Start != null) var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex;
// 创建一个要删除段落的列表
List<Paragraph> paragraphsToRemove = new List<Paragraph>();
foreach (var item in document.Paragraphs)
{ {
// 获取书签的起始位置 //中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的
//int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex;
var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex; if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos)
// 创建一个要删除段落的列表
List<Paragraph> paragraphsToRemove = new List<Paragraph>();
foreach (var item in document.Paragraphs)
{ {
//中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的 paragraphsToRemove.Add(item);
if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos)
{
paragraphsToRemove.Add(item);
}
} }
foreach (var paragraph in paragraphsToRemove)
{
document.RemoveParagraph(paragraph);
}
} }
// 保存修改 foreach (var paragraph in paragraphsToRemove)
document.SaveAs(memoryStream);
}
}
public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
XWPFDocument doc = new XWPFDocument(fs);
// 查找包含指定书签的段落及其索引
var bookmarkParagraph = doc.Paragraphs
.FirstOrDefault(p => p.GetCTP().GetBookmarkStartList().Any(b => b.name == StaticData.CultureInfo.en_US_bookMark));
if (bookmarkParagraph != null)
{ {
int bookmarkIndex = doc.Paragraphs.IndexOf(bookmarkParagraph); document.RemoveParagraph(paragraph);
if (isEn_US)
{
// 从书签所在段落开始,删除之前的所有段落
for (int i = bookmarkIndex - 1; i >= 0; i--)
{
doc.RemoveBodyElement(i);
}
}
else
{
// 删除书签之后的所有段落
for (int i = doc.Paragraphs.Count - 1; i >= bookmarkIndex; i--)
{
doc.RemoveBodyElement(i);
}
}
} }
doc.Write(memoryStream);
}
}
}
// 保存修改
document.SaveAs(memoryStream);
}
} }
public static void Npoi_GetInternationalTempleteStream(string filePath, Stream memoryStream)
{
var isEn_US = CultureInfo.CurrentCulture.Name == StaticData.CultureInfo.en_US;
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
XWPFDocument doc = new XWPFDocument(fs);
// 查找包含指定书签的段落及其索引
var bookmarkParagraph = doc.Paragraphs
.FirstOrDefault(p => p.GetCTP().GetBookmarkStartList().Any(b => b.name == StaticData.CultureInfo.en_US_bookMark));
if (bookmarkParagraph != null)
{
int bookmarkIndex = doc.Paragraphs.IndexOf(bookmarkParagraph);
if (isEn_US)
{
// 从书签所在段落开始,删除之前的所有段落
for (int i = bookmarkIndex - 1; i >= 0; i--)
{
doc.RemoveBodyElement(i);
}
}
else
{
// 删除书签之后的所有段落
for (int i = doc.Paragraphs.Count - 1; i >= bookmarkIndex; i--)
{
doc.RemoveBodyElement(i);
}
}
}
doc.Write(memoryStream);
}
}
} }