first commot
This commit is contained in:
275
Service/EmailHandleService.cs
Normal file
275
Service/EmailHandleService.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
using Service.EmailParseServices;
|
||||
|
||||
namespace Service;
|
||||
|
||||
public interface IEmailHandleService
|
||||
{
|
||||
Task<bool> HandleEmailAsync(
|
||||
string from,
|
||||
string subject,
|
||||
DateTime date,
|
||||
string body
|
||||
);
|
||||
|
||||
Task<bool> RefreshTransactionRecordsAsync(long emailMessageId);
|
||||
}
|
||||
|
||||
public class EmailHandleService(
|
||||
IOptions<EmailSettings> emailSettings,
|
||||
ILogger<EmailHandleService> logger,
|
||||
IEmailMessageRepository emailRepo,
|
||||
ITransactionRecordRepository trxRepo,
|
||||
IEnumerable<IEmailParseServices> emailParsers
|
||||
) : IEmailHandleService
|
||||
{
|
||||
public async Task<bool> HandleEmailAsync(
|
||||
string from,
|
||||
string subject,
|
||||
DateTime date,
|
||||
string body
|
||||
)
|
||||
{
|
||||
var emailMessage = await SaveEmailAsync(from, subject, date, body);
|
||||
|
||||
if (emailMessage == null)
|
||||
{
|
||||
throw new InvalidOperationException("邮件保存失败,无法继续处理");
|
||||
}
|
||||
|
||||
var filterForm = emailSettings.Value.FilterFromAddresses;
|
||||
if (filterForm.Length == 0)
|
||||
{
|
||||
logger.LogWarning("未配置邮件过滤条件,跳过账单处理");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!filterForm.Any(f => from.Contains(f)))
|
||||
{
|
||||
logger.LogInformation("邮件不符合发件人过滤条件,跳过账单处理");
|
||||
return true;
|
||||
}
|
||||
|
||||
var parsed = await ParseEmailBodyAsync(
|
||||
from,
|
||||
string.IsNullOrEmpty(emailMessage.Body)
|
||||
? emailMessage.HtmlBody
|
||||
: emailMessage.Body
|
||||
);
|
||||
if (parsed == null || parsed.Length == 0)
|
||||
{
|
||||
logger.LogWarning("未能成功解析邮件内容,跳过账单处理");
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.LogInformation("成功解析邮件,共 {Count} 条交易记录", parsed.Length);
|
||||
|
||||
bool allSuccess = true;
|
||||
foreach (var (card, reason, amount, balance, type, occurredAt) in parsed)
|
||||
{
|
||||
logger.LogInformation("处理交易记录: 卡号 {Card}, 交易原因 {Reason}, 金额 {Amount}, 余额 {Balance}, 类型 {Type}", card, reason, amount, balance, type);
|
||||
|
||||
var success = await SaveTransactionRecordAsync(
|
||||
card,
|
||||
reason,
|
||||
amount,
|
||||
balance,
|
||||
type,
|
||||
occurredAt ?? date,
|
||||
emailMessage.Id
|
||||
);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
allSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
public async Task<bool> RefreshTransactionRecordsAsync(long emailMessageId)
|
||||
{
|
||||
var emailMessage = await emailRepo.GetByIdAsync(emailMessageId);
|
||||
|
||||
if (emailMessage == null)
|
||||
{
|
||||
logger.LogWarning("未找到指定ID的邮件记录,无法刷新交易记录,ID: {Id}", emailMessageId);
|
||||
return false;
|
||||
}
|
||||
|
||||
var filterForm = emailSettings.Value.FilterFromAddresses;
|
||||
if (filterForm.Length == 0)
|
||||
{
|
||||
logger.LogWarning("未配置邮件过滤条件,跳过账单处理");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!filterForm.Any(f => emailMessage.From.Contains(f)))
|
||||
{
|
||||
logger.LogInformation("邮件不符合发件人过滤条件,跳过账单处理");
|
||||
return true;
|
||||
}
|
||||
|
||||
var parsed = await ParseEmailBodyAsync(
|
||||
emailMessage.From,
|
||||
string.IsNullOrEmpty(emailMessage.Body)
|
||||
? emailMessage.HtmlBody
|
||||
: emailMessage.Body
|
||||
);
|
||||
if (parsed == null || parsed.Length == 0)
|
||||
{
|
||||
logger.LogWarning("未能成功解析邮件内容,跳过账单处理");
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.LogInformation("成功解析邮件,共 {Count} 条交易记录", parsed.Length);
|
||||
|
||||
bool allSuccess = true;
|
||||
foreach (var (card, reason, amount, balance, type, occurredAt) in parsed)
|
||||
{
|
||||
logger.LogInformation("刷新交易记录: 卡号 {Card}, 交易原因 {Reason}, 金额 {Amount}, 余额 {Balance}, 类型 {Type}", card, reason, amount, balance, type);
|
||||
|
||||
var success = await SaveTransactionRecordAsync(
|
||||
card,
|
||||
reason,
|
||||
amount,
|
||||
balance,
|
||||
type,
|
||||
occurredAt ?? emailMessage.ReceivedDate,
|
||||
emailMessage.Id
|
||||
);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
allSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allSuccess;
|
||||
}
|
||||
|
||||
private async Task<EmailMessage?> SaveEmailAsync(
|
||||
string from,
|
||||
string subject,
|
||||
DateTime date,
|
||||
string body
|
||||
)
|
||||
{
|
||||
var emailEntity = new EmailMessage
|
||||
{
|
||||
From = from,
|
||||
Subject = subject,
|
||||
|
||||
ReceivedDate = date,
|
||||
};
|
||||
|
||||
// 正则判断是否为HTML内容
|
||||
if (Regex.IsMatch(body, @"<[^>]+>"))
|
||||
{
|
||||
emailEntity.HtmlBody = body;
|
||||
}
|
||||
else
|
||||
{
|
||||
emailEntity.Body = body;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var existsEmail = await emailRepo.ExistsAsync(from, subject, date, body);
|
||||
if (existsEmail != null)
|
||||
{
|
||||
logger.LogInformation("检测到重复邮件,跳过入库:{From} | {Subject} | {Date}", from, subject, date);
|
||||
return existsEmail;
|
||||
}
|
||||
|
||||
var ok = await emailRepo.AddAsync(emailEntity);
|
||||
if (ok)
|
||||
{
|
||||
logger.LogInformation("邮件已落库,ID: {Id}", emailEntity.Id);
|
||||
return emailEntity;
|
||||
}
|
||||
|
||||
logger.LogError("邮件落库失败");
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 原始邮件落库失败不阻塞交易记录,但记录告警
|
||||
logger.LogWarning(ex, "原始邮件落库失败");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> SaveTransactionRecordAsync(
|
||||
string card,
|
||||
string reason,
|
||||
decimal amount,
|
||||
decimal balance,
|
||||
TransactionType type,
|
||||
DateTime occurredAt,
|
||||
long emailMessageId
|
||||
)
|
||||
{
|
||||
// 根据 emailMessageId 检查是否已存在记录:存在则更新,否则新增
|
||||
var existing = await trxRepo.ExistsByEmailMessageIdAsync(emailMessageId, occurredAt);
|
||||
|
||||
if (existing != null)
|
||||
{
|
||||
existing.Card = card;
|
||||
existing.Reason = reason;
|
||||
existing.Amount = amount;
|
||||
existing.Balance = balance;
|
||||
existing.Type = type;
|
||||
existing.OccurredAt = occurredAt;
|
||||
|
||||
var updated = await trxRepo.UpdateAsync(existing);
|
||||
if (updated)
|
||||
{
|
||||
logger.LogInformation("交易记录已更新,卡号 {Card}, 金额 {Amount}", card, amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("交易记录更新失败,卡号 {Card}, 金额 {Amount}", card, amount);
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
var trx = new TransactionRecord
|
||||
{
|
||||
Card = card,
|
||||
Reason = reason,
|
||||
Amount = amount,
|
||||
Balance = balance,
|
||||
Type = type,
|
||||
OccurredAt = occurredAt,
|
||||
EmailMessageId = emailMessageId,
|
||||
ImportFrom = $"邮件"
|
||||
};
|
||||
|
||||
var inserted = await trxRepo.AddAsync(trx);
|
||||
if (inserted)
|
||||
{
|
||||
logger.LogInformation("交易记录已落库,卡号 {Card}, 金额 {Amount}", card, amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("交易记录落库失败,卡号 {Card}, 金额 {Amount}", card, amount);
|
||||
}
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
private async Task<(string card, string reason, decimal amount, decimal balance, TransactionType type, DateTime? occurredAt)[]?> ParseEmailBodyAsync(string from, string body)
|
||||
{
|
||||
var service = emailParsers.FirstOrDefault(s => s.CanParse(from, body));
|
||||
|
||||
if (service == null)
|
||||
{
|
||||
logger.LogWarning("未找到合适的邮件解析服务,跳过解析");
|
||||
return null;
|
||||
}
|
||||
|
||||
return await service.ParseAsync(body);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user