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; } }