diff --git a/src/Oms.Application/Oms.Application.csproj b/src/Oms.Application/Oms.Application.csproj
index b2b5427..efc0437 100644
--- a/src/Oms.Application/Oms.Application.csproj
+++ b/src/Oms.Application/Oms.Application.csproj
@@ -22,6 +22,7 @@
+
diff --git a/src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs b/src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs
index 5be7ca5..c95c8c0 100644
--- a/src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs
+++ b/src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs
@@ -1,6 +1,8 @@
-using System.Net;
-using System.Net.Mail;
+using System.Net.Sockets;
using Fake.Application;
+using MailKit.Net.Smtp;
+using MailKit.Security;
+using MimeKit;
namespace Oms.Application.Services.System;
@@ -11,17 +13,17 @@ internal static class BatchEmailSmtpHelper
internal static bool IsSecretMask(string? value) =>
string.Equals(value?.Trim(), SecretMask, StringComparison.Ordinal);
- internal sealed record SmtpEndpoint(string Host, int Port, bool EnableSsl);
+ internal sealed record SmtpEndpoint(string Host, int Port, SecureSocketOptions SocketOptions);
internal static SmtpEndpoint ResolveEndpoint(string? serverType)
{
return (serverType ?? string.Empty).Trim().ToLowerInvariant() switch
{
- "qiye_ssl" => new SmtpEndpoint("smtp.mxhichina.com", 465, true),
- "aliyun" => new SmtpEndpoint("smtp.aliyun.com", 25, false),
- "aliyun_ssl" => new SmtpEndpoint("smtp.aliyun.com", 465, true),
- "mxhichina" => new SmtpEndpoint("smtp.mxhichina.com", 25, false),
- _ => new SmtpEndpoint("smtp.mxhichina.com", 25, false)
+ "qiye_ssl" => new SmtpEndpoint("smtp.mxhichina.com", 465, SecureSocketOptions.SslOnConnect),
+ "aliyun" => new SmtpEndpoint("smtp.aliyun.com", 25, SecureSocketOptions.StartTlsWhenAvailable),
+ "aliyun_ssl" => new SmtpEndpoint("smtp.aliyun.com", 465, SecureSocketOptions.SslOnConnect),
+ "mxhichina" => new SmtpEndpoint("smtp.mxhichina.com", 25, SecureSocketOptions.StartTlsWhenAvailable),
+ _ => new SmtpEndpoint("smtp.mxhichina.com", 25, SecureSocketOptions.StartTlsWhenAvailable)
};
}
@@ -33,14 +35,14 @@ internal static class BatchEmailSmtpHelper
{
ValidateCredentials(smtpUsername, smtpPassword);
var username = smtpUsername.Trim();
- var endpoint = ResolveEndpoint(serverType);
- using var client = CreateClient(endpoint, username, smtpPassword);
- using var message = new MailMessage(username, username)
- {
- Subject = "OMS SMTP Test",
- Body = "OMS batch email connection test"
- };
- await SendWithClientAsync(client, message, cancellationToken);
+ var message = BuildMessage(
+ username,
+ username,
+ username,
+ "OMS SMTP Test",
+ "OMS batch email connection test",
+ "plain");
+ await SendWithMailKitAsync(username, smtpPassword, serverType, message, cancellationToken);
}
internal static async Task SendAsync(
@@ -62,20 +64,31 @@ internal static class BatchEmailSmtpHelper
}
var username = smtpUsername.Trim();
- var endpoint = ResolveEndpoint(serverType);
- using var client = CreateClient(endpoint, username, smtpPassword);
- var fromAddress = new MailAddress(
- username,
- string.IsNullOrWhiteSpace(fromName) ? username : fromName.Trim());
- using var message = new MailMessage
+ var displayName = string.IsNullOrWhiteSpace(fromName) ? username : fromName.Trim();
+ var message = BuildMessage(username, displayName, recipient, subject, body, bodyType);
+ await SendWithMailKitAsync(username, smtpPassword, serverType, message, cancellationToken);
+ }
+
+ private static MimeMessage BuildMessage(
+ string username,
+ string displayName,
+ string to,
+ string? subject,
+ string? body,
+ string? bodyType)
+ {
+ var message = new MimeMessage
{
- From = fromAddress,
- Subject = subject ?? string.Empty,
- Body = body ?? string.Empty,
- IsBodyHtml = string.Equals(bodyType, "html", StringComparison.OrdinalIgnoreCase)
+ Subject = subject ?? string.Empty
};
- message.To.Add(recipient);
- await SendWithClientAsync(client, message, cancellationToken);
+ message.From.Add(new MailboxAddress(displayName, username));
+ message.To.Add(MailboxAddress.Parse(to));
+ var isHtml = string.Equals(bodyType, "html", StringComparison.OrdinalIgnoreCase);
+ message.Body = new TextPart(isHtml ? "html" : "plain")
+ {
+ Text = body ?? string.Empty
+ };
+ return message;
}
private static void ValidateCredentials(string smtpUsername, string smtpPassword)
@@ -91,32 +104,41 @@ internal static class BatchEmailSmtpHelper
}
}
- private static SmtpClient CreateClient(SmtpEndpoint endpoint, string username, string password) =>
- new(endpoint.Host, endpoint.Port)
+ private static async Task SendWithMailKitAsync(
+ string username,
+ string password,
+ string? serverType,
+ MimeMessage message,
+ CancellationToken cancellationToken)
+ {
+ var endpoint = ResolveEndpoint(serverType);
+ using var client = new SmtpClient
{
- EnableSsl = endpoint.EnableSsl,
- DeliveryMethod = SmtpDeliveryMethod.Network,
- UseDefaultCredentials = false,
- Credentials = new NetworkCredential(username, password),
Timeout = 30_000
};
- private static async Task SendWithClientAsync(
- SmtpClient client,
- MailMessage message,
- CancellationToken cancellationToken)
- {
try
{
- await client.SendMailAsync(message, cancellationToken);
+ await client.ConnectAsync(endpoint.Host, endpoint.Port, endpoint.SocketOptions, cancellationToken);
+ await client.AuthenticateAsync(username, password, cancellationToken);
+ await client.SendAsync(message, cancellationToken);
+ await client.DisconnectAsync(true, cancellationToken);
}
- catch (SmtpException ex)
+ catch (AuthenticationException ex)
+ {
+ throw new BusinessException($"SMTP 认证失败:{ex.Message}");
+ }
+ catch (SmtpCommandException ex)
{
throw new BusinessException($"SMTP 发送失败:{ex.Message}");
}
- catch (Exception ex) when (ex is InvalidOperationException or FormatException)
+ catch (SmtpProtocolException ex)
{
- throw new BusinessException($"SMTP 发送失败:{ex.Message}");
+ throw new BusinessException($"SMTP 协议错误:{ex.Message}");
+ }
+ catch (Exception ex) when (ex is IOException or SocketException or TimeoutException)
+ {
+ throw new BusinessException($"SMTP 连接失败:{ex.Message}");
}
}
}