diff --git a/IRaCIS.Core.API/IRaCIS.Core.API.xml b/IRaCIS.Core.API/IRaCIS.Core.API.xml index 068efa5a9..061f40bcc 100644 --- a/IRaCIS.Core.API/IRaCIS.Core.API.xml +++ b/IRaCIS.Core.API/IRaCIS.Core.API.xml @@ -29,7 +29,7 @@ - + 系统用户登录接口[New] @@ -41,7 +41,7 @@ 新记录Id - + 添加或更新工作量[AUTH] diff --git a/IRaCIS.Core.Application/BaseService.cs b/IRaCIS.Core.Application/BaseService.cs index 8adf71179..c153e02d4 100644 --- a/IRaCIS.Core.Application/BaseService.cs +++ b/IRaCIS.Core.Application/BaseService.cs @@ -10,7 +10,7 @@ using Panda.DynamicWebApi.Attributes; using System.Diagnostics.CodeAnalysis; using ZiggyCreatures.Caching.Fusion; -namespace IRaCIS.Core.Application +namespace IRaCIS.Core.Application.Service { #pragma warning disable CS8618 diff --git a/IRaCIS.Core.Application/BusinessFilter/GlobalExceptionHandler.cs b/IRaCIS.Core.Application/BusinessFilter/GlobalExceptionHandler.cs index b8f82f8d5..70688e7e2 100644 --- a/IRaCIS.Core.Application/BusinessFilter/GlobalExceptionHandler.cs +++ b/IRaCIS.Core.Application/BusinessFilter/GlobalExceptionHandler.cs @@ -3,42 +3,41 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; -namespace IRaCIS.Core.Application.BusinessFilter +namespace IRaCIS.Core.Application.BusinessFilter; + + +/// +/// 不生效,不知道为啥 +/// +public class GlobalExceptionHandler : IExceptionHandler { - - /// - /// 不生效,不知道为啥 - /// - public class GlobalExceptionHandler : IExceptionHandler + private readonly ILogger _logger; + public GlobalExceptionHandler(ILogger logger) + { + this._logger = logger; + } + public ValueTask TryHandleAsync( + HttpContext httpContext, + Exception exception, + CancellationToken cancellationToken) { - private readonly ILogger _logger; - public GlobalExceptionHandler(ILogger logger) - { - this._logger = logger; - } - public ValueTask TryHandleAsync( - HttpContext httpContext, - Exception exception, - CancellationToken cancellationToken) - { - httpContext.Response.ContentType = "application/json"; + httpContext.Response.ContentType = "application/json"; - var ex = exception; - var errorInfo = $"Exception: {ex.Message}[{ex.StackTrace}]" + (ex.InnerException != null ? $" InnerException: {ex.InnerException.Message}[{ex.InnerException.StackTrace}]" : ""); + var ex = exception; + 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 ValueTask.FromResult(false); - } + // return true to signal that this exception is handled + return ValueTask.FromResult(false); } } diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs b/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs index c77dacdfa..a52ca0a52 100644 --- a/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs +++ b/IRaCIS.Core.Application/BusinessFilter/ModelActionFilter .cs @@ -4,33 +4,31 @@ using Microsoft.Extensions.Localization; using Newtonsoft.Json; -namespace IRaCIS.Core.Application.Filter +namespace IRaCIS.Core.Application.Filter; + + + +public class ModelActionFilter : ActionFilterAttribute, IActionFilter { - - - public class ModelActionFilter : ActionFilterAttribute, IActionFilter + public IStringLocalizer _localizer; + public ModelActionFilter(IStringLocalizer localizer) { - 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))); - } - } + _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))); + } + } } diff --git a/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs b/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs index 94187d5d8..994d9fa09 100644 --- a/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs +++ b/IRaCIS.Core.Application/BusinessFilter/ModelBinding.cs @@ -2,26 +2,25 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Filters; 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 - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter + public void OnResourceExecuting(ResourceExecutingContext context) { - public void OnResourceExecuting(ResourceExecutingContext context) - { - var factories = context.ValueProviderFactories; - //factories.RemoveType(); - factories.RemoveType(); - factories.RemoveType(); - context.HttpContext.Request.EnableBuffering(); - } - - public void OnResourceExecuted(ResourceExecutedContext context) - { - } + var factories = context.ValueProviderFactories; + //factories.RemoveType(); + factories.RemoveType(); + factories.RemoveType(); + context.HttpContext.Request.EnableBuffering(); + } + + public void OnResourceExecuted(ResourceExecutedContext context) + { } - #endregion } +#endregion diff --git a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs index ce2a978eb..1825c3728 100644 --- a/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/ProjectExceptionFilter.cs @@ -5,72 +5,71 @@ using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Localization; 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 _logger; + + public IStringLocalizer _localizer; + + public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + _localizer = localizer; + } + public void OnException(ExceptionContext context) + { + //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 - public IStringLocalizer _localizer; - - public ProjectExceptionFilter(IStringLocalizer localizer, ILogger logger) + if (!context.ExceptionHandled) { - _logger = logger; - _localizer = localizer; - } - public void OnException(ExceptionContext context) - { - //context.ExceptionHandled;//记录当前这个异常是否已经被处理过了 - - if (!context.ExceptionHandled) + if (context.Exception.GetType().Name == "DbUpdateConcurrencyException") { - 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)) { - //---并发更新,当前不允许该操作 - context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["ProjectException_ConcurrentUpdateNotAllowed"] + context.Exception.Message)); + info = $"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"; } - 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(context.Exception.Message, "", error!.Code, localizedInfo: info)); - context.Result = new JsonResult(ResponseOutput.NotOk(context.Exception.Message, "", error!.Code, localizedInfo: info)); - - - //warning 级别记录 - //_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"); - } - 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"); - + //warning 级别记录 + //_logger.LogWarning($"[{error!.LocalizedKey}]:{StaticData.Log_Locoalize_Dic[error!.LocalizedKey]}"); + } + 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 + { + //继续 } } } diff --git a/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs b/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs index 58467110b..509b6dc83 100644 --- a/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/TrialResourceFilter.cs @@ -8,80 +8,124 @@ using System.Text.RegularExpressions; using ZiggyCreatures.Caching.Fusion; using static IRaCIS.Core.Domain.Share.StaticData; -namespace IRaCIS.Core.Application.Filter +namespace IRaCIS.Core.Application.Filter; + +/// +/// 主要为了 处理项目结束 锁库,不允许操作 +/// +public class TrialResourceFilter : Attribute, IAsyncResourceFilter { - /// - /// 主要为了 处理项目结束 锁库,不允许操作 - /// - public class TrialResourceFilter : Attribute, IAsyncResourceFilter + private readonly IUserInfo _userInfo; + private readonly IFusionCache _fusionCache; + public IStringLocalizer _localizer; + private readonly IRepository _trialRepository; + private readonly List _trialOptList = new List(); + + + public TrialResourceFilter(IFusionCache fusionCache, IRepository trialRepository, IStringLocalizer localizer, IUserInfo userInfo, string trialOpt = null, string trialOpt2 = null, string trialOpt3 = null) { - private readonly IUserInfo _userInfo; - private readonly IFusionCache _fusionCache; - public IStringLocalizer _localizer; - private readonly IRepository _trialRepository; - private readonly List _trialOptList = new List(); + _fusionCache = fusionCache; + _userInfo = userInfo; + _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()); - public TrialResourceFilter(IFusionCache fusionCache, IRepository 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; - _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()); + //---对不起,您的账户没有操作权限。 + context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"])); + return; } - //优先选择异步的方法 - public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next) + #endregion + + + + //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(); - //var _trialOptList= typeFilter.Arguments.Select(t => t.ToString()).ToList(); + trialIdStr = context.HttpContext.Request.Query["trialId"]; + } - #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(); - // 后期列举出具体的类型,其他任何用户类型,都不允许操作 - if (_userInfo.UserTypeEnumInt == (int)UserTypeEnum.CRA && _userInfo.RequestUrl.ToLower() != "TrialDocument/userConfirm".ToLower()) + var trialIdIndex = headerStr.IndexOf("trialId"); + + + 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) { - //---对不起,您的账户没有操作权限。 - context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_NoAccessPermission"])); - - return; + trialIdStr = matchResult.Value; + } + else + { + //---正则取请求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 - var trialIdStr = string.Empty; - - if (!string.IsNullOrWhiteSpace(context.HttpContext.Request.Query["trialId"])) + if (trialIdIndex > -1) { - trialIdStr = context.HttpContext.Request.Query["trialId"]; - } + // (?<="trialId" *: *").*?(?=",) - //先尝试从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(); + //使用正则 [0-9a-fA-F]{8}(-[0-9a-fA-F]{4}){3}-[0-9a-fA-F]{12} - var trialIdIndex = headerStr.IndexOf("trialId"); - - - var matchResult = Regex.Match(headerStr.Substring(trialIdIndex), @"[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 { @@ -89,93 +133,48 @@ namespace IRaCIS.Core.Application.Filter context.Result = new JsonResult(ResponseOutput.NotOk(_localizer["TrialResource_ReferTrialIdFailed"])); } - } - 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); - - 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 + //使用字符串取 如果是swagger 可能有时取的不对 因为空格的原因 + //trialIdStr = contentFromBody.Substring(trialIdIndex + "TrialId".Length + 4, 3 } - //通过path 或者body 找到trialId 了 - if (!string.IsNullOrWhiteSpace(trialIdStr)) + #endregion + } + + //通过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(); - } + } + // 项目停止、或者完成 不允许操作 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"])); + } + + } } diff --git a/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs index daf59c30f..b8a4977f8 100644 --- a/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs +++ b/IRaCIS.Core.Application/BusinessFilter/UnifiedApiResultFilter.cs @@ -3,121 +3,120 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; -namespace IRaCIS.Core.Application.Service.BusinessFilter +namespace IRaCIS.Core.Application.Service.BusinessFilter; + +/// +/// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 +/// by zhouhang 2021.09.12 周末 +/// +public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter { - /// - /// 统一返回前端数据包装,之前在控制器包装,现在修改为动态Api 在ResultFilter这里包装,减少重复冗余代码 - /// by zhouhang 2021.09.12 周末 - /// - public class UnifiedApiResultFilter : Attribute, IAsyncResultFilter + private readonly ILogger _logger; + + public UnifiedApiResultFilter(ILogger logger) { - private readonly ILogger _logger; + _logger = logger; + } - public UnifiedApiResultFilter(ILogger logger) + /// + /// 异步版本 + /// + /// + /// + /// + public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) + { + + if (context.Result is ObjectResult objectResult) { - _logger = logger; - } + var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; - /// - /// 异步版本 - /// - /// - /// - /// - public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) - { - - if (context.Result is ObjectResult objectResult) + //是200 并且没有包装 那么包装结果 + if (statusCode == 200 && !(objectResult.Value is IResponseOutput)) { - var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode; - //是200 并且没有包装 那么包装结果 - if (statusCode == 200 && !(objectResult.Value is IResponseOutput)) + var type = objectResult.Value?.GetType(); + + + if (type != null && type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(ValueTuple<,>) || type.GetGenericTypeDefinition() == typeof(Tuple<,>))) { - 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(); - } + //报错 + //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(); } - //如果是200 是IResponseOutput 记录下日志 - else if (statusCode == 200 && (objectResult.Value is IResponseOutput)) + else { - var result = objectResult.Value as IResponseOutput; - - 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")); + var apiResponse = ResponseOutput.Ok(objectResult.Value); 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) + //如果是200 是IResponseOutput 记录下日志 + else if (statusCode == 200 && (objectResult.Value is IResponseOutput)) { - if (type.IsGenericType) + var result = objectResult.Value as IResponseOutput; + + if (!string.IsNullOrWhiteSpace(result.LocalizedInfo)) { - var genType = type.GetGenericTypeDefinition(); - if (genType == typeof(Tuple<>) - || genType == typeof(Tuple<,>) - || genType == typeof(Tuple<,>)) - return true; + //统一在这里记录国际化的日志信息 + _logger.LogWarning($"{result.LocalizedInfo}"); } - 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; } } diff --git a/IRaCIS.Core.Application/GlobalUsings.cs b/IRaCIS.Core.Application/GlobalUsings.cs index 1183f17bd..6798764ad 100644 --- a/IRaCIS.Core.Application/GlobalUsings.cs +++ b/IRaCIS.Core.Application/GlobalUsings.cs @@ -1,5 +1,4 @@ global using AutoMapper.QueryableExtensions; -global using IRaCIS.Core.Application; global using IRaCIS.Core.Application.Service; global using IRaCIS.Core.Domain.Models; global using IRaCIS.Core.Infra.EFCore; diff --git a/IRaCIS.Core.Application/Helper/CacheHelper.cs b/IRaCIS.Core.Application/Helper/CacheHelper.cs index 33d9cb9cd..c1cf93748 100644 --- a/IRaCIS.Core.Application/Helper/CacheHelper.cs +++ b/IRaCIS.Core.Application/Helper/CacheHelper.cs @@ -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}"; + + /// + /// 跳过阅片 + /// + /// + /// + public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache"; + + + /// + /// 开始阅片时间 + /// + /// + /// + public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime"; + + /// + /// 开始休息时间 + /// + /// + /// + public static string StartRestTime(Guid userId) => $"{userId}StartRestTime"; + +} + +public static class CacheHelper +{ + public static async Task GetTrialStatusAsync(Guid trialId, IRepository _trialRepository) { - //项目缓存 - 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}"; - - /// - /// 跳过阅片 - /// - /// - /// - public static string SkipReadingCacheKey(Guid userId) => $"{userId}SkipReadingCache"; - - - /// - /// 开始阅片时间 - /// - /// - /// - public static string StartReadingTimeKey(Guid userId) => $"{userId}StartReadingTime"; - - /// - /// 开始休息时间 - /// - /// - /// - public static string StartRestTime(Guid userId) => $"{userId}StartRestTime"; + var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync(); + return statusStr; } - public static class CacheHelper + public static async Task> GetSystemAnonymizationListAsync(IRepository _systemAnonymizationRepository) { - public static async Task GetTrialStatusAsync(Guid trialId, IRepository _trialRepository) - { - var statusStr = await _trialRepository.Where(t => t.Id == trialId, ignoreQueryFilters: true).Select(t => t.TrialStatusStr).FirstOrDefaultAsync(); + var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync(); - return statusStr; - } - - public static async Task> GetSystemAnonymizationListAsync(IRepository _systemAnonymizationRepository) - { - var list = await _systemAnonymizationRepository.Where(t => t.IsEnable).ToListAsync(); - - return list; - } + return list; } } diff --git a/IRaCIS.Core.Application/Helper/FileConvertHelper.cs b/IRaCIS.Core.Application/Helper/FileConvertHelper.cs index 51c3a5f65..5b34f64f3 100644 --- a/IRaCIS.Core.Application/Helper/FileConvertHelper.cs +++ b/IRaCIS.Core.Application/Helper/FileConvertHelper.cs @@ -1,34 +1,33 @@ 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}\""; - - static public void ConvertWordToPdf(string inputWordFilePath, string outputPdfDir) + // 启动 libreoffice 进程 + using (Process process = new Process()) { - // 设置 libreoffice 命令行参数 - string arguments = $"--headless --invisible --convert-to pdf \"{inputWordFilePath}\" --outdir \"{outputPdfDir}\""; + 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; - // 启动 libreoffice 进程 - 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.Start(); - - // 等待进程结束 - process.WaitForExit(); - } + // 等待进程结束 + process.WaitForExit(); } - - } + + } diff --git a/IRaCIS.Core.Application/Helper/HolidayHelper.cs b/IRaCIS.Core.Application/Helper/HolidayHelper.cs index cbd3be004..dede05206 100644 --- a/IRaCIS.Core.Application/Helper/HolidayHelper.cs +++ b/IRaCIS.Core.Application/Helper/HolidayHelper.cs @@ -1,106 +1,105 @@ using RestSharp; -namespace IRaCIS.Core.Application.Helper +namespace IRaCIS.Core.Application.Helper; + + +#region 对接模型 +public class HolidayCalendar { - - #region 对接模型 - public class HolidayCalendar - { - public string Name { get; set; } - public string Version { get; set; } - public string Generated { get; set; } - public string Timezone { get; set; } - public string Author { get; set; } - public string URL { get; set; } - public Dictionary> Years { get; set; } - } - - public class HolidayYear - { - public string Name { get; set; } - public DateTime StartDate { get; set; } - public DateTime EndDate { get; set; } - public int Duration { get; set; } - public List CompDays { get; set; } - public string URL { get; set; } - public string Memo { get; set; } - } - #endregion - - - - - public class HolidayHelper - { - /// - /// 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 RestClient _client => new RestClient("https://www.shuyz.com/githubfiles/china-holiday-calender/master/holidayAPI.json"); - - private static HolidayCalendar _holidayCalendar { get; set; } - - - private static List _holidays = new List(); - private static List _compensatedWorkdays = new List(); - - static HolidayHelper() - { - _holidayCalendar = _client.Get(new RestRequest()) ?? new HolidayCalendar(); - - foreach (var year in _holidayCalendar.Years.Values.SelectMany(x => x)) - { - // 添加节假日到假日集合 - for (DateTime date = year.StartDate; date <= year.EndDate; date = date.AddDays(1)) - { - _holidays.Add(date); - } - - // 添加补班日到补班日集合 - foreach (var compDay in year.CompDays) - { - _compensatedWorkdays.Add(compDay); - } - } - - } - - public static TimeSpan GetChinaWorkTimeSpan(DateTime startDate, DateTime endDate) - { - if (startDate > endDate) - { - throw new ArgumentException("结束日期必须大于或等于开始日期"); - } - - var diffTimeSpan = endDate - startDate; - - // 初始化工作日数为0 - int workdays = 0; - - // 找出在给定日期范围内的假期和补班日 - var filteredHolidays = _holidays.Where(d => d >= startDate && d <= endDate).ToList(); - var filteredCompensatedWorkdays = _compensatedWorkdays.Where(d => d >= startDate && d <= endDate).ToList(); - - // 遍历日期范围,计算工作日 注意 这里是小于 - for (DateTime date = startDate.Date; date < endDate.Date; date = date.AddDays(1)) - { - // 判断是否为工作日 - bool isWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; - bool isHoliday = filteredHolidays.Contains(date); - bool isCompensatedWorkday = filteredCompensatedWorkdays.Contains(date); - - // 如果是补班日,或是平日(非假期,非周末),则算作工作日 - if (isCompensatedWorkday || (!isWeekend && !isHoliday)) - { - workdays++; - } - } - - return new TimeSpan(workdays, diffTimeSpan.Hours, diffTimeSpan.Minutes, diffTimeSpan.Seconds); - - - } - - - } + public string Name { get; set; } + public string Version { get; set; } + public string Generated { get; set; } + public string Timezone { get; set; } + public string Author { get; set; } + public string URL { get; set; } + public Dictionary> Years { get; set; } +} + +public class HolidayYear +{ + public string Name { get; set; } + public DateTime StartDate { get; set; } + public DateTime EndDate { get; set; } + public int Duration { get; set; } + public List CompDays { get; set; } + public string URL { get; set; } + public string Memo { get; set; } +} +#endregion + + + + +public class HolidayHelper +{ + /// + /// 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 RestClient _client => new RestClient("https://www.shuyz.com/githubfiles/china-holiday-calender/master/holidayAPI.json"); + + private static HolidayCalendar _holidayCalendar { get; set; } + + + private static List _holidays = new List(); + private static List _compensatedWorkdays = new List(); + + static HolidayHelper() + { + _holidayCalendar = _client.Get(new RestRequest()) ?? new HolidayCalendar(); + + foreach (var year in _holidayCalendar.Years.Values.SelectMany(x => x)) + { + // 添加节假日到假日集合 + for (DateTime date = year.StartDate; date <= year.EndDate; date = date.AddDays(1)) + { + _holidays.Add(date); + } + + // 添加补班日到补班日集合 + foreach (var compDay in year.CompDays) + { + _compensatedWorkdays.Add(compDay); + } + } + + } + + public static TimeSpan GetChinaWorkTimeSpan(DateTime startDate, DateTime endDate) + { + if (startDate > endDate) + { + throw new ArgumentException("结束日期必须大于或等于开始日期"); + } + + var diffTimeSpan = endDate - startDate; + + // 初始化工作日数为0 + int workdays = 0; + + // 找出在给定日期范围内的假期和补班日 + var filteredHolidays = _holidays.Where(d => d >= startDate && d <= endDate).ToList(); + var filteredCompensatedWorkdays = _compensatedWorkdays.Where(d => d >= startDate && d <= endDate).ToList(); + + // 遍历日期范围,计算工作日 注意 这里是小于 + for (DateTime date = startDate.Date; date < endDate.Date; date = date.AddDays(1)) + { + // 判断是否为工作日 + bool isWeekend = date.DayOfWeek == DayOfWeek.Saturday || date.DayOfWeek == DayOfWeek.Sunday; + bool isHoliday = filteredHolidays.Contains(date); + bool isCompensatedWorkday = filteredCompensatedWorkdays.Contains(date); + + // 如果是补班日,或是平日(非假期,非周末),则算作工作日 + if (isCompensatedWorkday || (!isWeekend && !isHoliday)) + { + workdays++; + } + } + + return new TimeSpan(workdays, diffTimeSpan.Hours, diffTimeSpan.Minutes, diffTimeSpan.Seconds); + + + } + + } diff --git a/IRaCIS.Core.Application/Helper/IRCEmailPasswordHelper.cs b/IRaCIS.Core.Application/Helper/IRCEmailPasswordHelper.cs index a2c23c1bd..efb9ad4e2 100644 --- a/IRaCIS.Core.Application/Helper/IRCEmailPasswordHelper.cs +++ b/IRaCIS.Core.Application/Helper/IRCEmailPasswordHelper.cs @@ -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的位数,向上取整 - //取哪几个个值:最后一位和前面几位 - //其他:3个***。 - //比如:hlj23@126.com - //为:hlj***3@126.com + // 找到 "@" 符号的位置 + int atIndex = email.IndexOf('@'); - //he@126.com - //为:h*** e@126.com + string visiblePartBefore = email.Substring(0, atIndex); - 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; + } + + /// + /// 密码必须包含:1)8 – 32 个字符;2)至少1个数字;3) 至少1个大写字母;4)至少1个小写字母;5)至少1个特殊字符 (~!-@#$%^&*_+?) + /// + /// + 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); - - // 替换中间两位字符为星号 - string hiddenPartBeforeAt = visiblePartBefore.Substring(0, visibleLength - 1) + "***" + visiblePartBefore.Last(); - - - // 组合隐藏和可见部分 - string hiddenEmail = hiddenPartBeforeAt + "@" + afterAt; - - return hiddenEmail; + // 填充剩余的字符 + for (int i = requiredCharacters.Length; i < length; i++) + { + password[i] = allCharacters[Random.Next(allCharacters.Length)]; } - /// - /// 密码必须包含:1)8 – 32 个字符;2)至少1个数字;3) 至少1个大写字母;4)至少1个小写字母;5)至少1个特殊字符 (~!-@#$%^&*_+?) - /// - /// - 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()); - } + // 随机打乱密码字符顺序 + return new string(password.OrderBy(_ => Random.Next()).ToArray()); } } diff --git a/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs b/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs index 390210a11..a1b47c837 100644 --- a/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs +++ b/IRaCIS.Core.Application/Helper/InternationalizationHelper.cs @@ -5,204 +5,203 @@ using Microsoft.Extensions.Configuration; using Newtonsoft.Json; 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); - - public static FileSystemWatcher FileSystemWatcher_US { get; set; } - public static FileSystemWatcher FileSystemWatcher_CN { get; set; } - - private static void VerifyFolder() + if (!Directory.Exists(JsonFileFolder) || + !Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase))) { - if (!Directory.Exists(JsonFileFolder) || - !Directory.GetFiles(JsonFileFolder).Any(filePath => Path.GetExtension(filePath).Equals(".json", StringComparison.OrdinalIgnoreCase))) - { - throw new BusinessValidationFailedException("国际化Json文件目录有误"); - } + throw new BusinessValidationFailedException("国际化Json文件目录有误"); } + } - public static async Task BatchAddJsonKeyValueAsync(List batchAddDtos) + public static async Task BatchAddJsonKeyValueAsync(List 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); - var cnJsonPath = Path.Combine(JsonFileFolder, StaticData.Zh_CN_Json); + JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load }); + // 添加或更新指定的键值对 - //更新json 文件 同时更新内存缓存的数据 - foreach (var filePath in new string[] { usJsonPath, cnJsonPath }) + if (filePath.Contains(StaticData.En_US_Json)) { - var json = await File.ReadAllTextAsync(filePath); - - JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load }); - - // 添加或更新指定的键值对 - - if (filePath.Contains(StaticData.En_US_Json)) + foreach (var item in batchAddDtos) { - foreach (var item in batchAddDtos) - { - jsonObject[item.Code] = item.Value; + jsonObject[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.En_US_Dic[item.Code] = item.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 _internationalizationRepository) + //更新json 文件 同时更新内存缓存的数据 + foreach (var filePath in new string[] { usJsonPath, cnJsonPath }) { - //查询数据库的数据 - var toJsonList = await _internationalizationRepository.Where(t => t.InternationalizationType == 1).Select(t => new + var json = await File.ReadAllTextAsync(filePath); + + JObject jsonObject = JObject.Parse(json, new JsonLoadSettings() { CommentHandling = CommentHandling.Load }); + + // 添加或更新指定的键值对 + + if (filePath.Contains(StaticData.En_US_Json)) { - t.Code, - t.Value, - t.ValueCN, - t.Description - }).ToListAsync(); + jsonObject[key] = value; - //组织成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.En_US_Dic[key] = value; //日志记录该信息方便自己人看, 返回给客户的是配置的 - StaticData.Log_Locoalize_Dic[tojsonItem.Code] = tojsonItem.Description; + StaticData.Log_Locoalize_Dic[key] = 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)) + else { - throw new BusinessValidationFailedException(StaticData.International("IRaCISCHangfireJob_FileNotFound")); + jsonObject[key] = valueCN; + + StaticData.Zh_CN_Dic[key] = valueCN; } - // //监测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); + await File.WriteAllTextAsync(filePath, jsonObject.ToString()); } - - - 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 _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; + } + } + } + + + + + + } diff --git a/IRaCIS.Core.Application/Helper/OSSService.cs b/IRaCIS.Core.Application/Helper/OSSService.cs index 1239cded2..eb441c9e3 100644 --- a/IRaCIS.Core.Application/Helper/OSSService.cs +++ b/IRaCIS.Core.Application/Helper/OSSService.cs @@ -15,268 +15,337 @@ using Minio.DataModel.Args; using System.Reactive.Linq; using System.Runtime.InteropServices; -namespace IRaCIS.Core.Application.Helper +namespace IRaCIS.Core.Application.Helper; + +#region 绑定和返回模型 + +[LowerCamelCaseJson] +public class MinIOOptions : AWSOptions { - #region 绑定和返回模型 + public int Port { get; set; } - [LowerCamelCaseJson] - public class MinIOOptions : AWSOptions +} + + +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 UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true); + public Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true); + + public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath); + + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + public Task GetSignedUrl(string ossRelativePath); + + public Task DeleteFromPrefix(string prefix); + + public Task GetObjectStoreTempToken(); +} + + +public class OSSService : IOSSService +{ + public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } + + private AliyunOSSTempToken AliyunOSSTempToken { get; set; } + + private AWSTempToken AWSTempToken { get; set; } + + + public OSSService(IOptionsMonitor options) { - public int Port { get; set; } + ObjectStoreServiceOptions = options.CurrentValue; + GetObjectStoreTempToken().GetAwaiter().GetResult(); } - - public class AWSOptions + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true) { - 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; } - } + var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}"; - 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 UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true); - public Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true); - - public Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath); - - public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } - - public Task GetSignedUrl(string ossRelativePath); - - public Task DeleteFromPrefix(string prefix); - - public Task GetObjectStoreTempToken(); - } - - - public class OSSService : IOSSService - { - public ObjectStoreServiceOptions ObjectStoreServiceOptions { get; set; } - - private AliyunOSSTempToken AliyunOSSTempToken { get; set; } - - private AWSTempToken AWSTempToken { get; set; } - - - public OSSService(IOptionsMonitor options) + try { - ObjectStoreServiceOptions = options.CurrentValue; - - GetObjectStoreTempToken().GetAwaiter().GetResult(); - } - - /// - /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder - /// - /// - /// - /// - /// - /// - public async Task UploadToOSSAsync(Stream fileStream, string oosFolderPath, string fileRealName, bool isFileNameAddGuid = true) - { - var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{fileRealName}" : $"{oosFolderPath}/{fileRealName}"; - - try + using (var memoryStream = new MemoryStream()) { - using (var memoryStream = new MemoryStream()) + fileStream.Seek(0, SeekOrigin.Begin); + + fileStream.CopyTo(memoryStream); + + memoryStream.Seek(0, SeekOrigin.Begin); + + + if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") { - fileStream.Seek(0, SeekOrigin.Begin); + var aliConfig = ObjectStoreServiceOptions.AliyunOSS; - fileStream.CopyTo(memoryStream); - - memoryStream.Seek(0, SeekOrigin.Begin); + var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken); - if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") + + // 上传文件 + 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 { - var aliConfig = ObjectStoreServiceOptions.AliyunOSS; + RegionEndpoint = RegionEndpoint.USEast1, + UseHttp = true, + }; - var _ossClient = new OssClient(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? aliConfig.EndPoint : aliConfig.InternalEndpoint, AliyunOSSTempToken.AccessKeyId, AliyunOSSTempToken.AccessKeySecret, AliyunOSSTempToken.SecurityToken); + var amazonS3Client = new AmazonS3Client(credentials, clientConfig); - - - // 上传文件 - var result = _ossClient.PutObject(aliConfig.BucketName, ossRelativePath, memoryStream); - - } - else if (ObjectStoreServiceOptions.ObjectStoreUse == "MinIO") + var putObjectRequest = new Amazon.S3.Model.PutObjectRequest() { - var minIOConfig = ObjectStoreServiceOptions.MinIO; + BucketName = awsConfig.BucketName, + InputStream = memoryStream, + Key = ossRelativePath, + }; - - 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("未定义的存储介质类型"); - } + await amazonS3Client.PutObjectAsync(putObjectRequest); + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); } } - catch (Exception ex) - { - - throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); - } - - - - - return "/" + ossRelativePath; + } + catch (Exception ex) + { + throw new BusinessValidationFailedException($"上传发生异常:{ex.Message}"); } - /// - /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder - /// - /// - /// - /// - /// - /// - public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true) - { - var localFileName = Path.GetFileName(localFilePath); - var ossRelativePath = isFileNameAddGuid ? $"{oosFolderPath}/{Guid.NewGuid()}_{localFileName}" : $"{oosFolderPath}/{localFileName}"; + return "/" + ossRelativePath; + + } + + + + /// + /// oosFolderPath 不要 "/ "开头 应该: TempFolder/ChildFolder + /// + /// + /// + /// + /// + /// + public async Task UploadToOSSAsync(string localFilePath, string oosFolderPath, bool isFileNameAddGuid = true) + { + 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) + { + + ossRelativePath = ossRelativePath.TrimStart('/'); + try + { if (ObjectStoreServiceOptions.ObjectStoreUse == "AliyunOSS") @@ -286,24 +355,31 @@ namespace IRaCIS.Core.Application.Helper 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); + 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 putObjectArgs = new PutObjectArgs() - .WithBucket(minIOConfig.BucketName) - .WithObject(ossRelativePath) - .WithFileName(localFilePath); + var getObjectArgs = new GetObjectArgs() + .WithBucket(minIOConfig.BucketName) + .WithObject(ossRelativePath) + .WithFile(localFilePath); + + await minioClient.GetObjectAsync(getObjectArgs); - await minioClient.PutObjectAsync(putObjectArgs); } else if (ObjectStoreServiceOptions.ObjectStoreUse == "AWS") { @@ -321,291 +397,85 @@ namespace IRaCIS.Core.Application.Helper var amazonS3Client = new AmazonS3Client(credentials, clientConfig); - var putObjectRequest = new Amazon.S3.Model.PutObjectRequest() + var getObjectArgs = new Amazon.S3.Model.GetObjectRequest() { BucketName = awsConfig.BucketName, - FilePath = localFilePath, Key = ossRelativePath, }; - await amazonS3Client.PutObjectAsync(putObjectRequest); + + await (await amazonS3Client.GetObjectAsync(getObjectArgs)).WriteResponseStreamToFileAsync(localFilePath, true, CancellationToken.None); + } else { throw new BusinessValidationFailedException("未定义的存储介质类型"); } - return "/" + ossRelativePath; + } + catch (Exception ex) + { + throw new BusinessValidationFailedException("oss下载失败!" + ex.Message); } - public async Task DownLoadFromOSSAsync(string ossRelativePath, string localFilePath) + + + + } + + public async Task GetSignedUrl(string ossRelativePath) + { + ossRelativePath = ossRelativePath.TrimStart('/'); + try { - 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 GetSignedUrl(string ossRelativePath) - { - 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); - } - - } - - /// - /// 删除某个目录的文件 - /// - /// - /// - public async Task DeleteFromPrefix(string prefix) - { 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 + // 生成签名URL。 + var req = new GeneratePresignedUriRequest(aliConfig.BucketName, ossRelativePath, SignHttpMethod.Get) { - 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 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}"); - } + // 设置签名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 listArgs = new ListObjectsArgs().WithBucket(minIOConfig.BucketName).WithPrefix(prefix).WithRecursive(true); - - - - // 创建一个空列表用于存储对象键 - var objects = new List(); - - // 使用 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); - } + 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; @@ -621,144 +491,271 @@ namespace IRaCIS.Core.Application.Helper var amazonS3Client = new AmazonS3Client(credentials, clientConfig); - // 列出指定前缀下的所有对象 - var listObjectsRequest = new ListObjectsV2Request + var presignedUrl = await amazonS3Client.GetPreSignedURLAsync(new GetPreSignedUrlRequest() { BucketName = awsConfig.BucketName, - Prefix = prefix - }; + Key = ossRelativePath, + Expires = DateTime.UtcNow.AddMinutes(120) + }); - var listObjectsResponse = await amazonS3Client.ListObjectsV2Async(listObjectsRequest); - - if (listObjectsResponse.S3Objects.Count > 0) - { - // 准备删除请求 - var deleteObjectsRequest = new Amazon.S3.Model.DeleteObjectsRequest - { - BucketName = awsConfig.BucketName, - Objects = new List() - }; - - foreach (var s3Object in listObjectsResponse.S3Objects) - { - deleteObjectsRequest.Objects.Add(new KeyVersion - { - Key = s3Object.Key - }); - } - - // 批量删除对象 - var deleteObjectsResponse = await amazonS3Client.DeleteObjectsAsync(deleteObjectsRequest); - } + Uri uri = new Uri(presignedUrl); + string relativePath = uri.PathAndQuery; + return relativePath; } else { throw new BusinessValidationFailedException("未定义的存储介质类型"); } } - - - - - public async Task GetObjectStoreTempToken() + catch (Exception ex) { - 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(); - // 将设置为自定义的会话名称,例如oss-role-session。 - assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}"; - // 将替换为拥有上传文件到指定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 = 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, - - }; - - AWSTempToken = tempToken; - return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken }; - } - else - { - throw new BusinessValidationFailedException("未定义的存储介质类型"); - } + throw new BusinessValidationFailedException("oss授权url失败!" + ex.Message); } } + /// + /// 删除某个目录的文件 + /// + /// + /// + public async Task DeleteFromPrefix(string prefix) + { + 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 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(); + + // 使用 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() + }; + + 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 async Task 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(); + // 将设置为自定义的会话名称,例如oss-role-session。 + assumeRoleRequest.RoleSessionName = $"session-name-{NewId.NextGuid()}"; + // 将替换为拥有上传文件到指定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 = 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, + + }; + + AWSTempToken = tempToken; + return new ObjectStoreDTO() { ObjectStoreUse = ObjectStoreServiceOptions.ObjectStoreUse, AWS = tempToken }; + } + else + { + throw new BusinessValidationFailedException("未定义的存储介质类型"); + } + } } diff --git a/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs b/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs index 7fc34987b..2668892ee 100644 --- a/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs +++ b/IRaCIS.Core.Application/Helper/WordTempleteHelper.cs @@ -4,93 +4,92 @@ using System.Globalization; using Xceed.Document.NET; using Xceed.Words.NET; -namespace IRaCIS.Core.Application.Helper +namespace IRaCIS.Core.Application.Helper; + +/// +/// 利用DocX 库 处理word国际化模板 +/// +public static class WordTempleteHelper { - /// - /// 利用DocX 库 处理word国际化模板 - /// - public static class WordTempleteHelper + public static void DocX_GetInternationalTempleteStream(string filePath, Stream memoryStream) { - 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; - - using (DocX document = DocX.Load(filePath)) + if (bookmarkEn_Start != null) { - // 查找书签 - 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 paragraphsToRemove = new List(); + + foreach (var item in document.Paragraphs) { - // 获取书签的起始位置 - //int bookmarkCNStartPos = bookmarkCn_Start.Paragraph.StartIndex; + //中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的 - var bookmarkENStartPos = bookmarkEn_Start.Paragraph.StartIndex; - - // 创建一个要删除段落的列表 - List paragraphsToRemove = new List(); - - foreach (var item in document.Paragraphs) + if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos) { - //中文模板在前,英文在后,英文模板,就删除英文之前的,中文模板就删除英文之后的 - - if (isEn_US ? item.EndIndex < bookmarkENStartPos : item.StartIndex >= bookmarkENStartPos) - { - paragraphsToRemove.Add(item); - } + paragraphsToRemove.Add(item); } - - foreach (var paragraph in paragraphsToRemove) - { - document.RemoveParagraph(paragraph); - } - } - // 保存修改 - 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) + foreach (var paragraph in paragraphsToRemove) { - 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); - } - } + document.RemoveParagraph(paragraph); } - 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); + } + } + }