feat: implement tenant member stored card module
All checks were successful
Build and Deploy TenantApi + SkuWorker / build-and-deploy (push) Successful in 2m24s

This commit is contained in:
2026-03-04 09:14:57 +08:00
parent d96ca4971a
commit 2970134200
35 changed files with 12805 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
using TakeoutSaaS.Domain.Membership.Enums;
using TakeoutSaaS.Shared.Abstractions.Entities;
namespace TakeoutSaaS.Domain.Membership.Entities;
/// <summary>
/// 会员储值卡充值方案。
/// </summary>
public sealed class MemberStoredCardPlan : MultiTenantEntityBase
{
/// <summary>
/// 门店标识。
/// </summary>
public long StoreId { get; set; }
/// <summary>
/// 充值金额。
/// </summary>
public decimal RechargeAmount { get; set; }
/// <summary>
/// 赠送金额。
/// </summary>
public decimal GiftAmount { get; set; }
/// <summary>
/// 排序值(越小越靠前)。
/// </summary>
public int SortOrder { get; set; } = 100;
/// <summary>
/// 启用状态。
/// </summary>
public MemberStoredCardPlanStatus Status { get; set; } = MemberStoredCardPlanStatus.Enabled;
}

View File

@@ -0,0 +1,70 @@
using TakeoutSaaS.Domain.Payments.Enums;
using TakeoutSaaS.Shared.Abstractions.Entities;
namespace TakeoutSaaS.Domain.Membership.Entities;
/// <summary>
/// 会员储值卡充值记录。
/// </summary>
public sealed class MemberStoredCardRechargeRecord : MultiTenantEntityBase
{
/// <summary>
/// 门店标识。
/// </summary>
public long StoreId { get; set; }
/// <summary>
/// 会员标识。
/// </summary>
public long MemberId { get; set; }
/// <summary>
/// 充值方案标识(可空,表示非方案充值)。
/// </summary>
public long? PlanId { get; set; }
/// <summary>
/// 充值单号。
/// </summary>
public string RecordNo { get; set; } = string.Empty;
/// <summary>
/// 会员名称快照。
/// </summary>
public string MemberName { get; set; } = string.Empty;
/// <summary>
/// 会员手机号快照(脱敏)。
/// </summary>
public string MemberMobileMasked { get; set; } = string.Empty;
/// <summary>
/// 充值金额。
/// </summary>
public decimal RechargeAmount { get; set; }
/// <summary>
/// 赠送金额。
/// </summary>
public decimal GiftAmount { get; set; }
/// <summary>
/// 到账金额(充值+赠送)。
/// </summary>
public decimal ArrivedAmount { get; set; }
/// <summary>
/// 支付方式。
/// </summary>
public PaymentMethod PaymentMethod { get; set; } = PaymentMethod.Unknown;
/// <summary>
/// 备注。
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 充值时间UTC
/// </summary>
public DateTime RechargedAt { get; set; }
}

View File

@@ -0,0 +1,17 @@
namespace TakeoutSaaS.Domain.Membership.Enums;
/// <summary>
/// 储值卡方案状态。
/// </summary>
public enum MemberStoredCardPlanStatus
{
/// <summary>
/// 已停用。
/// </summary>
Disabled = 0,
/// <summary>
/// 已启用。
/// </summary>
Enabled = 1
}

View File

@@ -0,0 +1,141 @@
using TakeoutSaaS.Domain.Membership.Entities;
using TakeoutSaaS.Domain.Membership.Enums;
namespace TakeoutSaaS.Domain.Membership.Repositories;
/// <summary>
/// 储值卡模块仓储契约。
/// </summary>
public interface IStoredCardRepository
{
/// <summary>
/// 查询门店下全部储值方案。
/// </summary>
Task<IReadOnlyList<MemberStoredCardPlan>> GetPlansByStoreAsync(
long tenantId,
long storeId,
CancellationToken cancellationToken = default);
/// <summary>
/// 按标识查询储值方案。
/// </summary>
Task<MemberStoredCardPlan?> FindPlanByIdAsync(
long tenantId,
long storeId,
long planId,
CancellationToken cancellationToken = default);
/// <summary>
/// 新增储值方案。
/// </summary>
Task AddPlanAsync(MemberStoredCardPlan entity, CancellationToken cancellationToken = default);
/// <summary>
/// 更新储值方案。
/// </summary>
Task UpdatePlanAsync(MemberStoredCardPlan entity, CancellationToken cancellationToken = default);
/// <summary>
/// 删除储值方案。
/// </summary>
Task DeletePlanAsync(MemberStoredCardPlan entity, CancellationToken cancellationToken = default);
/// <summary>
/// 按方案聚合充值次数与金额。
/// </summary>
Task<Dictionary<long, MemberStoredCardPlanAggregateSnapshot>> GetPlanAggregatesAsync(
long tenantId,
long storeId,
IReadOnlyCollection<long> planIds,
CancellationToken cancellationToken = default);
/// <summary>
/// 获取储值方案页统计快照。
/// </summary>
Task<MemberStoredCardPlanStatsSnapshot> GetPlanStatsAsync(
long tenantId,
long storeId,
DateTime nowUtc,
CancellationToken cancellationToken = default);
/// <summary>
/// 查询充值记录分页。
/// </summary>
Task<(IReadOnlyList<MemberStoredCardRechargeRecord> Items, int TotalCount)> SearchRechargeRecordsAsync(
long tenantId,
long storeId,
DateTime? startUtc,
DateTime? endUtc,
string? keyword,
int page,
int pageSize,
CancellationToken cancellationToken = default);
/// <summary>
/// 查询充值记录导出数据。
/// </summary>
Task<IReadOnlyList<MemberStoredCardRechargeRecord>> ListRechargeRecordsForExportAsync(
long tenantId,
long storeId,
DateTime? startUtc,
DateTime? endUtc,
string? keyword,
CancellationToken cancellationToken = default);
/// <summary>
/// 新增充值记录。
/// </summary>
Task AddRechargeRecordAsync(MemberStoredCardRechargeRecord entity, CancellationToken cancellationToken = default);
/// <summary>
/// 持久化变更。
/// </summary>
Task SaveChangesAsync(CancellationToken cancellationToken = default);
}
/// <summary>
/// 储值方案聚合快照。
/// </summary>
public sealed record MemberStoredCardPlanAggregateSnapshot
{
/// <summary>
/// 方案标识。
/// </summary>
public required long PlanId { get; init; }
/// <summary>
/// 充值次数。
/// </summary>
public int RechargeCount { get; init; }
/// <summary>
/// 充值总额。
/// </summary>
public decimal TotalRechargeAmount { get; init; }
}
/// <summary>
/// 储值卡统计快照。
/// </summary>
public sealed record MemberStoredCardPlanStatsSnapshot
{
/// <summary>
/// 储值总额。
/// </summary>
public decimal TotalRechargeAmount { get; init; }
/// <summary>
/// 赠金总额。
/// </summary>
public decimal TotalGiftAmount { get; init; }
/// <summary>
/// 本月充值额。
/// </summary>
public decimal CurrentMonthRechargeAmount { get; init; }
/// <summary>
/// 储值用户数。
/// </summary>
public int RechargeMemberCount { get; init; }
}