fix: 修复发货重试后台服务不执行的问题

- 使用 IServiceScopeFactory 解决 scoped 服务依赖问题
- IWechatService 是 scoped 服务,不能直接注入到 singleton 后台服务
- 每次处理订单时创建新的 scope 来解析 IWechatService
- 将检查间隔从 60 秒改为 30 秒
- 优化 RedisService.GetKeysAsync 使用实时连接状态检查
This commit is contained in:
zpc 2026-02-10 17:22:38 +08:00
parent d4c15c8feb
commit 8991118f8d
2 changed files with 37 additions and 11 deletions

View File

@ -1,6 +1,7 @@
using System.Text.Json; using System.Text.Json;
using HoneyBox.Core.Interfaces; using HoneyBox.Core.Interfaces;
using HoneyBox.Model.Models.Auth; using HoneyBox.Model.Models.Auth;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -12,22 +13,22 @@ namespace HoneyBox.Core.Services;
/// </summary> /// </summary>
public class ShippingRetryBackgroundService : BackgroundService public class ShippingRetryBackgroundService : BackgroundService
{ {
private readonly IServiceScopeFactory _scopeFactory;
private readonly IRedisService _redisService; private readonly IRedisService _redisService;
private readonly IWechatService _wechatService;
private readonly ILogger<ShippingRetryBackgroundService> _logger; private readonly ILogger<ShippingRetryBackgroundService> _logger;
private const int MaxRetryCount = 10; // 最大重试次数 private const int MaxRetryCount = 10; // 最大重试次数
private const int RetryIntervalSeconds = 30; // 重试间隔(秒) private const int RetryIntervalSeconds = 30; // 重试间隔(秒)
private const int CheckIntervalSeconds = 60; // 检查间隔(秒) private const int CheckIntervalSeconds = 30; // 检查间隔(秒)
private const string RetryKeyPattern = "post_order:*"; private const string RetryKeyPattern = "post_order:*";
public ShippingRetryBackgroundService( public ShippingRetryBackgroundService(
IServiceScopeFactory scopeFactory,
IRedisService redisService, IRedisService redisService,
IWechatService wechatService,
ILogger<ShippingRetryBackgroundService> logger) ILogger<ShippingRetryBackgroundService> logger)
{ {
_scopeFactory = scopeFactory;
_redisService = redisService; _redisService = redisService;
_wechatService = wechatService;
_logger = logger; _logger = logger;
} }
@ -35,19 +36,32 @@ public class ShippingRetryBackgroundService : BackgroundService
{ {
_logger.LogInformation("发货重试后台服务已启动"); _logger.LogInformation("发货重试后台服务已启动");
// 启动时等待一小段时间,让其他服务初始化完成
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
try try
{ {
await ProcessFailedShippingOrdersAsync(stoppingToken); await ProcessFailedShippingOrdersAsync(stoppingToken);
} }
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "处理发货重试时发生异常"); _logger.LogError(ex, "处理发货重试时发生异常");
} }
// 等待下一次检查 try
await Task.Delay(TimeSpan.FromSeconds(CheckIntervalSeconds), stoppingToken); {
await Task.Delay(TimeSpan.FromSeconds(CheckIntervalSeconds), stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
} }
_logger.LogInformation("发货重试后台服务已停止"); _logger.LogInformation("发货重试后台服务已停止");
@ -63,11 +77,12 @@ public class ShippingRetryBackgroundService : BackgroundService
return; return;
} }
_logger.LogDebug("发现 {Count} 个待重试的发货订单", failedOrderKeys.Count()); var keysList = failedOrderKeys.ToList();
_logger.LogInformation("发现 {Count} 个待重试的发货订单", keysList.Count);
var nowTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var nowTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
foreach (var key in failedOrderKeys) foreach (var key in keysList)
{ {
if (stoppingToken.IsCancellationRequested) if (stoppingToken.IsCancellationRequested)
break; break;
@ -83,6 +98,7 @@ public class ShippingRetryBackgroundService : BackgroundService
} }
} }
private async Task ProcessSingleOrderAsync(string key, long nowTime) private async Task ProcessSingleOrderAsync(string key, long nowTime)
{ {
// 获取订单数据 // 获取订单数据
@ -125,17 +141,20 @@ public class ShippingRetryBackgroundService : BackgroundService
return; return;
} }
// 尝试重新发货 // 尝试重新发货 - 使用新的 scope 来解析 IWechatService
_logger.LogInformation("开始重试发货: OrderNo={OrderNo}, RetryCount={RetryCount}", _logger.LogInformation("开始重试发货: OrderNo={OrderNo}, RetryCount={RetryCount}",
orderData.order_num, orderData.retry_count + 1); orderData.order_num, orderData.retry_count + 1);
using var scope = _scopeFactory.CreateScope();
var wechatService = scope.ServiceProvider.GetRequiredService<IWechatService>();
var request = new WechatShippingRequest var request = new WechatShippingRequest
{ {
OpenId = orderData.openid, OpenId = orderData.openid,
OrderNo = orderData.order_num OrderNo = orderData.order_num
}; };
var result = await _wechatService.UploadShippingInfoAsync(request); var result = await wechatService.UploadShippingInfoAsync(request);
if (result.Success) if (result.Success)
{ {

View File

@ -107,7 +107,11 @@ public class RedisService : IRedisService, IDisposable
public async Task<IEnumerable<string>> GetKeysAsync(string pattern) public async Task<IEnumerable<string>> GetKeysAsync(string pattern)
{ {
if (_connection == null || !_isConnected) if (_connection == null)
return Enumerable.Empty<string>();
// 检查当前连接状态(而不是构造时的状态)
if (!_connection.IsConnected)
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
var keys = new List<string>(); var keys = new List<string>();
@ -116,6 +120,9 @@ public class RedisService : IRedisService, IDisposable
foreach (var endpoint in endpoints) foreach (var endpoint in endpoints)
{ {
var server = _connection.GetServer(endpoint); var server = _connection.GetServer(endpoint);
if (server == null || !server.IsConnected)
continue;
await foreach (var key in server.KeysAsync(pattern: pattern)) await foreach (var key in server.KeysAsync(pattern: pattern))
{ {
keys.Add(key.ToString()); keys.Add(key.ToString());