refactor: 订阅任务按租户上下文执行

This commit is contained in:
root
2026-01-29 14:51:21 +00:00
parent f9053356c2
commit 1622c38043
17 changed files with 177 additions and 218 deletions

View File

@@ -2,7 +2,9 @@ using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Application.App.Subscriptions.Commands;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Module.Scheduler.Options;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Module.Scheduler.Jobs;
@@ -11,6 +13,8 @@ namespace TakeoutSaaS.Module.Scheduler.Jobs;
/// </summary>
public sealed class SubscriptionAutoRenewalJob(
IMediator mediator,
ITenantRepository tenantRepository,
ITenantContextAccessor tenantContextAccessor,
IOptionsMonitor<SubscriptionAutomationOptions> optionsMonitor,
ILogger<SubscriptionAutoRenewalJob> logger)
{
@@ -19,18 +23,48 @@ public sealed class SubscriptionAutoRenewalJob(
/// </summary>
public async Task ExecuteAsync()
{
// 1. 读取配置并执行自动续费
// 1. 读取配置
var options = optionsMonitor.CurrentValue;
var result = await mediator.Send(new ProcessAutoRenewalCommand
{
RenewalDaysBeforeExpiry = options.AutoRenewalDaysBeforeExpiry
});
// 2. 记录执行结果
// 2. (空行后) 获取需要处理的租户列表(排除系统租户)
var tenants = await tenantRepository.SearchAsync(null, null, CancellationToken.None);
var targets = tenants.Where(x => x.Id > 0).ToList();
// 3. (空行后) 按租户逐个执行自动续费
var candidateCount = 0;
var createdBillCount = 0;
var previousContext = tenantContextAccessor.Current;
try
{
foreach (var tenant in targets)
{
tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "scheduler");
try
{
var result = await mediator.Send(new ProcessAutoRenewalCommand
{
RenewalDaysBeforeExpiry = options.AutoRenewalDaysBeforeExpiry
});
candidateCount += result.CandidateCount;
createdBillCount += result.CreatedBillCount;
}
catch (Exception ex)
{
logger.LogError(ex, "定时任务:自动续费执行失败 TenantId={TenantId}", tenant.Id);
}
}
}
finally
{
tenantContextAccessor.Current = previousContext;
}
// 4. (空行后) 记录执行结果
logger.LogInformation(
"定时任务:自动续费处理完成,候选 {CandidateCount},创建账单 {CreatedBillCount}",
result.CandidateCount,
result.CreatedBillCount);
"定时任务:自动续费处理完成,处理租户 {TenantCount}候选 {CandidateCount},创建账单 {CreatedBillCount}",
targets.Count,
candidateCount,
createdBillCount);
}
}

View File

