You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET Core 6.0中实现用户30天未活跃自动邮件通知

实现30天未活跃用户自动邮件通知方案(ASP.NET Core 6 + SQL Server)

1. 数据库准备

  • 确保用户表(如Users)包含LastActiveTime字段,用于记录用户最后一次活跃的时间。若没有该字段,执行以下SQL添加:
    ALTER TABLE Users ADD LastActiveTime DATETIME2 NOT NULL DEFAULT GETUTCDATE();
    
  • 在用户进行活跃操作(如登录、提交内容)时,更新该字段:
    // 在业务逻辑中更新用户最后活跃时间
    var user = await _dbContext.Users.FindAsync(userId);
    user.LastActiveTime = DateTime.UtcNow;
    await _dbContext.SaveChangesAsync();
    

2. 实现后台定时任务

ASP.NET Core 6内置IHostedService接口,可用于创建长期运行的后台任务,这里用它定时筛选未活跃用户并发送邮件。

2.1 创建后台服务类

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;

public class InactiveUserEmailService : BackgroundService
{
    private readonly IServiceScopeFactory _scopeFactory;
    private readonly ILogger<InactiveUserEmailService> _logger;
    private readonly TimeSpan _checkInterval = TimeSpan.FromDays(1); // 每日执行一次检查

    public InactiveUserEmailService(IServiceScopeFactory scopeFactory, ILogger<InactiveUserEmailService> logger)
    {
        _scopeFactory = scopeFactory;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("未活跃用户邮件通知服务已启动");

        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _scopeFactory.CreateScope();
                var emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>();
                var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();

                // 筛选30天未活跃且未收到过通知的用户(用UTC时间避免时区偏差)
                var inactiveUsers = await dbContext.Users
                    .Where(u => DateTime.UtcNow - u.LastActiveTime >= TimeSpan.FromDays(30)
                                && !u.HasReceivedInactiveNotification)
                    .ToListAsync(stoppingToken);

                foreach (var user in inactiveUsers)
                {
                    await emailSender.SendInactiveUserEmail(user.Email, user.UserName);
                    // 标记为已发送,避免重复通知
                    user.HasReceivedInactiveNotification = true;
                }

                await dbContext.SaveChangesAsync(stoppingToken);
                _logger.LogInformation($"本次处理了{inactiveUsers.Count}位未活跃用户");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "处理未活跃用户邮件通知时发生错误");
            }

            // 等待下一次执行间隔
            await Task.Delay(_checkInterval, stoppingToken);
        }

        _logger.LogInformation("未活跃用户邮件通知服务已停止");
    }
}

2.2 注册后台服务

在Program.cs中注册该服务:

builder.Services.AddHostedService<InactiveUserEmailService>();

3. 邮件发送服务实现

使用MailKit(替代已过时的SmtpClient)实现邮件发送功能。

3.1 安装依赖包

Install-Package MailKit
Install-Package MimeKit

3.2 实现邮件发送接口

using MailKit.Net.Smtp;
using MimeKit;

public interface IEmailSender
{
    Task SendInactiveUserEmail(string toEmail, string userName);
}

public class EmailSender : IEmailSender
{
    private readonly IConfiguration _configuration;

    public EmailSender(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public async Task SendInactiveUserEmail(string toEmail, string userName)
    {
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("平台名称", _configuration["Smtp:FromEmail"]));
        message.To.Add(new MailboxAddress(userName, toEmail));
        message.Subject = "好久不见!欢迎回到平台";

        message.Body = new TextPart("html")
        {
            Text = $@"
                <h3>您好,{userName}!</h3>
                <p>我们注意到您已经30天未登录平台,非常想念您。</p>
                <p><a href='{_configuration["App:LoginUrl"]}'>点击这里立即登录</a></p>
                <p>若此邮件非您本人操作,请忽略即可。</p>
            "
        };

        using var client = new SmtpClient();
        await client.ConnectAsync(
            _configuration["Smtp:Server"], 
            int.Parse(_configuration["Smtp:Port"]), 
            bool.Parse(_configuration["Smtp:UseSsl"]));
        await client.AuthenticateAsync(_configuration["Smtp:Username"], _configuration["Smtp:Password"]);
        await client.SendAsync(message);
        await client.DisconnectAsync(true);
    }
}

3.3 注册邮件服务与配置

在Program.cs中注册服务:

builder.Services.AddScoped<IEmailSender, EmailSender>();

在appsettings.json中添加SMTP配置:

{
  "Smtp": {
    "Server": "smtp.example.com",
    "Port": "587",
    "UseSsl": "true",
    "Username": "your-email@example.com",
    "Password": "your-email-password",
    "FromEmail": "your-email@example.com"
  },
  "App": {
    "LoginUrl": "https://your-app-domain.com/login"
  }
}

4. 可选优化方案

  • 避免重复通知:若需定期重复发送(如每30天一次),可将HasReceivedInactiveNotification字段替换为LastNotificationSentTime,判断当前时间与该字段的间隔是否大于30天。
  • 大用户量处理:若用户数量庞大,使用分页查询(Skip+Take)分批处理,避免内存占用过高。
  • 灵活调度替代:如需更灵活的任务调度(如指定每周一执行、手动触发),可使用Hangfire替代内置Hosted Service,配置SQL Server存储后注册周期性任务即可。
  • 错误重试机制:对邮件发送失败的用户添加重试逻辑,或记录到专门的错误表中后续手动处理。

内容的提问来源于stack exchange,提问作者Fillo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 22:11:05