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; /// /// 通知服务 邮件 /// [DynamicApi(Area = "app")] public class EmailNotificationService : BaseService, IEmailNotificationService, IDynamicApi { private readonly ILogger _logger; private readonly EmailConfiguration _emailConfig; private readonly INotificationTemplateService _templateService; public EmailNotificationService( ILogger logger, IOptions emailConfig, INotificationTemplateService templateService) { _logger = logger; _emailConfig = emailConfig.Value; _templateService = templateService; } /// /// 发送单个邮件 /// [HttpPost] public async Task 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; } /// /// 批量发送邮件 /// [HttpPost] public async Task 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; } /// /// 个性化批量发送邮件 /// [HttpPost] public async Task 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; } /// /// 使用模板发送邮件 /// [HttpPost] public async Task SendEmailByTemplateAsync(SendEmailByTemplateInput input) { var output = new SendEmailOutput { RecipientEmail = input.RecipientEmail }; try { // 渲染模板 var subjectInput = new RenderTemplateInput { Template = input.SubjectTemplate, Variables = input.Variables ?? new Dictionary() }; var subjectResult = await _templateService.RenderTemplateAsync(subjectInput); var contentInput = new RenderTemplateInput { Template = input.ContentTemplate, Variables = input.Variables ?? new Dictionary() }; 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; } } /// /// 验证邮箱地址格式 /// public async Task IsValidEmailAsync(string email) { return await Task.FromResult(IsValidEmail(email)); } /// /// 检查邮件服务器连接状态 /// public async Task 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 私有辅助方法 /// /// 验证邮箱地址格式 /// 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; } } /// /// 创建SMTP客户端 /// 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 }; } /// /// 创建邮件消息 /// 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; } /// /// 添加附件 /// private void AddAttachments(MailMessage mailMessage, List attachments) { foreach (var attachmentPath in attachments) { if (File.Exists(attachmentPath)) { var attachment = new Attachment(attachmentPath); mailMessage.Attachments.Add(attachment); } else { _logger.LogWarning("附件文件不存在:{Path}", attachmentPath); } } } /// /// 创建失败的批量发送输出 /// private BatchSendEmailOutput CreateFailedBatchOutput(IEnumerable 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 }