@@ -2,7 +2,9 @@ using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Application.App.Subscriptions.Commands;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Module.Scheduler.Options;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Module.Scheduler.Jobs;
@@ -11,6 +13,8 @@ namespace TakeoutSaaS.Module.Scheduler.Jobs;
/// </summary>
public sealed class SubscriptionExpiryCheckJob(
IMediator mediator,
ITenantRepository tenantRepository,
ITenantContextAccessor tenantContextAccessor,
IOptionsMonitor<SubscriptionAutomationOptions> optionsMonitor,
ILogger<SubscriptionExpiryCheckJob> logger)
{
@@ -19,17 +23,48 @@ public sealed class SubscriptionExpiryCheckJob(
/// </summary>
public async Task ExecuteAsync()
{
// 1. 读取配置并执行到期处理
// 1. 读取配置
var options = optionsMonitor.CurrentValue;
var result = await mediator.Send(new ProcessSubscriptionExpiryCommand
{
GracePeriodDays = options.GracePeriodDays
});
// 2. 记录执行结果
// 2. (空行后) 获取需要处理的租户列表(排除系统租户)
var tenants = await tenantRepository.SearchAsync(null, null, CancellationToken.None);
var targets = tenants.Where(x => x.Id > 0).ToList();
// 3. (空行后) 按租户逐个执行到期处理
var enteredGracePeriodCount = 0;
var suspendedCount = 0;
var previousContext = tenantContextAccessor.Current;
try
{
foreach (var tenant in targets)
{
tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "scheduler");
try
{
var result = await mediator.Send(new ProcessSubscriptionExpiryCommand
{
GracePeriodDays = options.GracePeriodDays
});
enteredGracePeriodCount += result.EnteredGracePeriodCount;
suspendedCount += result.SuspendedCount;
}
catch (Exception ex)
{
logger.LogError(ex, "定时任务:订阅到期检查执行失败 TenantId={TenantId}", tenant.Id);
}
}
}
finally
{
tenantContextAccessor.Current = previousContext;
}
// 4. (空行后) 记录执行结果
logger.LogInformation(
"定时任务:订阅到期检查完成,进入宽限期 {EnteredGracePeriodCount},暂停 {SuspendedCount}",
result.EnteredGracePeriodCount,
result.SuspendedCount);
"定时任务:订阅到期检查完成,处理租户 {TenantCount}进入宽限期 {EnteredGracePeriodCount},暂停 {SuspendedCount}",
targets.Count,
enteredGracePeriodCount,
suspendedCount);
}
}

View File

@@ -2,7 +2,9 @@ using MediatR;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Application.App.Subscriptions.Commands;
using TakeoutSaaS.Domain.Tenants.Repositories;
using TakeoutSaaS.Module.Scheduler.Options;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Module.Scheduler.Jobs;
@@ -11,6 +13,8 @@ namespace TakeoutSaaS.Module.Scheduler.Jobs;
/// </summary>
public sealed class SubscriptionRenewalReminderJob(
IMediator mediator,
ITenantRepository tenantRepository,
ITenantContextAccessor tenantContextAccessor,
IOptionsMonitor<SubscriptionAutomationOptions> optionsMonitor,
ILogger<SubscriptionRenewalReminderJob> logger)
{
@@ -19,17 +23,48 @@ public sealed class SubscriptionRenewalReminderJob(
/// </summary>
public async Task ExecuteAsync()
{
// 1. 读取配置并执行续费提醒
// 1. 读取配置
var options = optionsMonitor.CurrentValue;
var result = await mediator.Send(new ProcessRenewalRemindersCommand
{
ReminderDaysBeforeExpiry = options.ReminderDaysBeforeExpiry
});
// 2. 记录执行结果
// 2. (空行后) 获取需要处理的租户列表(排除系统租户)
var tenants = await tenantRepository.SearchAsync(null, null, CancellationToken.None);
var targets = tenants.Where(x => x.Id > 0).ToList();
// 3. (空行后) 按租户逐个执行续费提醒
var candidateCount = 0;
var createdReminderCount = 0;
var previousContext = tenantContextAccessor.Current;
try
{
foreach (var tenant in targets)
{
tenantContextAccessor.Current = new TenantContext(tenant.Id, tenant.Code, "scheduler");
try
{
var result = await mediator.Send(new ProcessRenewalRemindersCommand
{
ReminderDaysBeforeExpiry = options.ReminderDaysBeforeExpiry
});
candidateCount += result.CandidateCount;
createdReminderCount += result.CreatedReminderCount;
}
catch (Exception ex)
{
logger.LogError(ex, "定时任务:续费提醒执行失败 TenantId={TenantId}", tenant.Id);
}
}
}
finally
{
tenantContextAccessor.Current = previousContext;
}
// 4. (空行后) 记录执行结果
logger.LogInformation(
"定时任务:续费提醒处理完成,候选 {CandidateCount},创建 {CreatedReminderCount}",
result.CandidateCount,
result.CreatedReminderCount);
"定时任务:续费提醒处理完成,处理租户 {TenantCount}候选 {CandidateCount},创建 {CreatedReminderCount}",
targets.Count,
candidateCount,
createdReminderCount);
}
}