
- 新增通知系统完整架构,包含通知设置、历史记录、任务管理等核心功能 - 实现工作任务分配服务,支持人员和设备的智能分配 - 添加人员分组管理功能,支持灵活的通知目标配置 - 完善相关枚举定义和数据传输对象 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
409 lines
12 KiB
C#
409 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.Extensions.Configuration;
|
||
using Microsoft.Extensions.Logging;
|
||
using NPP.SmartSchedue.Api.Contracts.Services.Notification;
|
||
|
||
namespace NPP.SmartSchedue.Api.Services.Notification;
|
||
|
||
/// <summary>
|
||
/// 邮件通知服务实现
|
||
/// 决策点1:基础通知方式 - 邮件通知
|
||
/// </summary>
|
||
public class EmailNotificationService : IEmailNotificationService
|
||
{
|
||
private readonly ILogger<EmailNotificationService> _logger;
|
||
private readonly IConfiguration _configuration;
|
||
private readonly INotificationTemplateService _templateService;
|
||
|
||
// 邮件配置节点名称
|
||
private const string EmailConfigSection = "EmailNotification";
|
||
|
||
public EmailNotificationService(
|
||
ILogger<EmailNotificationService> 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 单个邮件发送
|
||
|
||
/// <summary>
|
||
/// 发送邮件通知
|
||
/// </summary>
|
||
public async Task<bool> 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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发送邮件通知(带附件)
|
||
/// </summary>
|
||
public async Task<bool> SendEmailWithAttachmentsAsync(
|
||
string recipientEmail,
|
||
string subject,
|
||
string content,
|
||
List<string> 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 批量邮件发送
|
||
|
||
/// <summary>
|
||
/// 批量发送邮件通知
|
||
/// </summary>
|
||
public async Task<Dictionary<string, bool>> BatchSendEmailAsync(
|
||
List<string> recipients,
|
||
string subject,
|
||
string content,
|
||
bool isHtml = true)
|
||
{
|
||
var results = new Dictionary<string, bool>();
|
||
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 个性化批量发送邮件通知
|
||
/// </summary>
|
||
public async Task<Dictionary<string, bool>> BatchSendPersonalizedEmailAsync(List<EmailItem> emailItems)
|
||
{
|
||
var results = new Dictionary<string, bool>();
|
||
|
||
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 邮件模板
|
||
|
||
/// <summary>
|
||
/// 使用模板发送邮件
|
||
/// </summary>
|
||
public async Task<bool> SendEmailByTemplateAsync(
|
||
string recipientEmail,
|
||
string subjectTemplate,
|
||
string contentTemplate,
|
||
Dictionary<string, string> 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 邮件发送状态检查
|
||
|
||
/// <summary>
|
||
/// 验证邮箱地址格式
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查邮件服务器连接状态
|
||
/// </summary>
|
||
public async Task<bool> 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 私有辅助方法
|
||
|
||
/// <summary>
|
||
/// 获取邮件配置
|
||
/// </summary>
|
||
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;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建SMTP客户端
|
||
/// </summary>
|
||
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;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 创建邮件消息
|
||
/// </summary>
|
||
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
|
||
|
||
/// <summary>
|
||
/// 邮件配置类
|
||
/// </summary>
|
||
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;
|
||
}
|
||
} |