diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs
index ae84a648b..680fa047b 100644
--- a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.WeChat.Work/LINGYUN/Abp/Notifications/WeChat/Work/WeChatWorkNotificationPublishProvider.cs
@@ -55,40 +55,13 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
}
var notificationData = await NotificationDataSerializer.ToStandard(notification.Data);
var toTag = notification.Data.GetTagOrNull() ?? notificationDefine?.GetTagOrNull();
- if (!toTag.IsNullOrWhiteSpace())
- {
- // 指定发送标签
- await PublishToAgentAsync(
- agentId,
- notification,
- notificationData.Title,
- notificationData.Message,
- notificationData.Description,
- toTag: toTag,
- cancellationToken: cancellationToken);
- return;
- }
var toParty = notification.Data.GetPartyOrNull() ?? notificationDefine?.GetPartyOrNull();
- if (!toParty.IsNullOrWhiteSpace())
- {
- // 指定发送部门
- await PublishToAgentAsync(
- agentId,
- notification,
- notificationData.Title,
- notificationData.Message,
- notificationData.Description,
- toParty: toParty,
- cancellationToken: cancellationToken);
- return;
- }
+ var toUsers = await WeChatWorkInternalUserFinder.FindUserIdentifierListAsync(identifiers.Select(id => id.UserId));
- var findUserList = await WeChatWorkInternalUserFinder
- .FindUserIdentifierListAsync(identifiers.Select(id => id.UserId));
-
- if (findUserList.Count == 0)
+ if (toUsers.IsNullOrEmpty() && toTag.IsNullOrWhiteSpace() && toParty.IsNullOrWhiteSpace())
{
- Logger.LogWarning("Unable to send work weixin messages because findUserList is empty.");
+ // touser、toparty、totag不能同时为空:https://developer.work.weixin.qq.com/document/path/90236
+ Logger.LogWarning("Unable to send work weixin messages because The recipient/department/label cannot be empty simultaneously.");
return;
}
@@ -99,7 +72,9 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
notificationData.Title,
notificationData.Message,
notificationData.Description,
- toUser: findUserList.JoinAsString("|"),
+ toTag: toTag,
+ toParty: toParty,
+ toUser: toUsers.JoinAsString("|"),
cancellationToken: cancellationToken);
}
@@ -137,13 +112,6 @@ public class WeChatWorkNotificationPublishProvider : NotificationPublishProvider
return;
}
- if (toUser.IsNullOrWhiteSpace() && toTag.IsNullOrWhiteSpace() && toParty.IsNullOrWhiteSpace())
- {
- // touser、toparty、totag不能同时为空:https://developer.work.weixin.qq.com/document/path/90236
- Logger.LogWarning("Unable to send work weixin messages because The recipient/department/label cannot be empty simultaneously.");
- return;
- }
-
message.ToUser = toUser;
message.ToTag = toTag;
message.ToParty = toParty;
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/FodyWeavers.xml b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/FodyWeavers.xsd b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/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.Webhook.WeChat.Work/LINGYUN.Abp.Notifications.Webhook.WeChat.Work.csproj b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN.Abp.Notifications.Webhook.WeChat.Work.csproj
new file mode 100644
index 000000000..c3ab836ae
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN.Abp.Notifications.Webhook.WeChat.Work.csproj
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0
+ LINGYUN.Abp.Notifications.Webhook.WeChat.Work
+ LINGYUN.Abp.Notifications.Webhook.WeChat.Work
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkModule.cs
new file mode 100644
index 000000000..0879e9be4
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkModule.cs
@@ -0,0 +1,18 @@
+using LINGYUN.Abp.WeChat.Work;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work;
+
+[DependsOn(
+ typeof(AbpNotificationsWebhookModule),
+ typeof(AbpWeChatWorkModule))]
+public class AbpNotificationsWebhookWeChatWorkModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.Contributors.Add(new WeChatWorkWebhookNotificationContributor());
+ });
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkOptions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkOptions.cs
new file mode 100644
index 000000000..d8e9cf700
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/AbpNotificationsWebhookWeChatWorkOptions.cs
@@ -0,0 +1,15 @@
+namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work;
+public class AbpNotificationsWebhookWeChatWorkOptions
+{
+ ///
+ /// 发送Markdown类型通知时是否使用MarkdownV2格式通知, 默认: true
+ ///
+ ///
+ /// 详见: https://developer.work.weixin.qq.com/document/path/99110#markdown-v2%E7%B1%BB%E5%9E%8B
+ ///
+ public bool UseMarkdownV2 { get; set; }
+ public AbpNotificationsWebhookWeChatWorkOptions()
+ {
+ UseMarkdownV2 = true;
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/WeChatWorkWebhookNotificationContributor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/WeChatWorkWebhookNotificationContributor.cs
new file mode 100644
index 000000000..a3d702b23
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook.WeChat.Work/LINGYUN/Abp/Notifications/Webhook/WeChat/Work/WeChatWorkWebhookNotificationContributor.cs
@@ -0,0 +1,74 @@
+using LINGYUN.Abp.WeChat.Work.Messages;
+using LINGYUN.Abp.WeChat.Work.Messages.Models;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using System;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Webhook.WeChat.Work;
+public class WeChatWorkWebhookNotificationContributor : IWebhookNotificationContributor
+{
+ public string Name => "WeChat.Work";
+
+ public async virtual Task ContributeAsync(IWebhookNotificationContext context)
+ {
+ var options = context.ServiceProvider.GetRequiredService>().Value;
+ var notificationDataSerializer = context.ServiceProvider.GetRequiredService();
+
+ var data = await notificationDataSerializer.ToStandard(context.Notification.Data);
+ var notificationContent = data.Message;
+
+ try
+ {
+ if (context.Notification.ContentType == NotificationContentType.Html)
+ {
+ notificationContent = CommonMark.CommonMarkConverter.Convert(notificationContent);
+ }
+
+ WeChatWorkWebhookMessage message;
+
+ switch (context.Notification.ContentType)
+ {
+ case NotificationContentType.Text:
+ message = new WeChatWorkWebhookTextMessage(
+ new WebhookTextMessage(notificationContent));
+ break;
+ case NotificationContentType.Html:
+ case NotificationContentType.Markdown:
+ if (options.UseMarkdownV2)
+ {
+ message = new WeChatWorkWebhookMarkdownV2Message(
+ new WebhookMarkdownV2Message(notificationContent));
+ }
+ else
+ {
+ message = new WeChatWorkWebhookMarkdownMessage(
+ new WebhookMarkdownMessage(notificationContent));
+ }
+ break;
+ default:
+ return;
+ }
+
+ if (message == null)
+ {
+ context.ServiceProvider
+ .GetService>()
+ ?.LogWarning("Unable to send work weixin messages because WeChatWorkMessage is null.");
+ return;
+ }
+
+ context.Webhook = new WebhookNotificationData(
+ context.Notification.Name,
+ message);
+ context.Handled = true;
+ }
+ catch (Exception ex)
+ {
+ context.ServiceProvider
+ .GetService>()
+ ?.LogWarning("Failed to parse the content of the Webhook message: {message}", ex.Message);
+ }
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/FodyWeavers.xml b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/FodyWeavers.xml
new file mode 100644
index 000000000..1715698cc
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/FodyWeavers.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/FodyWeavers.xsd b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/FodyWeavers.xsd
new file mode 100644
index 000000000..3f3946e28
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/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.Webhook/LINGYUN.Abp.Notifications.Webhook.csproj b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN.Abp.Notifications.Webhook.csproj
new file mode 100644
index 000000000..2ca416a4c
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN.Abp.Notifications.Webhook.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+ netstandard2.0;netstandard2.1;net8.0;net9.0
+ LINGYUN.Abp.Notifications.Webhook
+ LINGYUN.Abp.Notifications.Webhook
+ false
+ false
+ false
+
+
+
+
+
+
+
+
+
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookModule.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookModule.cs
new file mode 100644
index 000000000..c11621169
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookModule.cs
@@ -0,0 +1,18 @@
+using LINGYUN.Abp.Webhooks;
+using Volo.Abp.Modularity;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+
+[DependsOn(
+ typeof(AbpNotificationsCoreModule),
+ typeof(AbpWebhooksModule))]
+public class AbpNotificationsWebhookModule : AbpModule
+{
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ Configure(options =>
+ {
+ options.PublishProviders.Add();
+ });
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookOptions.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookOptions.cs
new file mode 100644
index 000000000..5561b9f46
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/AbpNotificationsWebhookOptions.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public class AbpNotificationsWebhookOptions
+{
+ public IList Contributors { get; }
+ public AbpNotificationsWebhookOptions()
+ {
+ Contributors = new List();
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContext.cs
new file mode 100644
index 000000000..f23a8b129
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContext.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.DependencyInjection;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public interface IWebhookNotificationContext : IServiceProviderAccessor
+{
+ WebhookNotificationData Webhook { get; set; }
+ NotificationInfo Notification { get; }
+ bool Handled { get; set; }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContributor.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContributor.cs
new file mode 100644
index 000000000..757ff88fd
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/IWebhookNotificationContributor.cs
@@ -0,0 +1,8 @@
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public interface IWebhookNotificationContributor
+{
+ string Name { get; }
+ Task ContributeAsync(IWebhookNotificationContext context);
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationContext.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationContext.cs
new file mode 100644
index 000000000..f8d427640
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationContext.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public class WebhookNotificationContext : IWebhookNotificationContext
+{
+ public IServiceProvider ServiceProvider { get; }
+ public NotificationInfo Notification { get; }
+ public WebhookNotificationData Webhook { get; set; }
+ public bool Handled { get; set; }
+ public WebhookNotificationContext(IServiceProvider ServiceProvider, NotificationInfo notification)
+ {
+ Notification = notification;
+ }
+ public bool HasResolved()
+ {
+ return Handled || Webhook != null;
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationData.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationData.cs
new file mode 100644
index 000000000..561d6324a
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationData.cs
@@ -0,0 +1,16 @@
+using LINGYUN.Abp.Webhooks;
+using Volo.Abp;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public class WebhookNotificationData
+{
+ public string WebhookName { get; }
+ public object Data { get; }
+ public bool SendExactSameData { get; set; }
+ public WebhookHeader Headers { get; set; }
+ public WebhookNotificationData(string webhookName, object data)
+ {
+ WebhookName = Check.NotNullOrWhiteSpace(webhookName, nameof(webhookName));
+ Data = Check.NotNull(data, nameof(data));
+ }
+}
diff --git a/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationPublishProvider.cs b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationPublishProvider.cs
new file mode 100644
index 000000000..a7441d328
--- /dev/null
+++ b/aspnet-core/modules/realtime-notifications/LINGYUN.Abp.Notifications.Webhook/LINGYUN/Abp/Notifications/Webhook/WebhookNotificationPublishProvider.cs
@@ -0,0 +1,49 @@
+using LINGYUN.Abp.Webhooks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace LINGYUN.Abp.Notifications.Webhook;
+public class WebhookNotificationPublishProvider : NotificationPublishProvider
+{
+ public const string ProviderName = "Webhook";
+ public override string Name => ProviderName;
+
+ private readonly IServiceScopeFactory _serviceScopeFactory;
+ private readonly AbpNotificationsWebhookOptions _options;
+ private readonly IWebhookPublisher _webhookPublisher;
+
+ public WebhookNotificationPublishProvider(
+ IServiceScopeFactory serviceScopeFactory,
+ IWebhookPublisher webhookPublisher,
+ IOptions options)
+ {
+ _serviceScopeFactory = serviceScopeFactory;
+ _webhookPublisher = webhookPublisher;
+ _options = options.Value;
+ }
+
+ protected override async Task PublishAsync(NotificationInfo notification, IEnumerable identifiers, CancellationToken cancellationToken = default)
+ {
+ using var scope = _serviceScopeFactory.CreateScope();
+
+ foreach (var contributor in _options.Contributors)
+ {
+ var context = new WebhookNotificationContext(scope.ServiceProvider, notification);
+
+ await contributor.ContributeAsync(context);
+
+ if (context.HasResolved())
+ {
+ await _webhookPublisher.PublishAsync(
+ context.Webhook.WebhookName,
+ context.Webhook.Data,
+ notification.TenantId,
+ context.Webhook.SendExactSameData,
+ context.Webhook.Headers);
+ }
+ }
+ }
+}