using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
namespace TakeoutSaaS.Domain.Tenants.Repositories;
///
/// 租户账单仓储。
///
public interface ITenantBillingRepository
{
///
/// 查询账单列表,按状态与时间范围筛选。
///
/// 租户 ID。
/// 账单状态。
/// 开始时间(UTC)。
/// 结束时间(UTC)。
/// 取消标记。
/// 账单集合。
Task> SearchAsync(
long tenantId,
TenantBillingStatus? status,
DateTime? from,
DateTime? to,
CancellationToken cancellationToken = default);
///
/// 按 ID 获取账单。
///
/// 租户 ID。
/// 账单 ID。
/// 取消标记。
/// 账单实体或 null。
Task FindByIdAsync(long tenantId, long billingId, CancellationToken cancellationToken = default);
///
/// 按账单编号获取账单。
///
/// 租户 ID。
/// 账单编号。
/// 取消标记。
/// 账单实体或 null。
Task FindByStatementNoAsync(long tenantId, string statementNo, CancellationToken cancellationToken = default);
///
/// 按账单编号获取账单(不限租户,管理员端使用)。
///
/// 账单编号。
/// 取消标记。
/// 账单实体或 null。
Task GetByStatementNoAsync(string statementNo, CancellationToken cancellationToken = default);
///
/// 判断是否已存在指定周期开始时间的未取消账单(用于自动续费幂等)。
///
/// 租户 ID。
/// 账单周期开始时间(UTC)。
/// 取消标记。
/// 存在返回 true,否则 false。
Task ExistsNotCancelledByPeriodStartAsync(
long tenantId,
DateTime periodStart,
CancellationToken cancellationToken = default);
///
/// 获取逾期账单列表(已过到期日且未支付)。
///
/// 取消标记。
/// 逾期账单集合。
Task> GetOverdueBillingsAsync(CancellationToken cancellationToken = default);
///
/// 获取即将到期的账单列表(未来 N 天内到期且未支付)。
///
/// 提前天数。
/// 取消标记。
/// 即将到期的账单集合。
Task> GetBillingsDueSoonAsync(int daysAhead, CancellationToken cancellationToken = default);
///
/// 按租户 ID 获取账单列表。
///
/// 租户 ID。
/// 取消标记。
/// 账单集合。
Task> GetByTenantIdAsync(long tenantId, CancellationToken cancellationToken = default);
///
/// 按 ID 列表批量获取账单(管理员端/批量操作场景)。
///
/// 账单 ID 列表。
/// 取消标记。
/// 账单实体列表。
Task> GetByIdsAsync(
IReadOnlyCollection billingIds,
CancellationToken cancellationToken = default);
///
/// 新增账单。
///
/// 账单实体。
/// 取消标记。
/// 异步任务。
Task AddAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
///
/// 更新账单。
///
/// 账单实体。
/// 取消标记。
/// 异步任务。
Task UpdateAsync(TenantBillingStatement bill, CancellationToken cancellationToken = default);
///
/// 保存变更。
///
/// 取消标记。
/// 异步任务。
Task SaveChangesAsync(CancellationToken cancellationToken = default);
///
/// 管理员端分页查询账单列表(跨租户)。
///
/// 租户 ID 筛选(可选)。
/// 账单状态筛选(可选)。
/// 开始时间(UTC,可选)。
/// 结束时间(UTC,可选)。
/// 最小应付金额筛选(包含,可选)。
/// 最大应付金额筛选(包含,可选)。
/// 关键词搜索(账单号或租户名)。
/// 页码(从 1 开始)。
/// 页大小。
/// 取消标记。
/// 账单集合与总数。
Task<(IReadOnlyList Items, int Total)> SearchPagedAsync(
long? tenantId,
TenantBillingStatus? status,
DateTime? from,
DateTime? to,
decimal? minAmount,
decimal? maxAmount,
string? keyword,
int pageNumber,
int pageSize,
CancellationToken cancellationToken = default);
///
/// 获取账单统计数据(用于报表与仪表盘)。
///
/// 租户 ID(可选,管理员可查询所有租户)。
/// 统计开始时间(UTC)。
/// 统计结束时间(UTC)。
/// 分组方式(Day/Week/Month)。
/// 取消标记。
/// 统计结果。
Task GetStatisticsAsync(
long? tenantId,
DateTime startDate,
DateTime endDate,
string groupBy,
CancellationToken cancellationToken = default);
///
/// 按 ID 获取账单(不限租户,管理员端使用)。
///
/// 账单 ID。
/// 取消标记。
/// 账单实体或 null。
Task FindByIdAsync(long billingId, CancellationToken cancellationToken = default);
}
///
/// 账单统计结果。
///
public sealed record TenantBillingStatistics
{
///
/// 总账单金额(统计区间内)。
///
public decimal TotalAmount { get; init; }
///
/// 已支付金额(统计区间内)。
///
public decimal PaidAmount { get; init; }
///
/// 未支付金额(统计区间内)。
///
public decimal UnpaidAmount { get; init; }
///
/// 逾期金额(统计区间内)。
///
public decimal OverdueAmount { get; init; }
///
/// 总账单数量(统计区间内)。
///
public int TotalCount { get; init; }
///
/// 已支付账单数量(统计区间内)。
///
public int PaidCount { get; init; }
///
/// 未支付账单数量(统计区间内)。
///
public int UnpaidCount { get; init; }
///
/// 逾期账单数量(统计区间内)。
///
public int OverdueCount { get; init; }
///
/// 趋势数据(按 groupBy 聚合)。
///
public IReadOnlyList TrendData { get; init; } = [];
}
///
/// 账单趋势统计点。
///
public sealed record TenantBillingTrendDataPoint
{
///
/// 分组时间点(Day/Week/Month 的代表日期,UTC)。
///
public DateTime Period { get; init; }
///
/// 账单数量。
///
public int Count { get; init; }
///
/// 总金额。
///
public decimal TotalAmount { get; init; }
///
/// 已支付金额。
///
public decimal PaidAmount { get; init; }
}