邮件临时修改
parent
3bc4b86749
commit
abb9f47646
|
|
@ -1353,6 +1353,48 @@
|
||||||
</summary>
|
</summary>
|
||||||
<returns></returns>
|
<returns></returns>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.ProcessInboxFailureNotifications">
|
||||||
|
<summary>
|
||||||
|
处理收件箱中的邮件发送失败通知
|
||||||
|
</summary>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.ExtractOriginalMessageId(MimeKit.MimeMessage)">
|
||||||
|
<summary>
|
||||||
|
从失败通知邮件中提取原始邮件的Message-Id
|
||||||
|
</summary>
|
||||||
|
<param name="message"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.ExtractErrorMessage(MimeKit.MimeMessage)">
|
||||||
|
<summary>
|
||||||
|
从失败通知邮件中提取错误信息
|
||||||
|
</summary>
|
||||||
|
<param name="message"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.GetContentSummary(System.String)">
|
||||||
|
<summary>
|
||||||
|
获取邮件内容摘要(前200个字符)
|
||||||
|
</summary>
|
||||||
|
<param name="content"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.ProcessFailureNotificationByRecipient(MimeKit.MimeMessage)">
|
||||||
|
<summary>
|
||||||
|
通过收件人信息处理失败通知邮件
|
||||||
|
</summary>
|
||||||
|
<param name="message"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.IsLikelyRelatedEmail(MimeKit.MimeMessage,IRaCIS.Core.Domain.Models.EmailLog)">
|
||||||
|
<summary>
|
||||||
|
判断失败通知邮件是否与指定的EmailLog相关
|
||||||
|
</summary>
|
||||||
|
<param name="failureMessage"></param>
|
||||||
|
<param name="emailLog"></param>
|
||||||
|
<returns></returns>
|
||||||
|
</member>
|
||||||
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.BuildSearchQuery(IRaCIS.Core.Application.ViewModel.EmailLogQuery)">
|
<member name="M:IRaCIS.Core.Application.Service.EmailLogService.BuildSearchQuery(IRaCIS.Core.Application.ViewModel.EmailLogQuery)">
|
||||||
<summary>
|
<summary>
|
||||||
邮件筛选条件构建
|
邮件筛选条件构建
|
||||||
|
|
@ -9187,6 +9229,11 @@
|
||||||
任务Id
|
任务Id
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="P:IRaCIS.Core.Application.Service.Reading.Dto.GetReportsChartDataOutDto.Unit">
|
||||||
|
<summary>
|
||||||
|
单位
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
<member name="T:IRaCIS.Core.Application.Service.Reading.Dto.SaveTableQuestionMarkInDto">
|
<member name="T:IRaCIS.Core.Application.Service.Reading.Dto.SaveTableQuestionMarkInDto">
|
||||||
<summary>
|
<summary>
|
||||||
保存表格问题标记
|
保存表格问题标记
|
||||||
|
|
|
||||||
|
|
@ -234,12 +234,12 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public async Task<IResponseOutput> SynchronizationEmail()
|
public async Task<IResponseOutput> SynchronizationEmail()
|
||||||
{
|
{
|
||||||
|
var maxTime = await _emailLogRepository.MaxAsync(t => t.EmailDate);
|
||||||
var maxTime=await _emailLogRepository.MaxAsync(t => t.EmailDate);
|
var startDate = maxTime ?? DateTime.MinValue;
|
||||||
|
|
||||||
List<EmailLog> emailList = new List<EmailLog>();
|
List<EmailLog> emailList = new List<EmailLog>();
|
||||||
List<EmailRecipientLog> EmailRecipientLogList = new List<EmailRecipientLog>();
|
List<EmailRecipientLog> EmailRecipientLogList = new List<EmailRecipientLog>();
|
||||||
|
|
||||||
|
// 第一步:同步发件箱邮件
|
||||||
using (var client = new ImapClient())
|
using (var client = new ImapClient())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -248,18 +248,13 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
AuthenticateImap(client);
|
AuthenticateImap(client);
|
||||||
var sentFolder = OpenSentFolder(client);
|
var sentFolder = OpenSentFolder(client);
|
||||||
|
|
||||||
var startDate = maxTime ?? DateTime.MinValue;
|
|
||||||
var searchQuery = SearchQuery.All.And(SearchQuery.DeliveredAfter(startDate));
|
var searchQuery = SearchQuery.All.And(SearchQuery.DeliveredAfter(startDate));
|
||||||
var uids = sentFolder.Search(searchQuery).ToList();
|
var uids = sentFolder.Search(searchQuery).ToList();
|
||||||
|
|
||||||
|
|
||||||
foreach (var uid in uids)
|
foreach (var uid in uids)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
//var message = sentFolder.GetMessage(uid);
|
|
||||||
// var emailView = ConvertToEmailLogView(uid, message);
|
|
||||||
|
|
||||||
var message = sentFolder.GetMessage(uid);
|
var message = sentFolder.GetMessage(uid);
|
||||||
if (message.Date.DateTime <= startDate)
|
if (message.Date.DateTime <= startDate)
|
||||||
{
|
{
|
||||||
|
|
@ -267,7 +262,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
}
|
}
|
||||||
var emaillog = new EmailLog
|
var emaillog = new EmailLog
|
||||||
{
|
{
|
||||||
Id=NewId.NextGuid(),
|
Id = NewId.NextGuid(),
|
||||||
UniqueId = uid.ToString(),
|
UniqueId = uid.ToString(),
|
||||||
MessageId = message.MessageId ?? string.Empty,
|
MessageId = message.MessageId ?? string.Empty,
|
||||||
EmailSubject = message.Subject ?? string.Empty,
|
EmailSubject = message.Subject ?? string.Empty,
|
||||||
|
|
@ -282,7 +277,7 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
emaillog.SenderName = fromMailbox.Name ?? string.Empty;
|
emaillog.SenderName = fromMailbox.Name ?? string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<EmailRecipientLog> recipientLogs= new List<EmailRecipientLog>();
|
List<EmailRecipientLog> recipientLogs = new List<EmailRecipientLog>();
|
||||||
|
|
||||||
int sort = 0;
|
int sort = 0;
|
||||||
message.To.Mailboxes.ForEach(x =>
|
message.To.Mailboxes.ForEach(x =>
|
||||||
|
|
@ -318,22 +313,37 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sentFolder.Close();
|
sentFolder.Close();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine($"同步发件箱邮件时出错: {ex.Message}");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
client.Disconnect(true);
|
client.Disconnect(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _emailLogRepository.AddRangeAsync(emailList);
|
|
||||||
await _emailRecipientLogRepository.AddRangeAsync(EmailRecipientLogList);
|
|
||||||
await _emailLogRepository.SaveChangesAsync();
|
|
||||||
return ResponseOutput.Ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存同步的发件箱邮件
|
||||||
|
await _emailLogRepository.AddRangeAsync(emailList);
|
||||||
|
await _emailRecipientLogRepository.AddRangeAsync(EmailRecipientLogList);
|
||||||
|
await _emailLogRepository.SaveChangesAsync();
|
||||||
|
|
||||||
|
// 第二步:处理收件箱中的邮件发送失败通知
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await ProcessInboxFailureNotifications();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"处理收件箱失败通知邮件时出错: {ex.Message}");
|
||||||
|
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||||
|
// 即使处理失败通知邮件出错,也不影响整体同步操作的成功
|
||||||
|
// 记录警告但不中断主流程
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResponseOutput.Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IMailFolder OpenSentFolder(ImapClient client)
|
private IMailFolder OpenSentFolder(ImapClient client)
|
||||||
|
|
@ -391,6 +401,60 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
return folder;
|
return folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IMailFolder OpenInboxFolder(ImapClient client)
|
||||||
|
{
|
||||||
|
IMailFolder folder = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
folder = client.GetFolder(SpecialFolder.All);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
|
if (folder == null)
|
||||||
|
{
|
||||||
|
var candidates = new[] { "收件箱", "Inbox" };
|
||||||
|
foreach (var name in candidates)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var f = client.GetFolder(name);
|
||||||
|
if (f != null)
|
||||||
|
{
|
||||||
|
folder = f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folder == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var personal = client.GetFolder(client.PersonalNamespaces.FirstOrDefault());
|
||||||
|
if (personal != null)
|
||||||
|
{
|
||||||
|
foreach (var sub in personal.GetSubfolders(false))
|
||||||
|
{
|
||||||
|
if (string.Equals(sub.FullName, "Inbox", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
folder = sub;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (folder == null)
|
||||||
|
throw new InvalidOperationException("未找到收件箱文件夹");
|
||||||
|
|
||||||
|
folder.Open(FolderAccess.ReadOnly);
|
||||||
|
return folder;
|
||||||
|
}
|
||||||
|
|
||||||
private void AuthenticateImap(ImapClient client)
|
private void AuthenticateImap(ImapClient client)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -420,6 +484,267 @@ public class EmailLogService(IRepository<EmailLog> _emailLogRepository,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理收件箱中的邮件发送失败通知
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task ProcessInboxFailureNotifications()
|
||||||
|
{
|
||||||
|
using (var client = new ImapClient())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.Connect(_systemEmailConfig.Imap, 993, SecureSocketOptions.SslOnConnect);
|
||||||
|
AuthenticateImap(client);
|
||||||
|
var inboxFolder = OpenInboxFolder(client);
|
||||||
|
|
||||||
|
// 搜索可能包含失败通知的邮件
|
||||||
|
// 通常失败通知邮件会包含"失败"、"错误"、"undeliverable"、"delivery failed"等关键词
|
||||||
|
var searchQuery = SearchQuery.All.Or(SearchQuery.SubjectContains("失败"))
|
||||||
|
.Or(SearchQuery.SubjectContains("错误"))
|
||||||
|
.Or(SearchQuery.SubjectContains("undeliverable"))
|
||||||
|
.Or(SearchQuery.SubjectContains("delivery failed"))
|
||||||
|
.Or(SearchQuery.SubjectContains("Delivery Status Notification"))
|
||||||
|
.Or(SearchQuery.BodyContains("失败"))
|
||||||
|
.Or(SearchQuery.BodyContains("错误"))
|
||||||
|
.Or(SearchQuery.BodyContains("undeliverable"))
|
||||||
|
.Or(SearchQuery.BodyContains("delivery failed")).And(SearchQuery.DeliveredAfter(DateTime.Parse("2025-11-27")));
|
||||||
|
|
||||||
|
var uids = inboxFolder.Search(searchQuery).ToList();
|
||||||
|
var processedCount = 0;
|
||||||
|
var updatedCount = 0;
|
||||||
|
|
||||||
|
foreach (var uid in uids)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var message = inboxFolder.GetMessage(uid);
|
||||||
|
|
||||||
|
// 尝试从邮件内容中提取原始邮件的Message-Id或相关信息
|
||||||
|
var originalMessageId = ExtractOriginalMessageId(message);
|
||||||
|
if (!string.IsNullOrEmpty(originalMessageId))
|
||||||
|
{
|
||||||
|
// 查找对应的EmailLog记录
|
||||||
|
var emailLog = await _emailLogRepository
|
||||||
|
.Where(x => x.MessageId == originalMessageId)
|
||||||
|
.FirstOrDefaultAsync();
|
||||||
|
|
||||||
|
if (emailLog != null && emailLog.EmailStateEnum == EmailState.Success)
|
||||||
|
{
|
||||||
|
// 提取错误信息
|
||||||
|
var errorMessage = ExtractErrorMessage(message);
|
||||||
|
|
||||||
|
// 更新邮件状态为失败
|
||||||
|
emailLog.EmailStateEnum = EmailState.Error;
|
||||||
|
emailLog.ErrorMessage = errorMessage ?? "邮件发送失败,具体原因未知";
|
||||||
|
|
||||||
|
//await _emailLogRepository.UpdateAsync(emailLog);
|
||||||
|
updatedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果无法从Message-Id匹配,尝试从邮件内容中提取收件人信息进行匹配
|
||||||
|
await ProcessFailureNotificationByRecipient(message);
|
||||||
|
}
|
||||||
|
processedCount++;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"处理收件箱邮件 UID:{uid} 失败: {ex.Message}");
|
||||||
|
// 记录详细异常信息用于调试
|
||||||
|
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"收件箱失败通知邮件处理完成: 共处理 {processedCount} 封邮件,更新 {updatedCount} 条记录");
|
||||||
|
inboxFolder.Close();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"连接收件箱处理失败通知邮件时出错: {ex.Message}");
|
||||||
|
Console.WriteLine($"异常堆栈: {ex.StackTrace}");
|
||||||
|
throw; // 重新抛出异常,让上层调用者决定如何处理
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.Disconnect(true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"断开IMAP客户端连接时出错: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从失败通知邮件中提取原始邮件的Message-Id
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string ExtractOriginalMessageId(MimeMessage message)
|
||||||
|
{
|
||||||
|
// 首先检查邮件头中是否有原始Message-Id
|
||||||
|
var originalMessageId = message.Headers["Original-Message-ID"] ??
|
||||||
|
message.Headers["X-Original-Message-ID"] ??
|
||||||
|
message.Headers["In-Reply-To"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(originalMessageId))
|
||||||
|
{
|
||||||
|
// 清理Message-Id格式(移除尖括号)
|
||||||
|
originalMessageId = originalMessageId.Trim('<', '>');
|
||||||
|
return originalMessageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果邮件头中没有,尝试从邮件正文中提取
|
||||||
|
var content = message.HtmlBody ?? message.TextBody ?? string.Empty;
|
||||||
|
|
||||||
|
// 尝试匹配常见的Message-Id格式
|
||||||
|
var messageIdPattern = @"(?:Message-ID|Message-Id|message-id):\s*<?([^>\s]+)>?";
|
||||||
|
var match = System.Text.RegularExpressions.Regex.Match(content, messageIdPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从邮件引用部分提取
|
||||||
|
var referencePattern = @"^>.*Message-ID:\s*<?([^>\s]+)>?";
|
||||||
|
var matches = System.Text.RegularExpressions.Regex.Matches(content, referencePattern, System.Text.RegularExpressions.RegexOptions.Multiline | System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||||
|
if (matches.Count > 0)
|
||||||
|
{
|
||||||
|
return matches[0].Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从失败通知邮件中提取错误信息
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string ExtractErrorMessage(MimeMessage message)
|
||||||
|
{
|
||||||
|
var content = message.HtmlBody ?? message.TextBody ?? string.Empty;
|
||||||
|
|
||||||
|
// 尝试提取具体的错误信息
|
||||||
|
// 常见的错误信息模式
|
||||||
|
var errorPatterns = new[]
|
||||||
|
{
|
||||||
|
@"(?i)reason\s*:\s*([^\r\n]+)",
|
||||||
|
@"(?i)error\s*:\s*([^\r\n]+)",
|
||||||
|
@"(?i)failure\s*:\s*([^\r\n]+)",
|
||||||
|
@"(?i)诊断信息[::]\s*([^\r\n]+)",
|
||||||
|
@"(?i)错误详情[::]\s*([^\r\n]+)",
|
||||||
|
@"(?i)Technical details of permanent failure[::]\s*([^\r\n]+)",
|
||||||
|
@"(?i)The error that the other server returned was[::]\s*([^\r\n]+)"
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var pattern in errorPatterns)
|
||||||
|
{
|
||||||
|
var match = System.Text.RegularExpressions.Regex.Match(content, pattern);
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return match.Groups[1].Value.Trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果没有找到具体的错误信息,返回邮件主题和部分内容
|
||||||
|
return $"主题: {message.Subject}\n内容摘要: {GetContentSummary(content)}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取邮件内容摘要(前200个字符)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string GetContentSummary(string content)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(content))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
// 移除HTML标签
|
||||||
|
var plainText = System.Text.RegularExpressions.Regex.Replace(content, @"<[^>]+>", string.Empty);
|
||||||
|
plainText = System.Text.RegularExpressions.Regex.Replace(plainText, @"\s+", " ");
|
||||||
|
|
||||||
|
return plainText.Length > 200 ? plainText.Substring(0, 200) + "..." : plainText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 通过收件人信息处理失败通知邮件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private async Task ProcessFailureNotificationByRecipient(MimeMessage message)
|
||||||
|
{
|
||||||
|
var content = message.HtmlBody ?? message.TextBody ?? string.Empty;
|
||||||
|
|
||||||
|
// 尝试从邮件内容中提取原始收件人地址
|
||||||
|
var recipientPattern = @"(?:to|To|收件人)[::]\s*([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})";
|
||||||
|
var match = System.Text.RegularExpressions.Regex.Match(content, recipientPattern, System.Text.RegularExpressions.RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
var recipientEmail = match.Groups[1].Value;
|
||||||
|
|
||||||
|
// 查找最近发送给该收件人的邮件
|
||||||
|
var recentEmailLogs = await _emailLogRepository
|
||||||
|
.Where(x => x.EmailRecipientLogList.Any(r => r.RecipientAddress == recipientEmail))
|
||||||
|
.Where(x => x.EmailDate >= DateTime.Now.AddDays(-7)) // 最近7天内的邮件
|
||||||
|
.OrderByDescending(x => x.EmailDate)
|
||||||
|
.Take(5) // 取最近的5封
|
||||||
|
.ToListAsync();
|
||||||
|
|
||||||
|
foreach (var emailLog in recentEmailLogs)
|
||||||
|
{
|
||||||
|
if (emailLog.EmailStateEnum == EmailState.Success)
|
||||||
|
{
|
||||||
|
// 检查邮件主题是否匹配(如果失败通知邮件中包含原始主题)
|
||||||
|
if (IsLikelyRelatedEmail(message, emailLog))
|
||||||
|
{
|
||||||
|
var errorMessage = ExtractErrorMessage(message);
|
||||||
|
emailLog.EmailStateEnum = EmailState.Error;
|
||||||
|
emailLog.ErrorMessage = errorMessage ?? "邮件发送失败,具体原因未知";
|
||||||
|
//await _emailLogRepository.UpdateAsync(emailLog);
|
||||||
|
break; // 找到匹配的就停止
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断失败通知邮件是否与指定的EmailLog相关
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="failureMessage"></param>
|
||||||
|
/// <param name="emailLog"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool IsLikelyRelatedEmail(MimeMessage failureMessage, EmailLog emailLog)
|
||||||
|
{
|
||||||
|
var failureContent = failureMessage.HtmlBody ?? failureMessage.TextBody ?? failureMessage.Subject ?? string.Empty;
|
||||||
|
var emailSubject = emailLog.EmailSubject ?? string.Empty;
|
||||||
|
|
||||||
|
// 检查主题是否包含在失败通知中
|
||||||
|
if (!string.IsNullOrEmpty(emailSubject) &&
|
||||||
|
failureContent.Contains(emailSubject, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查发件人是否匹配
|
||||||
|
if (!string.IsNullOrEmpty(emailLog.SenderAddress) &&
|
||||||
|
(failureContent.Contains(emailLog.SenderAddress, StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
failureMessage.From.ToString().Contains(emailLog.SenderAddress, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//private string AcquireOAuth2TokenByPassword()
|
//private string AcquireOAuth2TokenByPassword()
|
||||||
//{
|
//{
|
||||||
// if (_systemEmailConfig.OAuthTenantId.IsNullOrEmpty() || _systemEmailConfig.OAuthClientId.IsNullOrEmpty())
|
// if (_systemEmailConfig.OAuthTenantId.IsNullOrEmpty() || _systemEmailConfig.OAuthClientId.IsNullOrEmpty())
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,9 @@ public class EmailLog : BaseFullAuditEntity
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EmailState EmailStateEnum { get; set; }
|
public EmailState EmailStateEnum { get; set; }
|
||||||
|
|
||||||
|
[StringLength(5000)]
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 邮件内容
|
/// 邮件内容
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
21270
IRaCIS.Core.Infra.EFCore/Migrations/20251128033532_EmailErrorMessage.Designer.cs
generated
Normal file
21270
IRaCIS.Core.Infra.EFCore/Migrations/20251128033532_EmailErrorMessage.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,30 @@
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class EmailErrorMessage : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "ErrorMessage",
|
||||||
|
table: "EmailLog",
|
||||||
|
type: "nvarchar(max)",
|
||||||
|
maxLength: 5000,
|
||||||
|
nullable: false,
|
||||||
|
defaultValue: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "ErrorMessage",
|
||||||
|
table: "EmailLog");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2400,6 +2400,11 @@ namespace IRaCIS.Core.Infra.EFCore.Migrations
|
||||||
.HasMaxLength(400)
|
.HasMaxLength(400)
|
||||||
.HasColumnType("nvarchar(400)");
|
.HasColumnType("nvarchar(400)");
|
||||||
|
|
||||||
|
b.Property<string>("ErrorMessage")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(5000)
|
||||||
|
.HasColumnType("nvarchar(max)");
|
||||||
|
|
||||||
b.Property<string>("MessageId")
|
b.Property<string>("MessageId")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
.HasMaxLength(400)
|
.HasMaxLength(400)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue