29 changed files with 442 additions and 54 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<ConfigureAwait ContinueOnCapturedContext="false" /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,30 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
||||
|
<xs:complexType> |
||||
|
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,20 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<Import Project="..\..\..\configureawait.props" /> |
||||
|
<Import Project="..\..\..\common.props" /> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Emailing" Version="$(VoloAbpPackageVersion)" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\identity\LINGYUN.Abp.Identity.Domain\LINGYUN.Abp.Identity.Domain.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Notifications\LINGYUN.Abp.Notifications.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,22 @@ |
|||||
|
using LINGYUN.Abp.Identity; |
||||
|
using Volo.Abp.Emailing; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Emailing; |
||||
|
|
||||
|
[DependsOn( |
||||
|
typeof(AbpNotificationModule), |
||||
|
typeof(AbpEmailingModule), |
||||
|
typeof(AbpIdentityDomainModule))] |
||||
|
public class AbpNotificationsEmailingModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpNotificationOptions>(options => |
||||
|
{ |
||||
|
options.PublishProviders.Add<EmailingNotificationPublishProvider>(); |
||||
|
options.NotificationDataMappings |
||||
|
.MappingDefault(EmailingNotificationPublishProvider.ProviderName, data => data); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,77 @@ |
|||||
|
using LINGYUN.Abp.Identity; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Emailing; |
||||
|
using Volo.Abp.TextTemplating; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Notifications.Emailing; |
||||
|
|
||||
|
public class EmailingNotificationPublishProvider : NotificationPublishProvider |
||||
|
{ |
||||
|
public const string ProviderName = NotificationProviderNames.Emailing; |
||||
|
|
||||
|
public override string Name => ProviderName; |
||||
|
|
||||
|
protected IEmailSender EmailSender { get; } |
||||
|
|
||||
|
protected ITemplateRenderer TemplateRenderer { get; } |
||||
|
|
||||
|
protected IStringLocalizerFactory LocalizerFactory { get; } |
||||
|
|
||||
|
protected IIdentityUserRepository UserRepository { get; } |
||||
|
|
||||
|
protected INotificationDefinitionManager NotificationDefinitionManager { get; } |
||||
|
|
||||
|
public EmailingNotificationPublishProvider( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IEmailSender emailSender, |
||||
|
ITemplateRenderer templateRenderer, |
||||
|
IStringLocalizerFactory localizerFactory, |
||||
|
IIdentityUserRepository userRepository, |
||||
|
INotificationDefinitionManager notificationDefinitionManager) |
||||
|
: base(serviceProvider) |
||||
|
{ |
||||
|
EmailSender = emailSender; |
||||
|
TemplateRenderer = templateRenderer; |
||||
|
LocalizerFactory = localizerFactory; |
||||
|
UserRepository = userRepository; |
||||
|
NotificationDefinitionManager = notificationDefinitionManager; |
||||
|
} |
||||
|
|
||||
|
protected async override Task PublishAsync( |
||||
|
NotificationInfo notification, |
||||
|
IEnumerable<UserIdentifier> identifiers, |
||||
|
CancellationToken cancellationToken = default) |
||||
|
{ |
||||
|
var userIds = identifiers.Select(x => x.UserId).ToList(); |
||||
|
var userList = await UserRepository.GetListByIdListAsync(userIds, cancellationToken: cancellationToken); |
||||
|
var emailAddress = userList.Where(x => x.EmailConfirmed).Select(x => x.Email).Distinct().JoinAsString(","); |
||||
|
|
||||
|
if (emailAddress.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
Logger.LogWarning("The subscriber did not confirm the email address and could not send email notifications!"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var notificationDefinition = NotificationDefinitionManager.Get(notification.Name); |
||||
|
var notificationDisplayName = notificationDefinition.DisplayName.Localize(LocalizerFactory).Value; |
||||
|
|
||||
|
notificationDefinition.Properties.TryGetValue("Template", out var template); |
||||
|
if (template == null) |
||||
|
{ |
||||
|
Logger.LogWarning("The email template is not specified, so the email notification cannot be sent!"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var content = await TemplateRenderer.RenderAsync( |
||||
|
template.ToString(), |
||||
|
globalContext: notification.Data.ExtraProperties); |
||||
|
|
||||
|
await EmailSender.SendAsync(emailAddress, notificationDisplayName, content); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
namespace LINGYUN.Abp.Notifications; |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 内置通知提供者
|
||||
|
/// </summary>
|
||||
|
public static class NotificationProviderNames |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// SignalR 实时通知
|
||||
|
/// </summary>
|
||||
|
public const string SignalR = "SignalR"; |
||||
|
/// <summary>
|
||||
|
/// 短信通知
|
||||
|
/// </summary>
|
||||
|
public const string Sms = "Sms"; |
||||
|
/// <summary>
|
||||
|
/// 邮件通知
|
||||
|
/// </summary>
|
||||
|
public const string Emailing = "Emailing"; |
||||
|
/// <summary>
|
||||
|
/// 微信小程序模板通知
|
||||
|
/// </summary>
|
||||
|
public const string WechatMiniProgram = "WeChat.MiniProgram"; |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
using Volo.Abp.TextTemplating; |
||||
|
using Volo.Abp.TextTemplating.Scriban; |
||||
|
|
||||
|
namespace LY.MicroService.RealtimeMessage.Emailing; |
||||
|
|
||||
|
public class RealtimeMessageTemplateProvider : TemplateDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(ITemplateDefinitionContext context) |
||||
|
{ |
||||
|
context.Add(CreateEmailTemplate()); |
||||
|
} |
||||
|
|
||||
|
protected virtual TemplateDefinition[] CreateEmailTemplate() |
||||
|
{ |
||||
|
return new TemplateDefinition[] |
||||
|
{ |
||||
|
new TemplateDefinition( |
||||
|
name: "EmailNotifierLayout", |
||||
|
defaultCultureName: "en", |
||||
|
isLayout: true) |
||||
|
.WithScribanEngine() |
||||
|
.WithVirtualFilePath( |
||||
|
"/Emailing/Templates/layout.tpl", |
||||
|
isInlineLocalized: false), |
||||
|
new TemplateDefinition( |
||||
|
name: "ExceptionNotifier", |
||||
|
defaultCultureName: "en", |
||||
|
layout: "EmailNotifierLayout") |
||||
|
.WithScribanEngine() |
||||
|
.WithVirtualFilePath( |
||||
|
"/Emailing/Templates/ExceptionNotifier", |
||||
|
isInlineLocalized: false), |
||||
|
new TemplateDefinition( |
||||
|
"NewTenantRegisterd", |
||||
|
defaultCultureName: "en", |
||||
|
layout: "EmailNotifierLayout") |
||||
|
.WithScribanEngine() |
||||
|
.WithVirtualFilePath( |
||||
|
"/Emailing/Templates/NewTenantRegisterd", |
||||
|
isInlineLocalized: false), |
||||
|
new TemplateDefinition( |
||||
|
"WelcomeToApplication", |
||||
|
defaultCultureName: "en", |
||||
|
layout: "EmailNotifierLayout") |
||||
|
.WithScribanEngine() |
||||
|
.WithVirtualFilePath( |
||||
|
"/Emailing/Templates/WelcomeToApplication", |
||||
|
isInlineLocalized: false), |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<br /> <b><font color="#0B610B">{{ header }}</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<ul> |
||||
|
<li>Type : {{ type }}</li> |
||||
|
<li>Message : {{ message }}</li> |
||||
|
<li>Alarm level : {{ loglevel }}</li> |
||||
|
<li>TriggerTime : {{ createTime }}</li> |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b><font color="#0B610B">Stack trace</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">{{ stacktrace }}</pre> |
||||
|
<br /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<br /> |
||||
|
<b style="float: right"><font color="#0B610B">{{ footer }}</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
@ -0,0 +1,37 @@ |
|||||
|
<table width="95%" cellpadding="0" cellspacing="0" style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif"> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<br /> <b><font color="#0B610B">{{ header }}</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<ul> |
||||
|
<li>异常类型 : {{ title }}</li> |
||||
|
<li>异常信息 : {{ message }}</li> |
||||
|
<li>告警级别 : {{ loglevel }}</li> |
||||
|
<li>触发时间 : {{ createTime }}</li> |
||||
|
</ul> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<b><font color="#0B610B">异常堆栈</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<pre style="font-size: 11pt; font-family: Tahoma, Arial, Helvetica, sans-serif">{{ stacktrace }}</pre> |
||||
|
<br /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td> |
||||
|
<br /> |
||||
|
<b style="float: right"><font color="#0B610B">{{ footer }}</font></b> |
||||
|
<hr size="2" width="100%" align="center" /> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</table> |
||||
@ -0,0 +1 @@ |
|||||
|
<h3>A new tenant {{name}} has been created.</h3> |
||||
@ -0,0 +1 @@ |
|||||
|
<h3>一个新的租户 {{name}} 已建立,切换租户开始! </h3> |
||||
@ -0,0 +1 @@ |
|||||
|
<h3>{{user}}, Welcome to my application!</h3> |
||||
@ -0,0 +1 @@ |
|||||
|
<h3>{{user}}, Welcome to my application!</h3> |
||||
@ -0,0 +1,10 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="UTF-8"> |
||||
|
<title>{{title}}</title> |
||||
|
</head> |
||||
|
<body leftmargin="8" marginwidth="0" topmargin="8" marginheight="4" offset="0"> |
||||
|
{{content}} |
||||
|
</body> |
||||
|
</html> |
||||
Loading…
Reference in new issue