ASP.NET Core 3.1多用户定时检测WooCommerce新订单并邮件通知方案
多用户WooCommerce订单检测后台服务实现
1. 基础实体定义
首先需要两个核心实体类,对应MySQL数据库表:
AppUser:存储用户信息和WooCommerce站点配置TrackedOrder:记录已发送通知的订单,避免重复通知
// AppUser.cs - 对应数据库用户表 public class AppUser { public int Id { get; set; } public string Email { get; set; } public string WooCommerceSiteUrl { get; set; } public string WooConsumerKey { get; set; } public string WooConsumerSecret { get; set; } // 记录最后一次检查订单的时间,用于筛选新订单 public DateTime LastOrderCheckTime { get; set; } = DateTime.UtcNow.AddHours(-1); } // TrackedOrder.cs - 对应已通知订单记录表 public class TrackedOrder { public int Id { get; set; } public int AppUserId { get; set; } // WooCommerce订单ID public long WooOrderId { get; set; } public DateTime NotificationSentTime { get; set; } = DateTime.UtcNow; public AppUser AppUser { get; set; } }
在DbContext中注册实体并配置约束:
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<AppUser> AppUsers { get; set; } public DbSet<TrackedOrder> TrackedOrders { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置复合唯一键:同一用户的同一订单只能记录一次 modelBuilder.Entity<TrackedOrder>() .HasIndex(t => new { t.AppUserId, t.WooOrderId }) .IsUnique(); } }
2. 后台服务实现
创建继承BackgroundService的后台服务,这是ASP.NET Core实现定时任务的标准方式:
public class WooOrderCheckService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger<WooOrderCheckService> _logger; // 随机检测间隔(5-10分钟),避免所有用户同时请求API private readonly TimeSpan _checkInterval = TimeSpan.FromMinutes(new Random().Next(5, 11)); public WooOrderCheckService(IServiceScopeFactory scopeFactory, ILogger<WooOrderCheckService> logger) { _scopeFactory = scopeFactory; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.LogInformation("WooCommerce订单检测服务启动,间隔{Minutes}分钟", _checkInterval.TotalMinutes); // 循环执行直到服务停止 while (!stoppingToken.IsCancellationRequested) { try { // 创建作用域:后台服务是单例,DbContext是Scoped,必须通过作用域获取 using var scope = _scopeFactory.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); var emailSender = scope.ServiceProvider.GetRequiredService<IEmailSender>(); // 获取所有需要检测的用户(可根据实际情况过滤,比如仅启用状态的用户) var users = await dbContext.AppUsers.ToListAsync(stoppingToken); // 并行处理用户任务(用户量大时可通过SemaphoreSlim限制并发数) var tasks = users.Select(user => ProcessUserOrdersAsync(user, dbContext, emailSender, stoppingToken)); await Task.WhenAll(tasks); } catch (Exception ex) { _logger.LogError(ex, "订单检测任务执行失败"); } // 等待下一轮检测 await Task.Delay(_checkInterval, stoppingToken); } _logger.LogInformation("WooCommerce订单检测服务已停止"); } private async Task ProcessUserOrdersAsync(AppUser user, AppDbContext dbContext, IEmailSender emailSender, CancellationToken stoppingToken) { try { _logger.LogInformation("开始检测用户{UserId}的WooCommerce订单", user.Id); // 1. 为当前用户创建WooCommerce客户端实例 var wooClient = new WooCommerce.NET.WooCommerceAPI( user.WooCommerceSiteUrl, user.WooConsumerKey, user.WooConsumerSecret); // 2. 获取最后一次检查时间之后的订单 var filter = new Dictionary<string, string> { {"after", user.LastOrderCheckTime.ToString("yyyy-MM-ddTHH:mm:ss")}, {"status", "processing,pending,on-hold,completed"} // 按需筛选订单状态 }; var orders = await wooClient.Order.GetAll(filter, stoppingToken); if (orders == null || !orders.Any()) { _logger.LogInformation("用户{UserId}无新订单", user.Id); user.LastOrderCheckTime = DateTime.UtcNow; await dbContext.SaveChangesAsync(stoppingToken); return; } // 3. 筛选未通知过的订单 var trackedOrderIds = await dbContext.TrackedOrders .Where(t => t.AppUserId == user.Id) .Select(t => t.WooOrderId) .ToListAsync(stoppingToken); var newOrders = orders.Where(o => !trackedOrderIds.Contains(o.id.Value)).ToList(); if (!newOrders.Any()) { _logger.LogInformation("用户{UserId}的订单已全部通知", user.Id); user.LastOrderCheckTime = DateTime.UtcNow; await dbContext.SaveChangesAsync(stoppingToken); return; } // 4. 发送邮件通知并记录已通知订单 foreach (var order in newOrders) { var subject = $"您有新的WooCommerce订单 #{order.id}"; var message = $"订单编号:{order.id}\n客户姓名:{order.billing.first_name} {order.billing.last_name}\n订单金额:{order.total}\n订单状态:{order.status}"; await emailSender.SendEmailAsync(user.Email, subject, message); dbContext.TrackedOrders.Add(new TrackedOrder { AppUserId = user.Id, WooOrderId = order.id.Value }); } // 5. 更新最后检查时间并保存所有变更 user.LastOrderCheckTime = DateTime.UtcNow; await dbContext.SaveChangesAsync(stoppingToken); _logger.LogInformation("用户{UserId}完成新订单通知,共{Count}条", user.Id, newOrders.Count); } catch (Exception ex) { _logger.LogError(ex, "处理用户{UserId}订单时出错", user.Id); } } }
3. 依赖注入配置
在Startup.cs的ConfigureServices方法中注册服务:
public void ConfigureServices(IServiceCollection services) { // 注册MySQL DbContext services.AddDbContext<AppDbContext>(options => options.UseMySql(Configuration.GetConnectionString("DefaultConnection"), new MySqlServerVersion(new Version(8, 0, 26)))); // 根据你的MySQL版本调整 // 注册后台服务 services.AddHostedService<WooOrderCheckService>(); // 注册邮件服务 services.AddTransient<IEmailSender, EmailSender>(); // 其他服务注册... }
4. 邮件服务实现示例
实现IEmailSender接口,使用SMTP发送邮件:
public class EmailSender : IEmailSender { private readonly IConfiguration _configuration; public EmailSender(IConfiguration configuration) { _configuration = configuration; } public async Task SendEmailAsync(string email, string subject, string message) { using var client = new SmtpClient(_configuration["Smtp:Host"], int.Parse(_configuration["Smtp:Port"])) { Credentials = new NetworkCredential(_configuration["Smtp:Username"], _configuration["Smtp:Password"]), EnableSsl = bool.Parse(_configuration["Smtp:EnableSsl"]) }; var mailMessage = new MailMessage( from: _configuration["Smtp:FromEmail"], to: email, subject: subject, body: message); await client.SendMailAsync(mailMessage); } }
在appsettings.json中配置SMTP信息:
{ "Smtp": { "Host": "smtp.example.com", "Port": "587", "Username": "your-email@example.com", "Password": "your-password", "EnableSsl": "true", "FromEmail": "notifications@your-app.com" } }
内容的提问来源于stack exchange,提问作者Stephan Miller
相关产品推荐
相关产品推荐

