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.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using NPP.SmartSchedue.Api.Contracts.Services.Notification;
namespace NPP.SmartSchedue.Api.Services.Notification;
///
/// 邮件通知服务实现
/// 决策点1:基础通知方式 - 邮件通知
///
public class EmailNotificationService : IEmailNotificationService
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly INotificationTemplateService _templateService;
// 邮件配置节点名称
private const string EmailConfigSection = "EmailNotification";
public EmailNotificationService(
ILogger logger,
IConfiguration configuration,
INotificationTemplateService templateService)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
_templateService = templateService ?? throw new ArgumentNullException(nameof(templateService));
}
#region 单个邮件发送
///
/// 发送邮件通知
///
public async Task SendEmailAsync(string recipientEmail, string subject, string content, bool isHtml = true)
{
try
{
if (!IsValidEmail(recipientEmail))
{
_logger.LogWarning("无效的邮箱地址:{Email}", recipientEmail);
return false;
}
var emailConfig = GetEmailConfiguration();
if (emailConfig == null)
{
_logger.LogError("邮件配置未找到或配置无效");
return false;
}
using var smtpClient = CreateSmtpClient(emailConfig);
using var mailMessage = CreateMailMessage(emailConfig, recipientEmail, subject, content, isHtml);
await smtpClient.SendMailAsync(mailMessage);
_logger.LogInformation("邮件发送成功:{Email} - {Subject}", recipientEmail, subject);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "发送邮件失败:{Email} - {Subject}", recipientEmail, subject);
return false;
}
}
///
/// 发送邮件通知(带附件)
///
public async Task SendEmailWithAttachmentsAsync(
string recipientEmail,
string subject,
string content,
List attachments,
bool isHtml = true)
{
try
{
if (!IsValidEmail(recipientEmail))
{
_logger.LogWarning("无效的邮箱地址:{Email}", recipientEmail);
return false;
}
var emailConfig = GetEmailConfiguration();
if (emailConfig == null)
{
_logger.LogError("邮件配置未找到或配置无效");
return false;
}
using var smtpClient = CreateSmtpClient(emailConfig);
using var mailMessage = CreateMailMessage(emailConfig, recipientEmail, subject, content, isHtml);
// 添加附件
if (attachments != null && attachments.Any())
{
foreach (var attachmentPath in attachments)
{
if (File.Exists(attachmentPath))
{
var attachment = new Attachment(attachmentPath);
mailMessage.Attachments.Add(attachment);
}
else
{
_logger.LogWarning("附件文件不存在:{Path}", attachmentPath);
}
}
}
await smtpClient.SendMailAsync(mailMessage);
_logger.LogInformation("带附件邮件发送成功:{Email} - {Subject}, 附件数量:{Count}",
recipientEmail, subject, attachments?.Count ?? 0);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "发送带附件邮件失败:{Email} - {Subject}", recipientEmail, subject);
return false;
}
}
#endregion
#region 批量邮件发送
///
/// 批量发送邮件通知
///
public async Task> BatchSendEmailAsync(
List recipients,
string subject,
string content,
bool isHtml = true)
{
var results = new Dictionary();
if (recipients == null || !recipients.Any())
{
_logger.LogWarning("批量发送邮件:收件人列表为空");
return results;
}
var emailConfig = GetEmailConfiguration();
if (emailConfig == null)
{
_logger.LogError("邮件配置未找到或配置无效");
foreach (var recipient in recipients)
{
results[recipient] = false;
}
return results;
}
// 并行发送邮件
var tasks = recipients.Select(async recipient =>
{
var success = await SendEmailAsync(recipient, subject, content, isHtml);
return new { Recipient = recipient, Success = success };
});
var taskResults = await Task.WhenAll(tasks);
foreach (var result in taskResults)
{
results[result.Recipient] = result.Success;
}
var successCount = results.Values.Count(r => r);
_logger.LogInformation("批量发送邮件完成:总数 {Total},成功 {Success},失败 {Failed}",
recipients.Count, successCount, recipients.Count - successCount);
return results;
}
///
/// 个性化批量发送邮件通知
///
public async Task> BatchSendPersonalizedEmailAsync(List emailItems)
{
var results = new Dictionary();
if (emailItems == null || !emailItems.Any())
{
_logger.LogWarning("个性化批量发送邮件:邮件项列表为空");
return results;
}
var emailConfig = GetEmailConfiguration();
if (emailConfig == null)
{
_logger.LogError("邮件配置未找到或配置无效");
foreach (var item in emailItems)
{
results[item.RecipientEmail] = false;
}
return results;
}
// 并行发送邮件
var tasks = emailItems.Select(async item =>
{
var success = await SendEmailWithAttachmentsAsync(
item.RecipientEmail,
item.Subject,
item.Content,
item.Attachments,
item.IsHtml);
return new { Recipient = item.RecipientEmail, Success = success };
});
var taskResults = await Task.WhenAll(tasks);
foreach (var result in taskResults)
{
results[result.Recipient] = result.Success;
}
var successCount = results.Values.Count(r => r);
_logger.LogInformation("个性化批量发送邮件完成:总数 {Total},成功 {Success},失败 {Failed}",
emailItems.Count, successCount, emailItems.Count - successCount);
return results;
}
#endregion
#region 邮件模板
///
/// 使用模板发送邮件
///
public async Task SendEmailByTemplateAsync(
string recipientEmail,
string subjectTemplate,
string contentTemplate,
Dictionary variables,
bool isHtml = true)
{
try
{
// 渲染模板
var subject = await _templateService.RenderTemplateAsync(subjectTemplate, variables);
var content = await _templateService.RenderTemplateAsync(contentTemplate, variables);
// 发送邮件
return await SendEmailAsync(recipientEmail, subject, content, isHtml);
}
catch (Exception ex)
{
_logger.LogError(ex, "使用模板发送邮件失败:{Email}", recipientEmail);
return false;
}
}
#endregion
#region 邮件发送状态检查
///
/// 验证邮箱地址格式
///
public 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;
}
}
///
/// 检查邮件服务器连接状态
///
public async Task CheckEmailServerConnectionAsync()
{
try
{
var emailConfig = GetEmailConfiguration();
if (emailConfig == null)
{
_logger.LogError("邮件配置未找到或配置无效");
return false;
}
using var smtpClient = CreateSmtpClient(emailConfig);
// 尝试连接到SMTP服务器
await Task.Run(() =>
{
smtpClient.Connect(emailConfig.SmtpServer, emailConfig.SmtpPort);
smtpClient.Disconnect(true);
});
_logger.LogInformation("邮件服务器连接测试成功:{Server}:{Port}",
emailConfig.SmtpServer, emailConfig.SmtpPort);
return true;
}
catch (Exception ex)
{
_logger.LogError(ex, "邮件服务器连接测试失败");
return false;
}
}
#endregion
#region 私有辅助方法
///
/// 获取邮件配置
///
private EmailConfiguration GetEmailConfiguration()
{
try
{
var config = new EmailConfiguration();
_configuration.GetSection(EmailConfigSection).Bind(config);
// 验证配置
if (string.IsNullOrWhiteSpace(config.SmtpServer) ||
config.SmtpPort <= 0 ||
string.IsNullOrWhiteSpace(config.SenderEmail) ||
string.IsNullOrWhiteSpace(config.SenderPassword))
{
_logger.LogError("邮件配置信息不完整");
return null;
}
return config;
}
catch (Exception ex)
{
_logger.LogError(ex, "获取邮件配置失败");
return null;
}
}
///
/// 创建SMTP客户端
///
private SmtpClient CreateSmtpClient(EmailConfiguration config)
{
var smtpClient = new SmtpClient(config.SmtpServer, config.SmtpPort)
{
Credentials = new NetworkCredential(config.SenderEmail, config.SenderPassword),
EnableSsl = config.EnableSsl,
DeliveryMethod = SmtpDeliveryMethod.Network,
Timeout = config.TimeoutSeconds * 1000
};
return smtpClient;
}
///
/// 创建邮件消息
///
private MailMessage CreateMailMessage(EmailConfiguration config, string recipientEmail, string subject, string content, bool isHtml)
{
var mailMessage = new MailMessage
{
From = new MailAddress(config.SenderEmail, config.SenderName),
Subject = subject,
Body = content,
IsBodyHtml = isHtml
};
mailMessage.To.Add(recipientEmail);
return mailMessage;
}
#endregion
///
/// 邮件配置类
///
private class EmailConfiguration
{
public string SmtpServer { get; set; } = "";
public int SmtpPort { get; set; } = 587;
public string SenderEmail { get; set; } = "";
public string SenderPassword { get; set; } = "";
public string SenderName { get; set; } = "NPP智能生产调度系统";
public bool EnableSsl { get; set; } = true;
public int TimeoutSeconds { get; set; } = 30;
}
}