新增定时任务 (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>
110 lines
4.5 KiB
C#
110 lines
4.5 KiB
C#
using TakeoutSaaS.Domain.Tenants.Entities;
|
||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||
|
||
namespace TakeoutSaaS.Domain.Tenants.Repositories;
|
||
|
||
/// <summary>
|
||
/// 租户账单仓储。
|
||
/// </summary>
|
||
public interface ITenantBillingRepository
|
||
{
|
||
/// <summary>
|
||
/// 查询账单列表,按状态与时间范围筛选。
|
||
/// </summary>
|
||
/// <param name="tenantId">租户 ID。</param>
|
||
/// <param name="status">账单状态。</param>
|
||
/// <param name="from">开始时间(UTC)。</param>
|
||
/// <param name="to">结束时间(UTC)。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>账单集合。</returns>
|
||
Task<IReadOnlyList<TenantBillingStatement>> SearchAsync(
|
||
long tenantId,
|
||
TenantBillingStatus? status,
|
||
DateTime? from,
|
||
DateTime? to,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 按 ID 获取账单。
|
||
/// </summary>
|
||
/// <param name="tenantId">租户 ID。</param>
|
||
/// <param name="billingId">账单 ID。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>账单实体或 null。</returns>
|
||
Task<TenantBillingStatement?> FindByIdAsync(long tenantId, long billingId, CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 按账单编号获取账单。
|
||
/// </summary>
|
||
/// <param name="tenantId">租户 ID。</param>
|
||
/// <param name="statementNo">账单编号。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>账单实体或 null。</returns>
|
||
Task<TenantBillingStatement?> FindByStatementNoAsync(long tenantId, string statementNo, CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 判断是否已存在指定周期开始时间的未取消账单(用于自动续费幂等)。
|
||
/// </summary>
|
||
/// <param name="tenantId">租户 ID。</param>
|
||
/// <param name="periodStart">账单周期开始时间(UTC)。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>存在返回 true,否则 false。</returns>
|
||
Task<bool> ExistsNotCancelledByPeriodStartAsync(
|
||
long tenantId,
|
||
DateTime periodStart,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 新增账单。
|
||
/// </summary>
|
||
/// <param name="bill">账单实体。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>异步任务。</returns>
|
||
Task AddAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 更新账单。
|
||
/// </summary>
|
||
/// <param name="bill">账单实体。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>异步任务。</returns>
|
||
Task UpdateAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 保存变更。
|
||
/// </summary>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>异步任务。</returns>
|
||
Task SaveChangesAsync(CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 管理员端分页查询账单列表(跨租户)。
|
||
/// </summary>
|
||
/// <param name="tenantId">租户 ID 筛选(可选)。</param>
|
||
/// <param name="status">账单状态筛选(可选)。</param>
|
||
/// <param name="from">开始时间(UTC,可选)。</param>
|
||
/// <param name="to">结束时间(UTC,可选)。</param>
|
||
/// <param name="keyword">关键词搜索(账单号或租户名)。</param>
|
||
/// <param name="pageNumber">页码(从 1 开始)。</param>
|
||
/// <param name="pageSize">页大小。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>账单集合与总数。</returns>
|
||
Task<(IReadOnlyList<TenantBillingStatement> Items, int Total)> SearchPagedAsync(
|
||
long? tenantId,
|
||
TenantBillingStatus? status,
|
||
DateTime? from,
|
||
DateTime? to,
|
||
string? keyword,
|
||
int pageNumber,
|
||
int pageSize,
|
||
CancellationToken cancellationToken = default);
|
||
|
||
/// <summary>
|
||
/// 按 ID 获取账单(不限租户,管理员端使用)。
|
||
/// </summary>
|
||
/// <param name="billingId">账单 ID。</param>
|
||
/// <param name="cancellationToken">取消标记。</param>
|
||
/// <returns>账单实体或 null。</returns>
|
||
Task<TenantBillingStatement?> FindByIdAsync(long billingId, CancellationToken cancellationToken = default);
|
||
}
|