paiban/NPP.SmartSchedue.Api/Services/Notification/EmailNotificationService.cs
Asoka.Wang 2b3f9acdce 123
2025-09-22 19:09:47 +08:00

404 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Net.Mail;
using System.Net;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using ZhonTai.Admin.Services;
using ZhonTai.DynamicApi;
using ZhonTai.DynamicApi.Attributes;
using NPP.SmartSchedue.Api.Contracts.Services.Notification;
using NPP.SmartSchedue.Api.Contracts.Services.Notification.Input;
using NPP.SmartSchedue.Api.Contracts.Services.Notification.Output;
using NPP.SmartSchedue.Api.Contracts.Core.Configuration;
namespace NPP.SmartSchedue.Api.Services.Notification;
/// <summary>
/// 通知服务 邮件
/// </summary>
[DynamicApi(Area = "app")]
public class EmailNotificationService : BaseService, IEmailNotificationService, IDynamicApi
{
private readonly ILogger<EmailNotificationService> _logger;
private readonly EmailConfiguration _emailConfig;
private readonly INotificationTemplateService _templateService;
public EmailNotificationService(
ILogger<EmailNotificationService> logger,
IOptions<EmailConfiguration> emailConfig,
INotificationTemplateService templateService)
{
_logger = logger;
_emailConfig = emailConfig.Value;
_templateService = templateService;
}
/// <summary>
/// 发送单个邮件
/// </summary>
[HttpPost]
public async Task<SendEmailOutput> SendEmailAsync(SendEmailInput input)
{
var output = new SendEmailOutput
{
RecipientEmail = input.RecipientEmail,
Subject = input.Subject
};
try
{
if (!_emailConfig.Enabled)
{
output.ErrorMessage = "邮件服务已禁用";
return output;
}
if (!_emailConfig.IsValid())
{
output.ErrorMessage = "邮件配置无效";
return output;
}
using var smtpClient = CreateSmtpClient();
using var mailMessage = CreateMailMessage(input.RecipientEmail, input.Subject, input.Content, input.IsHtml);
// 添加附件
if (input.Attachments?.Any() == true)
{
AddAttachments(mailMessage, input.Attachments);
}
await smtpClient.SendMailAsync(mailMessage);
output.IsSuccess = true;
_logger.LogInformation("邮件发送成功:{Email} - {Subject}", input.RecipientEmail, input.Subject);
}
catch (Exception ex)
{
output.ErrorMessage = ex.Message;
_logger.LogError(ex, "发送邮件失败:{Email} - {Subject}", input.RecipientEmail, input.Subject);
}
return output;
}
/// <summary>
/// 批量发送邮件
/// </summary>
[HttpPost]
public async Task<BatchSendEmailOutput> BatchSendEmailAsync(BatchSendEmailInput input)
{
var output = new BatchSendEmailOutput
{
TotalCount = input.Recipients.Count
};
if (!_emailConfig.Enabled)
{
return CreateFailedBatchOutput(input.Recipients, "邮件服务已禁用");
}
if (!_emailConfig.IsValid())
{
return CreateFailedBatchOutput(input.Recipients, "邮件配置无效");
}
// 并行发送邮件
var tasks = input.Recipients.Select(async recipient =>
{
var emailInput = new SendEmailInput
{
RecipientEmail = recipient,
Subject = input.Subject,
Content = input.Content,
IsHtml = input.IsHtml
};
return await SendEmailAsync(emailInput);
});
var results = await Task.WhenAll(tasks);
output.Results.AddRange(results);
output.SuccessCount = results.Count(r => r.IsSuccess);
output.FailedCount = results.Count(r => !r.IsSuccess);
_logger.LogInformation("批量发送邮件完成:总数 {Total},成功 {Success},失败 {Failed}",
output.TotalCount, output.SuccessCount, output.FailedCount);
return output;
}
/// <summary>
/// 个性化批量发送邮件
/// </summary>
[HttpPost]
public async Task<BatchSendEmailOutput> BatchSendPersonalizedEmailAsync(BatchSendPersonalizedEmailInput input)
{
var output = new BatchSendEmailOutput
{
TotalCount = input.EmailItems.Count
};
if (!_emailConfig.Enabled)
{
return CreateFailedBatchOutput(input.EmailItems.Select(e => e.RecipientEmail), "邮件服务已禁用");
}
if (!_emailConfig.IsValid())
{
return CreateFailedBatchOutput(input.EmailItems.Select(e => e.RecipientEmail), "邮件配置无效");
}
// 并行发送邮件
var tasks = input.EmailItems.Select(async item =>
{
var emailInput = new SendEmailInput
{
RecipientEmail = item.RecipientEmail,
Subject = item.Subject,
Content = item.Content,
IsHtml = item.IsHtml,
Attachments = item.Attachments
};
return await SendEmailAsync(emailInput);
});
var results = await Task.WhenAll(tasks);
output.Results.AddRange(results);
output.SuccessCount = results.Count(r => r.IsSuccess);
output.FailedCount = results.Count(r => !r.IsSuccess);
_logger.LogInformation("个性化批量发送邮件完成:总数 {Total},成功 {Success},失败 {Failed}",
output.TotalCount, output.SuccessCount, output.FailedCount);
return output;
}
/// <summary>
/// 使用模板发送邮件
/// </summary>
[HttpPost]
public async Task<SendEmailOutput> SendEmailByTemplateAsync(SendEmailByTemplateInput input)
{
var output = new SendEmailOutput
{
RecipientEmail = input.RecipientEmail
};
try
{
// 渲染模板
var subjectInput = new RenderTemplateInput
{
Template = input.SubjectTemplate,
Variables = input.Variables ?? new Dictionary<string, string>()
};
var subjectResult = await _templateService.RenderTemplateAsync(subjectInput);
var contentInput = new RenderTemplateInput
{
Template = input.ContentTemplate,
Variables = input.Variables ?? new Dictionary<string, string>()
};
var contentResult = await _templateService.RenderTemplateAsync(contentInput);
// 检查渲染是否成功
if (!subjectResult.Success)
{
output.ErrorMessage = "主题模板渲染失败: " + subjectResult.ErrorMessage;
return output;
}
if (!contentResult.Success)
{
output.ErrorMessage = "内容模板渲染失败: " + contentResult.ErrorMessage;
return output;
}
// 创建邮件发送请求
var emailInput = new SendEmailInput
{
RecipientEmail = input.RecipientEmail,
Subject = subjectResult.RenderedContent,
Content = contentResult.RenderedContent,
IsHtml = input.IsHtml
};
return await SendEmailAsync(emailInput);
}
catch (Exception ex)
{
output.ErrorMessage = ex.Message;
_logger.LogError(ex, "使用模板发送邮件失败:{Email}", input.RecipientEmail);
return output;
}
}
/// <summary>
/// 验证邮箱地址格式
/// </summary>
public async Task<bool> IsValidEmailAsync(string email)
{
return await Task.FromResult(IsValidEmail(email));
}
/// <summary>
/// 检查邮件服务器连接状态
/// </summary>
public async Task<EmailServerStatusOutput> CheckEmailServerStatusAsync()
{
var output = new EmailServerStatusOutput
{
SmtpServer = _emailConfig.SmtpServer,
SmtpPort = _emailConfig.SmtpPort
};
var startTime = DateTime.Now;
try
{
if (!_emailConfig.IsValid())
{
output.ErrorMessage = "邮件配置无效";
return output;
}
using var smtpClient = CreateSmtpClient();
// 创建一个测试邮件来验证连接
var testMail = new MailMessage
{
From = new MailAddress(_emailConfig.SenderEmail, _emailConfig.SenderName),
Subject = "连接测试",
Body = "这是一个连接测试邮件,可以忽略。",
IsBodyHtml = false
};
testMail.To.Add(_emailConfig.SenderEmail); // 发送给自己进行测试
await smtpClient.SendMailAsync(testMail);
output.IsAvailable = true;
output.ResponseTimeMs = (int)(DateTime.Now - startTime).TotalMilliseconds;
_logger.LogInformation("邮件服务器连接测试成功:{Server}:{Port}, 响应时间:{ResponseTime}ms",
_emailConfig.SmtpServer, _emailConfig.SmtpPort, output.ResponseTimeMs);
}
catch (Exception ex)
{
output.ErrorMessage = ex.Message;
output.ResponseTimeMs = (int)(DateTime.Now - startTime).TotalMilliseconds;
_logger.LogError(ex, "邮件服务器连接测试失败");
}
return output;
}
#region
/// <summary>
/// 验证邮箱地址格式
/// </summary>
private bool IsValidEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return false;
try
{
var emailRegex = new Regex(
@"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
return emailRegex.IsMatch(email);
}
catch
{
return false;
}
}
/// <summary>
/// 创建SMTP客户端
/// </summary>
private SmtpClient CreateSmtpClient()
{
return new SmtpClient(_emailConfig.SmtpServer, _emailConfig.SmtpPort)
{
Credentials = new NetworkCredential(_emailConfig.SenderEmail, _emailConfig.SenderPassword),
EnableSsl = _emailConfig.EnableSsl,
DeliveryMethod = SmtpDeliveryMethod.Network,
Timeout = _emailConfig.TimeoutSeconds * 1000
};
}
/// <summary>
/// 创建邮件消息
/// </summary>
private MailMessage CreateMailMessage(string recipientEmail, string subject, string content, bool isHtml)
{
var mailMessage = new MailMessage
{
From = new MailAddress(_emailConfig.SenderEmail, _emailConfig.SenderName),
Subject = subject,
Body = content,
IsBodyHtml = isHtml
};
mailMessage.To.Add(recipientEmail);
return mailMessage;
}
/// <summary>
/// 添加附件
/// </summary>
private void AddAttachments(MailMessage mailMessage, List<string> attachments)
{
foreach (var attachmentPath in attachments)
{
if (File.Exists(attachmentPath))
{
var attachment = new Attachment(attachmentPath);
mailMessage.Attachments.Add(attachment);
}
else
{
_logger.LogWarning("附件文件不存在:{Path}", attachmentPath);
}
}
}
/// <summary>
/// 创建失败的批量发送输出
/// </summary>
private BatchSendEmailOutput CreateFailedBatchOutput(IEnumerable<string> recipients, string errorMessage)
{
var output = new BatchSendEmailOutput
{
TotalCount = recipients.Count(),
FailedCount = recipients.Count()
};
foreach (var recipient in recipients)
{
output.Results.Add(new SendEmailOutput
{
RecipientEmail = recipient,
IsSuccess = false,
ErrorMessage = errorMessage
});
}
return output;
}
#endregion
}