Files
TakeoutSaaS.TenantApi/src/Application/TakeoutSaaS.Application/App/Subscriptions/Commands/ProcessAutoRenewalCommand.cs
MSuMshk 98f49ea7ad feat: 实现订阅自动化定时任务系统
新增定时任务 (Scheduler Module):
- SubscriptionAutoRenewalJob: 自动续费账单生成
- SubscriptionRenewalReminderJob: 续费提醒发送 (7/3/1天)
- SubscriptionExpiryCheckJob: 到期检查与宽限期处理

新增 Command/Handler:
- ProcessAutoRenewalCommand: 处理自动续费逻辑
- ProcessRenewalRemindersCommand: 处理续费提醒逻辑
- ProcessSubscriptionExpiryCommand: 处理订阅到期逻辑

配置项 (SubscriptionAutomationOptions):
- AutoRenewalDaysBeforeExpiry: 到期前N天生成续费账单
- ReminderDaysBeforeExpiry: 提醒天数数组
- GracePeriodDays: 宽限期天数
- 各任务执行小时配置

Repository 增强:
- ISubscriptionRepository: 新增自动化查询方法
- ITenantBillingRepository: 新增账单创建方法
- ITenantNotificationRepository: 新增通知创建方法

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 21:06:01 +08:00

34 lines
914 B
C#

using MediatR;
using System.ComponentModel.DataAnnotations;
namespace TakeoutSaaS.Application.App.Subscriptions.Commands;
/// <summary>
/// 处理自动续费:为开启自动续费且即将到期的订阅生成续费账单。
/// </summary>
public sealed record ProcessAutoRenewalCommand : IRequest<ProcessAutoRenewalResult>
{
/// <summary>
/// 到期前 N 天生成续费账单。
/// </summary>
[Range(0, 365, ErrorMessage = "续费提前天数必须在 0~365 之间")]
public int RenewalDaysBeforeExpiry { get; init; } = 3;
}
/// <summary>
/// 自动续费处理结果。
/// </summary>
public sealed record ProcessAutoRenewalResult
{
/// <summary>
/// 扫描到的候选订阅数量。
/// </summary>
public int CandidateCount { get; init; }
/// <summary>
/// 实际创建的账单数量。
/// </summary>
public int CreatedBillCount { get; init; }
}