diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index efd7a4322..d69ed2ef4 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -2,7 +2,7 @@ name: "Publish"
on:
push:
- branches: [ rel-8.3.4 ]
+ branches: [ main ]
env:
DOTNET_VERSION: "8.0.200"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 697605b3b..c0e87a66a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,7 +2,7 @@ name: "Tagged Release"
on:
push:
- branches: [ rel-8.3.4 ]
+ branches: [ main ]
jobs:
tagged-release:
diff --git a/aspnet-core/LINGYUN.MicroService.All.sln b/aspnet-core/LINGYUN.MicroService.All.sln
index 1ed9cfe2b..5581b0989 100644
--- a/aspnet-core/LINGYUN.MicroService.All.sln
+++ b/aspnet-core/LINGYUN.MicroService.All.sln
@@ -767,6 +767,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LINGYUN.Abp.OssManagement.I
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.HttpApi.Client", "modules\realtime-notifications\LINGYUN.Abp.Notifications.HttpApi.Client\LINGYUN.Abp.Notifications.HttpApi.Client.csproj", "{3CBD1342-C021-49FB-933F-FAC1DAFC7B48}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Templating", "modules\realtime-notifications\LINGYUN.Abp.Notifications.Templating\LINGYUN.Abp.Notifications.Templating.csproj", "{E2AC3DB8-D579-49A3-845B-528C77A1EC75}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Templating.Tests", "tests\LINGYUN.Abp.Notifications.Templating.Tests\LINGYUN.Abp.Notifications.Templating.Tests.csproj", "{F47EDB4F-1F92-4887-8F33-BFCEB61BB51A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1969,6 +1973,14 @@ Global
{3CBD1342-C021-49FB-933F-FAC1DAFC7B48}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CBD1342-C021-49FB-933F-FAC1DAFC7B48}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CBD1342-C021-49FB-933F-FAC1DAFC7B48}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E2AC3DB8-D579-49A3-845B-528C77A1EC75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E2AC3DB8-D579-49A3-845B-528C77A1EC75}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E2AC3DB8-D579-49A3-845B-528C77A1EC75}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E2AC3DB8-D579-49A3-845B-528C77A1EC75}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F47EDB4F-1F92-4887-8F33-BFCEB61BB51A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F47EDB4F-1F92-4887-8F33-BFCEB61BB51A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F47EDB4F-1F92-4887-8F33-BFCEB61BB51A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F47EDB4F-1F92-4887-8F33-BFCEB61BB51A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2340,6 +2352,8 @@ Global
{76DDE71D-00BD-4BC8-AEA2-31209E2B7E05} = {B05CB08F-C088-4D6D-97EE-A94A5D1AE4A6}
{267933BD-BFB8-4906-BA39-DF193B2FD558} = {B05CB08F-C088-4D6D-97EE-A94A5D1AE4A6}
{3CBD1342-C021-49FB-933F-FAC1DAFC7B48} = {1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E}
+ {E2AC3DB8-D579-49A3-845B-528C77A1EC75} = {1A23BB7F-1839-4204-88C5-7E9A6C9FBF1E}
+ {F47EDB4F-1F92-4887-8F33-BFCEB61BB51A} = {370D7CD5-1E17-4F3D-BBFA-03429F6D4F2F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C95FDF91-16F2-4A8B-A4BE-0E62D1B66718}
diff --git a/aspnet-core/LINGYUN.MicroService.SingleProject.sln b/aspnet-core/LINGYUN.MicroService.SingleProject.sln
index 7c62c9ff2..c0759b755 100644
--- a/aspnet-core/LINGYUN.MicroService.SingleProject.sln
+++ b/aspnet-core/LINGYUN.MicroService.SingleProject.sln
@@ -616,6 +616,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.AuditLogging.IP
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LY.MicroService.Applications.Single.EntityFrameworkCore.MySql", "migrations\LY.MicroService.Applications.Single.EntityFrameworkCore.MySql\LY.MicroService.Applications.Single.EntityFrameworkCore.MySql.csproj", "{746813A9-4221-42D8-AAB5-66CB69EC844F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LINGYUN.Abp.Notifications.Templating", "modules\realtime-notifications\LINGYUN.Abp.Notifications.Templating\LINGYUN.Abp.Notifications.Templating.csproj", "{8CFA17AA-568C-4B40-A48E-1212123ABDD6}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -1622,6 +1624,10 @@ Global
{746813A9-4221-42D8-AAB5-66CB69EC844F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{746813A9-4221-42D8-AAB5-66CB69EC844F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{746813A9-4221-42D8-AAB5-66CB69EC844F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8CFA17AA-568C-4B40-A48E-1212123ABDD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8CFA17AA-568C-4B40-A48E-1212123ABDD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8CFA17AA-568C-4B40-A48E-1212123ABDD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8CFA17AA-568C-4B40-A48E-1212123ABDD6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -1921,6 +1927,7 @@ Global
{5BE31786-EAAB-4C86-8FFF-C07F27FBFD1C} = {99B7CBDE-A251-4738-97F0-DB1DB484BEE1}
{77B3C7A4-15C7-4EFF-8451-4F13B4CCA4AE} = {C22741F9-FC56-4AE3-B543-9F15C779D345}
{746813A9-4221-42D8-AAB5-66CB69EC844F} = {0D69B63D-F082-4D57-9FF0-355642C56993}
+ {8CFA17AA-568C-4B40-A48E-1212123ABDD6} = {42F31C68-B8B2-4BE0-9AD0-A7DFA6092629}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {711A43C0-A2F8-4E5C-9B9F-F2551E4B3FF1}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xml b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xsd b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/FodyWeavers.xsd
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN.Abp.Notifications.Templating.csproj b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN.Abp.Notifications.Templating.csproj
new file mode 100644
index 000000000..188573b06
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN.Abp.Notifications.Templating.csproj
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0
+ LINGYUN.Abp.Notifications.Templating
+ LINGYUN.Abp.Notifications.Templating
+ false
+ false
+ false
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsResolveOptions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsResolveOptions.cs
new file mode 100644
index 000000000..8fde7a895
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsResolveOptions.cs
@@ -0,0 +1,17 @@
+using JetBrains.Annotations;
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public class AbpNotificationsResolveOptions
+{
+ ///
+ /// 模板解析提供者列表
+ ///
+ [NotNull]
+ public List TemplateResolvers { get; }
+
+ public AbpNotificationsResolveOptions()
+ {
+ TemplateResolvers = new List();
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingModule.cs
new file mode 100644
index 000000000..ddcabe3e9
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingModule.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+
+[DependsOn(typeof(AbpNotificationsCoreModule))]
+public class AbpNotificationsTemplatingModule : AbpModule
+{
+
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContext.cs
new file mode 100644
index 000000000..723f8a46c
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContext.cs
@@ -0,0 +1,11 @@
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public interface INotificationTemplateResolveContext : IServiceProviderAccessor
+{
+ NotificationTemplate Template { get; }
+
+ object Model { get; set; }
+
+ bool Handled { get; set; }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContributor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContributor.cs
new file mode 100644
index 000000000..9326eac5d
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolveContributor.cs
@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public interface INotificationTemplateResolveContributor
+{
+ string Name { get; }
+
+ Task ResolveAsync(INotificationTemplateResolveContext context);
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolver.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolver.cs
new file mode 100644
index 000000000..a88345afd
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/INotificationTemplateResolver.cs
@@ -0,0 +1,17 @@
+using JetBrains.Annotations;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+///
+/// 通知模板模型解析接口
+///
+public interface INotificationTemplateResolver
+{
+ ///
+ /// 解析模板数据
+ ///
+ ///
+ ///
+ [NotNull]
+ Task ResolveAsync(NotificationTemplate template);
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContext.cs
new file mode 100644
index 000000000..1de28038b
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContext.cs
@@ -0,0 +1,26 @@
+using System;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public class NotificationTemplateResolveContext : INotificationTemplateResolveContext
+{
+ public IServiceProvider ServiceProvider { get; }
+
+ public NotificationTemplate Template { get; }
+
+ public object Model { get; set; }
+
+ public bool Handled { get; set; }
+
+ public bool HasResolvedModel()
+ {
+ return Handled || Model != null;
+ }
+
+ public NotificationTemplateResolveContext(
+ NotificationTemplate template,
+ IServiceProvider serviceProvider)
+ {
+ Template = template;
+ ServiceProvider = serviceProvider;
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContributorBase.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContributorBase.cs
new file mode 100644
index 000000000..6e1d55080
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveContributorBase.cs
@@ -0,0 +1,13 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public abstract class NotificationTemplateResolveContributorBase : INotificationTemplateResolveContributor
+{
+ public abstract string Name { get; }
+ ///
+ /// 实现此接口处理模板数据
+ ///
+ ///
+ ///
+ public abstract Task ResolveAsync(INotificationTemplateResolveContext context);
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveResult.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveResult.cs
new file mode 100644
index 000000000..c1cd21968
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolveResult.cs
@@ -0,0 +1,16 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public class NotificationTemplateResolveResult
+{
+ ///
+ /// 模板数据
+ ///
+ public object Model { get; set; }
+
+ public List AppliedResolvers { get; }
+ public NotificationTemplateResolveResult()
+ {
+ AppliedResolvers = new List();
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolver.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolver.cs
new file mode 100644
index 000000000..b92d95044
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Templating/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolver.cs
@@ -0,0 +1,58 @@
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading.Tasks;
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public class NotificationTemplateResolver : INotificationTemplateResolver, ITransientDependency
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly AbpNotificationsResolveOptions _options;
+
+ public NotificationTemplateResolver(
+ IOptions options,
+ IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ _options = options.Value;
+ }
+ public async virtual Task ResolveAsync(NotificationTemplate template)
+ {
+ var result = new NotificationTemplateResolveResult();
+
+ using (var serviceScope = _serviceProvider.CreateScope())
+ {
+ var context = new NotificationTemplateResolveContext(template, serviceScope.ServiceProvider);
+
+ foreach (var resolveContributor in _options.TemplateResolvers)
+ {
+ // TODO: 设定为每一个通知都配置自己的解析提供者?
+ /**
+ if (resolveContributor.Name.Equals(template.Name))
+ {
+ await resolveContributor.ResolveAsync(context);
+ }
+
+ if (context.HasResolvedModel())
+ {
+ result.Model = context.Model;
+ break;
+ }
+ **/
+
+ await resolveContributor.ResolveAsync(context);
+
+ result.AppliedResolvers.Add(resolveContributor.Name);
+
+ if (context.HasResolvedModel())
+ {
+ result.Model = context.Model;
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/aspnet-core/services/Directory.Packages.props b/aspnet-core/services/Directory.Packages.props
index 9c3dce1ab..cbce42789 100644
--- a/aspnet-core/services/Directory.Packages.props
+++ b/aspnet-core/services/Directory.Packages.props
@@ -3,53 +3,6 @@
8.3.4
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/aspnet-core/services/LY.AIO.Applications.Single/LY.AIO.Applications.Single.csproj b/aspnet-core/services/LY.AIO.Applications.Single/LY.AIO.Applications.Single.csproj
index 0fbfdd223..61185e75c 100644
--- a/aspnet-core/services/LY.AIO.Applications.Single/LY.AIO.Applications.Single.csproj
+++ b/aspnet-core/services/LY.AIO.Applications.Single/LY.AIO.Applications.Single.csproj
@@ -1,4 +1,4 @@
-
+
@@ -54,6 +54,7 @@
+
@@ -212,8 +213,6 @@
-
-
@@ -261,12 +260,4 @@
-
-
-
-
-
-
-
-
diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs b/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs
index 8f9ee9a3e..5cc4b0e37 100644
--- a/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs
+++ b/aspnet-core/services/LY.MicroService.Applications.Single/EventBus/Distributed/NotificationEventHandler.cs
@@ -1,4 +1,5 @@
using LINGYUN.Abp.Notifications;
+using LINGYUN.Abp.Notifications.Templating;
using LY.MicroService.Applications.Single.BackgroundJobs;
using LY.MicroService.Applications.Single.MultiTenancy;
using Microsoft.Extensions.Localization;
@@ -65,6 +66,10 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
///
protected IStringLocalizerFactory StringLocalizerFactory { get; }
///
+ /// Reference to .
+ ///
+ protected INotificationTemplateResolver NotificationTemplateResolver { get; }
+ ///
/// Reference to .
///
protected INotificationDataSerializer NotificationDataSerializer { get; }
@@ -94,6 +99,7 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
IOptions options,
INotificationStore notificationStore,
INotificationDataSerializer notificationDataSerializer,
+ INotificationTemplateResolver notificationTemplateResolver,
INotificationDefinitionManager notificationDefinitionManager,
INotificationSubscriptionManager notificationSubscriptionManager,
INotificationPublishProviderManager notificationPublishProviderManager)
@@ -107,6 +113,7 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
StringLocalizerFactory = stringLocalizerFactory;
NotificationStore = notificationStore;
NotificationDataSerializer = notificationDataSerializer;
+ NotificationTemplateResolver = notificationTemplateResolver;
NotificationDefinitionManager = notificationDefinitionManager;
NotificationSubscriptionManager = notificationSubscriptionManager;
NotificationPublishProviderManager = notificationPublishProviderManager;
@@ -130,23 +137,25 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
}
using (CultureHelper.Use(culture, culture))
{
+ var result = await NotificationTemplateResolver.ResolveAsync(eventData.Data);
+
if (notification.NotificationType == NotificationType.System)
{
using (CurrentTenant.Change(null))
{
- await SendToTenantAsync(null, notification, eventData);
+ await SendToTenantAsync(null, notification, eventData, result);
var allActiveTenants = await TenantConfigurationCache.GetTenantsAsync();
foreach (var activeTenant in allActiveTenants)
{
- await SendToTenantAsync(activeTenant.Id, notification, eventData);
+ await SendToTenantAsync(activeTenant.Id, notification, eventData, result);
}
}
}
else
{
- await SendToTenantAsync(eventData.TenantId, notification, eventData);
+ await SendToTenantAsync(eventData.TenantId, notification, eventData, result);
}
}
}
@@ -183,7 +192,8 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
protected async virtual Task SendToTenantAsync(
Guid? tenantId,
NotificationDefinition notification,
- NotificationEto eventData)
+ NotificationEto eventData,
+ NotificationTemplateResolveResult templateResolveResult)
{
using (CurrentTenant.Change(tenantId))
{
@@ -219,7 +229,8 @@ namespace LY.MicroService.Applications.Single.EventBus.Distributed
// 由于模板通知受租户影响, 格式化失败的消息将被丢弃.
message = await TemplateRenderer.RenderAsync(
templateName: eventData.Data.Name,
- model: eventData.Data.ExtraProperties,
+ // 序列化之后数据可能需要自行处理, 如未处理数据, 将使用默认数据渲染模板
+ model: templateResolveResult.Model ?? eventData.Data.ExtraProperties,
cultureName: eventData.Data.Culture,
globalContext: new Dictionary
{
diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
index f19b3c14a..0581ca288 100644
--- a/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
+++ b/aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
@@ -202,6 +202,7 @@
+
diff --git a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs
index 4abe4ef46..4d38ff881 100644
--- a/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs
+++ b/aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs
@@ -1,6 +1,7 @@
using LINGYUN.Abp.AuditLogging.IP.Location;
using LINGYUN.Abp.EventBus.CAP;
using LINGYUN.Abp.IP2Region;
+using LINGYUN.Abp.Notifications.Templating;
using LY.MicroService.Applications.Single.EntityFrameworkCore;
using LY.MicroService.Applications.Single.EntityFrameworkCore.MySql;
using Volo.Abp.MailKit;
@@ -295,6 +296,8 @@ namespace LY.MicroService.Applications.Single;
typeof(AbpNotificationsSignalRModule),
// 通知模块 邮件通知
typeof(AbpNotificationsEmailingModule),
+ // 通知模块 模板解析
+ typeof(AbpNotificationsTemplatingModule),
// 通知模块 微信小程序
typeof(AbpNotificationsWeChatMiniProgramModule),
// 多租户模块 版本
diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs
index 96c338307..d6c9002df 100644
--- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs
+++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/EventBus/Distributed/NotificationEventHandler.cs
@@ -1,4 +1,5 @@
using LINGYUN.Abp.Notifications;
+using LINGYUN.Abp.Notifications.Templating;
using LY.MicroService.RealtimeMessage.BackgroundJobs;
using LY.MicroService.RealtimeMessage.MultiTenancy;
using Microsoft.Extensions.Localization;
@@ -74,6 +75,10 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
///
protected INotificationDataSerializer NotificationDataSerializer { get; }
///
+ /// Reference to .
+ ///
+ protected INotificationTemplateResolver NotificationTemplateResolver { get; }
+ ///
/// Reference to .
///
protected INotificationDefinitionManager NotificationDefinitionManager { get; }
@@ -99,6 +104,7 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
IOptions options,
INotificationStore notificationStore,
INotificationDataSerializer notificationDataSerializer,
+ INotificationTemplateResolver notificationTemplateResolver,
INotificationDefinitionManager notificationDefinitionManager,
INotificationSubscriptionManager notificationSubscriptionManager,
INotificationPublishProviderManager notificationPublishProviderManager)
@@ -112,6 +118,7 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
StringLocalizerFactory = stringLocalizerFactory;
NotificationStore = notificationStore;
NotificationDataSerializer = notificationDataSerializer;
+ NotificationTemplateResolver = notificationTemplateResolver;
NotificationDefinitionManager = notificationDefinitionManager;
NotificationSubscriptionManager = notificationSubscriptionManager;
NotificationPublishProviderManager = notificationPublishProviderManager;
@@ -135,23 +142,25 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
}
using (CultureHelper.Use(culture, culture))
{
+ var result = await NotificationTemplateResolver.ResolveAsync(eventData.Data);
+
if (notification.NotificationType == NotificationType.System)
{
using (CurrentTenant.Change(null))
{
- await SendToTenantAsync(null, notification, eventData);
+ await SendToTenantAsync(null, notification, eventData, result);
var allActiveTenants = await TenantConfigurationCache.GetTenantsAsync();
foreach (var activeTenant in allActiveTenants)
{
- await SendToTenantAsync(activeTenant.Id, notification, eventData);
+ await SendToTenantAsync(activeTenant.Id, notification, eventData, result);
}
}
}
else
{
- await SendToTenantAsync(eventData.TenantId, notification, eventData);
+ await SendToTenantAsync(eventData.TenantId, notification, eventData, result);
}
}
}
@@ -188,7 +197,8 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
protected async virtual Task SendToTenantAsync(
Guid? tenantId,
NotificationDefinition notification,
- NotificationEto eventData)
+ NotificationEto eventData,
+ NotificationTemplateResolveResult templateResolveResult)
{
using (CurrentTenant.Change(tenantId))
{
@@ -224,7 +234,8 @@ namespace LY.MicroService.RealtimeMessage.EventBus.Distributed
// 由于模板通知受租户影响, 格式化失败的消息将被丢弃.
message = await TemplateRenderer.RenderAsync(
templateName: eventData.Data.Name,
- model: eventData.Data.ExtraProperties,
+ // 解决序列化后的数据无法渲染模板
+ model: templateResolveResult.Model ?? eventData.Data.ExtraProperties,
cultureName: eventData.Data.Culture,
globalContext: new Dictionary
{
diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj
index b1adfb3ad..c8be1f727 100644
--- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj
+++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/LY.MicroService.RealtimeMessage.HttpApi.Host.csproj
@@ -89,6 +89,7 @@
+
diff --git a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs
index 2f970c91e..2029bf94a 100644
--- a/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs
+++ b/aspnet-core/services/LY.MicroService.RealtimeMessage.HttpApi.Host/RealtimeMessageHttpApiHostModule.cs
@@ -28,6 +28,7 @@ using LINGYUN.Abp.Notifications.EntityFrameworkCore;
using LINGYUN.Abp.Notifications.Jobs;
using LINGYUN.Abp.Notifications.SignalR;
using LINGYUN.Abp.Notifications.Sms;
+using LINGYUN.Abp.Notifications.Templating;
using LINGYUN.Abp.Notifications.WeChat.MiniProgram;
using LINGYUN.Abp.Notifications.WeChat.Work;
using LINGYUN.Abp.Notifications.WxPusher;
@@ -101,6 +102,7 @@ namespace LY.MicroService.RealtimeMessage;
typeof(AbpNotificationsWeChatMiniProgramModule),
typeof(AbpNotificationsWeChatWorkModule),
typeof(AbpNotificationsExceptionHandlingModule),
+ typeof(AbpNotificationsTemplatingModule),
typeof(AbpWeChatWorkHandlersModule),
typeof(AbpWeChatOfficialHandlersModule),
typeof(AbpIdentityNotificationsModule),
diff --git a/aspnet-core/templates/micro/PackageName.CompanyName.ProjectName.csproj b/aspnet-core/templates/micro/PackageName.CompanyName.ProjectName.csproj
index cceabf72e..17ebde66d 100644
--- a/aspnet-core/templates/micro/PackageName.CompanyName.ProjectName.csproj
+++ b/aspnet-core/templates/micro/PackageName.CompanyName.ProjectName.csproj
@@ -3,13 +3,13 @@
net8.0
true
LINGYUN.Abp.MicroService.Templates
- 8.3.0
+ 8.3.4
colin.in@foxmail.com
Abp framework micro-service template
MIT
false
https://github.com/colinin/abp-next-admin
- micro webapi cloud
+ abp micro webapi cloud
Template
git
https://github.com/colinin/abp-next-admin
diff --git a/aspnet-core/templates/micro/content/Directory.Packages.props b/aspnet-core/templates/micro/content/Directory.Packages.props
index af9607d7a..18e6f3e9a 100644
--- a/aspnet-core/templates/micro/content/Directory.Packages.props
+++ b/aspnet-core/templates/micro/content/Directory.Packages.props
@@ -2,8 +2,8 @@
8.2.0
2.14.1
- 8.3.0
- 8.3.0
+ 8.3.4
+ 8.3.4
8.0.0
8.0.0
8.0.0
diff --git a/aspnet-core/templates/micro/content/common.props b/aspnet-core/templates/micro/content/common.props
index d7d7622cf..6052366a9 100644
--- a/aspnet-core/templates/micro/content/common.props
+++ b/aspnet-core/templates/micro/content/common.props
@@ -1,12 +1,12 @@
latest
- 8.2.1
+ 8.3.4
colin
$(NoWarn);CS1591;CS0436;CS8618;NU1803
https://github.com/colinin/abp-next-admin
$(SolutionDir)LocalNuget
- 8.2.1
+ 8.3.4
MIT
git
https://github.com/colinin/abp-next-admin
diff --git a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/PackageName.CompanyName.ProjectName.HttpApi.Host.csproj b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/PackageName.CompanyName.ProjectName.HttpApi.Host.csproj
index 6ee2a7fc9..32ccca462 100644
--- a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/PackageName.CompanyName.ProjectName.HttpApi.Host.csproj
+++ b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/PackageName.CompanyName.ProjectName.HttpApi.Host.csproj
@@ -31,10 +31,6 @@
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
@@ -59,7 +55,7 @@
-
+
diff --git a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/Program.cs b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/Program.cs
index 092cdeb82..ad8792c62 100644
--- a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/Program.cs
+++ b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/Program.cs
@@ -23,15 +23,19 @@ public class Program
Log.Information("Starting web host.");
var builder = WebApplication.CreateBuilder(args);
- builder.Host.AddAppSettingsSecretsJson()
+ builder.Host
+ .AddAppSettingsSecretsJson()
.UseAutofac()
.ConfigureAppConfiguration((context, config) =>
{
- var configuration = config.Build();
- var agileConfigEnabled = configuration["AgileConfig:IsEnabled"];
- if (agileConfigEnabled.IsNullOrEmpty() || bool.Parse(agileConfigEnabled))
+ var agileConfig = context.Configuration.GetSection("AgileConfig");//IsEnabled
+ if (agileConfig.Exists())
{
- config.AddAgileConfig(new AgileConfig.Client.ConfigClient(configuration));
+ var isAgileConfigEnabled = agileConfig["IsEnabled"];
+ if (isAgileConfigEnabled.IsNullOrWhiteSpace() || bool.Parse(isAgileConfigEnabled))
+ {
+ config.AddAgileConfig(new AgileConfig.Client.ConfigClient(context.Configuration));
+ }
}
})
.UseSerilog((context, provider, config) =>
@@ -45,12 +49,11 @@ public class Program
options.ApplicationName = ProjectNameHttpApiHostModule.ApplicationName;
// 搜索 Modules 目录下所有文件作为插件
// 取消显示引用所有其他项目的模块,改为通过插件的形式引用
- var pluginFolder = Path.Combine(
- Directory.GetCurrentDirectory(), "Modules");
+ var pluginFolder = Path.Combine(Directory.GetCurrentDirectory(), "Modules");
+
DirectoryHelper.CreateIfNotExists(pluginFolder);
- options.PlugInSources.AddFolder(
- pluginFolder,
- SearchOption.AllDirectories);
+
+ options.PlugInSources.AddFolder(pluginFolder, SearchOption.AllDirectories);
});
var app = builder.Build();
await app.InitializeApplicationAsync();
diff --git a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/ProjectNameHttpApiHostModule.cs b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/ProjectNameHttpApiHostModule.cs
index ea6e034c3..b943b09a6 100644
--- a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/ProjectNameHttpApiHostModule.cs
+++ b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/ProjectNameHttpApiHostModule.cs
@@ -1,12 +1,14 @@
+using LINGYUN.Abp.AspNetCore.HttpOverrides;
using LINGYUN.Abp.AspNetCore.Mvc.Wrapper;
using LINGYUN.Abp.AuditLogging.Elasticsearch;
using LINGYUN.Abp.EventBus.CAP;
using LINGYUN.Abp.ExceptionHandling.Emailing;
+using LINGYUN.Abp.Identity.Session.AspNetCore;
using LINGYUN.Abp.LocalizationManagement.EntityFrameworkCore;
-using LINGYUN.Abp.TextTemplating.EntityFrameworkCore;
using LINGYUN.Abp.Saas.EntityFrameworkCore;
using LINGYUN.Abp.Serilog.Enrichers.Application;
using LINGYUN.Abp.Serilog.Enrichers.UniqueId;
+using LINGYUN.Abp.TextTemplating.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
@@ -22,13 +24,11 @@ using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DistributedLocking;
using Volo.Abp.FeatureManagement.EntityFrameworkCore;
using Volo.Abp.Http.Client.IdentityModel.Web;
+using Volo.Abp.MailKit;
using Volo.Abp.Modularity;
using Volo.Abp.PermissionManagement.EntityFrameworkCore;
using Volo.Abp.SettingManagement.EntityFrameworkCore;
using Volo.Abp.Swashbuckle;
-using LINGYUN.Abp.AspNetCore.HttpOverrides;
-using LINGYUN.Abp.Identity.Session.AspNetCore;
-using Volo.Abp.MailKit;
namespace PackageName.CompanyName.ProjectName;
diff --git a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/appsettings.json b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/appsettings.json
index b9c69ab64..4f3ee4d30 100644
--- a/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/appsettings.json
+++ b/aspnet-core/templates/micro/content/host/PackageName.CompanyName.ProjectName.HttpApi.Host/appsettings.json
@@ -1,4 +1,9 @@
{
+ "App": {
+ "CorsOrigins": "http://127.0.0.1:30000",
+ "RefreshClaimsUrl": "http://127.0.0.1:30015/",
+ "HealthChecks": "/healthz"
+ },
"Clock": {
"Kind": "Local"
},
@@ -44,6 +49,9 @@
"path": "Logs/Debug-.log",
"restrictedToMinimumLevel": "Debug",
"rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "retainedFileCountLimit": 50,
+ "fileSizeLimitBytes": 5242880,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
@@ -53,6 +61,9 @@
"path": "Logs/Info-.log",
"restrictedToMinimumLevel": "Information",
"rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "retainedFileCountLimit": 50,
+ "fileSizeLimitBytes": 5242880,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
@@ -62,6 +73,9 @@
"path": "Logs/Warn-.log",
"restrictedToMinimumLevel": "Warning",
"rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "retainedFileCountLimit": 50,
+ "fileSizeLimitBytes": 5242880,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
@@ -71,6 +85,9 @@
"path": "Logs/Error-.log",
"restrictedToMinimumLevel": "Error",
"rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "retainedFileCountLimit": 50,
+ "fileSizeLimitBytes": 5242880,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
},
@@ -80,6 +97,9 @@
"path": "Logs/Fatal-.log",
"restrictedToMinimumLevel": "Fatal",
"rollingInterval": "Day",
+ "rollOnFileSizeLimit": true,
+ "retainedFileCountLimit": 50,
+ "fileSizeLimitBytes": 5242880,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level:u3}] [{SourceContext}] [{ProcessId}] [{ThreadId}] - {Message:lj}{NewLine}{Exception}"
}
}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN.Abp.Notifications.Templating.Tests.csproj b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN.Abp.Notifications.Templating.Tests.csproj
new file mode 100644
index 000000000..e4783658c
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN.Abp.Notifications.Templating.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net8.0
+
+ false
+ Debug;Release;
+ AnyCPU
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestBase.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestBase.cs
new file mode 100644
index 000000000..c9d76e56e
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestBase.cs
@@ -0,0 +1,8 @@
+using LINGYUN.Abp.Tests;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+
+public class AbpNotificationsTemplatingTestBase : AbpTestsBase
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestModule.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestModule.cs
new file mode 100644
index 000000000..4fb515f7d
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/AbpNotificationsTemplatingTestModule.cs
@@ -0,0 +1,14 @@
+using LINGYUN.Abp.Tests;
+using Volo.Abp.Json;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+
+[DependsOn(
+ typeof(AbpNotificationsTemplatingModule),
+ typeof(AbpJsonModule),
+ typeof(AbpTestsBaseModule))]
+public class AbpNotificationsTemplatingTestModule : AbpModule
+{
+
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NewtownsoftJsonTemplateResolveContributor.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NewtownsoftJsonTemplateResolveContributor.cs
new file mode 100644
index 000000000..6b93271c4
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NewtownsoftJsonTemplateResolveContributor.cs
@@ -0,0 +1,20 @@
+using Newtonsoft.Json.Linq;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+internal class NewtownsoftJsonTemplateResolveContributor : INotificationTemplateResolveContributor
+{
+ public string Name => "ToDynamic";
+
+ public Task ResolveAsync(INotificationTemplateResolveContext context)
+ {
+ var jsonObject = new JObject();
+ foreach (var prop in context.Template.ExtraProperties)
+ {
+ jsonObject.Add(prop.Key, prop.Value.ToString());
+ }
+ context.Model = jsonObject.ToObject();
+
+ return Task.CompletedTask;
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationModel.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationModel.cs
new file mode 100644
index 000000000..4122ff61b
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationModel.cs
@@ -0,0 +1,29 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+
+public class NotificationSimpleModel
+{
+ public string Name { get; set; }
+ public string Firend { get; set; }
+}
+
+public class NotificationModel
+{
+ public string Name { get; set; }
+ public List Jobs { get; set; }
+}
+
+public class NotificationJob
+{
+ public string Name { get; set; }
+ public NotificationJob()
+ {
+
+ }
+
+ public NotificationJob(string name)
+ {
+ Name = name;
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolverTests.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolverTests.cs
new file mode 100644
index 000000000..70b14f5ea
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/NotificationTemplateResolverTests.cs
@@ -0,0 +1,75 @@
+using LINGYUN.Abp.RealTime;
+using Newtonsoft.Json;
+using Shouldly;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Json;
+using Xunit;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+public class NotificationTemplateResolverTests : AbpNotificationsTemplatingTestBase
+{
+ private IJsonSerializer _jsonSerializer;
+ public NotificationTemplateResolverTests()
+ {
+ _jsonSerializer = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task Resolve_Deserialize_To_Object()
+ {
+ var notificationTemplate = new NotificationTemplate(
+ "Test",
+ data: new Dictionary
+ {
+ { "Name", "Tom" },
+ { "Jobs", new List
+ {
+ new NotificationJob("Catch Jerry"),
+ new NotificationJob("Hit Pike")
+ }
+ }
+ });
+
+ var receivedEto = _jsonSerializer.Deserialize>(
+ _jsonSerializer.Serialize(
+ new RealTimeEto(notificationTemplate)));
+
+ var contributor = new ToObjectNotificationTemplateResolveContributor();
+ var context = new NotificationTemplateResolveContext(receivedEto.Data, ServiceProvider);
+
+ await contributor.ResolveAsync(context);
+
+ var model = context.Model.ShouldBeOfType();
+ model.Name.ShouldBe("Tom");
+ model.Jobs.Count.ShouldBe(2);
+ model.Jobs[0].Name.ShouldBe("Catch Jerry");
+ model.Jobs[1].Name.ShouldBe("Hit Pike");
+ }
+
+ [Fact]
+ public async Task Resolve_Deserialize_To_Dynamic()
+ {
+ var notificationTemplate = new NotificationTemplate(
+ "Test",
+ data: new Dictionary
+ {
+ { "Name", "Tom" },
+ { "Firend", "Jerry" }
+ });
+
+ var receivedEto = _jsonSerializer.Deserialize>(
+ _jsonSerializer.Serialize(
+ new RealTimeEto(notificationTemplate)));
+
+ var contributor = new NewtownsoftJsonTemplateResolveContributor();
+ var context = new NotificationTemplateResolveContext(receivedEto.Data, ServiceProvider);
+
+ await contributor.ResolveAsync(context);
+
+ dynamic model = context.Model;
+
+ Assert.Equal(model.Name, "Tom");
+ Assert.Equal(model.Firend, "Jerry");
+ }
+}
diff --git a/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/ToObjectNotificationTemplateResolveContributor.cs b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/ToObjectNotificationTemplateResolveContributor.cs
new file mode 100644
index 000000000..b3a410f84
--- /dev/null
+++ b/aspnet-core/tests/LINGYUN.Abp.Notifications.Templating.Tests/LINGYUN/Abp/Notifications/Templating/ToObjectNotificationTemplateResolveContributor.cs
@@ -0,0 +1,29 @@
+using Microsoft.Extensions.DependencyInjection;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Data;
+using Volo.Abp.Json;
+
+namespace LINGYUN.Abp.Notifications.Templating;
+internal class ToObjectNotificationTemplateResolveContributor : INotificationTemplateResolveContributor
+{
+ public string Name => "ToObject";
+
+ public Task ResolveAsync(INotificationTemplateResolveContext context)
+ {
+ var model = new NotificationModel();
+
+ var nameObj = context.Template.GetProperty(nameof(NotificationModel.Name));
+ model.Name = nameObj.ToString();
+
+ var jobsObj = context.Template.GetProperty(nameof(NotificationModel.Jobs));
+
+ var jsonSerializer = context.ServiceProvider.GetRequiredService();
+
+ model.Jobs = jsonSerializer.Deserialize>(jobsObj.ToString());
+
+ context.Model = model;
+
+ return Task.CompletedTask;
+ }
+}