Initial_commit_SecMPS_v2
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.BackgroundServices.mail;
|
||||
using VolPro.Core.PerformanceMonitor;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices
|
||||
{
|
||||
public static class BackgroundServiceExtensions
|
||||
{
|
||||
public static IServiceCollection AddBackgroundServices(this WebApplicationBuilder builder)
|
||||
{
|
||||
// 注册邮件服务
|
||||
builder.Services.AddSingleton<IMailService, MailService>();
|
||||
// 注册邮件后台服务
|
||||
builder.Services.AddHostedService<MailBackgroundService>();
|
||||
|
||||
return builder.Services;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件服务接口
|
||||
/// </summary>
|
||||
public interface IMailService
|
||||
{
|
||||
/// <summary>
|
||||
/// 直接发送邮件(同步发送)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
/// <returns></returns>
|
||||
Task SendAsync(string email, string subject, string body, bool isHtml = true);
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
/// <returns></returns>
|
||||
Task SendQueuedAsync(string email, string subject, string body, bool isHtml = true);
|
||||
|
||||
/// <summary>
|
||||
/// 发送邮件(加入队列,同步方法)
|
||||
/// </summary>
|
||||
/// <param name="email">接收人邮箱,多个用逗号分隔</param>
|
||||
/// <param name="subject">主题</param>
|
||||
/// <param name="body">内容</param>
|
||||
/// <param name="isHtml">是否HTML</param>
|
||||
void Send(string email, string subject, string body, bool isHtml = true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Quartz.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件后台服务,使用Channel队列处理邮件发送
|
||||
/// </summary>
|
||||
public class MailBackgroundService : BackgroundService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public MailBackgroundService(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
Console.WriteLine("发送邮件服务已启动");
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 从队列中读取邮件请求
|
||||
var mailRequest = await MailService.MailQueue.Reader.ReadAsync(stoppingToken);
|
||||
if (mailRequest.RetryCount > mailRequest.MaxRetryCount)
|
||||
{
|
||||
break;
|
||||
}
|
||||
using (var scope = _serviceProvider.CreateScope())
|
||||
{
|
||||
var mailService = scope.ServiceProvider.GetRequiredService<IMailService>();
|
||||
|
||||
try
|
||||
{
|
||||
await mailService.SendAsync(mailRequest.MailMessage.To,
|
||||
mailRequest.MailMessage.Subject,
|
||||
mailRequest.MailMessage.Body,
|
||||
mailRequest.MailMessage.IsHtml);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
mailRequest.RetryCount++;
|
||||
Console.WriteLine($"邮件发送异常:{ex.Message + ex.StackTrace}");
|
||||
Core.Services.Logger.AddAsync($"邮件发送异常:{ex.Message + ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ChannelClosedException)
|
||||
{
|
||||
// 队列已关闭,退出循环
|
||||
break;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 取消令牌触发,退出循环
|
||||
break;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 邮件队列处理出现未知错误,短暂延迟后继续处理
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override async Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// 关闭队列写入器
|
||||
MailService.MailQueue.Writer.Complete();
|
||||
|
||||
// 等待所有待处理的邮件完成
|
||||
await base.StopAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件请求模型,用于队列处理
|
||||
/// </summary>
|
||||
public class MailRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 请求ID
|
||||
/// </summary>
|
||||
public Guid Id { get; set; } = Guid.NewGuid();
|
||||
|
||||
/// <summary>
|
||||
/// 邮件消息
|
||||
/// </summary>
|
||||
public required MailRequestMessage MailMessage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 创建时间
|
||||
/// </summary>
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// 重试次数
|
||||
/// </summary>
|
||||
public int RetryCount { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 最大重试次数
|
||||
/// </summary>
|
||||
public int MaxRetryCount { get; set; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// 下次重试时间
|
||||
/// </summary>
|
||||
public DateTime? NextRetryTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件消息模型
|
||||
/// </summary>
|
||||
public class MailRequestMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// 收件人地址
|
||||
/// </summary>
|
||||
public string To { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 抄送人地址
|
||||
/// </summary>
|
||||
public string Cc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 密送人地址
|
||||
/// </summary>
|
||||
public string Bcc { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件主题
|
||||
/// </summary>
|
||||
public required string Subject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 邮件内容
|
||||
/// </summary>
|
||||
public required string Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为HTML格式
|
||||
/// </summary>
|
||||
public bool IsHtml { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 附件路径列表
|
||||
/// </summary>
|
||||
public List<string> Attachments { get; set; } = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 优先级
|
||||
/// </summary>
|
||||
public MailPriority Priority { get; set; } = MailPriority.Normal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 邮件优先级
|
||||
/// </summary>
|
||||
public enum MailPriority
|
||||
{
|
||||
Low = 0,
|
||||
Normal = 1,
|
||||
High = 2
|
||||
}
|
||||
}
|
||||
175
api_sqlsugar/VolPro.Core/BackgroundServices/mail/MailService.cs
Normal file
175
api_sqlsugar/VolPro.Core/BackgroundServices/mail/MailService.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Configuration;
|
||||
using VolPro.Core.Extensions;
|
||||
|
||||
namespace VolPro.Core.BackgroundServices.mail
|
||||
{
|
||||
/// <summary>
|
||||
/// 邮件服务实现
|
||||
/// </summary>
|
||||
public class MailService : IMailService
|
||||
{
|
||||
// 邮件队列Channel
|
||||
private static readonly Channel<MailRequest> _mailQueue;
|
||||
|
||||
// 提供静态访问Channel的方法
|
||||
public static Channel<MailRequest> MailQueue => _mailQueue;
|
||||
|
||||
// 邮件配置信息
|
||||
private static string address { get; set; }
|
||||
private static string authPwd { get; set; }
|
||||
private static string name { get; set; }
|
||||
private static string host { get; set; }
|
||||
private static int port;
|
||||
private static bool enableSsl { get; set; }
|
||||
|
||||
static MailService()
|
||||
{
|
||||
// 初始化邮件队列Channel
|
||||
_mailQueue = Channel.CreateBounded<MailRequest>(new BoundedChannelOptions(1000)
|
||||
{
|
||||
FullMode = BoundedChannelFullMode.Wait
|
||||
});
|
||||
|
||||
// 读取邮件配置
|
||||
var section = AppSetting.GetSection("Mail");
|
||||
address = section["Address"];
|
||||
authPwd = section["AuthPwd"];
|
||||
name = section["Name"];
|
||||
host = section["Host"];
|
||||
port = section["Port"].GetInt();
|
||||
enableSsl = section["EnableSsl"].GetBool();
|
||||
}
|
||||
|
||||
public MailService()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 实际发送邮件的核心方法
|
||||
/// </summary>
|
||||
private async Task SendEmailCoreAsync(string emails, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
// 解析多个接收人
|
||||
var recipients = new List<string>();
|
||||
if (!string.IsNullOrEmpty(emails))
|
||||
{
|
||||
foreach (var addr in emails.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
recipients.Add(addr.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
if (recipients.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建邮件消息
|
||||
using var message = new MailMessage
|
||||
{
|
||||
From = new MailAddress(address, name)
|
||||
};
|
||||
|
||||
// 添加收件人
|
||||
foreach (var recipient in recipients)
|
||||
{
|
||||
message.To.Add(recipient);
|
||||
}
|
||||
|
||||
message.Subject = subject;
|
||||
message.Body = body;
|
||||
message.IsBodyHtml = isHtml;
|
||||
|
||||
// 配置SMTP客户端
|
||||
using var smtpClient = new SmtpClient
|
||||
{
|
||||
Host = host,
|
||||
Port = port,
|
||||
EnableSsl = enableSsl,
|
||||
Credentials = new NetworkCredential(address, authPwd),
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
UseDefaultCredentials = false
|
||||
};
|
||||
|
||||
// 发送邮件
|
||||
await smtpClient.SendMailAsync(message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
private async Task SendEmailQueuedAsync(MailRequestMessage mailMessage)
|
||||
{
|
||||
var request = new MailRequest
|
||||
{
|
||||
MailMessage = mailMessage,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
};
|
||||
await _mailQueue.Writer.WriteAsync(request);
|
||||
}
|
||||
|
||||
public void Send(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// 支持多个接收人,用逗号分隔
|
||||
var mailMessage = new MailRequestMessage
|
||||
{
|
||||
To = email,
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
IsHtml = isHtml
|
||||
};
|
||||
_mailQueue.Writer.TryWrite(new MailRequest
|
||||
{
|
||||
MailMessage = mailMessage,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接发送邮件(同步发送,不加入队列)
|
||||
/// </summary>
|
||||
public async Task SendAsync(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
await SendEmailCoreAsync(email, subject, body, isHtml);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步发送邮件(加入队列)
|
||||
/// </summary>
|
||||
public async Task SendQueuedAsync(string email, string subject, string body, bool isHtml = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(email))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var mailMessage = new MailRequestMessage
|
||||
{
|
||||
To = email,
|
||||
Subject = subject,
|
||||
Body = body,
|
||||
IsHtml = isHtml
|
||||
};
|
||||
|
||||
await SendEmailQueuedAsync(mailMessage);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user