Files
EmailBill/Service/Jobs/EmailSyncJob.cs
孙诚 9719c6043a
All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 30s
Docker Build & Deploy / Deploy to Production (push) Successful in 7s
新增定时账单功能
2025-12-29 15:20:32 +08:00

186 lines
6.6 KiB
C#

using MimeKit;
using Quartz;
namespace Service.Jobs;
/// <summary>
/// 邮件同步定时任务
/// </summary>
public class EmailSyncJob(
IOptions<EmailSettings> emailSettings,
IServiceProvider serviceProvider,
IEmailHandleService emailHandleService,
ILogger<EmailSyncJob> logger) : IJob
{
private readonly Dictionary<string, IEmailFetchService> _emailFetchServices = new();
private bool _isInitialized;
public async Task Execute(IJobExecutionContext context)
{
try
{
logger.LogInformation("开始执行邮件同步任务");
// 如果未初始化,先初始化连接
if (!_isInitialized)
{
await InitializeConnectionsAsync();
}
// 执行邮件同步
await FetchAndPostCmbTransactionsAsync();
logger.LogInformation("邮件同步任务执行完成");
}
catch (Exception ex)
{
logger.LogError(ex, "邮件同步任务执行出错");
throw; // 让 Quartz 知道任务失败
}
}
/// <summary>
/// 初始化所有邮箱连接
/// </summary>
private async Task InitializeConnectionsAsync()
{
if (_isInitialized)
{
logger.LogWarning("连接已初始化,跳过重复初始化");
return;
}
try
{
if (emailSettings.Value.SmtpList.Length == 0)
{
logger.LogWarning("未配置邮箱账户,无法初始化连接");
return;
}
logger.LogInformation("开始初始化 {EmailCount} 个邮箱连接...", emailSettings.Value.SmtpList.Length);
// 并行初始化所有邮箱连接
var tasks = emailSettings.Value.SmtpList.Select(async emailConfig =>
{
try
{
var emailFetchService = ActivatorUtilities.CreateInstance<EmailFetchService>(serviceProvider);
var success = await emailFetchService.ConnectAsync(
emailConfig.ImapHost,
emailConfig.ImapPort,
emailConfig.UseSsl,
emailConfig.Email,
emailConfig.Password);
if (success)
{
_emailFetchServices[emailConfig.Email] = emailFetchService;
logger.LogInformation("邮箱 {Email} 连接建立成功", emailConfig.Email);
}
else
{
logger.LogError("邮箱 {Email} 连接建立失败", emailConfig.Email);
}
}
catch (Exception ex)
{
logger.LogError(ex, "初始化邮箱 {Email} 连接时出错", emailConfig.Email);
}
});
await Task.WhenAll(tasks);
_isInitialized = true;
logger.LogInformation("所有邮箱连接初始化完成,成功连接 {Count} 个邮箱", _emailFetchServices.Count);
}
catch (Exception ex)
{
logger.LogError(ex, "初始化邮箱连接失败");
}
}
/// <summary>
/// 抓取并处理招商银行邮件交易
/// </summary>
private async Task FetchAndPostCmbTransactionsAsync()
{
try
{
if (_emailFetchServices.Count == 0)
{
logger.LogWarning("没有可用的邮箱连接,跳过抓取");
return;
}
logger.LogInformation("开始抓取 {EmailCount} 个邮箱的邮件", _emailFetchServices.Count);
// 并行处理多个邮箱
var tasks = _emailFetchServices.Select(async kvp =>
{
var email = kvp.Key;
var emailFetchService = kvp.Value;
try
{
// 获取未读邮件
var unreadMessages = await emailFetchService.FetchUnreadMessagesAsync();
logger.LogInformation("邮箱 {Email} 获取到 {MessageCount} 封未读邮件", email, unreadMessages.Count);
foreach (var (message, uid) in unreadMessages)
{
try
{
logger.LogDebug("邮件信息 - 发送者: {From}, 主题: {Subject}, 接收时间: {Date}",
message.From, message.Subject, message.Date);
logger.LogDebug("邮件内容预览: {Preview}", GetEmailBodyPreview(message));
if (await emailHandleService.HandleEmailAsync(
email,
message.From.ToString(),
message.Subject,
message.Date.DateTime,
message.TextBody ?? message.HtmlBody ?? string.Empty
) || (DateTime.Now - message.Date.DateTime > TimeSpan.FromDays(3)))
{
#if DEBUG
logger.LogDebug("DEBUG 模式下,跳过标记已读步骤");
#else
// 标记邮件为已读
await emailFetchService.MarkAsReadAsync(uid);
#endif
}
}
catch (Exception ex)
{
logger.LogError(ex, "处理邮件时出错");
}
}
logger.LogInformation("邮箱 {Email} 邮件抓取完成", email);
}
catch (Exception ex)
{
logger.LogError(ex, "邮箱 {Email} 邮件抓取失败", email);
}
});
await Task.WhenAll(tasks);
logger.LogInformation("所有邮箱邮件抓取完成");
}
catch (Exception ex)
{
logger.LogError(ex, "抓取邮件异常");
}
}
/// <summary>
/// 获取邮件内容预览
/// </summary>
private static string GetEmailBodyPreview(MimeMessage message)
{
var body = message.HtmlBody ?? message.TextBody ?? string.Empty;
var preview = body.Length > 100 ? body.Substring(0, 100) + "..." : body;
return preview.Replace("\n", " ").Replace("\r", "");
}
}