添加测试项目,添加脱敏支持!
example/ExampleApeart.Application/usermodels/RoleInfoAppService.cs
diff --git a/example/ExampleApeart.Application/usermodels/RoleInfoAppService.cs b/example/ExampleApeart.Application/usermodels/RoleInfoAppService.cs
index de8fed6..258238b 100644
--- a/example/ExampleApeart.Application/usermodels/RoleInfoAppService.cs
+++ b/example/ExampleApeart.Application/usermodels/RoleInfoAppService.cs
@@ -9,6 +9,7 @@ using PasteApeart.Handler;
using PasteApeart.Handler.Extensions;
using PasteApeart.Handler.Level;
using PasteApeart.Helper;
+using PasteApeart.Helper.Extensions;
using System.Linq.Dynamic.Core;
namespace ExampleApeart.usermodels
example/ExampleApeart.Test/ExampleApeart.Test.csproj
diff --git a/example/ExampleApeart.Test/ExampleApeart.Test.csproj b/example/ExampleApeart.Test/ExampleApeart.Test.csproj
new file mode 100644
index 0000000..93d150e
--- /dev/null
+++ b/example/ExampleApeart.Test/ExampleApeart.Test.csproj
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net8.0</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.19" />
+ <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
+ <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
+ <PackageReference Include="xunit.v3" Version="4.0.2" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\..\framework\PasteApeart.Helper\PasteApeart.Helper.csproj" />
+ </ItemGroup>
+
+</Project>
example/ExampleApeart.Test/HalfHideTestHelper.cs
diff --git a/example/ExampleApeart.Test/HalfHideTestHelper.cs b/example/ExampleApeart.Test/HalfHideTestHelper.cs
new file mode 100644
index 0000000..2c6957e
--- /dev/null
+++ b/example/ExampleApeart.Test/HalfHideTestHelper.cs
@@ -0,0 +1,189 @@
+using PasteApeart.Helper;
+using System.Text.Json;
+using Xunit;
+
+namespace ExampleApeart.Test
+{
+ /// <summary>
+ /// 测试脱敏处理
+ /// </summary>
+ public class HalfHideTestHelper
+ {
+ private readonly ITestOutputHelper _output;
+ public HalfHideTestHelper(ITestOutputHelper output) { _output = output; }
+
+ /// <summary>
+ /// 标记新对象单个
+ /// </summary>
+ [Fact]
+ public void NewSingle()
+ {
+ var model = new HalfNewModel { Name = "1345678901", Age = 5, Info = new HalfNewIncludeModel { Email = "buijhu@admin.com", Desc = "bhuijk" } };
+ var result = HalfHideHelper.PasteMaskData(model, typeof(HalfNewModel));
+ _output.WriteLine("原对象:" + JsonSerializer.Serialize(model));
+ _output.WriteLine("新对象:" + JsonSerializer.Serialize(result));
+ }
+
+ /// <summary>
+ /// 标记新对象的集合
+ /// </summary>
+ [Fact]
+ public void NewList()
+ {
+ var list = new List<HalfNewModel>();
+ var model = new HalfNewModel { Name = "1345678901", Age = 5 };
+ list.Add(model);
+ var model2 = new HalfNewModel { Name = "40987654567765", Age = 15, Info = new HalfNewIncludeModel { Email = "cvbnkjhgfdty@qq.com", Desc = "list.info" } };
+ list.Add(model2);
+
+ var result = HalfHideHelper.PasteMaskData(list, typeof(HalfNewModel));
+
+ _output.WriteLine("原对象:" + JsonSerializer.Serialize(list));
+ _output.WriteLine("新对象:" + JsonSerializer.Serialize(result));
+
+ }
+
+ /// <summary>
+ /// 标记单个
+ /// </summary>
+ [Fact]
+ public void Single()
+ {
+ var model = new HalfModel { Name = "1345678901", Age = 5, Info = new HalfIncludeModel { Email = "buijhu@admin.com", Desc = "bhuijk" } };
+ var result = HalfHideHelper.PasteMaskData(model, typeof(HalfModel));
+ _output.WriteLine("原对象:" + JsonSerializer.Serialize(model));
+ _output.WriteLine("新对象:" + JsonSerializer.Serialize(result));
+ }
+
+ /// <summary>
+ /// 标记集合
+ /// </summary>
+ [Fact]
+ public void List()
+ {
+ var list = new List<HalfModel>();
+ var model = new HalfModel { Name = "1345678901", Age = 5 };
+ list.Add(model);
+ var model2 = new HalfModel { Name = "40987654567765", Age = 15, Info = new HalfIncludeModel { Email = "cvbnkjhgfdty@qq.com", Desc = "list.info" } };
+ list.Add(model2);
+
+ var result = HalfHideHelper.PasteMaskData(list, typeof(HalfModel));
+
+ _output.WriteLine("原对象:" + JsonSerializer.Serialize(list));
+ _output.WriteLine("新对象:" + JsonSerializer.Serialize(result));
+
+ }
+
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide(4, 4, '*', true)]
+ public class HalfNewModel
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide]
+ public string Name { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int Age { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide]
+ public HalfNewIncludeModel Info { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public HalfExpendModel Model { get; set; } = new HalfExpendModel();
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public class HalfExpendModel
+ {
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int High { get; set; } = 100;
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int Width { get; set; } = 1920;
+
+ }
+
+
+ /// <summary>
+ ///
+ /// </summary>
+ public class HalfNewIncludeModel
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide(4, 5, '.')]
+ public string Email { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public string Desc { get; set; }
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide(3, 4, '*', false)]
+ public class HalfModel
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide]
+ public string Name { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public int Age { get; set; }
+
+ /// <summary>
+ /// 关键字段要标记,否则不会继续下探
+ /// </summary>
+ [PasteHalfHide]
+ public HalfIncludeModel Info { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public HalfExpendModel Model { get; set; } = new HalfExpendModel();
+ }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public class HalfIncludeModel
+ {
+ /// <summary>
+ ///
+ /// </summary>
+ [PasteHalfHide(4, 5, '#')]
+ public string Email { get; set; }
+
+ /// <summary>
+ ///
+ /// </summary>
+ public string Desc { get; set; }
+ }
+}
example/ExampleApeart.sln
diff --git a/example/ExampleApeart.sln b/example/ExampleApeart.sln
index a44bb56..d031664 100644
--- a/example/ExampleApeart.sln
+++ b/example/ExampleApeart.sln
@@ -33,6 +33,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PasteApeart.Application", "
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PasteApeart.Helper", "..\framework\PasteApeart.Helper\PasteApeart.Helper.csproj", "{93B87C2D-64EF-BBAB-95C4-8C3F5DB477EB}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExampleApeart.Test", "ExampleApeart.Test\ExampleApeart.Test.csproj", "{1A55630D-C4E6-4FEF-97F0-0706FDD082DB}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A79658C0-D6FC-43A3-A828-764AC234223D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -253,6 +257,22 @@ Global
{93B87C2D-64EF-BBAB-95C4-8C3F5DB477EB}.Release|x64.Build.0 = Release|x64
{93B87C2D-64EF-BBAB-95C4-8C3F5DB477EB}.Release|x86.ActiveCfg = Release|Any CPU
{93B87C2D-64EF-BBAB-95C4-8C3F5DB477EB}.Release|x86.Build.0 = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|ARM64.ActiveCfg = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|ARM64.Build.0 = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|x64.Build.0 = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Debug|x86.Build.0 = Debug|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|ARM64.ActiveCfg = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|ARM64.Build.0 = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|x64.ActiveCfg = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|x64.Build.0 = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|x86.ActiveCfg = Release|Any CPU
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -271,6 +291,7 @@ Global
{B4E0F5D3-E931-B1F3-62C4-B9D33F59191E} = {A674AE5C-C826-48E0-9DA8-8978FAFEE10A}
{FCEDEE51-A9DC-FC42-61C3-78D85EEB35F9} = {A674AE5C-C826-48E0-9DA8-8978FAFEE10A}
{93B87C2D-64EF-BBAB-95C4-8C3F5DB477EB} = {A674AE5C-C826-48E0-9DA8-8978FAFEE10A}
+ {1A55630D-C4E6-4FEF-97F0-0706FDD082DB} = {A79658C0-D6FC-43A3-A828-764AC234223D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D5B6327E-927F-4D35-9FFE-1EBDA1F97354}
framework/Directory.Build.props
diff --git a/framework/Directory.Build.props b/framework/Directory.Build.props
index 4878e9e..856c9c3 100644
--- a/framework/Directory.Build.props
+++ b/framework/Directory.Build.props
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
- <Version>26.10.10</Version>
- <FileVersion>26.10.10</FileVersion>
- <AssemblyVersion>26.10.10</AssemblyVersion>
+ <Version>26.10.11</Version>
+ <FileVersion>26.10.11</FileVersion>
+ <AssemblyVersion>26.10.11</AssemblyVersion>
</PropertyGroup>
</Project>
\ No newline at end of file
framework/PasteApeart.Application/IPasteDefaultAppService.cs
diff --git a/framework/PasteApeart.Application/IPasteDefaultAppService.cs b/framework/PasteApeart.Application/IPasteDefaultAppService.cs
index 728d112..418ea4f 100644
--- a/framework/PasteApeart.Application/IPasteDefaultAppService.cs
+++ b/framework/PasteApeart.Application/IPasteDefaultAppService.cs
@@ -97,24 +97,35 @@ namespace PasteApeart.Application
/// <param name="searchObject"></param>
/// <param name="entityType"></param>
/// <param name="searchType"></param>
- /// <param name="input"></param>
+ /// <param name="page">当前页码,第1页要读取totalCount</param>
/// <param name="listDtoType"></param>
/// <returns></returns>
- Task<dynamic> DirectPageAsync(object searchObject, Type entityType, Type searchType, InputSearchBase input, Type listDtoType);
+ Task<dynamic> DirectPageAsync(object searchObject, Type entityType, Type searchType, int page, Type listDtoType);
/// <summary>
- ///
+ /// 获取新增 元数据
/// </summary>
/// <param name="entityName"></param>
/// <returns></returns>
Task<VoloModelInfo> ReadAddModel(string entityName);
+
/// <summary>
- ///
+ /// 获取对象详细的元数据
/// </summary>
/// <param name="entityName"></param>
/// <param name="id"></param>
/// <returns></returns>
Task<VoloModelInfo> ReadDetailModel(string entityName, string id);
+
+ /// <summary>
+ /// 获取对象详细
+ /// </summary>
+ /// <param name="entityType"></param>
+ /// <param name="dtoType"></param>
+ /// <param name="id"></param>
+ /// <returns></returns>
+ Task<object> ReadDetailObjectAsync(Type entityType, Type dtoType, string id);
+
/// <summary>
///
/// </summary>
framework/PasteApeart.Application/PasteApeart.Application.xml
diff --git a/framework/PasteApeart.Application/PasteApeart.Application.xml b/framework/PasteApeart.Application/PasteApeart.Application.xml
index 3b58755..c71e24f 100644
--- a/framework/PasteApeart.Application/PasteApeart.Application.xml
+++ b/framework/PasteApeart.Application/PasteApeart.Application.xml
@@ -285,32 +285,41 @@
<param name="searchName"></param>
<returns></returns>
</member>
- <member name="M:PasteApeart.Application.IPasteDefaultAppService.DirectPageAsync(System.Object,System.Type,System.Type,PasteApeart.Application.Contracts.InputSearchBase,System.Type)">
+ <member name="M:PasteApeart.Application.IPasteDefaultAppService.DirectPageAsync(System.Object,System.Type,System.Type,System.Int32,System.Type)">
<summary>
复用 按页查询
</summary>
<param name="searchObject"></param>
<param name="entityType"></param>
<param name="searchType"></param>
- <param name="input"></param>
+ <param name="page">当前页码,第1页要读取totalCount</param>
<param name="listDtoType"></param>
<returns></returns>
</member>
<member name="M:PasteApeart.Application.IPasteDefaultAppService.ReadAddModel(System.String)">
<summary>
-
+ 获取新增 元数据
</summary>
<param name="entityName"></param>
<returns></returns>
</member>
<member name="M:PasteApeart.Application.IPasteDefaultAppService.ReadDetailModel(System.String,System.String)">
<summary>
-
+ 获取对象详细的元数据
</summary>
<param name="entityName"></param>
<param name="id"></param>
<returns></returns>
</member>
+ <member name="M:PasteApeart.Application.IPasteDefaultAppService.ReadDetailObjectAsync(System.Type,System.Type,System.String)">
+ <summary>
+ 获取对象详细
+ </summary>
+ <param name="entityType"></param>
+ <param name="dtoType"></param>
+ <param name="id"></param>
+ <returns></returns>
+ </member>
<member name="M:PasteApeart.Application.IPasteDefaultAppService.ReadListModel(System.String,System.String)">
<summary>
@@ -664,7 +673,13 @@
</member>
<member name="M:PasteApeart.Application.PasteDefaultAppService.ReadDetailModel(System.String,System.String)">
<summary>
- 获取详细
+ 获取详细 获取详细对应的元数据
+ </summary>
+ <returns></returns>
+ </member>
+ <member name="M:PasteApeart.Application.PasteDefaultAppService.ReadDetailObjectAsync(System.Type,System.Type,System.String)">
+ <summary>
+ 获取详细 获取数据本身
</summary>
<returns></returns>
</member>
@@ -693,14 +708,14 @@
<returns></returns>
<exception cref="T:PasteApeart.Application.Contracts.PasteCodeException"></exception>
</member>
- <member name="M:PasteApeart.Application.PasteDefaultAppService.DirectPageAsync(System.Object,System.Type,System.Type,PasteApeart.Application.Contracts.InputSearchBase,System.Type)">
+ <member name="M:PasteApeart.Application.PasteDefaultAppService.DirectPageAsync(System.Object,System.Type,System.Type,System.Int32,System.Type)">
<summary>
直接获取页面数据 用于快捷开发
</summary>
<param name="searchObject"></param>
<param name="entityType"></param>
<param name="searchType"></param>
- <param name="input"></param>
+ <param name="page">第1页要返回totalCount</param>
<param name="listDtoType"></param>
<returns></returns>
</member>
framework/PasteApeart.Application/PasteDefaultAppService.cs
diff --git a/framework/PasteApeart.Application/PasteDefaultAppService.cs b/framework/PasteApeart.Application/PasteDefaultAppService.cs
index 7e8a03e..9346c73 100644
--- a/framework/PasteApeart.Application/PasteDefaultAppService.cs
+++ b/framework/PasteApeart.Application/PasteDefaultAppService.cs
@@ -128,7 +128,7 @@ namespace PasteApeart.Application
}
/// <summary>
- /// 获取详细
+ /// 获取详细 获取详细对应的元数据
/// </summary>
/// <returns></returns>
public async Task<VoloModelInfo> ReadDetailModel(string entityName, string id)
@@ -144,6 +144,53 @@ namespace PasteApeart.Application
throw new PasteCodeException($"找不到对应的实体或 DTO 类型: {entityName}");
}
+ var dto = await ReadDetailObjectAsync(entityType,dtoType,id);
+
+ //// 2. 动态查询实体
+ //var entity = await _defaultHelper.FindEntityByIdWithIncludeAsync(_dbContext, entityType, id);
+ //if (entity == null)
+ //{
+ // throw new PasteCodeException("查询的信息不存在,无法执行编辑操作!");
+ //}
+
+ //await DefaultFilterDataAsync(entityType, entity, "detail");
+ //var dto = Activator.CreateInstance(dtoType);
+ //_defaultHelper.MapProperties(entity, dto, entityType, dtoType);
+
+ ////构建外表等 Include的如何搞定???
+ //await _defaultHelper.BuildOuterQuery(_dbContext, dto, dtoType);
+
+ ////进行脱敏处理
+ //var hasHalfHideAttribute = dtoType.GetCustomAttribute<PasteHalfHideAttribute>();
+ //if (hasHalfHideAttribute != null)
+ //{
+ // HalfHideHelper.PasteMaskData(dto, dtoType);
+ //}
+
+ // 4. 动态读取 DTO 的属性信息
+ return await DefaultBuildPropertyInfo("detail", dto, dtoType);
+
+ }
+
+
+ /// <summary>
+ /// 获取详细 获取数据本身
+ /// </summary>
+ /// <returns></returns>
+ public async Task<object> ReadDetailObjectAsync(Type entityType,Type dtoType, string id)
+ {
+
+ //entityName = DefaultMapperEntityName(entityName);
+
+ //// 1. 根据实体名称查找对应的实体类型和 DTO 类型
+ //var entityType = _defaultHelper.FindEntityType(entityName);
+ //var dtoType = _defaultHelper.FindDtoType(entityName, "Dto");
+
+ //if (entityType == null || dtoType == null)
+ //{
+ // throw new PasteCodeException($"找不到对应的实体或 DTO 类型: {entityName}");
+ //}
+
// 2. 动态查询实体
var entity = await _defaultHelper.FindEntityByIdWithIncludeAsync(_dbContext, entityType, id);
if (entity == null)
@@ -157,16 +204,26 @@ namespace PasteApeart.Application
// 3. 动态映射实体到 DTO
//var dto = DefaultHelper.MapEntityToDto(ObjectMapper, entity, entityType, dtoType);
+
+
//构建外表等 Include的如何搞定???
await _defaultHelper.BuildOuterQuery(_dbContext, dto, dtoType);
+ //进行脱敏处理
+ var hasHalfHideAttribute = dtoType.GetCustomAttribute<PasteHalfHideAttribute>();
+ if (hasHalfHideAttribute != null)
+ {
+ HalfHideHelper.PasteMaskData(dto, dtoType);
+ }
+
// 4. 动态读取 DTO 的属性信息
//var dataModel = DefaultHelper.ReadModelProperties(dto, dtoType);
//return dataModel;
- return await DefaultBuildPropertyInfo("detail", dto, dtoType);
-
+ //return await DefaultBuildPropertyInfo("detail", dto, dtoType);
+ return dto;
}
+
/// <summary>
/// 获取列表格式
/// </summary>
@@ -280,7 +337,7 @@ namespace PasteApeart.Application
var input = _defaultHelper.MapperToQueryModel(queryString, searchType, searchObject);
- dynamic pagedto = await DirectPageAsync(searchObject, entityType, searchType, input, listDtoType);
+ dynamic pagedto = await DirectPageAsync(searchObject, entityType, searchType, input.page, listDtoType);
if (input.page == 1)
{
if (queryString.Contains("request=one"))
@@ -349,15 +406,15 @@ namespace PasteApeart.Application
/// <param name="searchObject"></param>
/// <param name="entityType"></param>
/// <param name="searchType"></param>
- /// <param name="input"></param>
+ /// <param name="page">第1页要返回totalCount</param>
/// <param name="listDtoType"></param>
/// <returns></returns>
- public async Task<dynamic> DirectPageAsync(object searchObject, Type entityType, Type searchType, InputSearchBase input, Type listDtoType)
+ public async Task<dynamic> DirectPageAsync(object searchObject, Type entityType, Type searchType, int page, Type listDtoType)
{
dynamic pageDto = new ExpandoObject();
IQueryable query = _defaultHelper.BuildDynamicQuery(_dbContext, entityType, searchType, searchObject);
- if (input.page == 1)
+ if (page == 1)
{
pageDto.totalCount = await _defaultHelper.ExecuteCountQuery(query, entityType);
}
@@ -387,6 +444,14 @@ namespace PasteApeart.Application
}
//查询外表 并实现 这里是简单的外表ID的查询 如果有更复杂的,还需要实现
await _defaultHelper.BuildOuterData(_dbContext, listDtoType, dtos);
+
+ //进行脱敏处理
+ var hasHalfHideAttribute = listDtoType.GetCustomAttribute<PasteHalfHideAttribute>();
+ if (hasHalfHideAttribute != null)
+ {
+ HalfHideHelper.PasteMaskData(dtos, listDtoType);
+ }
+
// 7. 设置结果并返回
//pagedDto.Items = dtos.Cast<object>().ToList();
pageDto.items = dtos;
framework/PasteApeart.Handler/common/EventActionHandler.cs
diff --git a/framework/PasteApeart.Handler/common/EventActionHandler.cs b/framework/PasteApeart.Handler/common/EventActionHandler.cs
index 10a9a39..e43a8aa 100644
--- a/framework/PasteApeart.Handler/common/EventActionHandler.cs
+++ b/framework/PasteApeart.Handler/common/EventActionHandler.cs
@@ -8,11 +8,14 @@ using System.Text.Json;
namespace PasteApeart.Handler
{
+
+
/// <summary>
/// 队列处理 可以被RabbitMQ或者Channel的消费者调用
/// </summary>
public class EventActionHandler : IEventActionHandler
{
+ private const string DefaultHandleMethodName = "HandleEventAsync";
private readonly ILogger<EventActionHandler> _logger;
private readonly IServiceProvider _serviceProvider;
@@ -20,7 +23,18 @@ namespace PasteApeart.Handler
private readonly Type[] _dtoTypeArray = null;
/// <summary>
- /// 队列处理 可以被RabbitMQ或者Channel的消费者调用
+ /// 事件名 -> 事件类型。找不到时不写缓存。
+ /// 全系统 types 是同一份,所以 key 只放 eventName。
+ /// </summary>
+ private readonly ConcurrentDictionary<string, Type> _eventTypeCache = new();
+
+ /// <summary>
+ /// (事件类型, 处理方法名) -> (处理器接口类型, 处理方法)。
+ /// methodName 有不同入口,必须进 key。
+ /// </summary>
+ private readonly ConcurrentDictionary<(Type EventType, string MethodName), (Type HandlerType, MethodInfo HandleMethod)> _handlerCache = new();
+ /// <summary>
+ ///
/// </summary>
/// <param name="logger"></param>
/// <param name="serviceProvider"></param>
@@ -37,13 +51,9 @@ namespace PasteApeart.Handler
}
}
-
- private readonly ConcurrentDictionary<string, Type> _eventTypeCache = new();
-
- private readonly ConcurrentDictionary<Type, (Type HandlerType, MethodInfo HandleMethod)> _handlerCache = new();
-
+ #region 字符串入口
/// <summary>
- /// 主要适用于分布式队列 比如RabitMQ等
+ ///
/// </summary>
/// <param name="msg"></param>
/// <returns></returns>
@@ -62,7 +72,6 @@ namespace PasteApeart.Handler
await ActionReturnResultAsync(msg, _dtoTypeArray);
}
-
/// <summary>
///
/// </summary>
@@ -77,14 +86,13 @@ namespace PasteApeart.Handler
}
await ActionReturnResultAsync(msg, types);
}
-
/// <summary>
///
/// </summary>
/// <param name="msg"></param>
/// <param name="methodName"></param>
/// <returns></returns>
- public async Task<object> ActionResultAsync(string msg, string methodName = "HandleEventAsync")
+ public async Task<object> ActionResultAsync(string msg, string methodName = DefaultHandleMethodName)
{
if (string.IsNullOrWhiteSpace(msg))
{
@@ -100,7 +108,6 @@ namespace PasteApeart.Handler
return await ActionReturnResultAsync(msg, _dtoTypeArray, methodName);
}
-
/// <summary>
///
/// </summary>
@@ -108,7 +115,7 @@ namespace PasteApeart.Handler
/// <param name="types"></param>
/// <param name="methodName"></param>
/// <returns></returns>
- public async Task<object> ActionResultAsync(string msg, Type[] types, string methodName = "HandleEventAsync")
+ public async Task<object> ActionResultAsync(string msg, Type[] types, string methodName = DefaultHandleMethodName)
{
if (string.IsNullOrWhiteSpace(msg))
{
@@ -116,121 +123,6 @@ namespace PasteApeart.Handler
}
return await ActionReturnResultAsync(msg, types, methodName);
}
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="msg"></param>
- /// <param name="types"></param>
- /// <returns></returns>
- private async Task ActionAsync(string msg, Type[] types)
- {
- object input = null;
- object handler = null;
- try
- {
- // 1. 只反序列化一次,使用 System.Text.Json 性能更好
- using var jsonDoc = JsonDocument.Parse(msg);
- var root = jsonDoc.RootElement;
- JsonElement eventNameProperty;
- // 使用不区分大小写的匹配,更健壮
- if (!root.TryGetProperty("eventName", out eventNameProperty))
- {
- root.TryGetProperty("EventName", out eventNameProperty);
- }
- // 获取字符串值并验证
- string eventName = eventNameProperty.GetString();
- if (string.IsNullOrEmpty(eventName))
- {
- _logger.LogError($"EventName 字段值为空: {msg}");
- return;
- }
- // 2. 获取事件类型(带缓存)
- var modelType = _eventTypeCache.GetOrAdd(eventName, name =>
- {
- return types.FirstOrDefault(x => x.Name == name);
- });
- if (modelType == null)
- {
- _logger.LogError($"未找到事件类型: {eventName}");
- return;
- }
- // 3. 反序列化为具体类型
- input = JsonSerializer.Deserialize(msg, modelType, JsonConfig.Options);
- if (input == null)
- {
- _logger.LogError($"无法将消息反序列化为类型 {eventName}: {msg}");
- return;
- }
- using var scope = _serviceProvider.CreateScope();
- if (input is IEventModel tryModel)
- {
- if (tryModel.ExpireSecond > 0)
- {
- await _channelHelper.WriteAutoQueueAsync(tryModel);
- return;
- }
- }
- // 4. 获取处理器信息(带缓存)
- var (handlerType, handleMethod) = _handlerCache.GetOrAdd(modelType, type =>
- {
- var hType = typeof(IEventHandler<>).MakeGenericType(type);
- var method = hType.GetMethod("HandleEventAsync");
- return (hType, method);
- });
- if (handleMethod == null)
- {
- _logger.LogError($"处理器 {handlerType.Name} 没有 HandleEventAsync 方法");
- return;
- }
- handler = scope.ServiceProvider.GetRequiredService(handlerType);
- if (handler == null)
- {
- _logger.LogError($"没有找到 {eventName} 的 IEventHandler 实现");
- return;
- }
- try
- {
- // 5. 执行处理
- var task = handleMethod.Invoke(handler, new[] { input }) as Task;
- if (task != null)
- {
- await task;
- }
- else
- {
- _logger.LogError($"无法调用 {eventName} 的处理方法");
- }
- }
- catch (Exception handlerExl)
- {
- _logger.LogException(handlerExl);
- }
- finally
- {
- try
- {
- if (handler is IDisposable disposable)
- {
- disposable.Dispose();
- }
- if (handler is IAsyncDisposable disposable1)
- {
- await disposable1.DisposeAsync();
- }
- }
- catch (Exception exlDisposable)
- {
- _logger.LogException(exlDisposable);
- }
- }
- }
- catch (Exception ex)
- {
- _logger.LogException(ex);
- }
- }
-
/// <summary>
///
/// </summary>
@@ -238,84 +130,69 @@ namespace PasteApeart.Handler
/// <param name="types"></param>
/// <param name="methodName"></param>
/// <returns></returns>
- private async Task<object> ActionReturnResultAsync(string msg, Type[] types, string methodName = "HandleEventAsync")
+ public virtual async Task<object> ActionReturnResultAsync(string msg, Type[] types, string methodName = DefaultHandleMethodName)
{
- object input = null;
object handler = null;
try
{
- // 1. 只反序列化一次,使用 System.Text.Json 性能更好
using var jsonDoc = JsonDocument.Parse(msg);
var root = jsonDoc.RootElement;
+
JsonElement eventNameProperty;
- // 使用不区分大小写的匹配,更健壮
if (!root.TryGetProperty("eventName", out eventNameProperty))
{
root.TryGetProperty("EventName", out eventNameProperty);
}
- // 获取字符串值并验证
+
string eventName = eventNameProperty.GetString();
if (string.IsNullOrEmpty(eventName))
{
_logger.LogError($"EventName 字段值为空: {msg}");
return null;
}
- // 2. 获取事件类型(带缓存)
- var modelType = _eventTypeCache.GetOrAdd(eventName, name =>
- {
- return types.FirstOrDefault(x => x.Name == name);
- });
+
+ var modelType = ResolveEventType(eventName, types);
if (modelType == null)
{
_logger.LogError($"未找到事件类型: {eventName}");
return null;
}
- // 3. 反序列化为具体类型
- input = JsonSerializer.Deserialize(msg, modelType, JsonConfig.Options);
+
+ var input = JsonSerializer.Deserialize(msg, modelType, JsonConfig.Options);
if (input == null)
{
_logger.LogError($"无法将消息反序列化为类型 {eventName}: {msg}");
return null;
}
+
using var scope = _serviceProvider.CreateScope();
- if (input is IEventModel tryModel)
+
+ if (input is IEventModel tryModel && tryModel.ExpireSecond > 0)
{
- if (tryModel.ExpireSecond > 0)
- {
- await _channelHelper.WriteAutoQueueAsync(tryModel);
- return null;
- }
+ await _channelHelper.WriteAutoQueueAsync(tryModel);
+ return null;
}
- // 4. 获取处理器信息(带缓存)
- var (handlerType, handleMethod) = _handlerCache.GetOrAdd(modelType, type =>
- {
- var hType = typeof(IEventHandler<>).MakeGenericType(type);
- var method = hType.GetMethod(methodName);
- return (hType, method);
- });
+
+ var (handlerType, handleMethod) = ResolveHandlerInfo(modelType, methodName);
if (handleMethod == null)
{
_logger.LogError($"处理器 {handlerType.Name} 没有 {methodName} 方法");
return null;
}
+
handler = scope.ServiceProvider.GetRequiredService(handlerType);
if (handler == null)
{
_logger.LogError($"没有找到 {eventName} 的 IEventHandler 实现");
return null;
}
+
try
{
- // 5. 执行处理
var task = handleMethod.Invoke(handler, new[] { input }) as Task;
if (task != null)
{
await task;
- //// 从 handler 接口上读 Result 属性(而不是从 Task<T> 里读)
- //var resultProp = handlerType.GetProperty("Result");
- //return resultProp?.GetValue(handler);
-
- // 用具体类型读,而不是接口类型
var resultProp = handler.GetType().GetProperty("Result");
return resultProp?.GetValue(handler);
}
@@ -327,29 +204,10 @@ namespace PasteApeart.Handler
catch (Exception handlerExl)
{
_logger.LogException(handlerExl);
- //TODO 是否自动尝试... .. .
- if (input is IEventModel inputModel)
- {
- //if(inputModel.tr)
- }
}
finally
{
- try
- {
- if (handler is IDisposable disposable)
- {
- disposable.Dispose();
- }
- if (handler is IAsyncDisposable disposable1)
- {
- await disposable1.DisposeAsync();
- }
- }
- catch (Exception exlDisposable)
- {
- _logger.LogException(exlDisposable);
- }
+ await DisposeHandlerAsync(handler);
}
}
catch (Exception ex)
@@ -359,10 +217,9 @@ namespace PasteApeart.Handler
return null;
}
+ #endregion
- private readonly ConcurrentDictionary<Type, Type> _eventHandlerCache = new();
- private readonly ConcurrentDictionary<Type, MethodInfo> _handleMethodCache = new();
-
+ #region 对象入口(本地 Channel 模式)
/// <summary>
///
/// </summary>
@@ -373,7 +230,6 @@ namespace PasteApeart.Handler
{
await ActionMessageAsync(read);
}
-
/// <summary>
///
/// </summary>
@@ -384,44 +240,48 @@ namespace PasteApeart.Handler
{
await ActionMessageAsync(read);
}
-
/// <summary>
- /// 主要适用于本地Chennel模式
+ ///
/// </summary>
/// <param name="read"></param>
/// <returns></returns>
- public async Task ActionMessageAsync(IEventModel read)
+ public virtual async Task ActionMessageAsync(IEventModel read)
{
- _logger.LogInformation($"Event Msg:{read.EventName}");
- //read是一个IEventModel
- var typeName = read.GetType().Name;
- var eventType = read.GetType();
- using var _scope = _serviceProvider.CreateScope();
- var handlerType = _eventHandlerCache.GetOrAdd(eventType, t => typeof(IEventHandler<>).MakeGenericType(t));
- var handler = _scope.ServiceProvider.GetService(handlerType);
- // 通过依赖注入获取处理器实例
- if (handler == null)
+ if (read == null)
{
- _logger.LogError($"没有找到 {typeName} 的 IEventHandler 实现!");
- return;
- }
- // 获取处理方法(带缓存)
- //var handleMethod = _handleMethodCache.GetOrAdd(eventType, t =>
- // handlerType.GetMethod("HandleEventAsync"));
- var handleMethod = _handleMethodCache.GetOrAdd(handlerType, t => t.GetMethod("HandleEventAsync"));
- if (handleMethod == null)
- {
- _logger.LogError($"无法找到 {typeName} 的 HandleEventAsync 方法");
+ _logger.LogError("read 不能为空");
return;
}
+
+ _logger.LogInformation($"Event Msg:{read.EventName}");
+
+ var eventType = read.GetType();
+ var typeName = eventType.Name;
+
+ object handler = null;
try
{
+ using var scope = _serviceProvider.CreateScope();
+
+ var (handlerType, handleMethod) = ResolveHandlerInfo(eventType, DefaultHandleMethodName);
+ if (handleMethod == null)
+ {
+ _logger.LogError($"无法找到 {typeName} 的 {DefaultHandleMethodName} 方法");
+ return;
+ }
+
+ handler = scope.ServiceProvider.GetService(handlerType);
+ if (handler == null)
+ {
+ _logger.LogError($"没有找到 {typeName} 的 IEventHandler 实现!");
+ return;
+ }
+
var task = handleMethod.Invoke(handler, new[] { read }) as Task;
if (task != null)
{
await task;
}
- //_logger.LogInformation($"执行了 {typeName} 的 HandleEventAsync 函数");
}
catch (Exception ex)
{
@@ -429,23 +289,532 @@ namespace PasteApeart.Handler
}
finally
{
- try
+ await DisposeHandlerAsync(handler);
+ }
+ }
+
+ #endregion
+
+ #region 缓存解析 & 公共方法
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="eventName"></param>
+ /// <param name="types"></param>
+ /// <returns></returns>
+ private Type ResolveEventType(string eventName, Type[] types)
+ {
+ if (types == null || types.Length == 0)
+ {
+ return null;
+ }
+
+ if (_eventTypeCache.TryGetValue(eventName, out var cached))
+ {
+ return cached;
+ }
+
+ var modelType = types.FirstOrDefault(x => x.Name == eventName);
+ if (modelType != null)
+ {
+ _eventTypeCache.TryAdd(eventName, modelType);
+ }
+ return modelType;
+ }
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="eventType"></param>
+ /// <param name="methodName"></param>
+ /// <returns></returns>
+ private (Type HandlerType, MethodInfo HandleMethod) ResolveHandlerInfo(Type eventType, string methodName)
+ {
+ if (string.IsNullOrEmpty(methodName))
+ {
+ methodName = DefaultHandleMethodName;
+ }
+
+ return _handlerCache.GetOrAdd((eventType, methodName), key =>
+ {
+ var handlerType = typeof(IEventHandler<>).MakeGenericType(key.EventType);
+ var handleMethod = handlerType.GetMethod(key.MethodName);
+ return (handlerType, handleMethod);
+ });
+ }
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="handler"></param>
+ /// <returns></returns>
+ private async Task DisposeHandlerAsync(object handler)
+ {
+ if (handler == null)
+ {
+ return;
+ }
+
+ try
+ {
+ if (handler is IAsyncDisposable asyncDisposable)
{
- if (handler is IDisposable disposable)
- {
- disposable.Dispose();
- }
- if (handler is IAsyncDisposable disposable1)
- {
- await disposable1.DisposeAsync();
- }
+ await asyncDisposable.DisposeAsync();
}
- catch (Exception exl)
+ else if (handler is IDisposable disposable)
{
- _logger.LogException(exl);
+ disposable.Dispose();
}
}
+ catch (Exception ex)
+ {
+ _logger.LogException(ex);
+ }
}
+ #endregion
}
+
+ #region 以下是历史代码
+
+ ///// <summary>
+ ///// 队列处理 可以被RabbitMQ或者Channel的消费者调用
+ ///// </summary>
+ //public class EventActionHandler : IEventActionHandler
+ //{
+
+ // private readonly ILogger<EventActionHandler> _logger;
+ // private readonly IServiceProvider _serviceProvider;
+ // private readonly IChannelHelper _channelHelper;
+ // private readonly Type[] _dtoTypeArray = null;
+
+ // /// <summary>
+ // /// 队列处理 可以被RabbitMQ或者Channel的消费者调用
+ // /// </summary>
+ // /// <param name="logger"></param>
+ // /// <param name="serviceProvider"></param>
+ // /// <param name="channelHelper"></param>
+ // /// <param name="dtoTypes"></param>
+ // public EventActionHandler(ILogger<EventActionHandler> logger, IServiceProvider serviceProvider, IChannelHelper channelHelper, Type[] dtoTypes = null)
+ // {
+ // _logger = logger;
+ // _serviceProvider = serviceProvider;
+ // _channelHelper = channelHelper;
+ // if (dtoTypes != null)
+ // {
+ // _dtoTypeArray = dtoTypes;
+ // }
+ // }
+
+
+ // private readonly ConcurrentDictionary<string, Type> _eventTypeCache = new();
+
+ // private readonly ConcurrentDictionary<Type, (Type HandlerType, MethodInfo HandleMethod)> _handlerCache = new();
+
+ // /// <summary>
+ // /// 主要适用于分布式队列 比如RabitMQ等
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <returns></returns>
+ // public async Task ActionMessageAsync(string msg)
+ // {
+ // if (string.IsNullOrWhiteSpace(msg))
+ // {
+ // return;
+ // }
+
+ // if (_dtoTypeArray?.Any() != true)
+ // {
+ // _logger.LogError("ActionMessageAsync 的string模式下,构造函数的dtoTypes是必传的,表示EventModel所在程序集的类型集合");
+ // return;
+ // }
+
+ // await ActionReturnResultAsync(msg, _dtoTypeArray);
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <param name="types"></param>
+ // /// <returns></returns>
+ // public async Task ActionMessageAsync(string msg, Type[] types)
+ // {
+ // if (string.IsNullOrWhiteSpace(msg))
+ // {
+ // return;
+ // }
+ // await ActionReturnResultAsync(msg, types);
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <param name="methodName"></param>
+ // /// <returns></returns>
+ // public async Task<object> ActionResultAsync(string msg, string methodName = "HandleEventAsync")
+ // {
+ // if (string.IsNullOrWhiteSpace(msg))
+ // {
+ // _logger.LogError("传入的数据不能为空");
+ // return null;
+ // }
+
+ // if (_dtoTypeArray?.Any() != true)
+ // {
+ // _logger.LogError("ActionMessageAsync 的string模式下,构造函数的dtoTypes是必传的,表示EventModel所在程序集的类型集合");
+ // return null;
+ // }
+
+ // return await ActionReturnResultAsync(msg, _dtoTypeArray, methodName);
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <param name="types"></param>
+ // /// <param name="methodName"></param>
+ // /// <returns></returns>
+ // public async Task<object> ActionResultAsync(string msg, Type[] types, string methodName = "HandleEventAsync")
+ // {
+ // if (string.IsNullOrWhiteSpace(msg))
+ // {
+ // return null;
+ // }
+ // return await ActionReturnResultAsync(msg, types, methodName);
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <param name="types"></param>
+ // /// <returns></returns>
+ // private async Task ActionAsync(string msg, Type[] types)
+ // {
+ // object input = null;
+ // object handler = null;
+ // try
+ // {
+ // // 1. 只反序列化一次,使用 System.Text.Json 性能更好
+ // using var jsonDoc = JsonDocument.Parse(msg);
+ // var root = jsonDoc.RootElement;
+ // JsonElement eventNameProperty;
+ // // 使用不区分大小写的匹配,更健壮
+ // if (!root.TryGetProperty("eventName", out eventNameProperty))
+ // {
+ // root.TryGetProperty("EventName", out eventNameProperty);
+ // }
+ // // 获取字符串值并验证
+ // string eventName = eventNameProperty.GetString();
+ // if (string.IsNullOrEmpty(eventName))
+ // {
+ // _logger.LogError($"EventName 字段值为空: {msg}");
+ // return;
+ // }
+ // // 2. 获取事件类型(带缓存)
+ // var modelType = _eventTypeCache.GetOrAdd(eventName, name =>
+ // {
+ // return types.FirstOrDefault(x => x.Name == name);
+ // });
+ // if (modelType == null)
+ // {
+ // _logger.LogError($"未找到事件类型: {eventName}");
+ // return;
+ // }
+ // // 3. 反序列化为具体类型
+ // input = JsonSerializer.Deserialize(msg, modelType, JsonConfig.Options);
+ // if (input == null)
+ // {
+ // _logger.LogError($"无法将消息反序列化为类型 {eventName}: {msg}");
+ // return;
+ // }
+ // using var scope = _serviceProvider.CreateScope();
+ // if (input is IEventModel tryModel)
+ // {
+ // if (tryModel.ExpireSecond > 0)
+ // {
+ // await _channelHelper.WriteAutoQueueAsync(tryModel);
+ // return;
+ // }
+ // }
+ // // 4. 获取处理器信息(带缓存)
+ // var (handlerType, handleMethod) = _handlerCache.GetOrAdd(modelType, type =>
+ // {
+ // var hType = typeof(IEventHandler<>).MakeGenericType(type);
+ // var method = hType.GetMethod("HandleEventAsync");
+ // return (hType, method);
+ // });
+ // if (handleMethod == null)
+ // {
+ // _logger.LogError($"处理器 {handlerType.Name} 没有 HandleEventAsync 方法");
+ // return;
+ // }
+ // handler = scope.ServiceProvider.GetRequiredService(handlerType);
+ // if (handler == null)
+ // {
+ // _logger.LogError($"没有找到 {eventName} 的 IEventHandler 实现");
+ // return;
+ // }
+ // try
+ // {
+ // // 5. 执行处理
+ // var task = handleMethod.Invoke(handler, new[] { input }) as Task;
+ // if (task != null)
+ // {
+ // await task;
+ // }
+ // else
+ // {
+ // _logger.LogError($"无法调用 {eventName} 的处理方法");
+ // }
+ // }
+ // catch (Exception handlerExl)
+ // {
+ // _logger.LogException(handlerExl);
+ // }
+ // finally
+ // {
+ // try
+ // {
+ // if (handler is IDisposable disposable)
+ // {
+ // disposable.Dispose();
+ // }
+ // if (handler is IAsyncDisposable disposable1)
+ // {
+ // await disposable1.DisposeAsync();
+ // }
+ // }
+ // catch (Exception exlDisposable)
+ // {
+ // _logger.LogException(exlDisposable);
+ // }
+ // }
+ // }
+ // catch (Exception ex)
+ // {
+ // _logger.LogException(ex);
+ // }
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="msg"></param>
+ // /// <param name="types"></param>
+ // /// <param name="methodName"></param>
+ // /// <returns></returns>
+ // public virtual async Task<object> ActionReturnResultAsync(string msg, Type[] types, string methodName = "HandleEventAsync")
+ // {
+ // object input = null;
+ // object handler = null;
+ // try
+ // {
+ // // 1. 只反序列化一次,使用 System.Text.Json 性能更好
+ // using var jsonDoc = JsonDocument.Parse(msg);
+ // var root = jsonDoc.RootElement;
+ // JsonElement eventNameProperty;
+ // // 使用不区分大小写的匹配,更健壮
+ // if (!root.TryGetProperty("eventName", out eventNameProperty))
+ // {
+ // root.TryGetProperty("EventName", out eventNameProperty);
+ // }
+ // // 获取字符串值并验证
+ // string eventName = eventNameProperty.GetString();
+ // if (string.IsNullOrEmpty(eventName))
+ // {
+ // _logger.LogError($"EventName 字段值为空: {msg}");
+ // return null;
+ // }
+ // // 2. 获取事件类型(带缓存)
+ // var modelType = _eventTypeCache.GetOrAdd(eventName, name =>
+ // {
+ // return types.FirstOrDefault(x => x.Name == name);
+ // });
+ // if (modelType == null)
+ // {
+ // _logger.LogError($"未找到事件类型: {eventName}");
+ // return null;
+ // }
+ // // 3. 反序列化为具体类型
+ // input = JsonSerializer.Deserialize(msg, modelType, JsonConfig.Options);
+ // if (input == null)
+ // {
+ // _logger.LogError($"无法将消息反序列化为类型 {eventName}: {msg}");
+ // return null;
+ // }
+ // using var scope = _serviceProvider.CreateScope();
+ // if (input is IEventModel tryModel)
+ // {
+ // if (tryModel.ExpireSecond > 0)
+ // {
+ // await _channelHelper.WriteAutoQueueAsync(tryModel);
+ // return null;
+ // }
+ // }
+ // // 4. 获取处理器信息(带缓存)
+ // var (handlerType, handleMethod) = _handlerCache.GetOrAdd(modelType, type =>
+ // {
+ // var hType = typeof(IEventHandler<>).MakeGenericType(type);
+ // var method = hType.GetMethod(methodName);
+ // return (hType, method);
+ // });
+ // if (handleMethod == null)
+ // {
+ // _logger.LogError($"处理器 {handlerType.Name} 没有 {methodName} 方法");
+ // return null;
+ // }
+ // handler = scope.ServiceProvider.GetRequiredService(handlerType);
+ // if (handler == null)
+ // {
+ // _logger.LogError($"没有找到 {eventName} 的 IEventHandler 实现");
+ // return null;
+ // }
+ // try
+ // {
+ // // 5. 执行处理
+ // var task = handleMethod.Invoke(handler, new[] { input }) as Task;
+ // if (task != null)
+ // {
+ // await task;
+ // //// 从 handler 接口上读 Result 属性(而不是从 Task<T> 里读)
+ // //var resultProp = handlerType.GetProperty("Result");
+ // //return resultProp?.GetValue(handler);
+
+ // // 用具体类型读,而不是接口类型
+ // var resultProp = handler.GetType().GetProperty("Result");
+ // return resultProp?.GetValue(handler);
+ // }
+ // else
+ // {
+ // _logger.LogError($"无法调用 {eventName} 的处理方法");
+ // }
+ // }
+ // catch (Exception handlerExl)
+ // {
+ // _logger.LogException(handlerExl);
+ // //TODO 是否自动尝试... .. .
+ // if (input is IEventModel inputModel)
+ // {
+ // //if(inputModel.tr)
+ // }
+ // }
+ // finally
+ // {
+ // try
+ // {
+ // if (handler is IDisposable disposable)
+ // {
+ // disposable.Dispose();
+ // }
+ // if (handler is IAsyncDisposable disposable1)
+ // {
+ // await disposable1.DisposeAsync();
+ // }
+ // }
+ // catch (Exception exlDisposable)
+ // {
+ // _logger.LogException(exlDisposable);
+ // }
+ // }
+ // }
+ // catch (Exception ex)
+ // {
+ // _logger.LogException(ex);
+ // }
+ // return null;
+ // }
+
+
+ // private readonly ConcurrentDictionary<Type, Type> _eventHandlerCache = new();
+ // private readonly ConcurrentDictionary<Type, MethodInfo> _handleMethodCache = new();
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="read"></param>
+ // /// <returns></returns>
+ // [Obsolete("Use ActionMessageAsync")]
+ // public async Task HanlderEventMessage(IEventModel read)
+ // {
+ // await ActionMessageAsync(read);
+ // }
+
+ // /// <summary>
+ // ///
+ // /// </summary>
+ // /// <param name="read"></param>
+ // /// <returns></returns>
+ // [Obsolete("Use ActionMessageAsync")]
+ // public async Task HanlderEventMessage(string read)
+ // {
+ // await ActionMessageAsync(read);
+ // }
+
+ // /// <summary>
+ // /// 主要适用于本地Chennel模式
+ // /// </summary>
+ // /// <param name="read"></param>
+ // /// <returns></returns>
+ // public virtual async Task ActionMessageAsync(IEventModel read)
+ // {
+ // _logger.LogInformation($"Event Msg:{read.EventName}");
+ // //read是一个IEventModel
+ // var typeName = read.GetType().Name;
+ // var eventType = read.GetType();
+ // using var _scope = _serviceProvider.CreateScope();
+ // var handlerType = _eventHandlerCache.GetOrAdd(eventType, t => typeof(IEventHandler<>).MakeGenericType(t));
+ // var handler = _scope.ServiceProvider.GetService(handlerType);
+ // // 通过依赖注入获取处理器实例
+ // if (handler == null)
+ // {
+ // _logger.LogError($"没有找到 {typeName} 的 IEventHandler 实现!");
+ // return;
+ // }
+ // // 获取处理方法(带缓存)
+ // //var handleMethod = _handleMethodCache.GetOrAdd(eventType, t =>
+ // // handlerType.GetMethod("HandleEventAsync"));
+ // var handleMethod = _handleMethodCache.GetOrAdd(handlerType, t => t.GetMethod("HandleEventAsync"));
+ // if (handleMethod == null)
+ // {
+ // _logger.LogError($"无法找到 {typeName} 的 HandleEventAsync 方法");
+ // return;
+ // }
+ // try
+ // {
+ // var task = handleMethod.Invoke(handler, new[] { read }) as Task;
+ // if (task != null)
+ // {
+ // await task;
+ // }
+ // //_logger.LogInformation($"执行了 {typeName} 的 HandleEventAsync 函数");
+ // }
+ // catch (Exception ex)
+ // {
+ // _logger.LogError(ex, $"处理事件 {eventType.Name} 时发生错误");
+ // }
+ // finally
+ // {
+ // try
+ // {
+ // if (handler is IDisposable disposable)
+ // {
+ // disposable.Dispose();
+ // }
+ // if (handler is IAsyncDisposable disposable1)
+ // {
+ // await disposable1.DisposeAsync();
+ // }
+ // }
+ // catch (Exception exl)
+ // {
+ // _logger.LogException(exl);
+ // }
+ // }
+ // }
+
+ //}
+
+ #endregion
}
framework/PasteApeart.Helper/BuilderHelper.cs
diff --git a/framework/PasteApeart.Helper/BuilderHelper.cs b/framework/PasteApeart.Helper/BuilderHelper.cs
index 4130690..6f71ef2 100644
--- a/framework/PasteApeart.Helper/BuilderHelper.cs
+++ b/framework/PasteApeart.Helper/BuilderHelper.cs
@@ -2517,179 +2517,17 @@ namespace PasteApeart.Helper
}
-
- #region 脱敏处理
-
- /// <summary>
- /// 对数据进行脱敏处理
- /// </summary>
- /// <param name="data">要处理的数据(可以是object或List)</param>
- /// <param name="targetType">目标类型</param>
- /// <returns>脱敏后的数据</returns>
- public static object PasteMaskData(object data, Type targetType)
- {
- if (data == null || targetType == null)
- {
- return data;
- }
- // 检查类型是否有 [PasteHalfHide] 特性
- var classAttribute = targetType.GetCustomAttribute<PasteHalfHideAttribute>();
-
- // 如果类型没有脱敏特性,直接返回原数据
- if (classAttribute == null)
- {
- return data;
- }
- var properties = ReadTypeProperties(targetType);// targetType.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
- if (classAttribute.CreateNewObj)
- {
- if (data is IEnumerable list)
- {
- //var listType = list.GetType().GetGenericArguments()[0];
- var resultList = new List<object>();
- foreach (var item in list)
- {
- if (item.GetType() != targetType)
- {
- resultList.Add(item);
- continue;
- }
- var maskedItem = MaskNewSingleObject(item, targetType, properties);
- resultList.Add(maskedItem);
- }
- return resultList;
- }
- }
- else
- {
- if (data is IEnumerable list)
- {
- foreach (var item in list)
- {
- MaskSingleObject(item, targetType, properties);
- }
- }
- }
-
- if (classAttribute.CreateNewObj)
- {
- // 处理单个对象
- return MaskNewSingleObject(data, targetType, properties);
- }
- else
- {
- MaskSingleObject(data, targetType, properties);
- return data;
- }
- }
-
/// <summary>
- /// 原对象上处理
+ /// 脱敏数据 基于特性PasteHalfHide
/// </summary>
- /// <param name="obj"></param>
- /// <param name="type"></param>
- /// <param name="properties"></param>
+ /// <param name="data"></param>
+ /// <param name="targetType"></param>
/// <returns></returns>
- private static void MaskSingleObject(object obj, Type type, PropertyInfo[] properties)
- {
- if (obj == null)
- return;
-
- foreach (var property in properties)
- {
- var attr = property.GetCustomAttribute<PasteHalfHideAttribute>();
- if (attr == null)
- continue;
-
- var originalValue = property.GetValue(obj)?.ToString();
- if (string.IsNullOrEmpty(originalValue))
- continue;
-
- var maskedValue = Desensitize(originalValue, attr);
- property.SetValue(obj, maskedValue); // 直接修改原对象
- }
-
- //return obj;
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="obj"></param>
- /// <param name="type"></param>
- /// <param name="properties"></param>
- /// <returns></returns>
- private static object MaskNewSingleObject(object obj, Type type, PropertyInfo[] properties)
- {
- if (obj == null)
- return null;
-
- // 创建对象副本
- var result = Activator.CreateInstance(type);
- //var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
-
- foreach (var property in properties)
- {
- // 检查属性是否有 [PasteHalfHide] 特性
- var attr = property.GetCustomAttribute<PasteHalfHideAttribute>();
-
- if (attr == null)
- {
- // 没有特性,直接复制值
- var value = property.GetValue(obj);
- property.SetValue(result, value);
- continue;
- }
-
- // 获取属性值
- var originalValue = property.GetValue(obj)?.ToString();
- if (string.IsNullOrEmpty(originalValue))
- {
- property.SetValue(result, originalValue);
- continue;
- }
-
- // 进行脱敏处理
- var maskedValue = Desensitize(originalValue, attr);
- property.SetValue(result, maskedValue);
- }
-
- return result;
- }
-
- /// <summary>
- /// 执行脱敏
- /// </summary>
- private static string Desensitize(string value, PasteHalfHideAttribute attr)
- {
- if (string.IsNullOrEmpty(value))
- {
- return value;
- }
-
- return DesensitizeCustom(value, attr.StartLength, attr.EndLength, attr.MaskChar);
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="value"></param>
- /// <param name="startLength"></param>
- /// <param name="endLength"></param>
- /// <param name="maskChar"></param>
- /// <returns></returns>
- private static string DesensitizeCustom(string value, int startLength, int endLength, char maskChar)
+ [Obsolete("Use HalfHideHelper.PasteMaskData")]
+ public static object PasteMaskData(object data, Type targetType)
{
- if (string.IsNullOrEmpty(value)) return value;
- if (value.Length <= startLength + endLength) return value;
-
- return value.Substring(0, startLength) +
- new string(maskChar, value.Length - startLength - endLength) +
- value.Substring(value.Length - endLength);
+ return HalfHideHelper.PasteMaskData(data, targetType);
}
- #endregion
-
}
}
framework/PasteApeart.Helper/HalfHideHelper.cs
diff --git a/framework/PasteApeart.Helper/HalfHideHelper.cs b/framework/PasteApeart.Helper/HalfHideHelper.cs
new file mode 100644
index 0000000..f9ff8dc
--- /dev/null
+++ b/framework/PasteApeart.Helper/HalfHideHelper.cs
@@ -0,0 +1,307 @@
+using System.Collections;
+using System.Reflection;
+
+namespace PasteApeart.Helper
+{
+ /// <summary>
+ ///
+ /// </summary>
+ public class HalfHideHelper
+ {
+
+ #region 脱敏处理 跟级的Class一定要标记PasteHalfHide特性
+
+ /// <summary>
+ /// 对字段进行脱敏处理,注意[PasteHalfHide]如果没在Class标记,则使用默认值,就近原则!
+ /// </summary>
+ /// <param name="data"></param>
+ /// <param name="targetType"></param>
+ /// <returns></returns>
+ public static object PasteMaskData(object data, Type targetType)
+ {
+ if (data == null || targetType == null)
+ {
+ return data;
+ }
+
+ var classAttribute = targetType.GetCustomAttribute<PasteHalfHideAttribute>();
+ if (classAttribute == null)
+ {
+ //跟级一定要有class的特性,这样可以避免无用扫描
+ return data;
+ }
+
+ var properties = ReadTypeProperties(targetType);
+ bool createNew = classAttribute.CreateNewObj; // 唯一决策点,向下传
+
+ if (data is IEnumerable list && data is not string)
+ {
+ if (createNew)
+ {
+ var resultList = new List<object>();
+ foreach (var item in list)
+ {
+ if (item == null || !targetType.IsInstanceOfType(item))
+ {
+ resultList.Add(item);
+ continue;
+ }
+ resultList.Add(MaskNewSingleObject(item, targetType, properties, createNew, classAttribute));
+ }
+ return resultList;
+ }
+ else
+ {
+ foreach (var item in list)
+ {
+ if (item == null || !targetType.IsInstanceOfType(item))
+ continue;
+ MaskSingleObject(item, targetType, properties, createNew, classAttribute);
+ }
+ return data;
+ }
+ }
+
+ if (createNew)
+ return MaskNewSingleObject(data, targetType, properties, createNew, classAttribute);
+
+ MaskSingleObject(data, targetType, properties, createNew, classAttribute);
+ return data;
+ }
+
+ /// <summary>
+ /// 原地修改模式(createNew 恒 false)
+ /// 字段特性就近优先;字段没标但类标了 -> 用类级;都没标 -> 跳过
+ /// </summary>
+ private static void MaskSingleObject(object obj, Type type, PropertyInfo[] properties, bool createNew, PasteHalfHideAttribute inputAttribute)
+ {
+ if (obj == null) return;
+
+ var classAttr = type.GetCustomAttribute<PasteHalfHideAttribute>();
+ if (classAttr == null)
+ {
+ classAttr = inputAttribute;
+ }
+
+ foreach (var property in properties)
+ {
+ if (!property.CanRead || !property.CanWrite) continue;
+
+ var propAttr = property.GetCustomAttribute<PasteHalfHideAttribute>();
+ var value = property.GetValue(obj);
+
+ bool shouldMask = propAttr != null || classAttr != null;
+ if (!shouldMask) continue;
+
+ // string
+ if (property.PropertyType == typeof(string))
+ {
+ var s = value as string;
+ if (string.IsNullOrEmpty(s)) continue;
+ var attr = propAttr ?? classAttr; // 就近优先
+ property.SetValue(obj, Desensitize(s, attr));
+ continue;
+ }
+
+ // 值类型
+ if (property.PropertyType.IsValueType || property.PropertyType == typeof(DateTime))
+ continue;
+
+ // 集合
+ if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
+ {
+ if (value is not IEnumerable srcList) continue;
+ var elementType = GetEnumerableElementType(property.PropertyType);
+ if (elementType == null) continue;
+ var elementProps = ReadTypeProperties(elementType);
+ foreach (var item in srcList)
+ {
+ if (item == null || !elementType.IsInstanceOfType(item)) continue;
+ MaskSingleObject(item, elementType, elementProps, createNew, classAttr);
+ }
+ continue;
+ }
+
+ // 复杂对象
+ if (value == null) continue;
+ var childType = property.PropertyType;
+ var childProps = ReadTypeProperties(childType);
+ MaskSingleObject(value, childType, childProps, createNew, classAttr);
+ }
+ }
+
+ /// <summary>
+ /// 副本模式(createNew 恒 true)
+ /// 不吞值:字段没标 -> 原样复制原值;字段标了 -> 处理
+ /// 类没标不再提前 return 空对象
+ /// </summary>
+ private static object MaskNewSingleObject(object obj, Type type, PropertyInfo[] properties, bool createNew, PasteHalfHideAttribute inputAttribute)
+ {
+ if (obj == null) return null;
+
+ var classAttr = type.GetCustomAttribute<PasteHalfHideAttribute>();
+ if (classAttr == null)
+ {
+ classAttr = inputAttribute;
+ }
+ var result = Activator.CreateInstance(type);
+
+ foreach (var property in properties)
+ {
+ if (!property.CanRead || !property.CanWrite) continue;
+
+ var propAttr = property.GetCustomAttribute<PasteHalfHideAttribute>();
+ var value = property.GetValue(obj);
+
+ bool shouldMask = propAttr != null || classAttr != null;
+
+ // string
+ if (property.PropertyType == typeof(string))
+ {
+ var s = value as string;
+ if (!shouldMask || string.IsNullOrEmpty(s))
+ {
+ property.SetValue(result, s); // 没标/空值:原样复制,不吞
+ continue;
+ }
+ var attr = propAttr ?? classAttr;
+ property.SetValue(result, Desensitize(s, attr));
+ continue;
+ }
+
+ // 值类型
+ if (property.PropertyType.IsValueType || property.PropertyType == typeof(DateTime))
+ {
+ property.SetValue(result, value);
+ continue;
+ }
+
+ // 集合
+ if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
+ {
+ if (value is not IEnumerable srcList)
+ {
+ property.SetValue(result, value);
+ continue;
+ }
+ var elementType = GetEnumerableElementType(property.PropertyType);
+ if (elementType == null)
+ {
+ property.SetValue(result, value);
+ continue;
+ }
+ if (!shouldMask)
+ {
+ // 没标:原样复制集合引用
+ property.SetValue(result, value);
+ continue;
+ }
+ var elementProps = ReadTypeProperties(elementType);
+ var newList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType));
+ foreach (var item in srcList)
+ {
+ if (item == null || !elementType.IsInstanceOfType(item))
+ {
+ newList.Add(item);
+ continue;
+ }
+ newList.Add(MaskNewSingleObject(item, elementType, elementProps, createNew, classAttr));
+ }
+ property.SetValue(result, newList);
+ continue;
+ }
+
+ // 复杂对象
+ if (!shouldMask || value == null)
+ {
+ property.SetValue(result, value); // 没标/空值:原样复制,不吞
+ continue;
+ }
+
+ var childType = property.PropertyType;
+ var childProps = ReadTypeProperties(childType);
+ property.SetValue(result, MaskNewSingleObject(value, childType, childProps, createNew, classAttr));
+ }
+
+ return result;
+ }
+
+ private static Type GetEnumerableElementType(Type t)
+ {
+ if (t.IsArray) return t.GetElementType();
+ if (t.IsGenericType && t.GetGenericArguments().Length == 1)
+ return t.GetGenericArguments()[0];
+ var iface = t.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>));
+ return iface?.GetGenericArguments()[0];
+ }
+
+ private static string Desensitize(string value, PasteHalfHideAttribute attr)
+ {
+ if (string.IsNullOrEmpty(value)) return value;
+ return DesensitizeCustom(value, attr.StartLength, attr.EndLength, attr.MaskChar);
+ }
+
+ private static string DesensitizeCustom(string value, int startLength, int endLength, char maskChar)
+ {
+ if (string.IsNullOrEmpty(value)) return value;
+ if (startLength < 0) startLength = 0;
+ if (endLength < 0) endLength = 0;
+ if (value.Length <= startLength + endLength) return value;
+
+ return value.Substring(0, startLength) +
+ new string(maskChar, value.Length - startLength - endLength) +
+ value.Substring(value.Length - endLength);
+ }
+
+ #endregion
+
+ /// <summary>
+ ///
+ /// </summary>
+ /// <param name="searchType"></param>
+ /// <returns></returns>
+ private static PropertyInfo[] ReadTypeProperties(Type searchType)
+ {
+
+ var typeName = searchType.FullName;
+
+ var allProperties = searchType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
+
+ // 辅助方法:计算类型的继承深度
+ int GetInheritanceDepth(Type type)
+ {
+ int depth = 0;
+ while (type.BaseType != null && type.BaseType != typeof(object))
+ {
+ depth++;
+ type = type.BaseType;
+ }
+ return depth;
+ }
+
+ // 按继承深度从远到近排序
+ var sortedProperties = allProperties
+ .OrderBy(p => GetInheritanceDepth(p.DeclaringType))
+ .ToArray();
+
+ var hasDuplicates = sortedProperties.Length != sortedProperties.Select(p => p.Name).Distinct().Count();
+ if (!hasDuplicates)
+ {
+ return sortedProperties;
+ }
+
+ // 去重:保留最近的(后面的覆盖前面的)
+ var propertyDict = new Dictionary<string, PropertyInfo>();
+ foreach (var prop in sortedProperties)
+ {
+ propertyDict[prop.Name] = prop;
+ }
+
+ return propertyDict.Values
+ .OrderBy(p => GetInheritanceDepth(p.DeclaringType))
+ .ToArray();
+
+ }
+
+ }
+}
framework/PasteApeart.Helper/PasteApeartAttribute.cs
diff --git a/framework/PasteApeart.Helper/PasteApeartAttribute.cs b/framework/PasteApeart.Helper/PasteApeartAttribute.cs
index 083989f..4dac959 100644
--- a/framework/PasteApeart.Helper/PasteApeartAttribute.cs
+++ b/framework/PasteApeart.Helper/PasteApeartAttribute.cs
@@ -1074,16 +1074,16 @@ namespace PasteApeart.Helper
public char MaskChar { get; set; } = '*';
/// <summary>
- ///
+ /// 创建新对象,还是在原值上修改,顶级Class上有效,字段上无效
/// </summary>
public bool CreateNewObj { get; set; } = false;
/// <summary>
/// 服务端特性 脱敏处理 需要Class先标记,表示这个对象有字段要脱敏 然后字段标记
/// </summary>
- /// <param name="startLen"></param>
- /// <param name="endLen"></param>
- /// <param name="hideChar"></param>
+ /// <param name="startLen">开头保持位数</param>
+ /// <param name="endLen">尾巴保持位数</param>
+ /// <param name="hideChar">默认字段的脱敏字符</param>
/// <param name="newObj">新对象上处理,否则为原对象上变更</param>
public PasteHalfHideAttribute(int startLen = 3, int endLen = 4, char hideChar = '*', bool newObj = false)
{
manage/page/pasteform/form.js
diff --git a/manage/page/pasteform/form.js b/manage/page/pasteform/form.js
index 8dca2c5..3d0c85f 100644
--- a/manage/page/pasteform/form.js
+++ b/manage/page/pasteform/form.js
@@ -2949,7 +2949,7 @@ function InitMarkDownEditor(item) {
BtnExpend = editormd.BtnMenu;
- _mdEditor = editormd(_editor, {
+ var _mdEditor = editormd(_editor, {
height: _high,
saveHTMLToTextarea: true,
imageUpload: true,
manage/page/pasteform/view.html
diff --git a/manage/page/pasteform/view.html b/manage/page/pasteform/view.html
index e4e6efc..6e811d1 100644
--- a/manage/page/pasteform/view.html
+++ b/manage/page/pasteform/view.html
@@ -121,7 +121,7 @@
<%}%>
<% break;
case "password": %>
- <input name="<%:=item.name%>" autocomplete="off" class="input-text" <% if(item.required){%>required<%}%> type="password" onblur="handlerBlur(this)" <% if(item.readonly){ %>readonly<% } %> value="<%:=item.value || ''%>" <%if(item.maxlength!=0){%> maxlength="<%:=item.maxlength%>"<%}%> placeholder="<%:=item.placeholder%>" />
+ <input name="<%:=item.name%>" autocomplete="new-password" class="input-text" <% if(item.required){%>required<%}%> type="password" onblur="handlerBlur(this)" <% if(item.readonly){ %>readonly<% } %> value="<%:=item.value || ''%>" <%if(item.maxlength!=0){%> maxlength="<%:=item.maxlength%>"<%}%> placeholder="<%:=item.placeholder%>" />
<span class="Hui-iconfont Hui-iconfont-del spanclean" onclick="handlerClean(this)"></span>
<% break;
case "html": %>